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