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