1 /* $OpenBSD: parse.y,v 1.51 2021/10/15 15:01:28 naddy Exp $ */ 2 3 /* 4 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 5 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 7 * Copyright (c) 2001 Markus Friedl. All rights reserved. 8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 9 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 10 * 11 * Permission to use, copy, modify, and distribute this software for any 12 * purpose with or without fee is hereby granted, provided that the above 13 * copyright notice and this permission notice appear in all copies. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24 %{ 25 #include <sys/types.h> 26 #include <sys/socket.h> 27 #include <sys/stat.h> 28 #include <net/route.h> 29 #include <netinet/in.h> 30 #include <arpa/inet.h> 31 32 #include <ctype.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <unistd.h> 36 #include <ifaddrs.h> 37 #include <limits.h> 38 #include <netdb.h> 39 #include <stdarg.h> 40 #include <stdio.h> 41 #include <string.h> 42 #include <syslog.h> 43 44 #include "ospf6.h" 45 #include "ospf6d.h" 46 #include "ospfe.h" 47 #include "log.h" 48 49 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 50 static struct file { 51 TAILQ_ENTRY(file) entry; 52 FILE *stream; 53 char *name; 54 size_t ungetpos; 55 size_t ungetsize; 56 u_char *ungetbuf; 57 int eof_reached; 58 int lineno; 59 int errors; 60 } *file, *topfile; 61 struct file *pushfile(const char *, int); 62 int popfile(void); 63 int check_file_secrecy(int, const char *); 64 int yyparse(void); 65 int yylex(void); 66 int yyerror(const char *, ...) 67 __attribute__((__format__ (printf, 1, 2))) 68 __attribute__((__nonnull__ (1))); 69 int kw_cmp(const void *, const void *); 70 int lookup(char *); 71 int igetc(void); 72 int lgetc(int); 73 void lungetc(int); 74 int findeol(void); 75 76 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 77 struct sym { 78 TAILQ_ENTRY(sym) entry; 79 int used; 80 int persist; 81 char *nam; 82 char *val; 83 }; 84 int symset(const char *, const char *, int); 85 char *symget(const char *); 86 87 void clear_config(struct ospfd_conf *xconf); 88 u_int32_t get_rtr_id(void); 89 int host(const char *, struct in6_addr *); 90 int prefix(const char *, struct in6_addr *, u_int8_t *); 91 92 static struct ospfd_conf *conf; 93 static int errors = 0; 94 95 struct area *area = NULL; 96 struct iface *iface = NULL; 97 98 struct config_defaults { 99 u_int16_t dead_interval; 100 u_int16_t transmit_delay; 101 u_int16_t hello_interval; 102 u_int16_t rxmt_interval; 103 u_int16_t metric; 104 u_int8_t priority; 105 u_int8_t p2p; 106 }; 107 108 struct config_defaults globaldefs; 109 struct config_defaults areadefs; 110 struct config_defaults ifacedefs; 111 struct config_defaults *defs; 112 113 struct area *conf_get_area(struct in_addr); 114 int conf_check_rdomain(u_int); 115 116 typedef struct { 117 union { 118 int64_t number; 119 char *string; 120 struct redistribute *redist; 121 struct in_addr id; 122 } v; 123 int lineno; 124 } YYSTYPE; 125 126 %} 127 128 %token AREA INTERFACE ROUTERID FIBPRIORITY FIBUPDATE REDISTRIBUTE RTLABEL 129 %token RDOMAIN STUB ROUTER SPFDELAY SPFHOLDTIME EXTTAG 130 %token METRIC P2P PASSIVE 131 %token HELLOINTERVAL TRANSMITDELAY 132 %token RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY 133 %token SET TYPE 134 %token YES NO 135 %token DEMOTE 136 %token INCLUDE 137 %token ERROR 138 %token DEPEND ON 139 %token <v.string> STRING 140 %token <v.number> NUMBER 141 %type <v.number> yesno no optlist, optlist_l option demotecount 142 %type <v.string> string dependon 143 %type <v.redist> redistribute 144 %type <v.id> areaid 145 146 %% 147 148 grammar : /* empty */ 149 | grammar include '\n' 150 | grammar '\n' 151 | grammar conf_main '\n' 152 | grammar varset '\n' 153 | grammar area '\n' 154 | grammar error '\n' { file->errors++; } 155 ; 156 157 include : INCLUDE STRING { 158 struct file *nfile; 159 160 if ((nfile = pushfile($2, 161 !(conf->opts & OSPFD_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 yesno : YES { $$ = 1; } 187 | NO { $$ = 0; } 188 ; 189 190 no : /* empty */ { $$ = 0; } 191 | NO { $$ = 1; } 192 193 varset : STRING '=' string { 194 char *s = $1; 195 if (conf->opts & OSPFD_OPT_VERBOSE) 196 printf("%s = \"%s\"\n", $1, $3); 197 while (*s++) { 198 if (isspace((unsigned char)*s)) { 199 yyerror("macro name cannot contain " 200 "whitespace"); 201 free($1); 202 free($3); 203 YYERROR; 204 } 205 } 206 if (symset($1, $3, 0) == -1) 207 fatal("cannot store variable"); 208 free($1); 209 free($3); 210 } 211 ; 212 213 conf_main : ROUTERID STRING { 214 if (!inet_aton($2, &conf->rtr_id)) { 215 yyerror("error parsing router-id"); 216 free($2); 217 YYERROR; 218 } 219 free($2); 220 } 221 | FIBPRIORITY NUMBER { 222 if ($2 <= RTP_NONE || $2 > RTP_MAX) { 223 yyerror("invalid fib-priority"); 224 YYERROR; 225 } 226 conf->fib_priority = $2; 227 } 228 | FIBUPDATE yesno { 229 if ($2 == 0) 230 conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE; 231 else 232 conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE; 233 } 234 | redistribute { 235 SIMPLEQ_INSERT_TAIL(&conf->redist_list, $1, entry); 236 conf->redistribute = 1; 237 } 238 | RTLABEL STRING EXTTAG NUMBER { 239 if ($4 < 0 || $4 > UINT_MAX) { 240 yyerror("invalid external route tag"); 241 free($2); 242 YYERROR; 243 } 244 rtlabel_tag(rtlabel_name2id($2), $4); 245 free($2); 246 } 247 | RDOMAIN NUMBER { 248 if ($2 < 0 || $2 > RT_TABLEID_MAX) { 249 yyerror("invalid rdomain"); 250 YYERROR; 251 } 252 conf->rdomain = $2; 253 } 254 | SPFDELAY NUMBER { 255 if ($2 < MIN_SPF_DELAY || $2 > MAX_SPF_DELAY) { 256 yyerror("spf-delay out of range " 257 "(%d-%d)", MIN_SPF_DELAY, 258 MAX_SPF_DELAY); 259 YYERROR; 260 } 261 conf->spf_delay = $2; 262 } 263 | SPFHOLDTIME NUMBER { 264 if ($2 < MIN_SPF_HOLDTIME || $2 > MAX_SPF_HOLDTIME) { 265 yyerror("spf-holdtime out of range " 266 "(%d-%d)", MIN_SPF_HOLDTIME, 267 MAX_SPF_HOLDTIME); 268 YYERROR; 269 } 270 conf->spf_hold_time = $2; 271 } 272 | STUB ROUTER yesno { 273 if ($3) 274 conf->flags |= OSPFD_FLAG_STUB_ROUTER; 275 else 276 /* allow to force non stub mode */ 277 conf->flags &= ~OSPFD_FLAG_STUB_ROUTER; 278 } 279 | defaults 280 ; 281 282 redistribute : no REDISTRIBUTE STRING optlist dependon { 283 struct redistribute *r; 284 285 if ((r = calloc(1, sizeof(*r))) == NULL) 286 fatal(NULL); 287 if (!strcmp($3, "default")) 288 r->type = REDIST_DEFAULT; 289 else if (!strcmp($3, "static")) 290 r->type = REDIST_STATIC; 291 else if (!strcmp($3, "connected")) 292 r->type = REDIST_CONNECTED; 293 else if (prefix($3, &r->addr, &r->prefixlen)) { 294 r->type = REDIST_ADDR; 295 conf->redist_label_or_prefix = !$1; 296 } 297 else { 298 yyerror("unknown redistribute type"); 299 free($3); 300 free(r); 301 YYERROR; 302 } 303 304 if ($1) 305 r->type |= REDIST_NO; 306 r->metric = $4; 307 if ($5) 308 strlcpy(r->dependon, $5, sizeof(r->dependon)); 309 else 310 r->dependon[0] = '\0'; 311 free($3); 312 free($5); 313 $$ = r; 314 } 315 | no REDISTRIBUTE RTLABEL STRING optlist dependon { 316 struct redistribute *r; 317 318 if ((r = calloc(1, sizeof(*r))) == NULL) 319 fatal(NULL); 320 r->type = REDIST_LABEL; 321 r->label = rtlabel_name2id($4); 322 if ($1) 323 r->type |= REDIST_NO; 324 else 325 conf->redist_label_or_prefix = 1; 326 r->metric = $5; 327 if ($6) 328 strlcpy(r->dependon, $6, sizeof(r->dependon)); 329 else 330 r->dependon[0] = '\0'; 331 free($4); 332 free($6); 333 $$ = r; 334 } 335 ; 336 337 optlist : /* empty */ { $$ = DEFAULT_REDIST_METRIC; } 338 | SET option { 339 $$ = $2; 340 if (($$ & LSA_METRIC_MASK) == 0) 341 $$ |= DEFAULT_REDIST_METRIC; 342 } 343 | SET optnl '{' optnl optlist_l optnl '}' { 344 $$ = $5; 345 if (($$ & LSA_METRIC_MASK) == 0) 346 $$ |= DEFAULT_REDIST_METRIC; 347 } 348 ; 349 350 optlist_l : optlist_l comma option { 351 if ($1 & LSA_ASEXT_E_FLAG && $3 & LSA_ASEXT_E_FLAG) { 352 yyerror("redistribute type already defined"); 353 YYERROR; 354 } 355 if ($1 & LSA_METRIC_MASK && $3 & LSA_METRIC_MASK) { 356 yyerror("redistribute metric already defined"); 357 YYERROR; 358 } 359 $$ = $1 | $3; 360 } 361 | option { $$ = $1; } 362 ; 363 364 option : METRIC NUMBER { 365 if ($2 == 0 || $2 > MAX_METRIC) { 366 yyerror("invalid redistribute metric"); 367 YYERROR; 368 } 369 $$ = $2; 370 } 371 | TYPE NUMBER { 372 switch ($2) { 373 case 1: 374 $$ = 0; 375 break; 376 case 2: 377 $$ = LSA_ASEXT_E_FLAG; 378 break; 379 default: 380 yyerror("only external type 1 and 2 allowed"); 381 YYERROR; 382 } 383 } 384 ; 385 386 dependon : /* empty */ { $$ = NULL; } 387 | DEPEND ON STRING { 388 if (strlen($3) >= IFNAMSIZ) { 389 yyerror("interface name %s too long", $3); 390 free($3); 391 YYERROR; 392 } 393 if ((if_findname($3)) == NULL) { 394 yyerror("unknown interface %s", $3); 395 free($3); 396 YYERROR; 397 } 398 $$ = $3; 399 } 400 ; 401 402 defaults : METRIC NUMBER { 403 if ($2 < MIN_METRIC || $2 > MAX_METRIC) { 404 yyerror("metric out of range (%d-%d)", 405 MIN_METRIC, MAX_METRIC); 406 YYERROR; 407 } 408 defs->metric = $2; 409 } 410 | ROUTERPRIORITY NUMBER { 411 if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) { 412 yyerror("router-priority out of range (%d-%d)", 413 MIN_PRIORITY, MAX_PRIORITY); 414 YYERROR; 415 } 416 defs->priority = $2; 417 } 418 | ROUTERDEADTIME NUMBER { 419 if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) { 420 yyerror("router-dead-time out of range (%d-%d)", 421 MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME); 422 YYERROR; 423 } 424 defs->dead_interval = $2; 425 } 426 | TRANSMITDELAY NUMBER { 427 if ($2 < MIN_TRANSMIT_DELAY || 428 $2 > MAX_TRANSMIT_DELAY) { 429 yyerror("transmit-delay out of range (%d-%d)", 430 MIN_TRANSMIT_DELAY, MAX_TRANSMIT_DELAY); 431 YYERROR; 432 } 433 defs->transmit_delay = $2; 434 } 435 | HELLOINTERVAL NUMBER { 436 if ($2 < MIN_HELLO_INTERVAL || 437 $2 > MAX_HELLO_INTERVAL) { 438 yyerror("hello-interval out of range (%d-%d)", 439 MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL); 440 YYERROR; 441 } 442 defs->hello_interval = $2; 443 } 444 | RETRANSMITINTERVAL NUMBER { 445 if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) { 446 yyerror("retransmit-interval out of range " 447 "(%d-%d)", MIN_RXMT_INTERVAL, 448 MAX_RXMT_INTERVAL); 449 YYERROR; 450 } 451 defs->rxmt_interval = $2; 452 } 453 | TYPE P2P { 454 defs->p2p = 1; 455 } 456 ; 457 458 optnl : '\n' optnl 459 | 460 ; 461 462 nl : '\n' optnl /* one newline or more */ 463 ; 464 465 comma : ',' 466 | /*empty*/ 467 ; 468 469 area : AREA areaid { 470 area = conf_get_area($2); 471 472 memcpy(&areadefs, defs, sizeof(areadefs)); 473 defs = &areadefs; 474 } '{' optnl areaopts_l '}' { 475 area = NULL; 476 defs = &globaldefs; 477 } 478 ; 479 480 demotecount : NUMBER { $$ = $1; } 481 | /*empty*/ { $$ = 1; } 482 ; 483 484 areaid : NUMBER { 485 if ($1 < 0 || $1 > 0xffffffff) { 486 yyerror("invalid area id"); 487 YYERROR; 488 } 489 $$.s_addr = htonl($1); 490 } 491 | STRING { 492 if (inet_aton($1, &$$) == 0) { 493 yyerror("error parsing area"); 494 free($1); 495 YYERROR; 496 } 497 free($1); 498 } 499 ; 500 501 areaopts_l : areaopts_l areaoptsl nl 502 | areaoptsl optnl 503 ; 504 505 areaoptsl : interface 506 | DEMOTE STRING demotecount { 507 if ($3 < 1 || $3 > 255) { 508 yyerror("demote count out of range (1-255)"); 509 free($2); 510 YYERROR; 511 } 512 area->demote_level = $3; 513 if (strlcpy(area->demote_group, $2, 514 sizeof(area->demote_group)) >= 515 sizeof(area->demote_group)) { 516 yyerror("demote group name \"%s\" too long", 517 $2); 518 free($2); 519 YYERROR; 520 } 521 free($2); 522 if (carp_demote_init(area->demote_group, 523 conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) { 524 yyerror("error initializing group \"%s\"", 525 area->demote_group); 526 YYERROR; 527 } 528 } 529 | defaults 530 ; 531 532 interface : INTERFACE STRING { 533 if ((iface = if_findname($2)) == NULL) { 534 yyerror("unknown interface %s", $2); 535 free($2); 536 YYERROR; 537 } 538 if (IN6_IS_ADDR_UNSPECIFIED(&iface->addr)) { 539 yyerror("unnumbered interface %s", $2); 540 free($2); 541 YYERROR; 542 } 543 free($2); 544 iface->area = area; 545 LIST_INSERT_HEAD(&area->iface_list, iface, entry); 546 547 memcpy(&ifacedefs, defs, sizeof(ifacedefs)); 548 defs = &ifacedefs; 549 } interface_block { 550 iface->dead_interval = defs->dead_interval; 551 iface->transmit_delay = defs->transmit_delay; 552 iface->hello_interval = defs->hello_interval; 553 iface->rxmt_interval = defs->rxmt_interval; 554 iface->metric = defs->metric; 555 iface->priority = defs->priority; 556 iface->cflags |= F_IFACE_CONFIGURED; 557 if (defs->p2p == 1) 558 iface->type = IF_TYPE_POINTOPOINT; 559 iface = NULL; 560 /* interface is always part of an area */ 561 defs = &areadefs; 562 } 563 ; 564 565 interface_block : '{' optnl interfaceopts_l '}' 566 | '{' optnl '}' 567 | 568 ; 569 570 interfaceopts_l : interfaceopts_l interfaceoptsl nl 571 | interfaceoptsl optnl 572 ; 573 574 interfaceoptsl : PASSIVE { iface->cflags |= F_IFACE_PASSIVE; } 575 | DEMOTE STRING { 576 if (strlcpy(iface->demote_group, $2, 577 sizeof(iface->demote_group)) >= 578 sizeof(iface->demote_group)) { 579 yyerror("demote group name \"%s\" too long", 580 $2); 581 free($2); 582 YYERROR; 583 } 584 free($2); 585 if (carp_demote_init(iface->demote_group, 586 conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) { 587 yyerror("error initializing group \"%s\"", 588 iface->demote_group); 589 YYERROR; 590 } 591 } 592 | dependon { 593 struct iface *depend_if = NULL; 594 595 if ($1) { 596 strlcpy(iface->dependon, $1, 597 sizeof(iface->dependon)); 598 depend_if = if_findname($1); 599 iface->depend_ok = ifstate_is_up(depend_if); 600 } else { 601 iface->dependon[0] = '\0'; 602 iface->depend_ok = 1; 603 } 604 } 605 | defaults 606 ; 607 608 %% 609 610 struct keywords { 611 const char *k_name; 612 int k_val; 613 }; 614 615 int 616 yyerror(const char *fmt, ...) 617 { 618 va_list ap; 619 char *msg; 620 621 file->errors++; 622 va_start(ap, fmt); 623 if (vasprintf(&msg, fmt, ap) == -1) 624 fatalx("yyerror vasprintf"); 625 va_end(ap); 626 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 627 free(msg); 628 return (0); 629 } 630 631 int 632 kw_cmp(const void *k, const void *e) 633 { 634 return (strcmp(k, ((const struct keywords *)e)->k_name)); 635 } 636 637 int 638 lookup(char *s) 639 { 640 /* this has to be sorted always */ 641 static const struct keywords keywords[] = { 642 {"area", AREA}, 643 {"demote", DEMOTE}, 644 {"depend", DEPEND}, 645 {"external-tag", EXTTAG}, 646 {"fib-priority", FIBPRIORITY}, 647 {"fib-update", FIBUPDATE}, 648 {"hello-interval", HELLOINTERVAL}, 649 {"include", INCLUDE}, 650 {"interface", INTERFACE}, 651 {"metric", METRIC}, 652 {"no", NO}, 653 {"on", ON}, 654 {"p2p", P2P}, 655 {"passive", PASSIVE}, 656 {"rdomain", RDOMAIN}, 657 {"redistribute", REDISTRIBUTE}, 658 {"retransmit-interval", RETRANSMITINTERVAL}, 659 {"router", ROUTER}, 660 {"router-dead-time", ROUTERDEADTIME}, 661 {"router-id", ROUTERID}, 662 {"router-priority", ROUTERPRIORITY}, 663 {"rtlabel", RTLABEL}, 664 {"set", SET}, 665 {"spf-delay", SPFDELAY}, 666 {"spf-holdtime", SPFHOLDTIME}, 667 {"stub", STUB}, 668 {"transmit-delay", TRANSMITDELAY}, 669 {"type", TYPE}, 670 {"yes", YES} 671 }; 672 const struct keywords *p; 673 674 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 675 sizeof(keywords[0]), kw_cmp); 676 677 if (p) 678 return (p->k_val); 679 else 680 return (STRING); 681 } 682 683 #define START_EXPAND 1 684 #define DONE_EXPAND 2 685 686 static int expanding; 687 688 int 689 igetc(void) 690 { 691 int c; 692 693 while (1) { 694 if (file->ungetpos > 0) 695 c = file->ungetbuf[--file->ungetpos]; 696 else 697 c = getc(file->stream); 698 699 if (c == START_EXPAND) 700 expanding = 1; 701 else if (c == DONE_EXPAND) 702 expanding = 0; 703 else 704 break; 705 } 706 return (c); 707 } 708 709 int 710 lgetc(int quotec) 711 { 712 int c, next; 713 714 if (quotec) { 715 if ((c = igetc()) == EOF) { 716 yyerror("reached end of file while parsing " 717 "quoted string"); 718 if (file == topfile || popfile() == EOF) 719 return (EOF); 720 return (quotec); 721 } 722 return (c); 723 } 724 725 while ((c = igetc()) == '\\') { 726 next = igetc(); 727 if (next != '\n') { 728 c = next; 729 break; 730 } 731 yylval.lineno = file->lineno; 732 file->lineno++; 733 } 734 735 if (c == EOF) { 736 /* 737 * Fake EOL when hit EOF for the first time. This gets line 738 * count right if last line in included file is syntactically 739 * invalid and has no newline. 740 */ 741 if (file->eof_reached == 0) { 742 file->eof_reached = 1; 743 return ('\n'); 744 } 745 while (c == EOF) { 746 if (file == topfile || popfile() == EOF) 747 return (EOF); 748 c = igetc(); 749 } 750 } 751 return (c); 752 } 753 754 void 755 lungetc(int c) 756 { 757 if (c == EOF) 758 return; 759 760 if (file->ungetpos >= file->ungetsize) { 761 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); 762 if (p == NULL) 763 err(1, "%s", __func__); 764 file->ungetbuf = p; 765 file->ungetsize *= 2; 766 } 767 file->ungetbuf[file->ungetpos++] = c; 768 } 769 770 int 771 findeol(void) 772 { 773 int c; 774 775 /* skip to either EOF or the first real EOL */ 776 while (1) { 777 c = lgetc(0); 778 if (c == '\n') { 779 file->lineno++; 780 break; 781 } 782 if (c == EOF) 783 break; 784 } 785 return (ERROR); 786 } 787 788 int 789 yylex(void) 790 { 791 char buf[8096]; 792 char *p, *val; 793 int quotec, next, c; 794 int token; 795 796 top: 797 p = buf; 798 while ((c = lgetc(0)) == ' ' || c == '\t') 799 ; /* nothing */ 800 801 yylval.lineno = file->lineno; 802 if (c == '#') 803 while ((c = lgetc(0)) != '\n' && c != EOF) 804 ; /* nothing */ 805 if (c == '$' && !expanding) { 806 while (1) { 807 if ((c = lgetc(0)) == EOF) 808 return (0); 809 810 if (p + 1 >= buf + sizeof(buf) - 1) { 811 yyerror("string too long"); 812 return (findeol()); 813 } 814 if (isalnum(c) || c == '_') { 815 *p++ = c; 816 continue; 817 } 818 *p = '\0'; 819 lungetc(c); 820 break; 821 } 822 val = symget(buf); 823 if (val == NULL) { 824 yyerror("macro '%s' not defined", buf); 825 return (findeol()); 826 } 827 p = val + strlen(val) - 1; 828 lungetc(DONE_EXPAND); 829 while (p >= val) { 830 lungetc((unsigned char)*p); 831 p--; 832 } 833 lungetc(START_EXPAND); 834 goto top; 835 } 836 837 switch (c) { 838 case '\'': 839 case '"': 840 quotec = c; 841 while (1) { 842 if ((c = lgetc(quotec)) == EOF) 843 return (0); 844 if (c == '\n') { 845 file->lineno++; 846 continue; 847 } else if (c == '\\') { 848 if ((next = lgetc(quotec)) == EOF) 849 return (0); 850 if (next == quotec || next == ' ' || 851 next == '\t') 852 c = next; 853 else if (next == '\n') { 854 file->lineno++; 855 continue; 856 } else 857 lungetc(next); 858 } else if (c == quotec) { 859 *p = '\0'; 860 break; 861 } else if (c == '\0') { 862 yyerror("syntax error"); 863 return (findeol()); 864 } 865 if (p + 1 >= buf + sizeof(buf) - 1) { 866 yyerror("string too long"); 867 return (findeol()); 868 } 869 *p++ = c; 870 } 871 yylval.v.string = strdup(buf); 872 if (yylval.v.string == NULL) 873 err(1, "%s", __func__); 874 return (STRING); 875 } 876 877 #define allowed_to_end_number(x) \ 878 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 879 880 if (c == '-' || isdigit(c)) { 881 do { 882 *p++ = c; 883 if ((size_t)(p-buf) >= sizeof(buf)) { 884 yyerror("string too long"); 885 return (findeol()); 886 } 887 } while ((c = lgetc(0)) != EOF && isdigit(c)); 888 lungetc(c); 889 if (p == buf + 1 && buf[0] == '-') 890 goto nodigits; 891 if (c == EOF || allowed_to_end_number(c)) { 892 const char *errstr = NULL; 893 894 *p = '\0'; 895 yylval.v.number = strtonum(buf, LLONG_MIN, 896 LLONG_MAX, &errstr); 897 if (errstr) { 898 yyerror("\"%s\" invalid number: %s", 899 buf, errstr); 900 return (findeol()); 901 } 902 return (NUMBER); 903 } else { 904 nodigits: 905 while (p > buf + 1) 906 lungetc((unsigned char)*--p); 907 c = (unsigned char)*--p; 908 if (c == '-') 909 return (c); 910 } 911 } 912 913 #define allowed_in_string(x) \ 914 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 915 x != '{' && x != '}' && \ 916 x != '!' && x != '=' && x != '#' && \ 917 x != ',')) 918 919 if (isalnum(c) || c == ':' || c == '_') { 920 do { 921 *p++ = c; 922 if ((size_t)(p-buf) >= sizeof(buf)) { 923 yyerror("string too long"); 924 return (findeol()); 925 } 926 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 927 lungetc(c); 928 *p = '\0'; 929 if ((token = lookup(buf)) == STRING) 930 if ((yylval.v.string = strdup(buf)) == NULL) 931 err(1, "%s", __func__); 932 return (token); 933 } 934 if (c == '\n') { 935 yylval.lineno = file->lineno; 936 file->lineno++; 937 } 938 if (c == EOF) 939 return (0); 940 return (c); 941 } 942 943 int 944 check_file_secrecy(int fd, const char *fname) 945 { 946 struct stat st; 947 948 if (fstat(fd, &st)) { 949 log_warn("cannot stat %s", fname); 950 return (-1); 951 } 952 if (st.st_uid != 0 && st.st_uid != getuid()) { 953 log_warnx("%s: owner not root or current user", fname); 954 return (-1); 955 } 956 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 957 log_warnx("%s: group writable or world read/writable", fname); 958 return (-1); 959 } 960 return (0); 961 } 962 963 struct file * 964 pushfile(const char *name, int secret) 965 { 966 struct file *nfile; 967 968 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 969 log_warn("%s", __func__); 970 return (NULL); 971 } 972 if ((nfile->name = strdup(name)) == NULL) { 973 log_warn("%s", __func__); 974 free(nfile); 975 return (NULL); 976 } 977 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 978 log_warn("%s: %s", __func__, nfile->name); 979 free(nfile->name); 980 free(nfile); 981 return (NULL); 982 } else if (secret && 983 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 984 fclose(nfile->stream); 985 free(nfile->name); 986 free(nfile); 987 return (NULL); 988 } 989 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; 990 nfile->ungetsize = 16; 991 nfile->ungetbuf = malloc(nfile->ungetsize); 992 if (nfile->ungetbuf == NULL) { 993 log_warn("%s", __func__); 994 fclose(nfile->stream); 995 free(nfile->name); 996 free(nfile); 997 return (NULL); 998 } 999 TAILQ_INSERT_TAIL(&files, nfile, entry); 1000 return (nfile); 1001 } 1002 1003 int 1004 popfile(void) 1005 { 1006 struct file *prev; 1007 1008 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 1009 prev->errors += file->errors; 1010 1011 TAILQ_REMOVE(&files, file, entry); 1012 fclose(file->stream); 1013 free(file->name); 1014 free(file->ungetbuf); 1015 free(file); 1016 file = prev; 1017 return (file ? 0 : EOF); 1018 } 1019 1020 struct ospfd_conf * 1021 parse_config(char *filename, int opts) 1022 { 1023 struct sym *sym, *next; 1024 1025 if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL) 1026 fatal("parse_config"); 1027 conf->opts = opts; 1028 if (conf->opts & OSPFD_OPT_STUB_ROUTER) 1029 conf->flags |= OSPFD_FLAG_STUB_ROUTER; 1030 1031 bzero(&globaldefs, sizeof(globaldefs)); 1032 defs = &globaldefs; 1033 defs->dead_interval = DEFAULT_RTR_DEAD_TIME; 1034 defs->transmit_delay = DEFAULT_TRANSMIT_DELAY; 1035 defs->hello_interval = DEFAULT_HELLO_INTERVAL; 1036 defs->rxmt_interval = DEFAULT_RXMT_INTERVAL; 1037 defs->metric = DEFAULT_METRIC; 1038 defs->priority = DEFAULT_PRIORITY; 1039 defs->p2p = 0; 1040 1041 conf->spf_delay = DEFAULT_SPF_DELAY; 1042 conf->spf_hold_time = DEFAULT_SPF_HOLDTIME; 1043 conf->spf_state = SPF_IDLE; 1044 conf->fib_priority = RTP_OSPF; 1045 1046 if ((file = pushfile(filename, 1047 !(conf->opts & OSPFD_OPT_NOACTION))) == NULL) { 1048 free(conf); 1049 return (NULL); 1050 } 1051 topfile = file; 1052 1053 LIST_INIT(&conf->area_list); 1054 LIST_INIT(&conf->cand_list); 1055 SIMPLEQ_INIT(&conf->redist_list); 1056 1057 yyparse(); 1058 errors = file->errors; 1059 popfile(); 1060 1061 /* Free macros and check which have not been used. */ 1062 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { 1063 if ((conf->opts & OSPFD_OPT_VERBOSE2) && !sym->used) 1064 fprintf(stderr, "warning: macro '%s' not " 1065 "used\n", sym->nam); 1066 if (!sym->persist) { 1067 free(sym->nam); 1068 free(sym->val); 1069 TAILQ_REMOVE(&symhead, sym, entry); 1070 free(sym); 1071 } 1072 } 1073 1074 /* check that all interfaces belong to the configured rdomain */ 1075 errors += conf_check_rdomain(conf->rdomain); 1076 1077 if (errors) { 1078 clear_config(conf); 1079 return (NULL); 1080 } 1081 1082 if (conf->rtr_id.s_addr == 0) 1083 conf->rtr_id.s_addr = get_rtr_id(); 1084 1085 return (conf); 1086 } 1087 1088 int 1089 symset(const char *nam, const char *val, int persist) 1090 { 1091 struct sym *sym; 1092 1093 TAILQ_FOREACH(sym, &symhead, entry) { 1094 if (strcmp(nam, sym->nam) == 0) 1095 break; 1096 } 1097 1098 if (sym != NULL) { 1099 if (sym->persist == 1) 1100 return (0); 1101 else { 1102 free(sym->nam); 1103 free(sym->val); 1104 TAILQ_REMOVE(&symhead, sym, entry); 1105 free(sym); 1106 } 1107 } 1108 if ((sym = calloc(1, sizeof(*sym))) == NULL) 1109 return (-1); 1110 1111 sym->nam = strdup(nam); 1112 if (sym->nam == NULL) { 1113 free(sym); 1114 return (-1); 1115 } 1116 sym->val = strdup(val); 1117 if (sym->val == NULL) { 1118 free(sym->nam); 1119 free(sym); 1120 return (-1); 1121 } 1122 sym->used = 0; 1123 sym->persist = persist; 1124 TAILQ_INSERT_TAIL(&symhead, sym, entry); 1125 return (0); 1126 } 1127 1128 int 1129 cmdline_symset(char *s) 1130 { 1131 char *sym, *val; 1132 int ret; 1133 1134 if ((val = strrchr(s, '=')) == NULL) 1135 return (-1); 1136 sym = strndup(s, val - s); 1137 if (sym == NULL) 1138 errx(1, "%s: strndup", __func__); 1139 ret = symset(sym, val + 1, 1); 1140 free(sym); 1141 1142 return (ret); 1143 } 1144 1145 char * 1146 symget(const char *nam) 1147 { 1148 struct sym *sym; 1149 1150 TAILQ_FOREACH(sym, &symhead, entry) { 1151 if (strcmp(nam, sym->nam) == 0) { 1152 sym->used = 1; 1153 return (sym->val); 1154 } 1155 } 1156 return (NULL); 1157 } 1158 1159 struct area * 1160 conf_get_area(struct in_addr id) 1161 { 1162 struct area *a; 1163 1164 a = area_find(conf, id); 1165 if (a) 1166 return (a); 1167 a = area_new(); 1168 LIST_INSERT_HEAD(&conf->area_list, a, entry); 1169 1170 a->id.s_addr = id.s_addr; 1171 1172 return (a); 1173 } 1174 1175 int 1176 conf_check_rdomain(u_int rdomain) 1177 { 1178 struct area *a; 1179 struct iface *i, *idep; 1180 struct redistribute *r; 1181 int errs = 0; 1182 1183 SIMPLEQ_FOREACH(r, &conf->redist_list, entry) 1184 if (r->dependon[0] != '\0') { 1185 idep = if_findname(r->dependon); 1186 if (idep->rdomain != rdomain) { 1187 logit(LOG_CRIT, 1188 "depend on %s: interface not in rdomain %u", 1189 idep->name, rdomain); 1190 errs++; 1191 } 1192 } 1193 1194 LIST_FOREACH(a, &conf->area_list, entry) 1195 LIST_FOREACH(i, &a->iface_list, entry) { 1196 if (i->rdomain != rdomain) { 1197 logit(LOG_CRIT, 1198 "interface %s not in rdomain %u", 1199 i->name, rdomain); 1200 errs++; 1201 } 1202 if (i->dependon[0] != '\0') { 1203 idep = if_findname(i->dependon); 1204 if (idep->rdomain != rdomain) { 1205 logit(LOG_CRIT, 1206 "depend on %s: interface not in " 1207 "rdomain %u", 1208 idep->name, rdomain); 1209 errs++; 1210 } 1211 } 1212 } 1213 1214 return (errs); 1215 } 1216 1217 void 1218 conf_clear_redist_list(struct redist_list *rl) 1219 { 1220 struct redistribute *r; 1221 while ((r = SIMPLEQ_FIRST(rl)) != NULL) { 1222 SIMPLEQ_REMOVE_HEAD(rl, entry); 1223 free(r); 1224 } 1225 } 1226 1227 void 1228 clear_config(struct ospfd_conf *xconf) 1229 { 1230 struct area *a; 1231 1232 while ((a = LIST_FIRST(&xconf->area_list)) != NULL) { 1233 LIST_REMOVE(a, entry); 1234 area_del(a); 1235 } 1236 1237 conf_clear_redist_list(&xconf->redist_list); 1238 1239 free(xconf); 1240 } 1241 1242 u_int32_t 1243 get_rtr_id(void) 1244 { 1245 struct ifaddrs *ifap, *ifa; 1246 u_int32_t ip = 0, cur, localnet; 1247 1248 localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); 1249 1250 if (getifaddrs(&ifap) == -1) 1251 fatal("getifaddrs"); 1252 1253 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 1254 if (strncmp(ifa->ifa_name, "carp", 4) == 0) 1255 continue; 1256 if (ifa->ifa_addr == NULL || 1257 ifa->ifa_addr->sa_family != AF_INET) 1258 continue; 1259 cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; 1260 if ((cur & localnet) == localnet) /* skip 127/8 */ 1261 continue; 1262 if (ntohl(cur) < ntohl(ip) || ip == 0) 1263 ip = cur; 1264 } 1265 freeifaddrs(ifap); 1266 1267 if (ip == 0) 1268 fatal("router-id is 0.0.0.0"); 1269 1270 return (ip); 1271 } 1272 1273 int 1274 host(const char *s, struct in6_addr *addr) 1275 { 1276 struct addrinfo hints, *r; 1277 1278 if (s == NULL) 1279 return (0); 1280 1281 bzero(addr, sizeof(struct in6_addr)); 1282 bzero(&hints, sizeof(hints)); 1283 hints.ai_family = AF_INET6; 1284 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 1285 hints.ai_flags = AI_NUMERICHOST; 1286 if (getaddrinfo(s, "0", &hints, &r) == 0) { 1287 *addr = ((struct sockaddr_in6 *)r->ai_addr)->sin6_addr; 1288 /* XXX address scope !!! */ 1289 /* ((struct sockaddr_in6 *)r->ai_addr)->sin6_scope_id */ 1290 freeaddrinfo(r); 1291 return (1); 1292 } 1293 return (0); 1294 } 1295 1296 int 1297 prefix(const char *s, struct in6_addr *addr, u_int8_t *plen) 1298 { 1299 char *p, *ps; 1300 const char *errstr; 1301 int mask; 1302 1303 if (s == NULL) 1304 return (0); 1305 1306 if ((p = strrchr(s, '/')) != NULL) { 1307 mask = strtonum(p + 1, 0, 128, &errstr); 1308 if (errstr) 1309 errx(1, "invalid netmask: %s", errstr); 1310 1311 if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL) 1312 err(1, "%s", __func__); 1313 strlcpy(ps, s, strlen(s) - strlen(p) + 1); 1314 1315 if (host(ps, addr) == 0) { 1316 free(ps); 1317 return (0); 1318 } 1319 1320 inet6applymask(addr, addr, mask); 1321 *plen = mask; 1322 return (1); 1323 } 1324 *plen = 128; 1325 return (host(s, addr)); 1326 } 1327