1*cea17583Sbket /* $OpenBSD: parse.y,v 1.6 2018/07/20 17:55:09 bket Exp $ */ 253293e44Sflorian 353293e44Sflorian /* 453293e44Sflorian * Copyright (c) 2018 Florian Obser <florian@openbsd.org> 553293e44Sflorian * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 653293e44Sflorian * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 753293e44Sflorian * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 853293e44Sflorian * Copyright (c) 2001 Markus Friedl. All rights reserved. 953293e44Sflorian * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 1053293e44Sflorian * Copyright (c) 2001 Theo de Raadt. All rights reserved. 1153293e44Sflorian * 1253293e44Sflorian * Permission to use, copy, modify, and distribute this software for any 1353293e44Sflorian * purpose with or without fee is hereby granted, provided that the above 1453293e44Sflorian * copyright notice and this permission notice appear in all copies. 1553293e44Sflorian * 1653293e44Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1753293e44Sflorian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1853293e44Sflorian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1953293e44Sflorian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 2053293e44Sflorian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 2153293e44Sflorian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 2253293e44Sflorian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 2353293e44Sflorian */ 2453293e44Sflorian 2553293e44Sflorian %{ 2653293e44Sflorian #include <sys/types.h> 2753293e44Sflorian #include <sys/queue.h> 2853293e44Sflorian #include <sys/socket.h> 2953293e44Sflorian #include <sys/stat.h> 3053293e44Sflorian 3153293e44Sflorian #include <netinet/in.h> 3253293e44Sflorian #include <net/if.h> 3353293e44Sflorian 3453293e44Sflorian #include <arpa/inet.h> 3553293e44Sflorian 3653293e44Sflorian #include <ctype.h> 3753293e44Sflorian #include <err.h> 3853293e44Sflorian #include <errno.h> 3953293e44Sflorian #include <event.h> 4053293e44Sflorian #include <ifaddrs.h> 4153293e44Sflorian #include <imsg.h> 4253293e44Sflorian #include <limits.h> 4353293e44Sflorian #include <stdarg.h> 4453293e44Sflorian #include <stdio.h> 4553293e44Sflorian #include <string.h> 4653293e44Sflorian #include <syslog.h> 4753293e44Sflorian #include <unistd.h> 4853293e44Sflorian 4953293e44Sflorian #include "log.h" 5053293e44Sflorian #include "rad.h" 5153293e44Sflorian #include "frontend.h" 5253293e44Sflorian 5353293e44Sflorian TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 5453293e44Sflorian static struct file { 5553293e44Sflorian TAILQ_ENTRY(file) entry; 5653293e44Sflorian FILE *stream; 5753293e44Sflorian char *name; 5853293e44Sflorian size_t ungetpos; 5953293e44Sflorian size_t ungetsize; 6053293e44Sflorian u_char *ungetbuf; 6153293e44Sflorian int eof_reached; 6253293e44Sflorian int lineno; 6353293e44Sflorian int errors; 6453293e44Sflorian } *file, *topfile; 6553293e44Sflorian struct file *pushfile(const char *, int); 6653293e44Sflorian int popfile(void); 6753293e44Sflorian int check_file_secrecy(int, const char *); 6853293e44Sflorian int yyparse(void); 6953293e44Sflorian int yylex(void); 7053293e44Sflorian int yyerror(const char *, ...) 7153293e44Sflorian __attribute__((__format__ (printf, 1, 2))) 7253293e44Sflorian __attribute__((__nonnull__ (1))); 7353293e44Sflorian int kw_cmp(const void *, const void *); 7453293e44Sflorian int lookup(char *); 7553293e44Sflorian int igetc(void); 7653293e44Sflorian int lgetc(int); 7753293e44Sflorian void lungetc(int); 7853293e44Sflorian int findeol(void); 7953293e44Sflorian 8053293e44Sflorian TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 8153293e44Sflorian struct sym { 8253293e44Sflorian TAILQ_ENTRY(sym) entry; 8353293e44Sflorian int used; 8453293e44Sflorian int persist; 8553293e44Sflorian char *nam; 8653293e44Sflorian char *val; 8753293e44Sflorian }; 8853293e44Sflorian 8953293e44Sflorian int symset(const char *, const char *, int); 9053293e44Sflorian char *symget(const char *); 9153293e44Sflorian 9253293e44Sflorian void clear_config(struct rad_conf *xconf); 9353293e44Sflorian 9453293e44Sflorian static struct rad_conf *conf; 9553293e44Sflorian static struct ra_options_conf *ra_options; 9653293e44Sflorian static int errors; 9753293e44Sflorian 9853293e44Sflorian static struct ra_iface_conf *ra_iface_conf; 9953293e44Sflorian static struct ra_prefix_conf *ra_prefix_conf; 10053293e44Sflorian 10153293e44Sflorian struct ra_prefix_conf *conf_get_ra_prefix(struct in6_addr*, int); 10253293e44Sflorian struct ra_iface_conf *conf_get_ra_iface(char *); 10353293e44Sflorian 10453293e44Sflorian typedef struct { 10553293e44Sflorian union { 10653293e44Sflorian int64_t number; 10753293e44Sflorian char *string; 10853293e44Sflorian } v; 10953293e44Sflorian int lineno; 11053293e44Sflorian } YYSTYPE; 11153293e44Sflorian 11253293e44Sflorian %} 11353293e44Sflorian 11453293e44Sflorian %token RA_IFACE YES NO INCLUDE ERROR 11553293e44Sflorian %token DEFAULT ROUTER HOP LIMIT MANAGED ADDRESS 11653293e44Sflorian %token CONFIGURATION OTHER LIFETIME REACHABLE TIME RETRANS TIMER 11753293e44Sflorian %token AUTO PREFIX VALID PREFERRED LIFETIME ONLINK AUTONOMOUS 118*cea17583Sbket %token ADDRESS_CONFIGURATION DNS NAMESERVER SEARCH MTU 11953293e44Sflorian 12053293e44Sflorian %token <v.string> STRING 12153293e44Sflorian %token <v.number> NUMBER 12253293e44Sflorian %type <v.number> yesno 12353293e44Sflorian %type <v.string> string 12453293e44Sflorian 12553293e44Sflorian %% 12653293e44Sflorian 12753293e44Sflorian grammar : /* empty */ 12853293e44Sflorian | grammar include '\n' 12953293e44Sflorian | grammar '\n' 13053293e44Sflorian | grammar { ra_options = &conf->ra_options; } conf_main '\n' 13153293e44Sflorian | grammar varset '\n' 13253293e44Sflorian | grammar ra_iface '\n' 13353293e44Sflorian | grammar error '\n' { file->errors++; } 13453293e44Sflorian ; 13553293e44Sflorian 13653293e44Sflorian include : INCLUDE STRING { 13753293e44Sflorian struct file *nfile; 13853293e44Sflorian 1393a682461Sflorian if ((nfile = pushfile($2, 0)) == NULL) { 14053293e44Sflorian yyerror("failed to include file %s", $2); 14153293e44Sflorian free($2); 14253293e44Sflorian YYERROR; 14353293e44Sflorian } 14453293e44Sflorian free($2); 14553293e44Sflorian 14653293e44Sflorian file = nfile; 14753293e44Sflorian lungetc('\n'); 14853293e44Sflorian } 14953293e44Sflorian ; 15053293e44Sflorian 15153293e44Sflorian string : string STRING { 15253293e44Sflorian if (asprintf(&$$, "%s %s", $1, $2) == -1) { 15353293e44Sflorian free($1); 15453293e44Sflorian free($2); 15553293e44Sflorian yyerror("string: asprintf"); 15653293e44Sflorian YYERROR; 15753293e44Sflorian } 15853293e44Sflorian free($1); 15953293e44Sflorian free($2); 16053293e44Sflorian } 16153293e44Sflorian | STRING 16253293e44Sflorian ; 16353293e44Sflorian 16453293e44Sflorian yesno : YES { $$ = 1; } 16553293e44Sflorian | NO { $$ = 0; } 16653293e44Sflorian ; 16753293e44Sflorian 16853293e44Sflorian varset : STRING '=' string { 16953293e44Sflorian char *s = $1; 17053293e44Sflorian if (cmd_opts & OPT_VERBOSE) 17153293e44Sflorian printf("%s = \"%s\"\n", $1, $3); 17253293e44Sflorian while (*s++) { 17353293e44Sflorian if (isspace((unsigned char)*s)) { 17453293e44Sflorian yyerror("macro name cannot contain " 17553293e44Sflorian "whitespace"); 17653293e44Sflorian YYERROR; 17753293e44Sflorian } 17853293e44Sflorian } 17953293e44Sflorian if (symset($1, $3, 0) == -1) 18053293e44Sflorian fatal("cannot store variable"); 18153293e44Sflorian free($1); 18253293e44Sflorian free($3); 18353293e44Sflorian } 18453293e44Sflorian ; 18553293e44Sflorian 18653293e44Sflorian conf_main : ra_opt_block { 18753293e44Sflorian ra_options = &conf->ra_options; 18853293e44Sflorian } 18953293e44Sflorian ; 19053293e44Sflorian 19153293e44Sflorian ra_opt_block : DEFAULT ROUTER yesno { 19253293e44Sflorian ra_options->dfr = $3; 19353293e44Sflorian } 19453293e44Sflorian | HOP LIMIT NUMBER { 19553293e44Sflorian ra_options->cur_hl = $3; 19653293e44Sflorian } 19753293e44Sflorian | MANAGED ADDRESS CONFIGURATION yesno { 19853293e44Sflorian ra_options->m_flag = $4; 19953293e44Sflorian } 20053293e44Sflorian | OTHER CONFIGURATION yesno { 20153293e44Sflorian ra_options->o_flag = $3; 20253293e44Sflorian } 20353293e44Sflorian | ROUTER LIFETIME NUMBER { 20453293e44Sflorian ra_options->router_lifetime = $3; 20553293e44Sflorian } 20653293e44Sflorian | REACHABLE TIME NUMBER { 20753293e44Sflorian ra_options->reachable_time = $3; 20853293e44Sflorian } 20953293e44Sflorian | RETRANS TIMER NUMBER { 21053293e44Sflorian ra_options->retrans_timer = $3; 21153293e44Sflorian } 212*cea17583Sbket | MTU NUMBER { 213*cea17583Sbket ra_options->mtu = $2; 214*cea17583Sbket } 21553293e44Sflorian ; 21653293e44Sflorian 21753293e44Sflorian optnl : '\n' optnl /* zero or more newlines */ 21853293e44Sflorian | /*empty*/ 21953293e44Sflorian ; 22053293e44Sflorian 22153293e44Sflorian nl : '\n' optnl /* one or more newlines */ 22253293e44Sflorian ; 22353293e44Sflorian 22453293e44Sflorian ra_iface : RA_IFACE STRING { 22553293e44Sflorian ra_iface_conf = conf_get_ra_iface($2); 22653293e44Sflorian /* set auto prefix defaults */ 22753293e44Sflorian ra_iface_conf->autoprefix = conf_get_ra_prefix(NULL, 0); 22853293e44Sflorian ra_options = &ra_iface_conf->ra_options; 22953293e44Sflorian } ra_iface_block { 23053293e44Sflorian ra_iface_conf = NULL; 23153293e44Sflorian } 23253293e44Sflorian ; 23353293e44Sflorian 23453293e44Sflorian ra_iface_block : '{' optnl ra_ifaceopts_l '}' 23553293e44Sflorian | '{' optnl '}' 23653293e44Sflorian | /* empty */ 23753293e44Sflorian ; 23853293e44Sflorian 23953293e44Sflorian ra_ifaceopts_l : ra_ifaceopts_l ra_ifaceoptsl nl 24053293e44Sflorian | ra_ifaceoptsl optnl 24153293e44Sflorian ; 24253293e44Sflorian 24353293e44Sflorian ra_ifaceoptsl : NO AUTO PREFIX { 24453293e44Sflorian free(ra_iface_conf->autoprefix); 24553293e44Sflorian ra_iface_conf->autoprefix = NULL; 24653293e44Sflorian } 24753293e44Sflorian | AUTO PREFIX { 24853293e44Sflorian if (ra_iface_conf->autoprefix == NULL) 24953293e44Sflorian ra_iface_conf->autoprefix = 250cddacf98Sflorian conf_get_ra_prefix(NULL, 0); 251cddacf98Sflorian ra_prefix_conf = ra_iface_conf->autoprefix; 25253293e44Sflorian } ra_prefix_block { 25353293e44Sflorian ra_prefix_conf = NULL; 25453293e44Sflorian } 25553293e44Sflorian | PREFIX STRING { 25653293e44Sflorian struct in6_addr addr; 25753293e44Sflorian int prefixlen; 25853293e44Sflorian 25953293e44Sflorian memset(&addr, 0, sizeof(addr)); 26053293e44Sflorian prefixlen = inet_net_pton(AF_INET6, $2, &addr, 26153293e44Sflorian sizeof(addr)); 26253293e44Sflorian if (prefixlen == -1) { 26353293e44Sflorian yyerror("error parsing prefix"); 26453293e44Sflorian free($2); 26553293e44Sflorian YYERROR; 26653293e44Sflorian } 26753293e44Sflorian mask_prefix(&addr, prefixlen); 26853293e44Sflorian ra_prefix_conf = conf_get_ra_prefix(&addr, prefixlen); 26953293e44Sflorian } ra_prefix_block { 27053293e44Sflorian ra_prefix_conf = NULL; 27153293e44Sflorian } 2724c40b7e8Sflorian | DNS dns_block 27353293e44Sflorian | ra_opt_block 27453293e44Sflorian ; 27553293e44Sflorian 27653293e44Sflorian ra_prefix_block : '{' optnl ra_prefixopts_l '}' 27753293e44Sflorian | '{' optnl '}' 27853293e44Sflorian | /* empty */ 27953293e44Sflorian ; 28053293e44Sflorian 28153293e44Sflorian ra_prefixopts_l : ra_prefixopts_l ra_prefixoptsl nl 28253293e44Sflorian | ra_prefixoptsl optnl 28353293e44Sflorian ; 28453293e44Sflorian 28553293e44Sflorian ra_prefixoptsl : VALID LIFETIME NUMBER { 28653293e44Sflorian ra_prefix_conf->vltime = $3; 28753293e44Sflorian } 28853293e44Sflorian | PREFERRED LIFETIME NUMBER { 28953293e44Sflorian ra_prefix_conf->pltime = $3; 29053293e44Sflorian } 29153293e44Sflorian | ONLINK yesno { 29253293e44Sflorian ra_prefix_conf->lflag = $2; 29353293e44Sflorian } 29453293e44Sflorian | AUTONOMOUS ADDRESS_CONFIGURATION yesno { 29553293e44Sflorian ra_prefix_conf->aflag = $3; 29653293e44Sflorian } 29753293e44Sflorian ; 2984c40b7e8Sflorian dns_block : '{' optnl dnsopts_l '}' 2994c40b7e8Sflorian | '{' optnl '}' 3004c40b7e8Sflorian | /* empty */ 3014c40b7e8Sflorian ; 30253293e44Sflorian 3034c40b7e8Sflorian dnsopts_l : dnsopts_l dnsoptsl nl 3044c40b7e8Sflorian | dnsoptsl optnl 3054c40b7e8Sflorian ; 3064c40b7e8Sflorian 3074c40b7e8Sflorian dnsoptsl : LIFETIME NUMBER { 3084c40b7e8Sflorian ra_iface_conf->rdns_lifetime = $2; 3094c40b7e8Sflorian } 3108fe22fd3Sflorian | NAMESERVER nserver_block 3114c40b7e8Sflorian | SEARCH search_block 3124c40b7e8Sflorian ; 3138fe22fd3Sflorian nserver_block : '{' optnl nserveropts_l '}' 3144c40b7e8Sflorian | '{' optnl '}' 3158fe22fd3Sflorian | nserveroptsl 3164c40b7e8Sflorian | /* empty */ 3174c40b7e8Sflorian ; 3184c40b7e8Sflorian 3198fe22fd3Sflorian nserveropts_l : nserveropts_l nserveroptsl optnl 3208fe22fd3Sflorian | nserveroptsl optnl 3214c40b7e8Sflorian ; 3224c40b7e8Sflorian 3238fe22fd3Sflorian nserveroptsl : STRING { 3244c40b7e8Sflorian struct ra_rdnss_conf *ra_rdnss_conf; 3254c40b7e8Sflorian struct in6_addr addr; 3264c40b7e8Sflorian 3274c40b7e8Sflorian memset(&addr, 0, sizeof(addr)); 3284c40b7e8Sflorian if (inet_pton(AF_INET6, $1, &addr) 3294c40b7e8Sflorian != 1) { 3308fe22fd3Sflorian yyerror("error parsing nameserver address %s", 3314c40b7e8Sflorian $1); 3324c40b7e8Sflorian free($1); 3334c40b7e8Sflorian YYERROR; 3344c40b7e8Sflorian } 3354c40b7e8Sflorian if ((ra_rdnss_conf = calloc(1, sizeof(*ra_rdnss_conf))) 3364c40b7e8Sflorian == NULL) 3374c40b7e8Sflorian err(1, "%s", __func__); 3384c40b7e8Sflorian memcpy(&ra_rdnss_conf->rdnss, &addr, sizeof(addr)); 3394c40b7e8Sflorian SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_rdnss_list, 3404c40b7e8Sflorian ra_rdnss_conf, entry); 3414c40b7e8Sflorian ra_iface_conf->rdnss_count++; 3424c40b7e8Sflorian } 3434c40b7e8Sflorian ; 3444c40b7e8Sflorian search_block : '{' optnl searchopts_l '}' 3454c40b7e8Sflorian | '{' optnl '}' 3464c40b7e8Sflorian | searchoptsl 3474c40b7e8Sflorian | /* empty */ 3484c40b7e8Sflorian ; 3494c40b7e8Sflorian 3504c40b7e8Sflorian searchopts_l : searchopts_l searchoptsl optnl 3514c40b7e8Sflorian | searchoptsl optnl 3524c40b7e8Sflorian ; 3534c40b7e8Sflorian 3544c40b7e8Sflorian searchoptsl : STRING { 3554c40b7e8Sflorian struct ra_dnssl_conf *ra_dnssl_conf; 3564c40b7e8Sflorian size_t len; 3574c40b7e8Sflorian 3584c40b7e8Sflorian if ((ra_dnssl_conf = calloc(1, 3594c40b7e8Sflorian sizeof(*ra_dnssl_conf))) == NULL) 3604c40b7e8Sflorian err(1, "%s", __func__); 3614c40b7e8Sflorian 3624c40b7e8Sflorian if ((len = strlcpy(ra_dnssl_conf->search, $1, 3634c40b7e8Sflorian sizeof(ra_dnssl_conf->search))) > 3644c40b7e8Sflorian sizeof(ra_dnssl_conf->search)) { 3654c40b7e8Sflorian yyerror("search string too long"); 3664c40b7e8Sflorian free($1); 3674c40b7e8Sflorian YYERROR; 3684c40b7e8Sflorian } 3694c40b7e8Sflorian if (ra_dnssl_conf->search[len] != '.') { 3704c40b7e8Sflorian if ((len = strlcat(ra_dnssl_conf->search, ".", 3714c40b7e8Sflorian sizeof(ra_dnssl_conf->search))) > 3724c40b7e8Sflorian sizeof(ra_dnssl_conf->search)) { 3734c40b7e8Sflorian yyerror("search string too long"); 3744c40b7e8Sflorian free($1); 3754c40b7e8Sflorian YYERROR; 3764c40b7e8Sflorian } 3774c40b7e8Sflorian } 3784c40b7e8Sflorian SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_dnssl_list, 3794c40b7e8Sflorian ra_dnssl_conf, entry); 3804c40b7e8Sflorian ra_iface_conf->dnssl_len += len + 1; 3814c40b7e8Sflorian } 3824c40b7e8Sflorian ; 38353293e44Sflorian %% 38453293e44Sflorian 38553293e44Sflorian struct keywords { 38653293e44Sflorian const char *k_name; 38753293e44Sflorian int k_val; 38853293e44Sflorian }; 38953293e44Sflorian 39053293e44Sflorian int 39153293e44Sflorian yyerror(const char *fmt, ...) 39253293e44Sflorian { 39353293e44Sflorian va_list ap; 39453293e44Sflorian char *msg; 39553293e44Sflorian 39653293e44Sflorian file->errors++; 39753293e44Sflorian va_start(ap, fmt); 39853293e44Sflorian if (vasprintf(&msg, fmt, ap) == -1) 39953293e44Sflorian fatalx("yyerror vasprintf"); 40053293e44Sflorian va_end(ap); 40153293e44Sflorian logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 40253293e44Sflorian free(msg); 40353293e44Sflorian return (0); 40453293e44Sflorian } 40553293e44Sflorian 40653293e44Sflorian int 40753293e44Sflorian kw_cmp(const void *k, const void *e) 40853293e44Sflorian { 40953293e44Sflorian return (strcmp(k, ((const struct keywords *)e)->k_name)); 41053293e44Sflorian } 41153293e44Sflorian 41253293e44Sflorian int 41353293e44Sflorian lookup(char *s) 41453293e44Sflorian { 41553293e44Sflorian /* This has to be sorted always. */ 41653293e44Sflorian static const struct keywords keywords[] = { 41753293e44Sflorian {"address", ADDRESS}, 41853293e44Sflorian {"address-configuration", ADDRESS_CONFIGURATION}, 41953293e44Sflorian {"auto", AUTO}, 42053293e44Sflorian {"autonomous", AUTONOMOUS}, 42153293e44Sflorian {"configuration", CONFIGURATION}, 42253293e44Sflorian {"default", DEFAULT}, 4234c40b7e8Sflorian {"dns", DNS}, 42453293e44Sflorian {"hop", HOP}, 42553293e44Sflorian {"include", INCLUDE}, 42653293e44Sflorian {"interface", RA_IFACE}, 42753293e44Sflorian {"lifetime", LIFETIME}, 42853293e44Sflorian {"limit", LIMIT}, 42953293e44Sflorian {"managed", MANAGED}, 430*cea17583Sbket {"mtu", MTU}, 4318fe22fd3Sflorian {"nameserver", NAMESERVER}, 43253293e44Sflorian {"no", NO}, 43353293e44Sflorian {"on-link", ONLINK}, 43453293e44Sflorian {"other", OTHER}, 43553293e44Sflorian {"preferred", PREFERRED}, 43653293e44Sflorian {"prefix", PREFIX}, 43753293e44Sflorian {"reachable", REACHABLE}, 43853293e44Sflorian {"retrans", RETRANS}, 43953293e44Sflorian {"router", ROUTER}, 4404c40b7e8Sflorian {"search", SEARCH}, 44153293e44Sflorian {"time", TIME}, 44253293e44Sflorian {"timer", TIMER}, 44353293e44Sflorian {"valid", VALID}, 44453293e44Sflorian {"yes", YES}, 44553293e44Sflorian }; 44653293e44Sflorian const struct keywords *p; 44753293e44Sflorian 44853293e44Sflorian p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 44953293e44Sflorian sizeof(keywords[0]), kw_cmp); 45053293e44Sflorian 45153293e44Sflorian if (p) 45253293e44Sflorian return (p->k_val); 45353293e44Sflorian else 45453293e44Sflorian return (STRING); 45553293e44Sflorian } 45653293e44Sflorian 45753293e44Sflorian #define START_EXPAND 1 45853293e44Sflorian #define DONE_EXPAND 2 45953293e44Sflorian 46053293e44Sflorian static int expanding; 46153293e44Sflorian 46253293e44Sflorian int 46353293e44Sflorian igetc(void) 46453293e44Sflorian { 46553293e44Sflorian int c; 46653293e44Sflorian 46753293e44Sflorian while (1) { 46853293e44Sflorian if (file->ungetpos > 0) 46953293e44Sflorian c = file->ungetbuf[--file->ungetpos]; 47053293e44Sflorian else 47153293e44Sflorian c = getc(file->stream); 47253293e44Sflorian 47353293e44Sflorian if (c == START_EXPAND) 47453293e44Sflorian expanding = 1; 47553293e44Sflorian else if (c == DONE_EXPAND) 47653293e44Sflorian expanding = 0; 47753293e44Sflorian else 47853293e44Sflorian break; 47953293e44Sflorian } 48053293e44Sflorian return (c); 48153293e44Sflorian } 48253293e44Sflorian 48353293e44Sflorian int 48453293e44Sflorian lgetc(int quotec) 48553293e44Sflorian { 48653293e44Sflorian int c, next; 48753293e44Sflorian 48853293e44Sflorian if (quotec) { 48953293e44Sflorian if ((c = igetc()) == EOF) { 49053293e44Sflorian yyerror("reached end of file while parsing " 49153293e44Sflorian "quoted string"); 49253293e44Sflorian if (file == topfile || popfile() == EOF) 49353293e44Sflorian return (EOF); 49453293e44Sflorian return (quotec); 49553293e44Sflorian } 49653293e44Sflorian return (c); 49753293e44Sflorian } 49853293e44Sflorian 49953293e44Sflorian while ((c = igetc()) == '\\') { 50053293e44Sflorian next = igetc(); 50153293e44Sflorian if (next != '\n') { 50253293e44Sflorian c = next; 50353293e44Sflorian break; 50453293e44Sflorian } 50553293e44Sflorian yylval.lineno = file->lineno; 50653293e44Sflorian file->lineno++; 50753293e44Sflorian } 50853293e44Sflorian 50953293e44Sflorian if (c == EOF) { 51053293e44Sflorian /* 51153293e44Sflorian * Fake EOL when hit EOF for the first time. This gets line 51253293e44Sflorian * count right if last line in included file is syntactically 51353293e44Sflorian * invalid and has no newline. 51453293e44Sflorian */ 51553293e44Sflorian if (file->eof_reached == 0) { 51653293e44Sflorian file->eof_reached = 1; 51753293e44Sflorian return ('\n'); 51853293e44Sflorian } 51953293e44Sflorian while (c == EOF) { 52053293e44Sflorian if (file == topfile || popfile() == EOF) 52153293e44Sflorian return (EOF); 52253293e44Sflorian c = igetc(); 52353293e44Sflorian } 52453293e44Sflorian } 52553293e44Sflorian return (c); 52653293e44Sflorian } 52753293e44Sflorian 52853293e44Sflorian void 52953293e44Sflorian lungetc(int c) 53053293e44Sflorian { 53153293e44Sflorian if (c == EOF) 53253293e44Sflorian return; 53353293e44Sflorian 53453293e44Sflorian if (file->ungetpos >= file->ungetsize) { 53553293e44Sflorian void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 53653293e44Sflorian if (p == NULL) 53753293e44Sflorian err(1, "lungetc"); 53853293e44Sflorian file->ungetbuf = p; 53953293e44Sflorian file->ungetsize *= 2; 54053293e44Sflorian } 54153293e44Sflorian file->ungetbuf[file->ungetpos++] = c; 54253293e44Sflorian } 54353293e44Sflorian 54453293e44Sflorian int 54553293e44Sflorian findeol(void) 54653293e44Sflorian { 54753293e44Sflorian int c; 54853293e44Sflorian 54953293e44Sflorian /* Skip to either EOF or the first real EOL. */ 55053293e44Sflorian while (1) { 55153293e44Sflorian c = lgetc(0); 55253293e44Sflorian if (c == '\n') { 55353293e44Sflorian file->lineno++; 55453293e44Sflorian break; 55553293e44Sflorian } 55653293e44Sflorian if (c == EOF) 55753293e44Sflorian break; 55853293e44Sflorian } 55953293e44Sflorian return (ERROR); 56053293e44Sflorian } 56153293e44Sflorian 56253293e44Sflorian int 56353293e44Sflorian yylex(void) 56453293e44Sflorian { 56553293e44Sflorian unsigned char buf[8096]; 56653293e44Sflorian unsigned char *p, *val; 56753293e44Sflorian int quotec, next, c; 56853293e44Sflorian int token; 56953293e44Sflorian 57053293e44Sflorian top: 57153293e44Sflorian p = buf; 57253293e44Sflorian while ((c = lgetc(0)) == ' ' || c == '\t') 57353293e44Sflorian ; /* nothing */ 57453293e44Sflorian 57553293e44Sflorian yylval.lineno = file->lineno; 57653293e44Sflorian if (c == '#') 57753293e44Sflorian while ((c = lgetc(0)) != '\n' && c != EOF) 57853293e44Sflorian ; /* nothing */ 57953293e44Sflorian if (c == '$' && !expanding) { 58053293e44Sflorian while (1) { 58153293e44Sflorian if ((c = lgetc(0)) == EOF) 58253293e44Sflorian return (0); 58353293e44Sflorian 58453293e44Sflorian if (p + 1 >= buf + sizeof(buf) - 1) { 58553293e44Sflorian yyerror("string too long"); 58653293e44Sflorian return (findeol()); 58753293e44Sflorian } 58853293e44Sflorian if (isalnum(c) || c == '_') { 58953293e44Sflorian *p++ = c; 59053293e44Sflorian continue; 59153293e44Sflorian } 59253293e44Sflorian *p = '\0'; 59353293e44Sflorian lungetc(c); 59453293e44Sflorian break; 59553293e44Sflorian } 59653293e44Sflorian val = symget(buf); 59753293e44Sflorian if (val == NULL) { 59853293e44Sflorian yyerror("macro '%s' not defined", buf); 59953293e44Sflorian return (findeol()); 60053293e44Sflorian } 60153293e44Sflorian p = val + strlen(val) - 1; 60253293e44Sflorian lungetc(DONE_EXPAND); 60353293e44Sflorian while (p >= val) { 60453293e44Sflorian lungetc(*p); 60553293e44Sflorian p--; 60653293e44Sflorian } 60753293e44Sflorian lungetc(START_EXPAND); 60853293e44Sflorian goto top; 60953293e44Sflorian } 61053293e44Sflorian 61153293e44Sflorian switch (c) { 61253293e44Sflorian case '\'': 61353293e44Sflorian case '"': 61453293e44Sflorian quotec = c; 61553293e44Sflorian while (1) { 61653293e44Sflorian if ((c = lgetc(quotec)) == EOF) 61753293e44Sflorian return (0); 61853293e44Sflorian if (c == '\n') { 61953293e44Sflorian file->lineno++; 62053293e44Sflorian continue; 62153293e44Sflorian } else if (c == '\\') { 62253293e44Sflorian if ((next = lgetc(quotec)) == EOF) 62353293e44Sflorian return (0); 62453293e44Sflorian if (next == quotec || c == ' ' || c == '\t') 62553293e44Sflorian c = next; 62653293e44Sflorian else if (next == '\n') { 62753293e44Sflorian file->lineno++; 62853293e44Sflorian continue; 62953293e44Sflorian } else 63053293e44Sflorian lungetc(next); 63153293e44Sflorian } else if (c == quotec) { 63253293e44Sflorian *p = '\0'; 63353293e44Sflorian break; 63453293e44Sflorian } else if (c == '\0') { 63553293e44Sflorian yyerror("syntax error"); 63653293e44Sflorian return (findeol()); 63753293e44Sflorian } 63853293e44Sflorian if (p + 1 >= buf + sizeof(buf) - 1) { 63953293e44Sflorian yyerror("string too long"); 64053293e44Sflorian return (findeol()); 64153293e44Sflorian } 64253293e44Sflorian *p++ = c; 64353293e44Sflorian } 64453293e44Sflorian yylval.v.string = strdup(buf); 64553293e44Sflorian if (yylval.v.string == NULL) 64653293e44Sflorian err(1, "yylex: strdup"); 64753293e44Sflorian return (STRING); 64853293e44Sflorian } 64953293e44Sflorian 65053293e44Sflorian #define allowed_to_end_number(x) \ 65153293e44Sflorian (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 65253293e44Sflorian 65353293e44Sflorian if (c == '-' || isdigit(c)) { 65453293e44Sflorian do { 65553293e44Sflorian *p++ = c; 65653293e44Sflorian if ((unsigned)(p-buf) >= sizeof(buf)) { 65753293e44Sflorian yyerror("string too long"); 65853293e44Sflorian return (findeol()); 65953293e44Sflorian } 66053293e44Sflorian } while ((c = lgetc(0)) != EOF && isdigit(c)); 66153293e44Sflorian lungetc(c); 66253293e44Sflorian if (p == buf + 1 && buf[0] == '-') 66353293e44Sflorian goto nodigits; 66453293e44Sflorian if (c == EOF || allowed_to_end_number(c)) { 66553293e44Sflorian const char *errstr = NULL; 66653293e44Sflorian 66753293e44Sflorian *p = '\0'; 66853293e44Sflorian yylval.v.number = strtonum(buf, LLONG_MIN, 66953293e44Sflorian LLONG_MAX, &errstr); 67053293e44Sflorian if (errstr) { 67153293e44Sflorian yyerror("\"%s\" invalid number: %s", 67253293e44Sflorian buf, errstr); 67353293e44Sflorian return (findeol()); 67453293e44Sflorian } 67553293e44Sflorian return (NUMBER); 67653293e44Sflorian } else { 67753293e44Sflorian nodigits: 67853293e44Sflorian while (p > buf + 1) 67953293e44Sflorian lungetc(*--p); 68053293e44Sflorian c = *--p; 68153293e44Sflorian if (c == '-') 68253293e44Sflorian return (c); 68353293e44Sflorian } 68453293e44Sflorian } 68553293e44Sflorian 68653293e44Sflorian #define allowed_in_string(x) \ 68753293e44Sflorian (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 68853293e44Sflorian x != '{' && x != '}' && \ 68953293e44Sflorian x != '!' && x != '=' && x != '#' && \ 69053293e44Sflorian x != ',')) 69153293e44Sflorian 69253293e44Sflorian if (isalnum(c) || c == ':' || c == '_') { 69353293e44Sflorian do { 69453293e44Sflorian *p++ = c; 69553293e44Sflorian if ((unsigned)(p-buf) >= sizeof(buf)) { 69653293e44Sflorian yyerror("string too long"); 69753293e44Sflorian return (findeol()); 69853293e44Sflorian } 69953293e44Sflorian } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 70053293e44Sflorian lungetc(c); 70153293e44Sflorian *p = '\0'; 70253293e44Sflorian if ((token = lookup(buf)) == STRING) 70353293e44Sflorian if ((yylval.v.string = strdup(buf)) == NULL) 70453293e44Sflorian err(1, "yylex: strdup"); 70553293e44Sflorian return (token); 70653293e44Sflorian } 70753293e44Sflorian if (c == '\n') { 70853293e44Sflorian yylval.lineno = file->lineno; 70953293e44Sflorian file->lineno++; 71053293e44Sflorian } 71153293e44Sflorian if (c == EOF) 71253293e44Sflorian return (0); 71353293e44Sflorian return (c); 71453293e44Sflorian } 71553293e44Sflorian 71653293e44Sflorian int 71753293e44Sflorian check_file_secrecy(int fd, const char *fname) 71853293e44Sflorian { 71953293e44Sflorian struct stat st; 72053293e44Sflorian 72153293e44Sflorian if (fstat(fd, &st)) { 72253293e44Sflorian log_warn("cannot stat %s", fname); 72353293e44Sflorian return (-1); 72453293e44Sflorian } 72553293e44Sflorian if (st.st_uid != 0 && st.st_uid != getuid()) { 72653293e44Sflorian log_warnx("%s: owner not root or current user", fname); 72753293e44Sflorian return (-1); 72853293e44Sflorian } 72953293e44Sflorian if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 73053293e44Sflorian log_warnx("%s: group writable or world read/writable", fname); 73153293e44Sflorian return (-1); 73253293e44Sflorian } 73353293e44Sflorian return (0); 73453293e44Sflorian } 73553293e44Sflorian 73653293e44Sflorian struct file * 73753293e44Sflorian pushfile(const char *name, int secret) 73853293e44Sflorian { 73953293e44Sflorian struct file *nfile; 74053293e44Sflorian 74153293e44Sflorian if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 74253293e44Sflorian log_warn("calloc"); 74353293e44Sflorian return (NULL); 74453293e44Sflorian } 74553293e44Sflorian if ((nfile->name = strdup(name)) == NULL) { 74653293e44Sflorian log_warn("strdup"); 74753293e44Sflorian free(nfile); 74853293e44Sflorian return (NULL); 74953293e44Sflorian } 75053293e44Sflorian if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 75153293e44Sflorian log_warn("%s", nfile->name); 75253293e44Sflorian free(nfile->name); 75353293e44Sflorian free(nfile); 75453293e44Sflorian return (NULL); 75553293e44Sflorian } else if (secret && 75653293e44Sflorian check_file_secrecy(fileno(nfile->stream), nfile->name)) { 75753293e44Sflorian fclose(nfile->stream); 75853293e44Sflorian free(nfile->name); 75953293e44Sflorian free(nfile); 76053293e44Sflorian return (NULL); 76153293e44Sflorian } 76253293e44Sflorian nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 76353293e44Sflorian nfile->ungetsize = 16; 76453293e44Sflorian nfile->ungetbuf = malloc(nfile->ungetsize); 76553293e44Sflorian if (nfile->ungetbuf == NULL) { 76653293e44Sflorian log_warn("malloc"); 76753293e44Sflorian fclose(nfile->stream); 76853293e44Sflorian free(nfile->name); 76953293e44Sflorian free(nfile); 77053293e44Sflorian return (NULL); 77153293e44Sflorian } 77253293e44Sflorian TAILQ_INSERT_TAIL(&files, nfile, entry); 77353293e44Sflorian return (nfile); 77453293e44Sflorian } 77553293e44Sflorian 77653293e44Sflorian int 77753293e44Sflorian popfile(void) 77853293e44Sflorian { 77953293e44Sflorian struct file *prev; 78053293e44Sflorian 78153293e44Sflorian if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 78253293e44Sflorian prev->errors += file->errors; 78353293e44Sflorian 78453293e44Sflorian TAILQ_REMOVE(&files, file, entry); 78553293e44Sflorian fclose(file->stream); 78653293e44Sflorian free(file->name); 78753293e44Sflorian free(file->ungetbuf); 78853293e44Sflorian free(file); 78953293e44Sflorian file = prev; 79053293e44Sflorian return (file ? 0 : EOF); 79153293e44Sflorian } 79253293e44Sflorian 79353293e44Sflorian struct rad_conf * 79453293e44Sflorian parse_config(char *filename) 79553293e44Sflorian { 79653293e44Sflorian struct sym *sym, *next; 79753293e44Sflorian 79853293e44Sflorian conf = config_new_empty(); 79953293e44Sflorian ra_options = NULL; 80053293e44Sflorian 8013a682461Sflorian file = pushfile(filename, 0); 80253293e44Sflorian if (file == NULL) { 80353293e44Sflorian free(conf); 80453293e44Sflorian return (NULL); 80553293e44Sflorian } 80653293e44Sflorian topfile = file; 80753293e44Sflorian 80853293e44Sflorian yyparse(); 80953293e44Sflorian errors = file->errors; 81053293e44Sflorian popfile(); 81153293e44Sflorian 81253293e44Sflorian /* Free macros and check which have not been used. */ 81353293e44Sflorian TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 81453293e44Sflorian if ((cmd_opts & OPT_VERBOSE2) && !sym->used) 81553293e44Sflorian fprintf(stderr, "warning: macro '%s' not used\n", 81653293e44Sflorian sym->nam); 81753293e44Sflorian if (!sym->persist) { 81853293e44Sflorian free(sym->nam); 81953293e44Sflorian free(sym->val); 82053293e44Sflorian TAILQ_REMOVE(&symhead, sym, entry); 82153293e44Sflorian free(sym); 82253293e44Sflorian } 82353293e44Sflorian } 82453293e44Sflorian 82553293e44Sflorian if (errors) { 82653293e44Sflorian clear_config(conf); 82753293e44Sflorian return (NULL); 82853293e44Sflorian } 82953293e44Sflorian 83053293e44Sflorian return (conf); 83153293e44Sflorian } 83253293e44Sflorian 83353293e44Sflorian int 83453293e44Sflorian symset(const char *nam, const char *val, int persist) 83553293e44Sflorian { 83653293e44Sflorian struct sym *sym; 83753293e44Sflorian 83853293e44Sflorian TAILQ_FOREACH(sym, &symhead, entry) { 83953293e44Sflorian if (strcmp(nam, sym->nam) == 0) 84053293e44Sflorian break; 84153293e44Sflorian } 84253293e44Sflorian 84353293e44Sflorian if (sym != NULL) { 84453293e44Sflorian if (sym->persist == 1) 84553293e44Sflorian return (0); 84653293e44Sflorian else { 84753293e44Sflorian free(sym->nam); 84853293e44Sflorian free(sym->val); 84953293e44Sflorian TAILQ_REMOVE(&symhead, sym, entry); 85053293e44Sflorian free(sym); 85153293e44Sflorian } 85253293e44Sflorian } 85353293e44Sflorian if ((sym = calloc(1, sizeof(*sym))) == NULL) 85453293e44Sflorian return (-1); 85553293e44Sflorian 85653293e44Sflorian sym->nam = strdup(nam); 85753293e44Sflorian if (sym->nam == NULL) { 85853293e44Sflorian free(sym); 85953293e44Sflorian return (-1); 86053293e44Sflorian } 86153293e44Sflorian sym->val = strdup(val); 86253293e44Sflorian if (sym->val == NULL) { 86353293e44Sflorian free(sym->nam); 86453293e44Sflorian free(sym); 86553293e44Sflorian return (-1); 86653293e44Sflorian } 86753293e44Sflorian sym->used = 0; 86853293e44Sflorian sym->persist = persist; 86953293e44Sflorian TAILQ_INSERT_TAIL(&symhead, sym, entry); 87053293e44Sflorian return (0); 87153293e44Sflorian } 87253293e44Sflorian 87353293e44Sflorian int 87453293e44Sflorian cmdline_symset(char *s) 87553293e44Sflorian { 87653293e44Sflorian char *sym, *val; 87753293e44Sflorian int ret; 87853293e44Sflorian size_t len; 87953293e44Sflorian 88053293e44Sflorian if ((val = strrchr(s, '=')) == NULL) 88153293e44Sflorian return (-1); 88253293e44Sflorian 88353293e44Sflorian len = strlen(s) - strlen(val) + 1; 88453293e44Sflorian if ((sym = malloc(len)) == NULL) 88553293e44Sflorian errx(1, "cmdline_symset: malloc"); 88653293e44Sflorian 88753293e44Sflorian strlcpy(sym, s, len); 88853293e44Sflorian 88953293e44Sflorian ret = symset(sym, val + 1, 1); 89053293e44Sflorian free(sym); 89153293e44Sflorian 89253293e44Sflorian return (ret); 89353293e44Sflorian } 89453293e44Sflorian 89553293e44Sflorian char * 89653293e44Sflorian symget(const char *nam) 89753293e44Sflorian { 89853293e44Sflorian struct sym *sym; 89953293e44Sflorian 90053293e44Sflorian TAILQ_FOREACH(sym, &symhead, entry) { 90153293e44Sflorian if (strcmp(nam, sym->nam) == 0) { 90253293e44Sflorian sym->used = 1; 90353293e44Sflorian return (sym->val); 90453293e44Sflorian } 90553293e44Sflorian } 90653293e44Sflorian return (NULL); 90753293e44Sflorian } 90853293e44Sflorian 90953293e44Sflorian struct ra_prefix_conf * 91053293e44Sflorian conf_get_ra_prefix(struct in6_addr *addr, int prefixlen) 91153293e44Sflorian { 91253293e44Sflorian struct ra_prefix_conf *prefix; 91353293e44Sflorian 91453293e44Sflorian if (addr == NULL) { 91553293e44Sflorian if (ra_iface_conf->autoprefix != NULL) 91653293e44Sflorian return (ra_iface_conf->autoprefix); 91753293e44Sflorian } else { 91853293e44Sflorian SIMPLEQ_FOREACH(prefix, &ra_iface_conf->ra_prefix_list, entry) { 91953293e44Sflorian if (prefix->prefixlen == prefixlen && memcmp(addr, 92053293e44Sflorian &prefix->prefix, sizeof(*addr)) == 0) 92153293e44Sflorian return (prefix); 92253293e44Sflorian } 92353293e44Sflorian } 92453293e44Sflorian 92553293e44Sflorian prefix = calloc(1, sizeof(*prefix)); 92653293e44Sflorian if (prefix == NULL) 92753293e44Sflorian errx(1, "%s: calloc", __func__); 92853293e44Sflorian prefix->prefixlen = prefixlen; 92953293e44Sflorian prefix->vltime = 2592000; /* 30 days */ 93053293e44Sflorian prefix->pltime = 604800; /* 7 days */ 93153293e44Sflorian prefix->lflag = 1; 93253293e44Sflorian prefix->aflag = 1; 93353293e44Sflorian 93453293e44Sflorian if (addr == NULL) 93553293e44Sflorian ra_iface_conf->autoprefix = prefix; 93653293e44Sflorian else { 93753293e44Sflorian prefix->prefix = *addr; 93853293e44Sflorian SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list, prefix, 93953293e44Sflorian entry); 94053293e44Sflorian } 94153293e44Sflorian 94253293e44Sflorian return (prefix); 94353293e44Sflorian } 94453293e44Sflorian 94553293e44Sflorian struct ra_iface_conf * 94653293e44Sflorian conf_get_ra_iface(char *name) 94753293e44Sflorian { 94853293e44Sflorian struct ra_iface_conf *iface; 94953293e44Sflorian size_t n; 95053293e44Sflorian 95153293e44Sflorian SIMPLEQ_FOREACH(iface, &conf->ra_iface_list, entry) { 95253293e44Sflorian if (strcmp(name, iface->name) == 0) 95353293e44Sflorian return (iface); 95453293e44Sflorian } 95553293e44Sflorian 95653293e44Sflorian iface = calloc(1, sizeof(*iface)); 95753293e44Sflorian if (iface == NULL) 95853293e44Sflorian errx(1, "%s: calloc", __func__); 95953293e44Sflorian n = strlcpy(iface->name, name, sizeof(iface->name)); 96053293e44Sflorian if (n >= sizeof(iface->name)) 96153293e44Sflorian errx(1, "%s: name too long", __func__); 96253293e44Sflorian 96353293e44Sflorian /* Inherit attributes set in global section. */ 96453293e44Sflorian iface->ra_options = conf->ra_options; 96553293e44Sflorian 9664c40b7e8Sflorian iface->rdns_lifetime = DEFAULT_RDNS_LIFETIME; 9674c40b7e8Sflorian 96853293e44Sflorian SIMPLEQ_INIT(&iface->ra_prefix_list); 9694c40b7e8Sflorian SIMPLEQ_INIT(&iface->ra_rdnss_list); 9704c40b7e8Sflorian SIMPLEQ_INIT(&iface->ra_dnssl_list); 97153293e44Sflorian 97253293e44Sflorian SIMPLEQ_INSERT_TAIL(&conf->ra_iface_list, iface, entry); 97353293e44Sflorian 97453293e44Sflorian return (iface); 97553293e44Sflorian } 97653293e44Sflorian 97753293e44Sflorian void 97853293e44Sflorian clear_config(struct rad_conf *xconf) 97953293e44Sflorian { 98053293e44Sflorian struct ra_iface_conf *iface; 98153293e44Sflorian 98253293e44Sflorian while((iface = SIMPLEQ_FIRST(&xconf->ra_iface_list)) != NULL) { 98353293e44Sflorian SIMPLEQ_REMOVE_HEAD(&xconf->ra_iface_list, entry); 98453293e44Sflorian free(iface); 98553293e44Sflorian } 98653293e44Sflorian 98753293e44Sflorian free(xconf); 98853293e44Sflorian } 989