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