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