xref: /openbsd/usr.sbin/ospfd/parse.y (revision 08f6ba19)
1 /*	$OpenBSD: parse.y,v 1.104 2021/10/15 15:01:28 naddy Exp $ */
2 
3 /*
4  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
6  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
7  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
8  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
9  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
10  *
11  * Permission to use, copy, modify, and distribute this software for any
12  * purpose with or without fee is hereby granted, provided that the above
13  * copyright notice and this permission notice appear in all copies.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 %{
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <net/route.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <ctype.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <ifaddrs.h>
36 #include <limits.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <syslog.h>
41 
42 #include "ospf.h"
43 #include "ospfd.h"
44 #include "ospfe.h"
45 #include "log.h"
46 
47 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
48 static struct file {
49 	TAILQ_ENTRY(file)	 entry;
50 	FILE			*stream;
51 	char			*name;
52 	size_t			 ungetpos;
53 	size_t			 ungetsize;
54 	u_char			*ungetbuf;
55 	int			 eof_reached;
56 	int			 lineno;
57 	int			 errors;
58 } *file, *topfile;
59 struct file	*pushfile(const char *, int);
60 int		 popfile(void);
61 int		 check_file_secrecy(int, const char *);
62 int		 yyparse(void);
63 int		 yylex(void);
64 int		 yyerror(const char *, ...)
65     __attribute__((__format__ (printf, 1, 2)))
66     __attribute__((__nonnull__ (1)));
67 int		 kw_cmp(const void *, const void *);
68 int		 lookup(char *);
69 int		 igetc(void);
70 int		 lgetc(int);
71 void		 lungetc(int);
72 int		 findeol(void);
73 
74 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
75 struct sym {
76 	TAILQ_ENTRY(sym)	 entry;
77 	int			 used;
78 	int			 persist;
79 	char			*nam;
80 	char			*val;
81 };
82 int		 symset(const char *, const char *, int);
83 char		*symget(const char *);
84 
85 void		 clear_config(struct ospfd_conf *xconf);
86 int		 host(const char *, struct in_addr *, struct in_addr *);
87 
88 static struct ospfd_conf	*conf;
89 static int			 errors = 0;
90 
91 struct area	*area = NULL;
92 struct iface	*iface = NULL;
93 
94 struct config_defaults {
95 	char		auth_key[MAX_SIMPLE_AUTH_LEN];
96 	struct auth_md_head	 md_list;
97 	u_int32_t	dead_interval;
98 	u_int32_t	fast_hello_interval;
99 	u_int16_t	transmit_delay;
100 	u_int16_t	hello_interval;
101 	u_int16_t	rxmt_interval;
102 	u_int16_t	metric;
103 	enum auth_type	auth_type;
104 	u_int8_t	auth_keyid;
105 	u_int8_t	priority;
106 	u_int8_t	p2p;
107 };
108 
109 struct config_defaults	 globaldefs;
110 struct config_defaults	 areadefs;
111 struct config_defaults	 ifacedefs;
112 struct config_defaults	*defs;
113 
114 struct area	*conf_get_area(struct in_addr);
115 struct iface	*conf_get_if(struct kif *, struct kif_addr *);
116 int		 conf_check_rdomain(unsigned int);
117 
118 typedef struct {
119 	union {
120 		int64_t		 number;
121 		char		*string;
122 		struct redistribute *redist;
123 		struct in_addr	 id;
124 	} v;
125 	int lineno;
126 } YYSTYPE;
127 
128 %}
129 
130 %token	AREA INTERFACE ROUTERID FIBPRIORITY FIBUPDATE REDISTRIBUTE RTLABEL
131 %token	RDOMAIN RFC1583COMPAT STUB ROUTER SPFDELAY SPFHOLDTIME EXTTAG
132 %token	AUTHKEY AUTHTYPE AUTHMD AUTHMDKEYID
133 %token	METRIC P2P PASSIVE
134 %token	HELLOINTERVAL FASTHELLOINTERVAL TRANSMITDELAY
135 %token	RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY
136 %token	SET TYPE
137 %token	YES NO
138 %token	MSEC MINIMAL
139 %token	DEMOTE
140 %token	INCLUDE
141 %token	ERROR
142 %token	DEPEND ON
143 %token	<v.string>	STRING
144 %token	<v.number>	NUMBER
145 %type	<v.number>	yesno no optlist optlist_l option demotecount msec
146 %type	<v.number>	deadtime
147 %type	<v.string>	string dependon dependonopt
148 %type	<v.redist>	redistribute
149 %type	<v.id>		areaid
150 
151 %%
152 
153 grammar		: /* empty */
154 		| grammar include '\n'
155 		| grammar '\n'
156 		| grammar conf_main '\n'
157 		| grammar varset '\n'
158 		| grammar area '\n'
159 		| grammar error '\n'		{ file->errors++; }
160 		;
161 
162 include		: INCLUDE STRING		{
163 			struct file	*nfile;
164 
165 			if ((nfile = pushfile($2,
166 			    !(conf->opts & OSPFD_OPT_NOACTION))) == NULL) {
167 				yyerror("failed to include file %s", $2);
168 				free($2);
169 				YYERROR;
170 			}
171 			free($2);
172 
173 			file = nfile;
174 			lungetc('\n');
175 		}
176 		;
177 
178 string		: string STRING	{
179 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
180 				free($1);
181 				free($2);
182 				yyerror("string: asprintf");
183 				YYERROR;
184 			}
185 			free($1);
186 			free($2);
187 		}
188 		| STRING
189 		;
190 
191 yesno		: YES	{ $$ = 1; }
192 		| NO	{ $$ = 0; }
193 		;
194 
195 no		: /* empty */	{ $$ = 0; }
196 		| NO		{ $$ = 1; }
197 		;
198 
199 msec		: MSEC NUMBER {
200 			$$ = $2;
201 		}
202 		| NUMBER {
203 			$$ = $1 * 1000;
204 		}
205 		;
206 
207 varset		: STRING '=' string		{
208 			char *s = $1;
209 			if (conf->opts & OSPFD_OPT_VERBOSE)
210 				printf("%s = \"%s\"\n", $1, $3);
211 			while (*s++) {
212 				if (isspace((unsigned char)*s)) {
213 					yyerror("macro name cannot contain "
214 					    "whitespace");
215 					free($1);
216 					free($3);
217 					YYERROR;
218 				}
219 			}
220 			if (symset($1, $3, 0) == -1)
221 				fatal("cannot store variable");
222 			free($1);
223 			free($3);
224 		}
225 		;
226 
227 conf_main	: ROUTERID STRING {
228 			if (!inet_aton($2, &conf->rtr_id)) {
229 				yyerror("error parsing router-id");
230 				free($2);
231 				YYERROR;
232 			}
233 			free($2);
234 		}
235 		| FIBPRIORITY NUMBER {
236 			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
237 				yyerror("invalid fib-priority");
238 				YYERROR;
239 			}
240 			conf->fib_priority = $2;
241 		}
242 		| FIBUPDATE yesno {
243 			if ($2 == 0)
244 				conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE;
245 			else
246 				conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE;
247 		}
248 		| redistribute {
249 			SIMPLEQ_INSERT_TAIL(&conf->redist_list, $1, entry);
250 			conf->redistribute = 1;
251 		}
252 		| RTLABEL STRING EXTTAG NUMBER {
253 			if ($4 < 0 || $4 > UINT_MAX) {
254 				yyerror("invalid external route tag");
255 				free($2);
256 				YYERROR;
257 			}
258 			rtlabel_tag(rtlabel_name2id($2), $4);
259 			free($2);
260 		}
261 		| RDOMAIN NUMBER {
262 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
263 				yyerror("invalid rdomain");
264 				YYERROR;
265 			}
266 			conf->rdomain = $2;
267 		}
268 		| RFC1583COMPAT yesno {
269 			conf->rfc1583compat = $2;
270 		}
271 		| SPFDELAY msec {
272 			if ($2 < MIN_SPF_DELAY || $2 > MAX_SPF_DELAY) {
273 				yyerror("spf-delay out of range "
274 				    "(%d-%d)", MIN_SPF_DELAY,
275 				    MAX_SPF_DELAY);
276 				YYERROR;
277 			}
278 			conf->spf_delay = $2;
279 		}
280 		| SPFHOLDTIME msec {
281 			if ($2 < MIN_SPF_HOLDTIME || $2 > MAX_SPF_HOLDTIME) {
282 				yyerror("spf-holdtime out of range "
283 				    "(%d-%d)", MIN_SPF_HOLDTIME,
284 				    MAX_SPF_HOLDTIME);
285 				YYERROR;
286 			}
287 			conf->spf_hold_time = $2;
288 		}
289 		| STUB ROUTER yesno {
290 			if ($3)
291 				conf->flags |= OSPFD_FLAG_STUB_ROUTER;
292 			else
293 				/* allow to force non stub mode */
294 				conf->flags &= ~OSPFD_FLAG_STUB_ROUTER;
295 		}
296 		| defaults
297 		;
298 
299 
300 redistribute	: no REDISTRIBUTE NUMBER '/' NUMBER optlist dependonopt {
301 			struct redistribute	*r;
302 
303 			if ((r = calloc(1, sizeof(*r))) == NULL)
304 				fatal(NULL);
305 			r->type = REDIST_ADDR;
306 			if ($3 < 0 || $3 > 255 || $5 < 1 || $5 > 32) {
307 				yyerror("bad network: %llu/%llu", $3, $5);
308 				free(r);
309 				YYERROR;
310 			}
311 			r->addr.s_addr = htonl($3 << IN_CLASSA_NSHIFT);
312 			r->mask.s_addr = prefixlen2mask($5);
313 
314 			if ($1)
315 				r->type |= REDIST_NO;
316 			else
317 				conf->redist_label_or_prefix = 1;
318 			r->metric = $6;
319 			if ($7)
320 				strlcpy(r->dependon, $7, sizeof(r->dependon));
321 			else
322 				r->dependon[0] = '\0';
323 			free($7);
324 			$$ = r;
325 		}
326 		| no REDISTRIBUTE STRING optlist dependonopt {
327 			struct redistribute	*r;
328 
329 			if ((r = calloc(1, sizeof(*r))) == NULL)
330 				fatal(NULL);
331 			if (!strcmp($3, "default"))
332 				r->type = REDIST_DEFAULT;
333 			else if (!strcmp($3, "static"))
334 				r->type = REDIST_STATIC;
335 			else if (!strcmp($3, "connected"))
336 				r->type = REDIST_CONNECTED;
337 			else if (host($3, &r->addr, &r->mask)) {
338 				r->type = REDIST_ADDR;
339 				conf->redist_label_or_prefix = !$1;
340 			} else {
341 				yyerror("unknown redistribute type");
342 				free($3);
343 				free(r);
344 				YYERROR;
345 			}
346 
347 			if ($1)
348 				r->type |= REDIST_NO;
349 			r->metric = $4;
350 			if ($5)
351 				strlcpy(r->dependon, $5, sizeof(r->dependon));
352 			else
353 				r->dependon[0] = '\0';
354 			free($3);
355 			free($5);
356 			$$ = r;
357 		}
358 		| no REDISTRIBUTE RTLABEL STRING optlist dependonopt {
359 			struct redistribute	*r;
360 
361 			if ((r = calloc(1, sizeof(*r))) == NULL)
362 				fatal(NULL);
363 			r->type = REDIST_LABEL;
364 			r->label = rtlabel_name2id($4);
365 			if ($1)
366 				r->type |= REDIST_NO;
367 			else
368 				conf->redist_label_or_prefix = 1;
369 			r->metric = $5;
370 			if ($6)
371 				strlcpy(r->dependon, $6, sizeof(r->dependon));
372 			else
373 				r->dependon[0] = '\0';
374 			free($4);
375 			free($6);
376 			$$ = r;
377 		}
378 		;
379 
380 optlist		: /* empty */ 			{ $$ = DEFAULT_REDIST_METRIC; }
381 		| SET option 			{
382 			$$ = $2;
383 			if (($$ & LSA_METRIC_MASK) == 0)
384 				$$ |= DEFAULT_REDIST_METRIC;
385 		}
386 		| SET optnl '{' optnl optlist_l optnl '}' {
387 			$$ = $5;
388 			if (($$ & LSA_METRIC_MASK) == 0)
389 				$$ |= DEFAULT_REDIST_METRIC;
390 		}
391 		;
392 
393 optlist_l	: optlist_l comma option {
394 			if ($1 & LSA_ASEXT_E_FLAG && $3 & LSA_ASEXT_E_FLAG) {
395 				yyerror("redistribute type already defined");
396 				YYERROR;
397 			}
398 			if ($1 & LSA_METRIC_MASK && $3 & LSA_METRIC_MASK) {
399 				yyerror("redistribute metric already defined");
400 				YYERROR;
401 			}
402 			$$ = $1 | $3;
403 		}
404 		| option { $$ = $1; }
405 		;
406 
407 option		: METRIC NUMBER {
408 			if ($2 == 0 || $2 > MAX_METRIC) {
409 				yyerror("invalid redistribute metric");
410 				YYERROR;
411 			}
412 			$$ = $2;
413 		}
414 		| TYPE NUMBER {
415 			switch ($2) {
416 			case 1:
417 				$$ = 0;
418 				break;
419 			case 2:
420 				$$ = LSA_ASEXT_E_FLAG;
421 				break;
422 			default:
423 				yyerror("only external type 1 and 2 allowed");
424 				YYERROR;
425 			}
426 		}
427 		;
428 
429 dependonopt	: /* empty */		{ $$ = NULL; }
430 		| dependon
431 
432 dependon	: DEPEND ON STRING	{
433 			struct in_addr	 addr;
434 			struct kif	*kif;
435 
436 			if (strlen($3) >= IFNAMSIZ) {
437 				yyerror("interface name %s too long", $3);
438 				free($3);
439 				YYERROR;
440 			}
441 			bzero(&addr, sizeof(addr));
442 			if ((kif = kif_findname($3, addr, NULL)) == NULL) {
443 				yyerror("unknown interface %s", $3);
444 				free($3);
445 				YYERROR;
446 			}
447 			$$ = $3;
448 		}
449 		;
450 
451 authmd		: AUTHMD NUMBER STRING {
452 			if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) {
453 				yyerror("auth-md key-id out of range "
454 				    "(%d-%d)", MIN_MD_ID, MAX_MD_ID);
455 				free($3);
456 				YYERROR;
457 			}
458 			if (strlen($3) > MD5_DIGEST_LENGTH) {
459 				yyerror("auth-md key length out of range "
460 				    "(max length %d)",
461 				    MD5_DIGEST_LENGTH);
462 				free($3);
463 				YYERROR;
464 			}
465 			md_list_add(&defs->md_list, $2, $3);
466 			free($3);
467 		}
468 
469 authmdkeyid	: AUTHMDKEYID NUMBER {
470 			if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) {
471 				yyerror("auth-md-keyid out of range "
472 				    "(%d-%d)", MIN_MD_ID, MAX_MD_ID);
473 				YYERROR;
474 			}
475 			defs->auth_keyid = $2;
476 		}
477 
478 authtype	: AUTHTYPE STRING {
479 			enum auth_type	type;
480 
481 			if (!strcmp($2, "none"))
482 				type = AUTH_NONE;
483 			else if (!strcmp($2, "simple"))
484 				type = AUTH_SIMPLE;
485 			else if (!strcmp($2, "crypt"))
486 				type = AUTH_CRYPT;
487 			else {
488 				yyerror("unknown auth-type");
489 				free($2);
490 				YYERROR;
491 			}
492 			free($2);
493 			defs->auth_type = type;
494 		}
495 		;
496 
497 authkey		: AUTHKEY STRING {
498 			if (strlen($2) > MAX_SIMPLE_AUTH_LEN) {
499 				yyerror("auth-key too long (max length %d)",
500 				    MAX_SIMPLE_AUTH_LEN);
501 					free($2);
502 					YYERROR;
503 			}
504 			strncpy(defs->auth_key, $2,
505 			    sizeof(defs->auth_key));
506 			free($2);
507 		}
508 		;
509 
510 defaults	: METRIC NUMBER {
511 			if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
512 				yyerror("metric out of range (%d-%d)",
513 				    MIN_METRIC, MAX_METRIC);
514 				YYERROR;
515 			}
516 			defs->metric = $2;
517 		}
518 		| ROUTERPRIORITY NUMBER {
519 			if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
520 				yyerror("router-priority out of range (%d-%d)",
521 				    MIN_PRIORITY, MAX_PRIORITY);
522 				YYERROR;
523 			}
524 			defs->priority = $2;
525 		}
526 		| ROUTERDEADTIME deadtime {
527 			defs->dead_interval = $2;
528 		}
529 		| TRANSMITDELAY NUMBER {
530 			if ($2 < MIN_TRANSMIT_DELAY ||
531 			    $2 > MAX_TRANSMIT_DELAY) {
532 				yyerror("transmit-delay out of range (%d-%d)",
533 				    MIN_TRANSMIT_DELAY, MAX_TRANSMIT_DELAY);
534 				YYERROR;
535 			}
536 			defs->transmit_delay = $2;
537 		}
538 		| HELLOINTERVAL NUMBER {
539 			if ($2 < MIN_HELLO_INTERVAL ||
540 			    $2 > MAX_HELLO_INTERVAL) {
541 				yyerror("hello-interval out of range (%d-%d)",
542 				    MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
543 				YYERROR;
544 			}
545 			defs->hello_interval = $2;
546 		}
547 		| FASTHELLOINTERVAL MSEC NUMBER {
548 			if ($3 < MIN_FAST_INTERVAL ||
549 			    $3 > MAX_FAST_INTERVAL) {
550 				yyerror("fast-hello-interval msec out of "
551 				    "range (%d-%d)", MIN_FAST_INTERVAL,
552 				    MAX_FAST_INTERVAL);
553 				YYERROR;
554 			}
555 			defs->fast_hello_interval = $3;
556 		}
557 		| RETRANSMITINTERVAL NUMBER {
558 			if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
559 				yyerror("retransmit-interval out of range "
560 				    "(%d-%d)", MIN_RXMT_INTERVAL,
561 				    MAX_RXMT_INTERVAL);
562 				YYERROR;
563 			}
564 			defs->rxmt_interval = $2;
565 		}
566 		| TYPE P2P		{
567 			defs->p2p = 1;
568 		}
569 		| authtype
570 		| authkey
571 		| authmdkeyid
572 		| authmd
573 		;
574 
575 deadtime	: NUMBER {
576 			if ($1 < MIN_RTR_DEAD_TIME || $1 > MAX_RTR_DEAD_TIME) {
577 				yyerror("router-dead-time out of range (%d-%d)",
578 				    MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME);
579 				YYERROR;
580 			}
581 			$$ = $1;
582 		}
583 		| MINIMAL {
584 			$$ = FAST_RTR_DEAD_TIME;
585 		}
586 
587 optnl		: '\n' optnl
588 		|
589 		;
590 
591 nl		: '\n' optnl		/* one newline or more */
592 		;
593 
594 comma		: ','
595 		| /*empty*/
596 		;
597 
598 area		: AREA areaid {
599 			area = conf_get_area($2);
600 
601 			memcpy(&areadefs, defs, sizeof(areadefs));
602 			md_list_copy(&areadefs.md_list, &defs->md_list);
603 			defs = &areadefs;
604 		} '{' optnl areaopts_l optnl '}' {
605 			area = NULL;
606 			md_list_clr(&defs->md_list);
607 			defs = &globaldefs;
608 		}
609 		;
610 
611 demotecount	: NUMBER	{ $$ = $1; }
612 		| /*empty*/	{ $$ = 1; }
613 		;
614 
615 areaid		: NUMBER {
616 			if ($1 < 0 || $1 > 0xffffffff) {
617 				yyerror("invalid area id");
618 				YYERROR;
619 			}
620 			$$.s_addr = htonl($1);
621 		}
622 		| STRING {
623 			if (inet_aton($1, &$$) == 0) {
624 				yyerror("error parsing area");
625 				free($1);
626 				YYERROR;
627 			}
628 			free($1);
629 		}
630 		;
631 
632 areaopts_l	: areaopts_l nl areaoptsl
633 		| areaoptsl
634 		;
635 
636 areaoptsl	: interface
637 		| DEMOTE STRING	demotecount {
638 			if ($3 < 1 || $3 > 255) {
639 				yyerror("demote count out of range (1-255)");
640 				free($2);
641 				YYERROR;
642 			}
643 			area->demote_level = $3;
644 			if (strlcpy(area->demote_group, $2,
645 			    sizeof(area->demote_group)) >=
646 			    sizeof(area->demote_group)) {
647 				yyerror("demote group name \"%s\" too long",
648 				    $2);
649 				free($2);
650 				YYERROR;
651 			}
652 			free($2);
653 			if (carp_demote_init(area->demote_group,
654 			    conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
655 				yyerror("error initializing group \"%s\"",
656 				    area->demote_group);
657 				YYERROR;
658 			}
659 		}
660 		| defaults
661 		| STUB 			{ area->stub = 1; }
662 		| STUB redistribute {
663 			area->stub = 1;
664 			if ($2->type != REDIST_DEFAULT) {
665 				yyerror("bad redistribute option");
666 				YYERROR;
667 			}
668 			if (!SIMPLEQ_EMPTY(&area->redist_list)) {
669 				yyerror("area redistribute option only "
670 				    "allowed once");
671 				YYERROR;
672 			}
673 			SIMPLEQ_INSERT_TAIL(&area->redist_list, $2, entry);
674 		}
675 		;
676 
677 interface	: INTERFACE STRING	{
678 			struct kif	*kif;
679 			struct kif_addr	*ka = NULL;
680 			char		*s;
681 			struct in_addr	 addr;
682 
683 			s = strchr($2, ':');
684 			if (s) {
685 				*s++ = '\0';
686 				if (inet_aton(s, &addr) == 0) {
687 					yyerror(
688 					    "error parsing interface address");
689 					free($2);
690 					YYERROR;
691 				}
692 			} else
693 				addr.s_addr = 0;
694 
695 			if ((kif = kif_findname($2, addr, &ka)) == NULL) {
696 				yyerror("unknown interface %s", $2);
697 				free($2);
698 				YYERROR;
699 			}
700 			if (ka == NULL) {
701 				if (s)
702 					yyerror("address %s not configured on "
703 					    "interface %s", s, $2);
704 				else
705 					yyerror("unnumbered interface %s", $2);
706 				free($2);
707 				YYERROR;
708 			}
709 			free($2);
710 			iface = conf_get_if(kif, ka);
711 			if (iface == NULL)
712 				YYERROR;
713 			iface->area = area;
714 			LIST_INSERT_HEAD(&area->iface_list, iface, entry);
715 
716 			memcpy(&ifacedefs, defs, sizeof(ifacedefs));
717 			md_list_copy(&ifacedefs.md_list, &defs->md_list);
718 			defs = &ifacedefs;
719 		} interface_block {
720 			iface->dead_interval = defs->dead_interval;
721 			iface->fast_hello_interval = defs->fast_hello_interval;
722 			iface->transmit_delay = defs->transmit_delay;
723 			if (iface->dead_interval == FAST_RTR_DEAD_TIME)
724 				iface->hello_interval = 0;
725 			else
726 				iface->hello_interval = defs->hello_interval;
727 			iface->rxmt_interval = defs->rxmt_interval;
728 			iface->metric = defs->metric;
729 			iface->priority = defs->priority;
730 			iface->auth_type = defs->auth_type;
731 			iface->auth_keyid = defs->auth_keyid;
732 			if (defs->p2p == 1)
733 				iface->type = IF_TYPE_POINTOPOINT;
734 			memcpy(iface->auth_key, defs->auth_key,
735 			    sizeof(iface->auth_key));
736 			md_list_copy(&iface->auth_md_list, &defs->md_list);
737 			md_list_clr(&defs->md_list);
738 			iface = NULL;
739 			/* interface is always part of an area */
740 			defs = &areadefs;
741 		}
742 		;
743 
744 interface_block	: '{' optnl interfaceopts_l optnl '}'
745 		| '{' optnl '}'
746 		| /* empty */
747 		;
748 
749 interfaceopts_l	: interfaceopts_l nl interfaceoptsl
750 		| interfaceoptsl
751 		;
752 
753 interfaceoptsl	: PASSIVE		{ iface->passive = 1; }
754 		| DEMOTE STRING		{
755 			if (strlcpy(iface->demote_group, $2,
756 			    sizeof(iface->demote_group)) >=
757 			    sizeof(iface->demote_group)) {
758 				yyerror("demote group name \"%s\" too long",
759 				    $2);
760 				free($2);
761 				YYERROR;
762 			}
763 			free($2);
764 			if (carp_demote_init(iface->demote_group,
765 			    conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
766 				yyerror("error initializing group \"%s\"",
767 				    iface->demote_group);
768 				YYERROR;
769 			}
770 		}
771 		| dependon {
772 			struct in_addr	 addr;
773 			struct kif	*kif;
774 
775 			if ($1) {
776 				strlcpy(iface->dependon, $1,
777 				       	sizeof(iface->dependon));
778 				bzero(&addr, sizeof(addr));
779 				kif = kif_findname($1, addr, NULL);
780 				iface->depend_ok = ifstate_is_up(kif);
781 			} else {
782 				iface->dependon[0] = '\0';
783 				iface->depend_ok = 1;
784 			}
785 
786 			free($1);
787 		}
788 		| defaults
789 		;
790 
791 %%
792 
793 struct keywords {
794 	const char	*k_name;
795 	int		 k_val;
796 };
797 
798 int
799 yyerror(const char *fmt, ...)
800 {
801 	va_list		 ap;
802 	char		*msg;
803 
804 	file->errors++;
805 	va_start(ap, fmt);
806 	if (vasprintf(&msg, fmt, ap) == -1)
807 		fatalx("yyerror vasprintf");
808 	va_end(ap);
809 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
810 	free(msg);
811 	return (0);
812 }
813 
814 int
815 kw_cmp(const void *k, const void *e)
816 {
817 	return (strcmp(k, ((const struct keywords *)e)->k_name));
818 }
819 
820 int
821 lookup(char *s)
822 {
823 	/* this has to be sorted always */
824 	static const struct keywords keywords[] = {
825 		{"area",		AREA},
826 		{"auth-key",		AUTHKEY},
827 		{"auth-md",		AUTHMD},
828 		{"auth-md-keyid",	AUTHMDKEYID},
829 		{"auth-type",		AUTHTYPE},
830 		{"demote",		DEMOTE},
831 		{"depend",		DEPEND},
832 		{"external-tag",	EXTTAG},
833 		{"fast-hello-interval",	FASTHELLOINTERVAL},
834 		{"fib-priority",	FIBPRIORITY},
835 		{"fib-update",		FIBUPDATE},
836 		{"hello-interval",	HELLOINTERVAL},
837 		{"include",		INCLUDE},
838 		{"interface",		INTERFACE},
839 		{"metric",		METRIC},
840 		{"minimal",		MINIMAL},
841 		{"msec",		MSEC},
842 		{"no",			NO},
843 		{"on",			ON},
844 		{"p2p",			P2P},
845 		{"passive",		PASSIVE},
846 		{"rdomain",		RDOMAIN},
847 		{"redistribute",	REDISTRIBUTE},
848 		{"retransmit-interval",	RETRANSMITINTERVAL},
849 		{"rfc1583compat",	RFC1583COMPAT},
850 		{"router",		ROUTER},
851 		{"router-dead-time",	ROUTERDEADTIME},
852 		{"router-id",		ROUTERID},
853 		{"router-priority",	ROUTERPRIORITY},
854 		{"rtlabel",		RTLABEL},
855 		{"set",			SET},
856 		{"spf-delay",		SPFDELAY},
857 		{"spf-holdtime",	SPFHOLDTIME},
858 		{"stub",		STUB},
859 		{"transmit-delay",	TRANSMITDELAY},
860 		{"type",		TYPE},
861 		{"yes",			YES}
862 	};
863 	const struct keywords	*p;
864 
865 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
866 	    sizeof(keywords[0]), kw_cmp);
867 
868 	if (p)
869 		return (p->k_val);
870 	else
871 		return (STRING);
872 }
873 
874 #define START_EXPAND	1
875 #define DONE_EXPAND	2
876 
877 static int	expanding;
878 
879 int
880 igetc(void)
881 {
882 	int	c;
883 
884 	while (1) {
885 		if (file->ungetpos > 0)
886 			c = file->ungetbuf[--file->ungetpos];
887 		else
888 			c = getc(file->stream);
889 
890 		if (c == START_EXPAND)
891 			expanding = 1;
892 		else if (c == DONE_EXPAND)
893 			expanding = 0;
894 		else
895 			break;
896 	}
897 	return (c);
898 }
899 
900 int
901 lgetc(int quotec)
902 {
903 	int		c, next;
904 
905 	if (quotec) {
906 		if ((c = igetc()) == EOF) {
907 			yyerror("reached end of file while parsing "
908 			    "quoted string");
909 			if (file == topfile || popfile() == EOF)
910 				return (EOF);
911 			return (quotec);
912 		}
913 		return (c);
914 	}
915 
916 	while ((c = igetc()) == '\\') {
917 		next = igetc();
918 		if (next != '\n') {
919 			c = next;
920 			break;
921 		}
922 		yylval.lineno = file->lineno;
923 		file->lineno++;
924 	}
925 
926 	if (c == EOF) {
927 		/*
928 		 * Fake EOL when hit EOF for the first time. This gets line
929 		 * count right if last line in included file is syntactically
930 		 * invalid and has no newline.
931 		 */
932 		if (file->eof_reached == 0) {
933 			file->eof_reached = 1;
934 			return ('\n');
935 		}
936 		while (c == EOF) {
937 			if (file == topfile || popfile() == EOF)
938 				return (EOF);
939 			c = igetc();
940 		}
941 	}
942 	return (c);
943 }
944 
945 void
946 lungetc(int c)
947 {
948 	if (c == EOF)
949 		return;
950 
951 	if (file->ungetpos >= file->ungetsize) {
952 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
953 		if (p == NULL)
954 			err(1, "%s", __func__);
955 		file->ungetbuf = p;
956 		file->ungetsize *= 2;
957 	}
958 	file->ungetbuf[file->ungetpos++] = c;
959 }
960 
961 int
962 findeol(void)
963 {
964 	int	c;
965 
966 	/* skip to either EOF or the first real EOL */
967 	while (1) {
968 		c = lgetc(0);
969 		if (c == '\n') {
970 			file->lineno++;
971 			break;
972 		}
973 		if (c == EOF)
974 			break;
975 	}
976 	return (ERROR);
977 }
978 
979 int
980 yylex(void)
981 {
982 	char	 buf[8096];
983 	char	*p, *val;
984 	int	 quotec, next, c;
985 	int	 token;
986 
987 top:
988 	p = buf;
989 	while ((c = lgetc(0)) == ' ' || c == '\t')
990 		; /* nothing */
991 
992 	yylval.lineno = file->lineno;
993 	if (c == '#')
994 		while ((c = lgetc(0)) != '\n' && c != EOF)
995 			; /* nothing */
996 	if (c == '$' && !expanding) {
997 		while (1) {
998 			if ((c = lgetc(0)) == EOF)
999 				return (0);
1000 
1001 			if (p + 1 >= buf + sizeof(buf) - 1) {
1002 				yyerror("string too long");
1003 				return (findeol());
1004 			}
1005 			if (isalnum(c) || c == '_') {
1006 				*p++ = c;
1007 				continue;
1008 			}
1009 			*p = '\0';
1010 			lungetc(c);
1011 			break;
1012 		}
1013 		val = symget(buf);
1014 		if (val == NULL) {
1015 			yyerror("macro '%s' not defined", buf);
1016 			return (findeol());
1017 		}
1018 		p = val + strlen(val) - 1;
1019 		lungetc(DONE_EXPAND);
1020 		while (p >= val) {
1021 			lungetc((unsigned char)*p);
1022 			p--;
1023 		}
1024 		lungetc(START_EXPAND);
1025 		goto top;
1026 	}
1027 
1028 	switch (c) {
1029 	case '\'':
1030 	case '"':
1031 		quotec = c;
1032 		while (1) {
1033 			if ((c = lgetc(quotec)) == EOF)
1034 				return (0);
1035 			if (c == '\n') {
1036 				file->lineno++;
1037 				continue;
1038 			} else if (c == '\\') {
1039 				if ((next = lgetc(quotec)) == EOF)
1040 					return (0);
1041 				if (next == quotec || next == ' ' ||
1042 				    next == '\t')
1043 					c = next;
1044 				else if (next == '\n') {
1045 					file->lineno++;
1046 					continue;
1047 				} else
1048 					lungetc(next);
1049 			} else if (c == quotec) {
1050 				*p = '\0';
1051 				break;
1052 			} else if (c == '\0') {
1053 				yyerror("syntax error");
1054 				return (findeol());
1055 			}
1056 			if (p + 1 >= buf + sizeof(buf) - 1) {
1057 				yyerror("string too long");
1058 				return (findeol());
1059 			}
1060 			*p++ = c;
1061 		}
1062 		yylval.v.string = strdup(buf);
1063 		if (yylval.v.string == NULL)
1064 			err(1, "%s", __func__);
1065 		return (STRING);
1066 	}
1067 
1068 #define allowed_to_end_number(x) \
1069 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1070 
1071 	if (c == '-' || isdigit(c)) {
1072 		do {
1073 			*p++ = c;
1074 			if ((size_t)(p-buf) >= sizeof(buf)) {
1075 				yyerror("string too long");
1076 				return (findeol());
1077 			}
1078 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1079 		lungetc(c);
1080 		if (p == buf + 1 && buf[0] == '-')
1081 			goto nodigits;
1082 		if (c == EOF || allowed_to_end_number(c)) {
1083 			const char *errstr = NULL;
1084 
1085 			*p = '\0';
1086 			yylval.v.number = strtonum(buf, LLONG_MIN,
1087 			    LLONG_MAX, &errstr);
1088 			if (errstr) {
1089 				yyerror("\"%s\" invalid number: %s",
1090 				    buf, errstr);
1091 				return (findeol());
1092 			}
1093 			return (NUMBER);
1094 		} else {
1095 nodigits:
1096 			while (p > buf + 1)
1097 				lungetc((unsigned char)*--p);
1098 			c = (unsigned char)*--p;
1099 			if (c == '-')
1100 				return (c);
1101 		}
1102 	}
1103 
1104 #define allowed_in_string(x) \
1105 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1106 	x != '{' && x != '}' && \
1107 	x != '!' && x != '=' && x != '#' && \
1108 	x != ','))
1109 
1110 	if (isalnum(c) || c == ':' || c == '_') {
1111 		do {
1112 			*p++ = c;
1113 			if ((size_t)(p-buf) >= sizeof(buf)) {
1114 				yyerror("string too long");
1115 				return (findeol());
1116 			}
1117 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1118 		lungetc(c);
1119 		*p = '\0';
1120 		if ((token = lookup(buf)) == STRING)
1121 			if ((yylval.v.string = strdup(buf)) == NULL)
1122 				err(1, "%s", __func__);
1123 		return (token);
1124 	}
1125 	if (c == '\n') {
1126 		yylval.lineno = file->lineno;
1127 		file->lineno++;
1128 	}
1129 	if (c == EOF)
1130 		return (0);
1131 	return (c);
1132 }
1133 
1134 int
1135 check_file_secrecy(int fd, const char *fname)
1136 {
1137 	struct stat	st;
1138 
1139 	if (fstat(fd, &st)) {
1140 		log_warn("cannot stat %s", fname);
1141 		return (-1);
1142 	}
1143 	if (st.st_uid != 0 && st.st_uid != getuid()) {
1144 		log_warnx("%s: owner not root or current user", fname);
1145 		return (-1);
1146 	}
1147 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
1148 		log_warnx("%s: group writable or world read/writable", fname);
1149 		return (-1);
1150 	}
1151 	return (0);
1152 }
1153 
1154 struct file *
1155 pushfile(const char *name, int secret)
1156 {
1157 	struct file	*nfile;
1158 
1159 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
1160 		log_warn("%s", __func__);
1161 		return (NULL);
1162 	}
1163 	if ((nfile->name = strdup(name)) == NULL) {
1164 		log_warn("%s", __func__);
1165 		free(nfile);
1166 		return (NULL);
1167 	}
1168 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1169 		log_warn("%s: %s", __func__, nfile->name);
1170 		free(nfile->name);
1171 		free(nfile);
1172 		return (NULL);
1173 	} else if (secret &&
1174 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
1175 		fclose(nfile->stream);
1176 		free(nfile->name);
1177 		free(nfile);
1178 		return (NULL);
1179 	}
1180 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
1181 	nfile->ungetsize = 16;
1182 	nfile->ungetbuf = malloc(nfile->ungetsize);
1183 	if (nfile->ungetbuf == NULL) {
1184 		log_warn("%s", __func__);
1185 		fclose(nfile->stream);
1186 		free(nfile->name);
1187 		free(nfile);
1188 		return (NULL);
1189 	}
1190 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1191 	return (nfile);
1192 }
1193 
1194 int
1195 popfile(void)
1196 {
1197 	struct file	*prev;
1198 
1199 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1200 		prev->errors += file->errors;
1201 
1202 	TAILQ_REMOVE(&files, file, entry);
1203 	fclose(file->stream);
1204 	free(file->name);
1205 	free(file->ungetbuf);
1206 	free(file);
1207 	file = prev;
1208 	return (file ? 0 : EOF);
1209 }
1210 
1211 struct ospfd_conf *
1212 parse_config(char *filename, int opts)
1213 {
1214 	struct sym	*sym, *next;
1215 
1216 	if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL)
1217 		fatal("parse_config");
1218 	conf->opts = opts;
1219 	if (conf->opts & OSPFD_OPT_STUB_ROUTER)
1220 		conf->flags |= OSPFD_FLAG_STUB_ROUTER;
1221 
1222 	bzero(&globaldefs, sizeof(globaldefs));
1223 	defs = &globaldefs;
1224 	TAILQ_INIT(&defs->md_list);
1225 	defs->dead_interval = DEFAULT_RTR_DEAD_TIME;
1226 	defs->fast_hello_interval = DEFAULT_FAST_INTERVAL;
1227 	defs->transmit_delay = DEFAULT_TRANSMIT_DELAY;
1228 	defs->hello_interval = DEFAULT_HELLO_INTERVAL;
1229 	defs->rxmt_interval = DEFAULT_RXMT_INTERVAL;
1230 	defs->metric = DEFAULT_METRIC;
1231 	defs->priority = DEFAULT_PRIORITY;
1232 	defs->p2p = 0;
1233 
1234 	conf->spf_delay = DEFAULT_SPF_DELAY;
1235 	conf->spf_hold_time = DEFAULT_SPF_HOLDTIME;
1236 	conf->spf_state = SPF_IDLE;
1237 	conf->fib_priority = RTP_OSPF;
1238 
1239 	if ((file = pushfile(filename,
1240 	    !(conf->opts & OSPFD_OPT_NOACTION))) == NULL) {
1241 		free(conf);
1242 		return (NULL);
1243 	}
1244 	topfile = file;
1245 
1246 	LIST_INIT(&conf->area_list);
1247 	LIST_INIT(&conf->cand_list);
1248 	SIMPLEQ_INIT(&conf->redist_list);
1249 
1250 	yyparse();
1251 	errors = file->errors;
1252 	popfile();
1253 
1254 	/* Free macros and check which have not been used. */
1255 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1256 		if ((conf->opts & OSPFD_OPT_VERBOSE2) && !sym->used)
1257 			fprintf(stderr, "warning: macro '%s' not "
1258 			    "used\n", sym->nam);
1259 		if (!sym->persist) {
1260 			free(sym->nam);
1261 			free(sym->val);
1262 			TAILQ_REMOVE(&symhead, sym, entry);
1263 			free(sym);
1264 		}
1265 	}
1266 
1267 	/* free global config defaults */
1268 	md_list_clr(&globaldefs.md_list);
1269 
1270 	/* check that all interfaces belong to the configured rdomain */
1271 	errors += conf_check_rdomain(conf->rdomain);
1272 
1273 	if (errors) {
1274 		clear_config(conf);
1275 		return (NULL);
1276 	}
1277 
1278 	return (conf);
1279 }
1280 
1281 int
1282 symset(const char *nam, const char *val, int persist)
1283 {
1284 	struct sym	*sym;
1285 
1286 	TAILQ_FOREACH(sym, &symhead, entry) {
1287 		if (strcmp(nam, sym->nam) == 0)
1288 			break;
1289 	}
1290 
1291 	if (sym != NULL) {
1292 		if (sym->persist == 1)
1293 			return (0);
1294 		else {
1295 			free(sym->nam);
1296 			free(sym->val);
1297 			TAILQ_REMOVE(&symhead, sym, entry);
1298 			free(sym);
1299 		}
1300 	}
1301 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1302 		return (-1);
1303 
1304 	sym->nam = strdup(nam);
1305 	if (sym->nam == NULL) {
1306 		free(sym);
1307 		return (-1);
1308 	}
1309 	sym->val = strdup(val);
1310 	if (sym->val == NULL) {
1311 		free(sym->nam);
1312 		free(sym);
1313 		return (-1);
1314 	}
1315 	sym->used = 0;
1316 	sym->persist = persist;
1317 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1318 	return (0);
1319 }
1320 
1321 int
1322 cmdline_symset(char *s)
1323 {
1324 	char	*sym, *val;
1325 	int	ret;
1326 
1327 	if ((val = strrchr(s, '=')) == NULL)
1328 		return (-1);
1329 	sym = strndup(s, val - s);
1330 	if (sym == NULL)
1331 		errx(1, "%s: strndup", __func__);
1332 	ret = symset(sym, val + 1, 1);
1333 	free(sym);
1334 
1335 	return (ret);
1336 }
1337 
1338 char *
1339 symget(const char *nam)
1340 {
1341 	struct sym	*sym;
1342 
1343 	TAILQ_FOREACH(sym, &symhead, entry) {
1344 		if (strcmp(nam, sym->nam) == 0) {
1345 			sym->used = 1;
1346 			return (sym->val);
1347 		}
1348 	}
1349 	return (NULL);
1350 }
1351 
1352 struct area *
1353 conf_get_area(struct in_addr id)
1354 {
1355 	struct area	*a;
1356 
1357 	a = area_find(conf, id);
1358 	if (a)
1359 		return (a);
1360 	a = area_new();
1361 	LIST_INSERT_HEAD(&conf->area_list, a, entry);
1362 
1363 	a->id.s_addr = id.s_addr;
1364 
1365 	return (a);
1366 }
1367 
1368 struct iface *
1369 conf_get_if(struct kif *kif, struct kif_addr *ka)
1370 {
1371 	struct area	*a;
1372 	struct iface	*i;
1373 
1374 	LIST_FOREACH(a, &conf->area_list, entry)
1375 		LIST_FOREACH(i, &a->iface_list, entry)
1376 			if (i->ifindex == kif->ifindex &&
1377 			    i->addr.s_addr == ka->addr.s_addr) {
1378 				yyerror("interface %s already configured",
1379 				    kif->ifname);
1380 				return (NULL);
1381 			}
1382 
1383 	i = if_new(kif, ka);
1384 	i->auth_keyid = 1;
1385 
1386 	return (i);
1387 }
1388 
1389 int
1390 conf_check_rdomain(unsigned int rdomain)
1391 {
1392 	struct area		*a;
1393 	struct iface		*i;
1394 	struct in_addr		 addr;
1395 	struct kif		*kif;
1396 	struct redistribute	*r;
1397 	int			 errs = 0;
1398 
1399 	SIMPLEQ_FOREACH(r, &conf->redist_list, entry)
1400 		if (r->dependon[0] != '\0') {
1401 			bzero(&addr, sizeof(addr));
1402 			kif = kif_findname(r->dependon, addr, NULL);
1403 			if (kif->rdomain != rdomain) {
1404 				logit(LOG_CRIT,
1405 				    "depend on %s: interface not in rdomain %u",
1406 				    kif->ifname, rdomain);
1407 				errs++;
1408 			}
1409 		}
1410 
1411 	LIST_FOREACH(a, &conf->area_list, entry)
1412 		LIST_FOREACH(i, &a->iface_list, entry) {
1413 			if (i->rdomain != rdomain) {
1414 				logit(LOG_CRIT,
1415 				    "interface %s not in rdomain %u",
1416 				    i->name, rdomain);
1417 				errs++;
1418 			}
1419 			if (i->dependon[0] != '\0') {
1420 				bzero(&addr, sizeof(addr));
1421 				kif = kif_findname(i->dependon, addr, NULL);
1422 				if (kif->rdomain != rdomain) {
1423 					logit(LOG_CRIT,
1424 					    "depend on %s: interface not in "
1425 					    "rdomain %u",
1426 					    kif->ifname, rdomain);
1427 					errs++;
1428 				}
1429 			}
1430 		}
1431 
1432 	return (errs);
1433 }
1434 
1435 void
1436 conf_clear_redist_list(struct redist_list *rl)
1437 {
1438 	struct redistribute *r;
1439 	while ((r = SIMPLEQ_FIRST(rl)) != NULL) {
1440 		SIMPLEQ_REMOVE_HEAD(rl, entry);
1441 		free(r);
1442 	}
1443 }
1444 
1445 void
1446 clear_config(struct ospfd_conf *xconf)
1447 {
1448 	struct area	*a;
1449 
1450 	while ((a = LIST_FIRST(&xconf->area_list)) != NULL) {
1451 		LIST_REMOVE(a, entry);
1452 		area_del(a);
1453 	}
1454 
1455 	conf_clear_redist_list(&xconf->redist_list);
1456 
1457 	free(xconf);
1458 }
1459 
1460 u_int32_t
1461 get_rtr_id(void)
1462 {
1463 	struct ifaddrs		*ifap, *ifa;
1464 	u_int32_t		 ip = 0, cur, localnet;
1465 
1466 	localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
1467 
1468 	if (getifaddrs(&ifap) == -1)
1469 		fatal("getifaddrs");
1470 
1471 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1472 		if (strncmp(ifa->ifa_name, "carp", 4) == 0)
1473 			continue;
1474 		if (ifa->ifa_addr == NULL ||
1475 		    ifa->ifa_addr->sa_family != AF_INET)
1476 			continue;
1477 		cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
1478 		if ((cur & localnet) == localnet)	/* skip 127/8 */
1479 			continue;
1480 		if (ntohl(cur) < ntohl(ip) || ip == 0)
1481 			ip = cur;
1482 	}
1483 	freeifaddrs(ifap);
1484 
1485 	if (ip == 0)
1486 		fatal("router-id is 0.0.0.0");
1487 
1488 	return (ip);
1489 }
1490 
1491 int
1492 host(const char *s, struct in_addr *addr, struct in_addr *mask)
1493 {
1494 	struct in_addr		 ina;
1495 	int			 bits = 32;
1496 
1497 	bzero(&ina, sizeof(struct in_addr));
1498 	if (strrchr(s, '/') != NULL) {
1499 		if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
1500 			return (0);
1501 	} else {
1502 		if (inet_pton(AF_INET, s, &ina) != 1)
1503 			return (0);
1504 	}
1505 
1506 	addr->s_addr = ina.s_addr;
1507 	mask->s_addr = prefixlen2mask(bits);
1508 
1509 	return (1);
1510 }
1511