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