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