xref: /openbsd-src/usr.sbin/eigrpd/parse.y (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: parse.y,v 1.19 2016/09/02 16:44:33 renato Exp $ */
2 
3 /*
4  * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11  *
12  * Permission to use, copy, modify, and distribute this software for any
13  * purpose with or without fee is hereby granted, provided that the above
14  * copyright notice and this permission notice appear in all copies.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 %{
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <net/route.h>
30 
31 #include <arpa/inet.h>
32 #include <ctype.h>
33 #include <err.h>
34 #include <ifaddrs.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <syslog.h>
38 #include <unistd.h>
39 
40 #include "eigrpd.h"
41 #include "eigrpe.h"
42 #include "log.h"
43 
44 struct file {
45 	TAILQ_ENTRY(file)	 entry;
46 	FILE			*stream;
47 	char			*name;
48 	int			 lineno;
49 	int			 errors;
50 };
51 TAILQ_HEAD(files, file);
52 
53 struct sym {
54 	TAILQ_ENTRY(sym)	 entry;
55 	int			 used;
56 	int			 persist;
57 	char			*nam;
58 	char			*val;
59 };
60 TAILQ_HEAD(symhead, sym);
61 
62 struct config_defaults {
63 	uint8_t		kvalues[6];
64 	uint16_t	active_timeout;
65 	uint8_t		maximum_hops;
66 	uint8_t		maximum_paths;
67 	uint8_t		variance;
68 	struct redist_metric *dflt_metric;
69 	uint16_t	hello_interval;
70 	uint16_t	hello_holdtime;
71 	uint32_t	delay;
72 	uint32_t	bandwidth;
73 	uint8_t		splithorizon;
74 };
75 
76 typedef struct {
77 	union {
78 		int64_t			 number;
79 		char			*string;
80 		struct redistribute	*redist;
81 		struct redist_metric	*redist_metric;
82 	} v;
83 	int lineno;
84 } YYSTYPE;
85 
86 #define MAXPUSHBACK	128
87 
88 static int		 yyerror(const char *, ...)
89     __attribute__((__format__ (printf, 1, 2)))
90     __attribute__((__nonnull__ (1)));
91 static int		 kw_cmp(const void *, const void *);
92 static int		 lookup(char *);
93 static int		 lgetc(int);
94 static int		 lungetc(int);
95 static int		 findeol(void);
96 static int		 yylex(void);
97 static int		 check_file_secrecy(int, const char *);
98 static struct file	*pushfile(const char *, int);
99 static int		 popfile(void);
100 static int		 yyparse(void);
101 static int		 symset(const char *, const char *, int);
102 static char		*symget(const char *);
103 static struct eigrp	*conf_get_instance(uint16_t);
104 static struct eigrp_iface *conf_get_if(struct kif *);
105 static void		 clear_config(struct eigrpd_conf *xconf);
106 static uint32_t	 get_rtr_id(void);
107 static int		 get_prefix(const char *, union eigrpd_addr *, uint8_t *);
108 
109 static struct file		*file, *topfile;
110 static struct files		 files = TAILQ_HEAD_INITIALIZER(files);
111 static struct symhead		 symhead = TAILQ_HEAD_INITIALIZER(symhead);
112 static struct eigrpd_conf	*conf;
113 static int			 errors;
114 
115 static int			 af;
116 static struct eigrp		*eigrp;
117 static struct eigrp_iface	*ei;
118 
119 static struct config_defaults	 globaldefs;
120 static struct config_defaults	 afdefs;
121 static struct config_defaults	 asdefs;
122 static struct config_defaults	 ifacedefs;
123 static struct config_defaults	*defs;
124 
125 static unsigned char		*parsebuf;
126 static int			 parseindex;
127 static unsigned char		 pushback_buffer[MAXPUSHBACK];
128 static int			 pushback_index;
129 
130 %}
131 
132 %token	ROUTERID AS FIBUPDATE RDOMAIN REDISTRIBUTE METRIC DFLTMETRIC
133 %token	MAXHOPS MAXPATHS VARIANCE FIBPRIORITY_INT FIBPRIORITY_EXT
134 %token	FIBPRIORITY_SUMM SUMMARY_ADDR
135 %token	AF IPV4 IPV6 HELLOINTERVAL HOLDTIME KVALUES ACTIVETIMEOUT
136 %token	INTERFACE PASSIVE DELAY BANDWIDTH SPLITHORIZON
137 %token	YES NO
138 %token	INCLUDE
139 %token	ERROR
140 %token	<v.string>	STRING
141 %token	<v.number>	NUMBER
142 %type	<v.number>	yesno no eigrp_af
143 %type	<v.string>	string
144 %type	<v.redist>	redistribute
145 %type	<v.redist_metric> redist_metric opt_red_metric
146 
147 %%
148 
149 grammar		: /* empty */
150 		| grammar include '\n'
151 		| grammar '\n'
152 		| grammar conf_main '\n'
153 		| grammar varset '\n'
154 		| grammar af '\n'
155 		| grammar error '\n'		{ file->errors++; }
156 		;
157 
158 include		: INCLUDE STRING {
159 			struct file	*nfile;
160 
161 			if ((nfile = pushfile($2, 1)) == 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 optnl		: '\n' optnl
187 		|
188 		;
189 
190 nl		: '\n' optnl		/* one newline or more */
191 		;
192 
193 yesno		: YES	{ $$ = 1; }
194 		| NO	{ $$ = 0; }
195 		;
196 
197 no		: /* empty */	{ $$ = 0; }
198 		| NO		{ $$ = 1; }
199 		;
200 
201 eigrp_af	: IPV4	{ $$ = AF_INET; }
202 		| IPV6	{ $$ = AF_INET6; }
203 		;
204 
205 varset		: STRING '=' string {
206 			char *s = $1;
207 			if (global.cmd_opts & EIGRPD_OPT_VERBOSE)
208 				printf("%s = \"%s\"\n", $1, $3);
209 			while (*s++) {
210 				if (isspace((unsigned char)*s)) {
211 					yyerror("macro name cannot contain "
212 					    "whitespace");
213 					YYERROR;
214 				}
215 			}
216 			if (symset($1, $3, 0) == -1)
217 				fatal("cannot store variable");
218 			free($1);
219 			free($3);
220 		}
221 		;
222 
223 conf_main	: ROUTERID STRING {
224 			if (!inet_aton($2, &conf->rtr_id)) {
225 				yyerror("error parsing router-id");
226 				free($2);
227 				YYERROR;
228 			}
229 			free($2);
230 			if (bad_addr_v4(conf->rtr_id)) {
231 				yyerror("invalid router-id");
232 				YYERROR;
233 			}
234 		}
235 		| FIBUPDATE yesno {
236 			if ($2 == 0)
237 				conf->flags |= EIGRPD_FLAG_NO_FIB_UPDATE;
238 			else
239 				conf->flags &= ~EIGRPD_FLAG_NO_FIB_UPDATE;
240 		}
241 		| RDOMAIN NUMBER {
242 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
243 				yyerror("invalid rdomain");
244 				YYERROR;
245 			}
246 			conf->rdomain = $2;
247 		}
248 		| FIBPRIORITY_INT NUMBER {
249 			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
250 				yyerror("invalid fib-priority");
251 				YYERROR;
252 			}
253 			conf->fib_priority_internal = $2;
254 		}
255 		| FIBPRIORITY_EXT NUMBER {
256 			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
257 				yyerror("invalid fib-priority");
258 				YYERROR;
259 			}
260 			conf->fib_priority_external = $2;
261 		}
262 		| FIBPRIORITY_SUMM NUMBER {
263 			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
264 				yyerror("invalid fib-priority");
265 				YYERROR;
266 			}
267 			conf->fib_priority_summary = $2;
268 		}
269 		| defaults
270 		;
271 
272 af		: AF eigrp_af {
273 			af = $2;
274 			afdefs = *defs;
275 			defs = &afdefs;
276 		} af_block {
277 			af = AF_UNSPEC;
278 			defs = &globaldefs;
279 		}
280 		;
281 
282 af_block	: '{' optnl afopts_l '}'
283 		| '{' optnl '}'
284 		|
285 		;
286 
287 afopts_l	: afopts_l afoptsl nl
288 		| afoptsl optnl
289 		;
290 
291 afoptsl		: as
292 		| defaults
293 		;
294 
295 as		: AS NUMBER {
296 			if ($2 < EIGRP_MIN_AS || $2 > EIGRP_MAX_AS) {
297 				yyerror("invalid autonomous-system");
298 				YYERROR;
299 			}
300 			eigrp = conf_get_instance($2);
301 			if (eigrp == NULL)
302 				YYERROR;
303 
304 			asdefs = *defs;
305 			defs = &asdefs;
306 		} as_block {
307 			memcpy(eigrp->kvalues, defs->kvalues,
308 			    sizeof(eigrp->kvalues));
309 			eigrp->active_timeout = defs->active_timeout;
310 			eigrp->maximum_hops = defs->maximum_hops;
311 			eigrp->maximum_paths = defs->maximum_paths;
312 			eigrp->variance = defs->variance;
313 			eigrp->dflt_metric = defs->dflt_metric;
314 			eigrp = NULL;
315 			defs = &afdefs;
316 		}
317 		;
318 
319 as_block	: '{' optnl asopts_l '}'
320 		| '{' optnl '}'
321 		|
322 		;
323 
324 asopts_l	: asopts_l asoptsl nl
325 		| asoptsl optnl
326 		;
327 
328 asoptsl		: interface
329 		| redistribute {
330 			SIMPLEQ_INSERT_TAIL(&eigrp->redist_list, $1, entry);
331 		}
332 		| defaults
333 		;
334 
335 interface	: INTERFACE STRING {
336 			struct kif	*kif;
337 
338 			if ((kif = kif_findname($2)) == NULL) {
339 				yyerror("unknown interface %s", $2);
340 				free($2);
341 				YYERROR;
342 			}
343 			free($2);
344 			ei = conf_get_if(kif);
345 			if (ei == NULL)
346 				YYERROR;
347 
348 			ifacedefs = *defs;
349 			defs = &ifacedefs;
350 		} interface_block {
351 			ei->hello_holdtime = defs->hello_holdtime;
352 			ei->hello_interval = defs->hello_interval;
353 			ei->delay = defs->delay;
354 			ei->bandwidth = defs->bandwidth;
355 			ei->splithorizon = defs->splithorizon;
356 			ei = NULL;
357 			defs = &asdefs;
358 		}
359 		;
360 
361 interface_block	: '{' optnl interfaceopts_l '}'
362 		| '{' optnl '}'
363 		|
364 		;
365 
366 interfaceopts_l	: interfaceopts_l interfaceoptsl nl
367 		| interfaceoptsl optnl
368 		;
369 
370 interfaceoptsl	: PASSIVE { ei->passive = 1; }
371 		| SUMMARY_ADDR STRING {
372 			struct summary_addr	*s, *tmp;
373 
374 			if ((s = calloc(1, sizeof(*s))) == NULL)
375 				fatal(NULL);
376 			if (get_prefix($2, &s->prefix, &s->prefixlen) < 0) {
377 				yyerror("invalid summary-address");
378 				free($2);
379 				free(s);
380 				YYERROR;
381 			}
382 			free($2);
383 
384 			TAILQ_FOREACH(tmp, &ei->summary_list, entry) {
385 				if (eigrp_prefixcmp(af, &s->prefix,
386 				    &tmp->prefix, min(s->prefixlen,
387 				    tmp->prefixlen)) == 0) {
388 					yyerror("summary-address conflicts "
389 					    "with another summary-address "
390 					    "already configured");
391 					YYERROR;
392 				}
393 			}
394 
395 			TAILQ_INSERT_TAIL(&ei->summary_list, s, entry);
396 		}
397 		| iface_defaults
398 		;
399 
400 redistribute	: no REDISTRIBUTE STRING opt_red_metric {
401 			struct redistribute	*r;
402 
403 			if ((r = calloc(1, sizeof(*r))) == NULL)
404 				fatal(NULL);
405 			if (!strcmp($3, "default"))
406 				r->type = REDIST_DEFAULT;
407 			else if (!strcmp($3, "static"))
408 				r->type = REDIST_STATIC;
409 			else if (!strcmp($3, "rip"))
410 				r->type = REDIST_RIP;
411 			else if (!strcmp($3, "ospf"))
412 				r->type = REDIST_OSPF;
413 			else if (!strcmp($3, "connected"))
414 				r->type = REDIST_CONNECTED;
415 			else if (get_prefix($3, &r->addr, &r->prefixlen) >= 0)
416 				r->type = REDIST_ADDR;
417 			else {
418 				yyerror("invalid redistribute");
419 				free($3);
420 				free(r);
421 				YYERROR;
422 			}
423 
424 			r->af = af;
425 			if ($1)
426 				r->type |= REDIST_NO;
427 			r->metric = $4;
428 			free($3);
429 			$$ = r;
430 		}
431 		;
432 
433 redist_metric	: NUMBER NUMBER NUMBER NUMBER NUMBER {
434 			struct redist_metric	*m;
435 
436 			if ($1 < MIN_BANDWIDTH || $1 > MAX_BANDWIDTH) {
437 				yyerror("bandwidth out of range (%d-%d)",
438 				    MIN_BANDWIDTH, MAX_BANDWIDTH);
439 				YYERROR;
440 			}
441 			if ($2 < MIN_DELAY || $2 > MAX_DELAY) {
442 				yyerror("delay out of range (%d-%d)",
443 				    MIN_DELAY, MAX_DELAY);
444 				YYERROR;
445 			}
446 			if ($3 < MIN_RELIABILITY || $3 > MAX_RELIABILITY) {
447 				yyerror("reliability out of range (%d-%d)",
448 				    MIN_RELIABILITY, MAX_RELIABILITY);
449 				YYERROR;
450 			}
451 			if ($4 < MIN_LOAD || $4 > MAX_LOAD) {
452 				yyerror("load out of range (%d-%d)",
453 				    MIN_LOAD, MAX_LOAD);
454 				YYERROR;
455 			}
456 			if ($5 < MIN_MTU || $5 > MAX_MTU) {
457 				yyerror("mtu out of range (%d-%d)",
458 				    MIN_MTU, MAX_MTU);
459 				YYERROR;
460 			}
461 
462 			if ((m = calloc(1, sizeof(*m))) == NULL)
463 				fatal(NULL);
464 			m->bandwidth = $1;
465 			m->delay = $2;
466 			m->reliability = $3;
467 			m->load = $4;
468 			m->mtu = $5;
469 
470 			$$ = m;
471 		}
472 		;
473 
474 opt_red_metric	: /* empty */		{ $$ = NULL; }
475 		| METRIC redist_metric 	{ $$ = $2; }
476 		;
477 
478 defaults	: KVALUES NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER {
479 			if ($2 < MIN_KVALUE || $2 > MAX_KVALUE ||
480 			    $3 < MIN_KVALUE || $3 > MAX_KVALUE ||
481 			    $4 < MIN_KVALUE || $4 > MAX_KVALUE ||
482 			    $5 < MIN_KVALUE || $5 > MAX_KVALUE ||
483 			    $6 < MIN_KVALUE || $6 > MAX_KVALUE ||
484 			    $7 < MIN_KVALUE || $7 > MAX_KVALUE) {
485 				yyerror("k-value out of range (%d-%d)",
486 				    MIN_KVALUE, MAX_KVALUE);
487 				YYERROR;
488 			}
489 			defs->kvalues[0] = $2;
490 			defs->kvalues[1] = $3;
491 			defs->kvalues[2] = $4;
492 			defs->kvalues[3] = $5;
493 			defs->kvalues[4] = $6;
494 			defs->kvalues[5] = $7;
495 		}
496 		| ACTIVETIMEOUT NUMBER {
497 			if ($2 < MIN_ACTIVE_TIMEOUT ||
498 			    $2 > MAX_ACTIVE_TIMEOUT) {
499 				yyerror("active-timeout out of range (%d-%d)",
500 				    MIN_ACTIVE_TIMEOUT, MAX_ACTIVE_TIMEOUT);
501 				YYERROR;
502 			}
503 			defs->active_timeout = $2;
504 		}
505 		| MAXHOPS NUMBER {
506 			if ($2 < MIN_MAXIMUM_HOPS ||
507 			    $2 > MAX_MAXIMUM_HOPS) {
508 				yyerror("maximum-hops out of range (%d-%d)",
509 				    MIN_MAXIMUM_HOPS, MAX_MAXIMUM_HOPS);
510 				YYERROR;
511 			}
512 			defs->maximum_hops = $2;
513 		}
514 		| MAXPATHS NUMBER {
515 			if ($2 < MIN_MAXIMUM_PATHS ||
516 			    $2 > MAX_MAXIMUM_PATHS) {
517 				yyerror("maximum-paths out of range (%d-%d)",
518 				    MIN_MAXIMUM_PATHS, MAX_MAXIMUM_PATHS);
519 				YYERROR;
520 			}
521 			defs->maximum_paths = $2;
522 		}
523 		| VARIANCE NUMBER {
524 			if ($2 < MIN_VARIANCE ||
525 			    $2 > MAX_VARIANCE) {
526 				yyerror("variance out of range (%d-%d)",
527 				    MIN_VARIANCE, MAX_VARIANCE);
528 				YYERROR;
529 			}
530 			defs->variance = $2;
531 		}
532 		| DFLTMETRIC redist_metric {
533 			defs->dflt_metric = $2;
534 		}
535 		| iface_defaults
536 		;
537 
538 iface_defaults	: HELLOINTERVAL NUMBER {
539 			if ($2 < MIN_HELLO_INTERVAL ||
540 			    $2 > MAX_HELLO_INTERVAL) {
541 				yyerror("hello-interval out of range (%d-%d)",
542 				    MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
543 				YYERROR;
544 			}
545 			defs->hello_interval = $2;
546 		}
547 		| HOLDTIME NUMBER {
548 			if ($2 < MIN_HELLO_HOLDTIME ||
549 			    $2 > MAX_HELLO_HOLDTIME) {
550 				yyerror("hold-timel out of range (%d-%d)",
551 				    MIN_HELLO_HOLDTIME,
552 				    MAX_HELLO_HOLDTIME);
553 				YYERROR;
554 			}
555 			defs->hello_holdtime = $2;
556 		}
557 		| DELAY NUMBER {
558 			if ($2 < MIN_DELAY || $2 > MAX_DELAY) {
559 				yyerror("delay out of range (%d-%d)",
560 				    MIN_DELAY, MAX_DELAY);
561 				YYERROR;
562 			}
563 			defs->delay = $2;
564 		}
565 		| BANDWIDTH NUMBER {
566 			if ($2 < MIN_BANDWIDTH || $2 > MAX_BANDWIDTH) {
567 				yyerror("bandwidth out of range (%d-%d)",
568 				    MIN_BANDWIDTH, MAX_BANDWIDTH);
569 				YYERROR;
570 			}
571 			defs->bandwidth = $2;
572 		}
573 		| SPLITHORIZON yesno {
574 			defs->splithorizon = $2;
575 		}
576 		;
577 
578 %%
579 
580 struct keywords {
581 	const char	*k_name;
582 	int		 k_val;
583 };
584 
585 static int
586 yyerror(const char *fmt, ...)
587 {
588 	va_list		 ap;
589 	char		*msg;
590 
591 	file->errors++;
592 	va_start(ap, fmt);
593 	if (vasprintf(&msg, fmt, ap) == -1)
594 		fatalx("yyerror vasprintf");
595 	va_end(ap);
596 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
597 	free(msg);
598 	return (0);
599 }
600 
601 static int
602 kw_cmp(const void *k, const void *e)
603 {
604 	return (strcmp(k, ((const struct keywords *)e)->k_name));
605 }
606 
607 static int
608 lookup(char *s)
609 {
610 	/* this has to be sorted always */
611 	static const struct keywords keywords[] = {
612 		{"active-timeout",		ACTIVETIMEOUT},
613 		{"address-family",		AF},
614 		{"autonomous-system",		AS},
615 		{"bandwidth",			BANDWIDTH},
616 		{"default-metric",		DFLTMETRIC},
617 		{"delay",			DELAY},
618 		{"fib-priority-external",	FIBPRIORITY_EXT},
619 		{"fib-priority-internal",	FIBPRIORITY_INT},
620 		{"fib-priority-summary",	FIBPRIORITY_SUMM},
621 		{"fib-update",			FIBUPDATE},
622 		{"hello-interval",		HELLOINTERVAL},
623 		{"holdtime",			HOLDTIME},
624 		{"include",			INCLUDE},
625 		{"interface",			INTERFACE},
626 		{"ipv4",			IPV4},
627 		{"ipv6",			IPV6},
628 		{"k-values",			KVALUES},
629 		{"maximum-hops",		MAXHOPS},
630 		{"maximum-paths",		MAXPATHS},
631 		{"metric",			METRIC},
632 		{"no",				NO},
633 		{"passive",			PASSIVE},
634 		{"rdomain",			RDOMAIN},
635 		{"redistribute",		REDISTRIBUTE},
636 		{"router-id",			ROUTERID},
637 		{"split-horizon",		SPLITHORIZON},
638 		{"summary-address",		SUMMARY_ADDR},
639 		{"variance",			VARIANCE},
640 		{"yes",				YES}
641 	};
642 	const struct keywords	*p;
643 
644 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
645 	    sizeof(keywords[0]), kw_cmp);
646 
647 	if (p)
648 		return (p->k_val);
649 	else
650 		return (STRING);
651 }
652 
653 static int
654 lgetc(int quotec)
655 {
656 	int		c, next;
657 
658 	if (parsebuf) {
659 		/* Read character from the parsebuffer instead of input. */
660 		if (parseindex >= 0) {
661 			c = parsebuf[parseindex++];
662 			if (c != '\0')
663 				return (c);
664 			parsebuf = NULL;
665 		} else
666 			parseindex++;
667 	}
668 
669 	if (pushback_index)
670 		return (pushback_buffer[--pushback_index]);
671 
672 	if (quotec) {
673 		if ((c = getc(file->stream)) == EOF) {
674 			yyerror("reached end of file while parsing "
675 			    "quoted string");
676 			if (file == topfile || popfile() == EOF)
677 				return (EOF);
678 			return (quotec);
679 		}
680 		return (c);
681 	}
682 
683 	while ((c = getc(file->stream)) == '\\') {
684 		next = getc(file->stream);
685 		if (next != '\n') {
686 			c = next;
687 			break;
688 		}
689 		yylval.lineno = file->lineno;
690 		file->lineno++;
691 	}
692 
693 	while (c == EOF) {
694 		if (file == topfile || popfile() == EOF)
695 			return (EOF);
696 		c = getc(file->stream);
697 	}
698 	return (c);
699 }
700 
701 static int
702 lungetc(int c)
703 {
704 	if (c == EOF)
705 		return (EOF);
706 	if (parsebuf) {
707 		parseindex--;
708 		if (parseindex >= 0)
709 			return (c);
710 	}
711 	if (pushback_index < MAXPUSHBACK-1)
712 		return (pushback_buffer[pushback_index++] = c);
713 	else
714 		return (EOF);
715 }
716 
717 static int
718 findeol(void)
719 {
720 	int	c;
721 
722 	parsebuf = NULL;
723 
724 	/* skip to either EOF or the first real EOL */
725 	while (1) {
726 		if (pushback_index)
727 			c = pushback_buffer[--pushback_index];
728 		else
729 			c = lgetc(0);
730 		if (c == '\n') {
731 			file->lineno++;
732 			break;
733 		}
734 		if (c == EOF)
735 			break;
736 	}
737 	return (ERROR);
738 }
739 
740 static int
741 yylex(void)
742 {
743 	unsigned char	 buf[8096];
744 	unsigned char	*p, *val;
745 	int		 quotec, next, c;
746 	int		 token;
747 
748 top:
749 	p = buf;
750 	while ((c = lgetc(0)) == ' ' || c == '\t')
751 		; /* nothing */
752 
753 	yylval.lineno = file->lineno;
754 	if (c == '#')
755 		while ((c = lgetc(0)) != '\n' && c != EOF)
756 			; /* nothing */
757 	if (c == '$' && parsebuf == NULL) {
758 		while (1) {
759 			if ((c = lgetc(0)) == EOF)
760 				return (0);
761 
762 			if (p + 1 >= buf + sizeof(buf) - 1) {
763 				yyerror("string too long");
764 				return (findeol());
765 			}
766 			if (isalnum(c) || c == '_') {
767 				*p++ = c;
768 				continue;
769 			}
770 			*p = '\0';
771 			lungetc(c);
772 			break;
773 		}
774 		val = symget(buf);
775 		if (val == NULL) {
776 			yyerror("macro '%s' not defined", buf);
777 			return (findeol());
778 		}
779 		parsebuf = val;
780 		parseindex = 0;
781 		goto top;
782 	}
783 
784 	switch (c) {
785 	case '\'':
786 	case '"':
787 		quotec = c;
788 		while (1) {
789 			if ((c = lgetc(quotec)) == EOF)
790 				return (0);
791 			if (c == '\n') {
792 				file->lineno++;
793 				continue;
794 			} else if (c == '\\') {
795 				if ((next = lgetc(quotec)) == EOF)
796 					return (0);
797 				if (next == quotec || c == ' ' || c == '\t')
798 					c = next;
799 				else if (next == '\n') {
800 					file->lineno++;
801 					continue;
802 				} else
803 					lungetc(next);
804 			} else if (c == quotec) {
805 				*p = '\0';
806 				break;
807 			} else if (c == '\0') {
808 				yyerror("syntax error");
809 				return (findeol());
810 			}
811 			if (p + 1 >= buf + sizeof(buf) - 1) {
812 				yyerror("string too long");
813 				return (findeol());
814 			}
815 			*p++ = c;
816 		}
817 		yylval.v.string = strdup(buf);
818 		if (yylval.v.string == NULL)
819 			err(1, "yylex: strdup");
820 		return (STRING);
821 	}
822 
823 #define allowed_to_end_number(x) \
824 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
825 
826 	if (c == '-' || isdigit(c)) {
827 		do {
828 			*p++ = c;
829 			if ((unsigned)(p-buf) >= sizeof(buf)) {
830 				yyerror("string too long");
831 				return (findeol());
832 			}
833 		} while ((c = lgetc(0)) != EOF && isdigit(c));
834 		lungetc(c);
835 		if (p == buf + 1 && buf[0] == '-')
836 			goto nodigits;
837 		if (c == EOF || allowed_to_end_number(c)) {
838 			const char *errstr = NULL;
839 
840 			*p = '\0';
841 			yylval.v.number = strtonum(buf, LLONG_MIN,
842 			    LLONG_MAX, &errstr);
843 			if (errstr) {
844 				yyerror("\"%s\" invalid number: %s",
845 				    buf, errstr);
846 				return (findeol());
847 			}
848 			return (NUMBER);
849 		} else {
850 nodigits:
851 			while (p > buf + 1)
852 				lungetc(*--p);
853 			c = *--p;
854 			if (c == '-')
855 				return (c);
856 		}
857 	}
858 
859 #define allowed_in_string(x) \
860 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
861 	x != '{' && x != '}' && \
862 	x != '!' && x != '=' && x != '#' && \
863 	x != ','))
864 
865 	if (isalnum(c) || c == ':' || c == '_') {
866 		do {
867 			*p++ = c;
868 			if ((unsigned)(p-buf) >= sizeof(buf)) {
869 				yyerror("string too long");
870 				return (findeol());
871 			}
872 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
873 		lungetc(c);
874 		*p = '\0';
875 		if ((token = lookup(buf)) == STRING)
876 			if ((yylval.v.string = strdup(buf)) == NULL)
877 				err(1, "yylex: strdup");
878 		return (token);
879 	}
880 	if (c == '\n') {
881 		yylval.lineno = file->lineno;
882 		file->lineno++;
883 	}
884 	if (c == EOF)
885 		return (0);
886 	return (c);
887 }
888 
889 static int
890 check_file_secrecy(int fd, const char *fname)
891 {
892 	struct stat	st;
893 
894 	if (fstat(fd, &st)) {
895 		log_warn("cannot stat %s", fname);
896 		return (-1);
897 	}
898 	if (st.st_uid != 0 && st.st_uid != getuid()) {
899 		log_warnx("%s: owner not root or current user", fname);
900 		return (-1);
901 	}
902 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
903 		log_warnx("%s: group writable or world read/writable", fname);
904 		return (-1);
905 	}
906 	return (0);
907 }
908 
909 static struct file *
910 pushfile(const char *name, int secret)
911 {
912 	struct file	*nfile;
913 
914 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
915 		log_warn("calloc");
916 		return (NULL);
917 	}
918 	if ((nfile->name = strdup(name)) == NULL) {
919 		log_warn("strdup");
920 		free(nfile);
921 		return (NULL);
922 	}
923 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
924 		log_warn("%s", nfile->name);
925 		free(nfile->name);
926 		free(nfile);
927 		return (NULL);
928 	} else if (secret &&
929 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
930 		fclose(nfile->stream);
931 		free(nfile->name);
932 		free(nfile);
933 		return (NULL);
934 	}
935 	nfile->lineno = 1;
936 	TAILQ_INSERT_TAIL(&files, nfile, entry);
937 	return (nfile);
938 }
939 
940 static int
941 popfile(void)
942 {
943 	struct file	*prev;
944 
945 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
946 		prev->errors += file->errors;
947 
948 	TAILQ_REMOVE(&files, file, entry);
949 	fclose(file->stream);
950 	free(file->name);
951 	free(file);
952 	file = prev;
953 	return (file ? 0 : EOF);
954 }
955 
956 struct eigrpd_conf *
957 parse_config(char *filename)
958 {
959 	struct sym	*sym, *next;
960 
961 	conf = config_new_empty();
962 	conf->rdomain = 0;
963 	conf->fib_priority_internal = RTP_EIGRP;
964 	conf->fib_priority_external = RTP_EIGRP;
965 	conf->fib_priority_summary = RTP_EIGRP;
966 
967 	defs = &globaldefs;
968 	defs->kvalues[0] = defs->kvalues[2] = 1;
969 	defs->active_timeout = DEFAULT_ACTIVE_TIMEOUT;
970 	defs->maximum_hops = DEFAULT_MAXIMUM_HOPS;
971 	defs->maximum_paths = DEFAULT_MAXIMUM_PATHS;
972 	defs->variance = DEFAULT_VARIANCE;
973 	defs->hello_holdtime = DEFAULT_HELLO_HOLDTIME;
974 	defs->hello_interval = DEFAULT_HELLO_INTERVAL;
975 	defs->delay = DEFAULT_DELAY;
976 	defs->bandwidth = DEFAULT_BANDWIDTH;
977 	defs->splithorizon = 1;
978 
979 	if ((file = pushfile(filename,
980 	    !(global.cmd_opts & EIGRPD_OPT_NOACTION))) == NULL) {
981 		free(conf);
982 		return (NULL);
983 	}
984 	topfile = file;
985 
986 	yyparse();
987 	errors = file->errors;
988 	popfile();
989 
990 	/* Free macros and check which have not been used. */
991 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
992 		next = TAILQ_NEXT(sym, entry);
993 		if ((global.cmd_opts & EIGRPD_OPT_VERBOSE2) && !sym->used)
994 			fprintf(stderr, "warning: macro '%s' not "
995 			    "used\n", sym->nam);
996 		if (!sym->persist) {
997 			free(sym->nam);
998 			free(sym->val);
999 			TAILQ_REMOVE(&symhead, sym, entry);
1000 			free(sym);
1001 		}
1002 	}
1003 
1004 	if (errors) {
1005 		clear_config(conf);
1006 		return (NULL);
1007 	}
1008 
1009 	if (conf->rtr_id.s_addr == 0)
1010 		conf->rtr_id.s_addr = get_rtr_id();
1011 
1012 	return (conf);
1013 }
1014 
1015 static int
1016 symset(const char *nam, const char *val, int persist)
1017 {
1018 	struct sym	*sym;
1019 
1020 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
1021 	    sym = TAILQ_NEXT(sym, entry))
1022 		;	/* nothing */
1023 
1024 	if (sym != NULL) {
1025 		if (sym->persist == 1)
1026 			return (0);
1027 		else {
1028 			free(sym->nam);
1029 			free(sym->val);
1030 			TAILQ_REMOVE(&symhead, sym, entry);
1031 			free(sym);
1032 		}
1033 	}
1034 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1035 		return (-1);
1036 
1037 	sym->nam = strdup(nam);
1038 	if (sym->nam == NULL) {
1039 		free(sym);
1040 		return (-1);
1041 	}
1042 	sym->val = strdup(val);
1043 	if (sym->val == NULL) {
1044 		free(sym->nam);
1045 		free(sym);
1046 		return (-1);
1047 	}
1048 	sym->used = 0;
1049 	sym->persist = persist;
1050 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1051 	return (0);
1052 }
1053 
1054 int
1055 cmdline_symset(char *s)
1056 {
1057 	char	*sym, *val;
1058 	int	ret;
1059 	size_t	len;
1060 
1061 	if ((val = strrchr(s, '=')) == NULL)
1062 		return (-1);
1063 
1064 	len = strlen(s) - strlen(val) + 1;
1065 	if ((sym = malloc(len)) == NULL)
1066 		errx(1, "cmdline_symset: malloc");
1067 
1068 	strlcpy(sym, s, len);
1069 
1070 	ret = symset(sym, val + 1, 1);
1071 	free(sym);
1072 
1073 	return (ret);
1074 }
1075 
1076 static char *
1077 symget(const char *nam)
1078 {
1079 	struct sym	*sym;
1080 
1081 	TAILQ_FOREACH(sym, &symhead, entry)
1082 		if (strcmp(nam, sym->nam) == 0) {
1083 			sym->used = 1;
1084 			return (sym->val);
1085 		}
1086 	return (NULL);
1087 }
1088 
1089 static struct eigrp *
1090 conf_get_instance(uint16_t as)
1091 {
1092 	struct eigrp	*e, *tmp;
1093 
1094 	if (eigrp_find(conf, af, as)) {
1095 		yyerror("autonomous-system %u already configured"
1096 		    "for address-family %s", as, af_name(af));
1097 		return (NULL);
1098 	}
1099 
1100 	e = calloc(1, sizeof(struct eigrp));
1101 	if (e == NULL)
1102 		fatal(NULL);
1103 
1104 	e->af = af;
1105 	e->as = as;
1106 	SIMPLEQ_INIT(&e->redist_list);
1107 	TAILQ_INIT(&e->ei_list);
1108 	RB_INIT(&e->nbrs);
1109 	RB_INIT(&e->topology);
1110 
1111 	/* start local sequence number used by RTP */
1112 	e->seq_num = 1;
1113 
1114 	/* order by address-family and then by autonomous-system */
1115 	TAILQ_FOREACH(tmp, &conf->instances, entry)
1116 		if (tmp->af > e->af ||
1117 		    (tmp->af == e->af && tmp->as > e->as))
1118 			break;
1119 	if (tmp)
1120 		TAILQ_INSERT_BEFORE(tmp, e, entry);
1121 	else
1122 		TAILQ_INSERT_TAIL(&conf->instances, e, entry);
1123 
1124 	return (e);
1125 }
1126 
1127 static struct eigrp_iface *
1128 conf_get_if(struct kif *kif)
1129 {
1130 	struct eigrp_iface	*e;
1131 
1132 	TAILQ_FOREACH(e, &eigrp->ei_list, e_entry)
1133 		if (e->iface->ifindex == kif->ifindex) {
1134 			yyerror("interface %s already configured "
1135 			    "for address-family %s and "
1136 			    "autonomous-system %u", kif->ifname,
1137 			    af_name(af), eigrp->as);
1138 			return (NULL);
1139 		}
1140 
1141 	e = eigrp_if_new(conf, eigrp, kif);
1142 
1143 	return (e);
1144 }
1145 
1146 static void
1147 clear_config(struct eigrpd_conf *xconf)
1148 {
1149 	struct eigrp		*e;
1150 	struct redistribute	*r;
1151 	struct eigrp_iface	*i;
1152 	struct summary_addr	*s;
1153 
1154 	while ((e = TAILQ_FIRST(&xconf->instances)) != NULL) {
1155 		while (!SIMPLEQ_EMPTY(&e->redist_list)) {
1156 			r = SIMPLEQ_FIRST(&e->redist_list);
1157 			SIMPLEQ_REMOVE_HEAD(&e->redist_list, entry);
1158 			free(r);
1159 		}
1160 
1161 		while ((i = TAILQ_FIRST(&e->ei_list)) != NULL) {
1162 			RB_REMOVE(iface_id_head, &ifaces_by_id, i);
1163 			TAILQ_REMOVE(&e->ei_list, i, e_entry);
1164 			TAILQ_REMOVE(&e->ei_list, i, i_entry);
1165 			while ((s = TAILQ_FIRST(&i->summary_list)) != NULL) {
1166 				TAILQ_REMOVE(&i->summary_list, s, entry);
1167 				free(s);
1168 			}
1169 			if (TAILQ_EMPTY(&i->iface->ei_list)) {
1170 				TAILQ_REMOVE(&xconf->iface_list, i->iface, entry);
1171 				free(i->iface);
1172 			}
1173 			free(i);
1174 		}
1175 
1176 		TAILQ_REMOVE(&xconf->instances, e, entry);
1177 		free(e);
1178 	}
1179 
1180 	free(xconf);
1181 }
1182 
1183 static uint32_t
1184 get_rtr_id(void)
1185 {
1186 	struct ifaddrs		*ifap, *ifa;
1187 	uint32_t		 ip = 0, cur, localnet;
1188 
1189 	localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
1190 
1191 	if (getifaddrs(&ifap) == -1)
1192 		fatal("getifaddrs");
1193 
1194 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1195 		if (strncmp(ifa->ifa_name, "carp", 4) == 0)
1196 			continue;
1197 		if (ifa->ifa_addr->sa_family != AF_INET)
1198 			continue;
1199 		cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
1200 		if ((cur & localnet) == localnet)	/* skip 127/8 */
1201 			continue;
1202 		if (ntohl(cur) < ntohl(ip) || ip == 0)
1203 			ip = cur;
1204 	}
1205 	freeifaddrs(ifap);
1206 
1207 	if (ip == 0)
1208 		fatal("router-id is 0.0.0.0");
1209 
1210 	return (ip);
1211 }
1212 
1213 static int
1214 get_prefix(const char *s, union eigrpd_addr *addr, uint8_t *plen)
1215 {
1216 	char			*p, *ps;
1217 	const char		*errstr;
1218 	int			 maxplen;
1219 
1220 	switch (af) {
1221 	case AF_INET:
1222 		maxplen = 32;
1223 		break;
1224 	case AF_INET6:
1225 		maxplen = 128;
1226 		break;
1227 	default:
1228 		return (-1);
1229 	}
1230 
1231 	if ((p = strrchr(s, '/')) != NULL) {
1232 		*plen = strtonum(p + 1, 0, maxplen, &errstr);
1233 		if (errstr) {
1234 			log_warnx("prefixlen is %s: %s", errstr, p + 1);
1235 			return (-1);
1236 		}
1237 		if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
1238 			fatal("get_prefix: malloc");
1239 		strlcpy(ps, s, strlen(s) - strlen(p) + 1);
1240 	} else {
1241 		if ((ps = strdup(s)) == NULL)
1242 			fatal("get_prefix: strdup");
1243 		*plen = maxplen;
1244 	}
1245 
1246 	memset(addr, 0, sizeof(union eigrpd_addr));
1247 	switch (af) {
1248 	case AF_INET:
1249 		if (inet_pton(AF_INET, ps, &addr->v4) != 1) {
1250 			free(ps);
1251 			return (-1);
1252 		}
1253 		break;
1254 	case AF_INET6:
1255 		if (inet_pton(AF_INET6, ps, &addr->v6) != 1) {
1256 			free(ps);
1257 			return (-1);
1258 		}
1259 		break;
1260 	default:
1261 		free(ps);
1262 		return (-1);
1263 	}
1264 	eigrp_applymask(af, addr, addr, *plen);
1265 	free(ps);
1266 
1267 	return (0);
1268 }
1269