xref: /openbsd/usr.sbin/ospf6d/parse.y (revision 91f110e0)
1 /*	$OpenBSD: parse.y,v 1.24 2014/01/22 00:21:16 henning 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 <netinet/in.h>
29 #include <arpa/inet.h>
30 
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 <netdb.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <syslog.h>
42 
43 #include "ospf6.h"
44 #include "ospf6d.h"
45 #include "ospfe.h"
46 #include "log.h"
47 
48 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
49 static struct file {
50 	TAILQ_ENTRY(file)	 entry;
51 	FILE			*stream;
52 	char			*name;
53 	int			 lineno;
54 	int			 errors;
55 } *file, *topfile;
56 struct file	*pushfile(const char *, int);
57 int		 popfile(void);
58 int		 check_file_secrecy(int, const char *);
59 int		 yyparse(void);
60 int		 yylex(void);
61 int		 yyerror(const char *, ...);
62 int		 kw_cmp(const void *, const void *);
63 int		 lookup(char *);
64 int		 lgetc(int);
65 int		 lungetc(int);
66 int		 findeol(void);
67 
68 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
69 struct sym {
70 	TAILQ_ENTRY(sym)	 entry;
71 	int			 used;
72 	int			 persist;
73 	char			*nam;
74 	char			*val;
75 };
76 int		 symset(const char *, const char *, int);
77 char		*symget(const char *);
78 
79 void		 clear_config(struct ospfd_conf *xconf);
80 u_int32_t	 get_rtr_id(void);
81 int	 host(const char *, struct in6_addr *);
82 int	 prefix(const char *, struct in6_addr *, u_int8_t *);
83 
84 static struct ospfd_conf	*conf;
85 static int			 errors = 0;
86 
87 struct area	*area = NULL;
88 struct iface	*iface = NULL;
89 
90 struct config_defaults {
91 	u_int16_t	dead_interval;
92 	u_int16_t	transmit_delay;
93 	u_int16_t	hello_interval;
94 	u_int16_t	rxmt_interval;
95 	u_int16_t	metric;
96 	u_int8_t	priority;
97 };
98 
99 struct config_defaults	 globaldefs;
100 struct config_defaults	 areadefs;
101 struct config_defaults	 ifacedefs;
102 struct config_defaults	*defs;
103 
104 struct area	*conf_get_area(struct in_addr);
105 
106 typedef struct {
107 	union {
108 		int64_t		 number;
109 		char		*string;
110 		struct redistribute *redist;
111 	} v;
112 	int lineno;
113 } YYSTYPE;
114 
115 %}
116 
117 %token	AREA INTERFACE ROUTERID FIBUPDATE REDISTRIBUTE RTLABEL
118 %token	STUB ROUTER SPFDELAY SPFHOLDTIME EXTTAG
119 %token	METRIC PASSIVE
120 %token	HELLOINTERVAL TRANSMITDELAY
121 %token	RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY
122 %token	SET TYPE
123 %token	YES NO
124 %token	DEMOTE
125 %token	INCLUDE
126 %token	ERROR
127 %token	<v.string>	STRING
128 %token	<v.number>	NUMBER
129 %type	<v.number>	yesno no optlist, optlist_l option demotecount
130 %type	<v.string>	string
131 %type	<v.redist>	redistribute
132 
133 %%
134 
135 grammar		: /* empty */
136 		| grammar include '\n'
137 		| grammar '\n'
138 		| grammar conf_main '\n'
139 		| grammar varset '\n'
140 		| grammar area '\n'
141 		| grammar error '\n'		{ file->errors++; }
142 		;
143 
144 include		: INCLUDE STRING		{
145 			struct file	*nfile;
146 
147 			if ((nfile = pushfile($2, 1)) == NULL) {
148 				yyerror("failed to include file %s", $2);
149 				free($2);
150 				YYERROR;
151 			}
152 			free($2);
153 
154 			file = nfile;
155 			lungetc('\n');
156 		}
157 		;
158 
159 string		: string STRING	{
160 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
161 				free($1);
162 				free($2);
163 				yyerror("string: asprintf");
164 				YYERROR;
165 			}
166 			free($1);
167 			free($2);
168 		}
169 		| STRING
170 		;
171 
172 yesno		: YES	{ $$ = 1; }
173 		| NO	{ $$ = 0; }
174 		;
175 
176 no		: /* empty */	{ $$ = 0; }
177 		| NO		{ $$ = 1; }
178 
179 varset		: STRING '=' string		{
180 			if (conf->opts & OSPFD_OPT_VERBOSE)
181 				printf("%s = \"%s\"\n", $1, $3);
182 			if (symset($1, $3, 0) == -1)
183 				fatal("cannot store variable");
184 			free($1);
185 			free($3);
186 		}
187 		;
188 
189 conf_main	: ROUTERID STRING {
190 			if (!inet_aton($2, &conf->rtr_id)) {
191 				yyerror("error parsing router-id");
192 				free($2);
193 				YYERROR;
194 			}
195 			free($2);
196 		}
197 		| FIBUPDATE yesno {
198 			if ($2 == 0)
199 				conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE;
200 			else
201 				conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE;
202 		}
203 		| redistribute {
204 			SIMPLEQ_INSERT_TAIL(&conf->redist_list, $1, entry);
205 			conf->redistribute = 1;
206 		}
207 		| RTLABEL STRING EXTTAG NUMBER {
208 			if ($4 < 0 || $4 > UINT_MAX) {
209 				yyerror("invalid external route tag");
210 				free($2);
211 				YYERROR;
212 			}
213 			rtlabel_tag(rtlabel_name2id($2), $4);
214 			free($2);
215 		}
216 		| SPFDELAY NUMBER {
217 			if ($2 < MIN_SPF_DELAY || $2 > MAX_SPF_DELAY) {
218 				yyerror("spf-delay out of range "
219 				    "(%d-%d)", MIN_SPF_DELAY,
220 				    MAX_SPF_DELAY);
221 				YYERROR;
222 			}
223 			conf->spf_delay = $2;
224 		}
225 		| SPFHOLDTIME NUMBER {
226 			if ($2 < MIN_SPF_HOLDTIME || $2 > MAX_SPF_HOLDTIME) {
227 				yyerror("spf-holdtime out of range "
228 				    "(%d-%d)", MIN_SPF_HOLDTIME,
229 				    MAX_SPF_HOLDTIME);
230 				YYERROR;
231 			}
232 			conf->spf_hold_time = $2;
233 		}
234 		| STUB ROUTER yesno {
235 			if ($3)
236 				conf->flags |= OSPFD_FLAG_STUB_ROUTER;
237 			else
238 				/* allow to force non stub mode */
239 				conf->flags &= ~OSPFD_FLAG_STUB_ROUTER;
240 		}
241 		| defaults
242 		;
243 
244 redistribute	: no REDISTRIBUTE STRING optlist {
245 			struct redistribute	*r;
246 
247 			if ((r = calloc(1, sizeof(*r))) == NULL)
248 				fatal(NULL);
249 			if (!strcmp($3, "default"))
250 				r->type = REDIST_DEFAULT;
251 			else if (!strcmp($3, "static"))
252 				r->type = REDIST_STATIC;
253 			else if (!strcmp($3, "connected"))
254 				r->type = REDIST_CONNECTED;
255 			else if (prefix($3, &r->addr, &r->prefixlen))
256 				r->type = REDIST_ADDR;
257 			else {
258 				yyerror("unknown redistribute type");
259 				free($3);
260 				free(r);
261 				YYERROR;
262 			}
263 
264 			if ($1)
265 				r->type |= REDIST_NO;
266 			r->metric = $4;
267 			free($3);
268 			$$ = r;
269 		}
270 		| no REDISTRIBUTE RTLABEL STRING optlist {
271 			struct redistribute	*r;
272 
273 			if ((r = calloc(1, sizeof(*r))) == NULL)
274 				fatal(NULL);
275 			r->type = REDIST_LABEL;
276 			r->label = rtlabel_name2id($4);
277 			if ($1)
278 				r->type |= REDIST_NO;
279 			r->metric = $5;
280 			free($4);
281 			$$ = r;
282 		}
283 		;
284 
285 optlist		: /* empty */			{ $$ = DEFAULT_REDIST_METRIC; }
286 		| SET option			{
287 			$$ = $2;
288 			if (($$ & LSA_METRIC_MASK) == 0)
289 				$$ |= DEFAULT_REDIST_METRIC;
290 		}
291 		| SET optnl '{' optnl optlist_l optnl '}'	{
292 			$$ = $5;
293 			if (($$ & LSA_METRIC_MASK) == 0)
294 				$$ |= DEFAULT_REDIST_METRIC;
295 		}
296 		;
297 
298 optlist_l	: optlist_l comma option {
299 			if ($1 & LSA_ASEXT_E_FLAG && $3 & LSA_ASEXT_E_FLAG) {
300 				yyerror("redistribute type already defined");
301 				YYERROR;
302 			}
303 			if ($1 & LSA_METRIC_MASK && $3 & LSA_METRIC_MASK) {
304 				yyerror("redistribute metric already defined");
305 				YYERROR;
306 			}
307 			$$ = $1 | $3;
308 		}
309 		| option { $$ = $1; }
310 		;
311 
312 option		: METRIC NUMBER {
313 			if ($2 == 0 || $2 > MAX_METRIC) {
314 				yyerror("invalid redistribute metric");
315 				YYERROR;
316 			}
317 			$$ = $2;
318 		}
319 		| TYPE NUMBER {
320 			switch ($2) {
321 			case 1:
322 				$$ = 0;
323 				break;
324 			case 2:
325 				$$ = LSA_ASEXT_E_FLAG;
326 				break;
327 			default:
328 				yyerror("only external type 1 and 2 allowed");
329 				YYERROR;
330 			}
331 		}
332 		;
333 
334 defaults	: METRIC NUMBER {
335 			if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
336 				yyerror("metric out of range (%d-%d)",
337 				    MIN_METRIC, MAX_METRIC);
338 				YYERROR;
339 			}
340 			defs->metric = $2;
341 		}
342 		| ROUTERPRIORITY NUMBER {
343 			if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
344 				yyerror("router-priority out of range (%d-%d)",
345 				    MIN_PRIORITY, MAX_PRIORITY);
346 				YYERROR;
347 			}
348 			defs->priority = $2;
349 		}
350 		| ROUTERDEADTIME NUMBER {
351 			if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) {
352 				yyerror("router-dead-time out of range (%d-%d)",
353 				    MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME);
354 				YYERROR;
355 			}
356 			defs->dead_interval = $2;
357 		}
358 		| TRANSMITDELAY NUMBER {
359 			if ($2 < MIN_TRANSMIT_DELAY ||
360 			    $2 > MAX_TRANSMIT_DELAY) {
361 				yyerror("transmit-delay out of range (%d-%d)",
362 				    MIN_TRANSMIT_DELAY, MAX_TRANSMIT_DELAY);
363 				YYERROR;
364 			}
365 			defs->transmit_delay = $2;
366 		}
367 		| HELLOINTERVAL NUMBER {
368 			if ($2 < MIN_HELLO_INTERVAL ||
369 			    $2 > MAX_HELLO_INTERVAL) {
370 				yyerror("hello-interval out of range (%d-%d)",
371 				    MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
372 				YYERROR;
373 			}
374 			defs->hello_interval = $2;
375 		}
376 		| RETRANSMITINTERVAL NUMBER {
377 			if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
378 				yyerror("retransmit-interval out of range "
379 				    "(%d-%d)", MIN_RXMT_INTERVAL,
380 				    MAX_RXMT_INTERVAL);
381 				YYERROR;
382 			}
383 			defs->rxmt_interval = $2;
384 		}
385 		;
386 
387 optnl		: '\n' optnl
388 		|
389 		;
390 
391 nl		: '\n' optnl		/* one newline or more */
392 		;
393 
394 comma		: ','
395 		| /*empty*/
396 		;
397 
398 area		: AREA STRING {
399 			struct in_addr	id;
400 			if (inet_aton($2, &id) == 0) {
401 				yyerror("error parsing area");
402 				free($2);
403 				YYERROR;
404 			}
405 			free($2);
406 			area = conf_get_area(id);
407 
408 			memcpy(&areadefs, defs, sizeof(areadefs));
409 			defs = &areadefs;
410 		} '{' optnl areaopts_l '}' {
411 			area = NULL;
412 			defs = &globaldefs;
413 		}
414 		;
415 
416 demotecount	: NUMBER	{ $$ = $1; }
417 		| /*empty*/	{ $$ = 1; }
418 		;
419 
420 areaopts_l	: areaopts_l areaoptsl nl
421 		| areaoptsl optnl
422 		;
423 
424 areaoptsl	: interface
425 		| DEMOTE STRING	demotecount {
426 			if ($3 < 1 || $3 > 255) {
427 				yyerror("demote count out of range (1-255)");
428 				free($2);
429 				YYERROR;
430 			}
431 			area->demote_level = $3;
432 			if (strlcpy(area->demote_group, $2,
433 			    sizeof(area->demote_group)) >=
434 			    sizeof(area->demote_group)) {
435 				yyerror("demote group name \"%s\" too long");
436 				free($2);
437 				YYERROR;
438 			}
439 			free($2);
440 			if (carp_demote_init(area->demote_group,
441 			    conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
442 				yyerror("error initializing group \"%s\"",
443 				    area->demote_group);
444 				YYERROR;
445 			}
446 		}
447 		| defaults
448 		;
449 
450 interface	: INTERFACE STRING	{
451 			if ((iface = if_findname($2)) == NULL) {
452 				yyerror("unknown interface %s", $2);
453 				free($2);
454 				YYERROR;
455 			}
456 			if (IN6_IS_ADDR_UNSPECIFIED(&iface->addr)) {
457 				yyerror("unnumbered interface %s", $2);
458 				free($2);
459 				YYERROR;
460 			}
461 			free($2);
462 			iface->area_id.s_addr = area->id.s_addr;
463 			LIST_INSERT_HEAD(&area->iface_list, iface, entry);
464 
465 			memcpy(&ifacedefs, defs, sizeof(ifacedefs));
466 			defs = &ifacedefs;
467 		} interface_block {
468 			iface->dead_interval = defs->dead_interval;
469 			iface->transmit_delay = defs->transmit_delay;
470 			iface->hello_interval = defs->hello_interval;
471 			iface->rxmt_interval = defs->rxmt_interval;
472 			iface->metric = defs->metric;
473 			iface->priority = defs->priority;
474 			iface->cflags |= F_IFACE_CONFIGURED;
475 			iface = NULL;
476 			/* interface is always part of an area */
477 			defs = &areadefs;
478 		}
479 		;
480 
481 interface_block	: '{' optnl interfaceopts_l '}'
482 		| '{' optnl '}'
483 		|
484 		;
485 
486 interfaceopts_l	: interfaceopts_l interfaceoptsl nl
487 		| interfaceoptsl optnl
488 		;
489 
490 interfaceoptsl	: PASSIVE		{ iface->cflags |= F_IFACE_PASSIVE; }
491 		| DEMOTE STRING		{
492 			if (strlcpy(iface->demote_group, $2,
493 			    sizeof(iface->demote_group)) >=
494 			    sizeof(iface->demote_group)) {
495 				yyerror("demote group name \"%s\" too long");
496 				free($2);
497 				YYERROR;
498 			}
499 			free($2);
500 			if (carp_demote_init(iface->demote_group,
501 			    conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
502 				yyerror("error initializing group \"%s\"",
503 				    iface->demote_group);
504 				YYERROR;
505 			}
506 		}
507 		| defaults
508 		;
509 
510 %%
511 
512 struct keywords {
513 	const char	*k_name;
514 	int		 k_val;
515 };
516 
517 int
518 yyerror(const char *fmt, ...)
519 {
520 	va_list		 ap;
521 	char		*nfmt;
522 
523 	file->errors++;
524 	va_start(ap, fmt);
525 	if (asprintf(&nfmt, "%s:%d: %s", file->name, yylval.lineno, fmt) == -1)
526 		fatalx("yyerror asprintf");
527 	vlog(LOG_CRIT, nfmt, ap);
528 	va_end(ap);
529 	free(nfmt);
530 	return (0);
531 }
532 
533 int
534 kw_cmp(const void *k, const void *e)
535 {
536 	return (strcmp(k, ((const struct keywords *)e)->k_name));
537 }
538 
539 int
540 lookup(char *s)
541 {
542 	/* this has to be sorted always */
543 	static const struct keywords keywords[] = {
544 		{"area",		AREA},
545 		{"demote",		DEMOTE},
546 		{"external-tag",	EXTTAG},
547 		{"fib-update",		FIBUPDATE},
548 		{"hello-interval",	HELLOINTERVAL},
549 		{"include",		INCLUDE},
550 		{"interface",		INTERFACE},
551 		{"metric",		METRIC},
552 		{"no",			NO},
553 		{"passive",		PASSIVE},
554 		{"redistribute",	REDISTRIBUTE},
555 		{"retransmit-interval",	RETRANSMITINTERVAL},
556 		{"router",		ROUTER},
557 		{"router-dead-time",	ROUTERDEADTIME},
558 		{"router-id",		ROUTERID},
559 		{"router-priority",	ROUTERPRIORITY},
560 		{"rtlabel",		RTLABEL},
561 		{"set",			SET},
562 		{"spf-delay",		SPFDELAY},
563 		{"spf-holdtime",	SPFHOLDTIME},
564 		{"stub",		STUB},
565 		{"transmit-delay",	TRANSMITDELAY},
566 		{"type",		TYPE},
567 		{"yes",			YES}
568 	};
569 	const struct keywords	*p;
570 
571 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
572 	    sizeof(keywords[0]), kw_cmp);
573 
574 	if (p)
575 		return (p->k_val);
576 	else
577 		return (STRING);
578 }
579 
580 #define MAXPUSHBACK	128
581 
582 u_char	*parsebuf;
583 int	 parseindex;
584 u_char	 pushback_buffer[MAXPUSHBACK];
585 int	 pushback_index = 0;
586 
587 int
588 lgetc(int quotec)
589 {
590 	int		c, next;
591 
592 	if (parsebuf) {
593 		/* Read character from the parsebuffer instead of input. */
594 		if (parseindex >= 0) {
595 			c = parsebuf[parseindex++];
596 			if (c != '\0')
597 				return (c);
598 			parsebuf = NULL;
599 		} else
600 			parseindex++;
601 	}
602 
603 	if (pushback_index)
604 		return (pushback_buffer[--pushback_index]);
605 
606 	if (quotec) {
607 		if ((c = getc(file->stream)) == EOF) {
608 			yyerror("reached end of file while parsing "
609 			    "quoted string");
610 			if (file == topfile || popfile() == EOF)
611 				return (EOF);
612 			return (quotec);
613 		}
614 		return (c);
615 	}
616 
617 	while ((c = getc(file->stream)) == '\\') {
618 		next = getc(file->stream);
619 		if (next != '\n') {
620 			c = next;
621 			break;
622 		}
623 		yylval.lineno = file->lineno;
624 		file->lineno++;
625 	}
626 
627 	while (c == EOF) {
628 		if (file == topfile || popfile() == EOF)
629 			return (EOF);
630 		c = getc(file->stream);
631 	}
632 	return (c);
633 }
634 
635 int
636 lungetc(int c)
637 {
638 	if (c == EOF)
639 		return (EOF);
640 	if (parsebuf) {
641 		parseindex--;
642 		if (parseindex >= 0)
643 			return (c);
644 	}
645 	if (pushback_index < MAXPUSHBACK-1)
646 		return (pushback_buffer[pushback_index++] = c);
647 	else
648 		return (EOF);
649 }
650 
651 int
652 findeol(void)
653 {
654 	int	c;
655 
656 	parsebuf = NULL;
657 
658 	/* skip to either EOF or the first real EOL */
659 	while (1) {
660 		if (pushback_index)
661 			c = pushback_buffer[--pushback_index];
662 		else
663 			c = lgetc(0);
664 		if (c == '\n') {
665 			file->lineno++;
666 			break;
667 		}
668 		if (c == EOF)
669 			break;
670 	}
671 	return (ERROR);
672 }
673 
674 int
675 yylex(void)
676 {
677 	u_char	 buf[8096];
678 	u_char	*p, *val;
679 	int	 quotec, next, c;
680 	int	 token;
681 
682 top:
683 	p = buf;
684 	while ((c = lgetc(0)) == ' ' || c == '\t')
685 		; /* nothing */
686 
687 	yylval.lineno = file->lineno;
688 	if (c == '#')
689 		while ((c = lgetc(0)) != '\n' && c != EOF)
690 			; /* nothing */
691 	if (c == '$' && parsebuf == NULL) {
692 		while (1) {
693 			if ((c = lgetc(0)) == EOF)
694 				return (0);
695 
696 			if (p + 1 >= buf + sizeof(buf) - 1) {
697 				yyerror("string too long");
698 				return (findeol());
699 			}
700 			if (isalnum(c) || c == '_') {
701 				*p++ = c;
702 				continue;
703 			}
704 			*p = '\0';
705 			lungetc(c);
706 			break;
707 		}
708 		val = symget(buf);
709 		if (val == NULL) {
710 			yyerror("macro '%s' not defined", buf);
711 			return (findeol());
712 		}
713 		parsebuf = val;
714 		parseindex = 0;
715 		goto top;
716 	}
717 
718 	switch (c) {
719 	case '\'':
720 	case '"':
721 		quotec = c;
722 		while (1) {
723 			if ((c = lgetc(quotec)) == EOF)
724 				return (0);
725 			if (c == '\n') {
726 				file->lineno++;
727 				continue;
728 			} else if (c == '\\') {
729 				if ((next = lgetc(quotec)) == EOF)
730 					return (0);
731 				if (next == quotec || c == ' ' || c == '\t')
732 					c = next;
733 				else if (next == '\n') {
734 					file->lineno++;
735 					continue;
736 				} else
737 					lungetc(next);
738 			} else if (c == quotec) {
739 				*p = '\0';
740 				break;
741 			}
742 			if (p + 1 >= buf + sizeof(buf) - 1) {
743 				yyerror("string too long");
744 				return (findeol());
745 			}
746 			*p++ = c;
747 		}
748 		yylval.v.string = strdup(buf);
749 		if (yylval.v.string == NULL)
750 			err(1, "yylex: strdup");
751 		return (STRING);
752 	}
753 
754 #define allowed_to_end_number(x) \
755 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
756 
757 	if (c == '-' || isdigit(c)) {
758 		do {
759 			*p++ = c;
760 			if ((unsigned)(p-buf) >= sizeof(buf)) {
761 				yyerror("string too long");
762 				return (findeol());
763 			}
764 		} while ((c = lgetc(0)) != EOF && isdigit(c));
765 		lungetc(c);
766 		if (p == buf + 1 && buf[0] == '-')
767 			goto nodigits;
768 		if (c == EOF || allowed_to_end_number(c)) {
769 			const char *errstr = NULL;
770 
771 			*p = '\0';
772 			yylval.v.number = strtonum(buf, LLONG_MIN,
773 			    LLONG_MAX, &errstr);
774 			if (errstr) {
775 				yyerror("\"%s\" invalid number: %s",
776 				    buf, errstr);
777 				return (findeol());
778 			}
779 			return (NUMBER);
780 		} else {
781 nodigits:
782 			while (p > buf + 1)
783 				lungetc(*--p);
784 			c = *--p;
785 			if (c == '-')
786 				return (c);
787 		}
788 	}
789 
790 #define allowed_in_string(x) \
791 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
792 	x != '{' && x != '}' && \
793 	x != '!' && x != '=' && x != '#' && \
794 	x != ','))
795 
796 	if (isalnum(c) || c == ':' || c == '_') {
797 		do {
798 			*p++ = c;
799 			if ((unsigned)(p-buf) >= sizeof(buf)) {
800 				yyerror("string too long");
801 				return (findeol());
802 			}
803 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
804 		lungetc(c);
805 		*p = '\0';
806 		if ((token = lookup(buf)) == STRING)
807 			if ((yylval.v.string = strdup(buf)) == NULL)
808 				err(1, "yylex: strdup");
809 		return (token);
810 	}
811 	if (c == '\n') {
812 		yylval.lineno = file->lineno;
813 		file->lineno++;
814 	}
815 	if (c == EOF)
816 		return (0);
817 	return (c);
818 }
819 
820 int
821 check_file_secrecy(int fd, const char *fname)
822 {
823 	struct stat	st;
824 
825 	if (fstat(fd, &st)) {
826 		log_warn("cannot stat %s", fname);
827 		return (-1);
828 	}
829 	if (st.st_uid != 0 && st.st_uid != getuid()) {
830 		log_warnx("%s: owner not root or current user", fname);
831 		return (-1);
832 	}
833 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
834 		log_warnx("%s: group writable or world read/writable", fname);
835 		return (-1);
836 	}
837 	return (0);
838 }
839 
840 struct file *
841 pushfile(const char *name, int secret)
842 {
843 	struct file	*nfile;
844 
845 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
846 		log_warn("malloc");
847 		return (NULL);
848 	}
849 	if ((nfile->name = strdup(name)) == NULL) {
850 		log_warn("malloc");
851 		free(nfile);
852 		return (NULL);
853 	}
854 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
855 		log_warn("%s", nfile->name);
856 		free(nfile->name);
857 		free(nfile);
858 		return (NULL);
859 	} else if (secret &&
860 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
861 		fclose(nfile->stream);
862 		free(nfile->name);
863 		free(nfile);
864 		return (NULL);
865 	}
866 	nfile->lineno = 1;
867 	TAILQ_INSERT_TAIL(&files, nfile, entry);
868 	return (nfile);
869 }
870 
871 int
872 popfile(void)
873 {
874 	struct file	*prev;
875 
876 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
877 		prev->errors += file->errors;
878 
879 	TAILQ_REMOVE(&files, file, entry);
880 	fclose(file->stream);
881 	free(file->name);
882 	free(file);
883 	file = prev;
884 	return (file ? 0 : EOF);
885 }
886 
887 struct ospfd_conf *
888 parse_config(char *filename, int opts)
889 {
890 	struct sym	*sym, *next;
891 
892 	if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL)
893 		fatal("parse_config");
894 	conf->opts = opts;
895 	if (conf->opts & OSPFD_OPT_STUB_ROUTER)
896 		conf->flags |= OSPFD_FLAG_STUB_ROUTER;
897 
898 	bzero(&globaldefs, sizeof(globaldefs));
899 	defs = &globaldefs;
900 	defs->dead_interval = DEFAULT_RTR_DEAD_TIME;
901 	defs->transmit_delay = DEFAULT_TRANSMIT_DELAY;
902 	defs->hello_interval = DEFAULT_HELLO_INTERVAL;
903 	defs->rxmt_interval = DEFAULT_RXMT_INTERVAL;
904 	defs->metric = DEFAULT_METRIC;
905 	defs->priority = DEFAULT_PRIORITY;
906 
907 	conf->spf_delay = DEFAULT_SPF_DELAY;
908 	conf->spf_hold_time = DEFAULT_SPF_HOLDTIME;
909 	conf->spf_state = SPF_IDLE;
910 
911 	if ((file = pushfile(filename, !(conf->opts & OSPFD_OPT_NOACTION))) == NULL) {
912 		free(conf);
913 		return (NULL);
914 	}
915 	topfile = file;
916 
917 	LIST_INIT(&conf->area_list);
918 	LIST_INIT(&conf->cand_list);
919 	SIMPLEQ_INIT(&conf->redist_list);
920 
921 	yyparse();
922 	errors = file->errors;
923 	popfile();
924 
925 	/* Free macros and check which have not been used. */
926 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
927 		next = TAILQ_NEXT(sym, entry);
928 		if ((conf->opts & OSPFD_OPT_VERBOSE2) && !sym->used)
929 			fprintf(stderr, "warning: macro '%s' not "
930 			    "used\n", sym->nam);
931 		if (!sym->persist) {
932 			free(sym->nam);
933 			free(sym->val);
934 			TAILQ_REMOVE(&symhead, sym, entry);
935 			free(sym);
936 		}
937 	}
938 
939 	/* free global config defaults */
940 	if (errors) {
941 		clear_config(conf);
942 		return (NULL);
943 	}
944 
945 	if (conf->rtr_id.s_addr == 0)
946 		conf->rtr_id.s_addr = get_rtr_id();
947 
948 	return (conf);
949 }
950 
951 int
952 symset(const char *nam, const char *val, int persist)
953 {
954 	struct sym	*sym;
955 
956 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
957 	    sym = TAILQ_NEXT(sym, entry))
958 		;	/* nothing */
959 
960 	if (sym != NULL) {
961 		if (sym->persist == 1)
962 			return (0);
963 		else {
964 			free(sym->nam);
965 			free(sym->val);
966 			TAILQ_REMOVE(&symhead, sym, entry);
967 			free(sym);
968 		}
969 	}
970 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
971 		return (-1);
972 
973 	sym->nam = strdup(nam);
974 	if (sym->nam == NULL) {
975 		free(sym);
976 		return (-1);
977 	}
978 	sym->val = strdup(val);
979 	if (sym->val == NULL) {
980 		free(sym->nam);
981 		free(sym);
982 		return (-1);
983 	}
984 	sym->used = 0;
985 	sym->persist = persist;
986 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
987 	return (0);
988 }
989 
990 int
991 cmdline_symset(char *s)
992 {
993 	char	*sym, *val;
994 	int	ret;
995 	size_t	len;
996 
997 	if ((val = strrchr(s, '=')) == NULL)
998 		return (-1);
999 
1000 	len = strlen(s) - strlen(val) + 1;
1001 	if ((sym = malloc(len)) == NULL)
1002 		errx(1, "cmdline_symset: malloc");
1003 
1004 	strlcpy(sym, s, len);
1005 
1006 	ret = symset(sym, val + 1, 1);
1007 	free(sym);
1008 
1009 	return (ret);
1010 }
1011 
1012 char *
1013 symget(const char *nam)
1014 {
1015 	struct sym	*sym;
1016 
1017 	TAILQ_FOREACH(sym, &symhead, entry)
1018 		if (strcmp(nam, sym->nam) == 0) {
1019 			sym->used = 1;
1020 			return (sym->val);
1021 		}
1022 	return (NULL);
1023 }
1024 
1025 struct area *
1026 conf_get_area(struct in_addr id)
1027 {
1028 	struct area	*a;
1029 
1030 	a = area_find(conf, id);
1031 	if (a)
1032 		return (a);
1033 	a = area_new();
1034 	LIST_INSERT_HEAD(&conf->area_list, a, entry);
1035 
1036 	a->id.s_addr = id.s_addr;
1037 
1038 	return (a);
1039 }
1040 
1041 void
1042 clear_config(struct ospfd_conf *xconf)
1043 {
1044 	struct area	*a;
1045 
1046 	while ((a = LIST_FIRST(&xconf->area_list)) != NULL) {
1047 		LIST_REMOVE(a, entry);
1048 		area_del(a);
1049 	}
1050 
1051 	free(xconf);
1052 }
1053 
1054 u_int32_t
1055 get_rtr_id(void)
1056 {
1057 	struct ifaddrs		*ifap, *ifa;
1058 	u_int32_t		 ip = 0, cur, localnet;
1059 
1060 	localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
1061 
1062 	if (getifaddrs(&ifap) == -1)
1063 		fatal("getifaddrs");
1064 
1065 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1066 		if (strncmp(ifa->ifa_name, "carp", 4) == 0)
1067 			continue;
1068 		if (ifa->ifa_addr->sa_family != AF_INET)
1069 			continue;
1070 		cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
1071 		if ((cur & localnet) == localnet)	/* skip 127/8 */
1072 			continue;
1073 		if (ntohl(cur) < ntohl(ip) || ip == 0)
1074 			ip = cur;
1075 	}
1076 	freeifaddrs(ifap);
1077 
1078 	if (ip == 0)
1079 		fatal("router-id is 0.0.0.0");
1080 
1081 	return (ip);
1082 }
1083 
1084 int
1085 host(const char *s, struct in6_addr *addr)
1086 {
1087 	struct addrinfo	hints, *r;
1088 
1089 	if (s == NULL)
1090 		return (0);
1091 
1092 	bzero(addr, sizeof(struct in6_addr));
1093 	bzero(&hints, sizeof(hints));
1094 	hints.ai_family = AF_INET6;
1095 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
1096 	hints.ai_flags = AI_NUMERICHOST;
1097 	if (getaddrinfo(s, "0", &hints, &r) == 0) {
1098 		*addr = ((struct sockaddr_in6 *)r->ai_addr)->sin6_addr;
1099 		/* XXX address scope !!! */
1100 		/* ((struct sockaddr_in6 *)r->ai_addr)->sin6_scope_id */
1101 		freeaddrinfo(r);
1102 		return (1);
1103 	}
1104 	return (0);
1105 }
1106 
1107 int
1108 prefix(const char *s, struct in6_addr *addr, u_int8_t *plen)
1109 {
1110 	char		*p, *ps;
1111 	const char	*errstr;
1112 	int		 mask;
1113 
1114 	if (s == NULL)
1115 		return (0);
1116 
1117 	if ((p = strrchr(s, '/')) != NULL) {
1118 		mask = strtonum(p + 1, 0, 128, &errstr);
1119 		if (errstr)
1120 			errx(1, "invalid netmask: %s", errstr);
1121 
1122 		if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
1123 			err(1, "parse_prefix: malloc");
1124 		strlcpy(ps, s, strlen(s) - strlen(p) + 1);
1125 
1126 		if (host(ps, addr) == 0) {
1127 			free(ps);
1128 			return (0);
1129 		}
1130 
1131 		inet6applymask(addr, addr, mask);
1132 		*plen = mask;
1133 		return (1);
1134 	}
1135 	*plen = 128;
1136 	return (host(s, addr));
1137 }
1138