1 /* $OpenBSD: parse.y,v 1.34 2024/08/22 08:17:54 florian 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_pton(AF_INET, $2, &conf->rtr_id) != 1) {
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
yyerror(const char * fmt,...)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
kw_cmp(const void * k,const void * e)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
lookup(char * s)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
igetc(void)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
lgetc(int quotec)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
lungetc(int c)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
findeol(void)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
yylex(void)761 yylex(void)
762 {
763 char buf[8096];
764 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((unsigned char)*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((unsigned char)*--p);
879 c = (unsigned char)*--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
check_file_secrecy(int fd,const char * fname)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 *
pushfile(const char * name,int secret)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
popfile(void)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 *
parse_config(char * filename)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
symset(const char * nam,const char * val,int persist)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
cmdline_symset(char * s)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 *
symget(const char * nam)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 *
conf_get_instance(uint16_t as)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 *
conf_get_if(struct kif * kif)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
conf_check_rdomain(unsigned int rdomain)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
clear_config(struct eigrpd_conf * xconf)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
get_rtr_id(void)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 == NULL ||
1250 ifa->ifa_addr->sa_family != AF_INET)
1251 continue;
1252 cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
1253 if ((cur & localnet) == localnet) /* skip 127/8 */
1254 continue;
1255 if (ntohl(cur) < ntohl(ip) || ip == 0)
1256 ip = cur;
1257 }
1258 freeifaddrs(ifap);
1259
1260 if (ip == 0)
1261 fatal("router-id is 0.0.0.0");
1262
1263 return (ip);
1264 }
1265
1266 static int
get_prefix(const char * s,union eigrpd_addr * addr,uint8_t * plen)1267 get_prefix(const char *s, union eigrpd_addr *addr, uint8_t *plen)
1268 {
1269 char *p, *ps;
1270 const char *errstr;
1271 int maxplen;
1272
1273 switch (af) {
1274 case AF_INET:
1275 maxplen = 32;
1276 break;
1277 case AF_INET6:
1278 maxplen = 128;
1279 break;
1280 default:
1281 return (-1);
1282 }
1283
1284 if ((p = strrchr(s, '/')) != NULL) {
1285 *plen = strtonum(p + 1, 0, maxplen, &errstr);
1286 if (errstr) {
1287 log_warnx("prefixlen is %s: %s", errstr, p + 1);
1288 return (-1);
1289 }
1290 if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
1291 fatal("get_prefix: malloc");
1292 strlcpy(ps, s, strlen(s) - strlen(p) + 1);
1293 } else {
1294 if ((ps = strdup(s)) == NULL)
1295 fatal("get_prefix: strdup");
1296 *plen = maxplen;
1297 }
1298
1299 memset(addr, 0, sizeof(union eigrpd_addr));
1300 switch (af) {
1301 case AF_INET:
1302 if (inet_pton(AF_INET, ps, &addr->v4) != 1) {
1303 free(ps);
1304 return (-1);
1305 }
1306 break;
1307 case AF_INET6:
1308 if (inet_pton(AF_INET6, ps, &addr->v6) != 1) {
1309 free(ps);
1310 return (-1);
1311 }
1312 break;
1313 default:
1314 free(ps);
1315 return (-1);
1316 }
1317 eigrp_applymask(af, addr, addr, *plen);
1318 free(ps);
1319
1320 return (0);
1321 }
1322