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