1 /* $OpenBSD: parser.c,v 1.75 2017/01/13 18:59:12 phessler Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2016 Job Snijders <job@instituut.net> 6 * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 23 #include <err.h> 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <limits.h> 27 #include <netdb.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "parser.h" 34 #include "irrfilter.h" 35 36 enum token_type { 37 NOTOKEN, 38 ENDTOKEN, 39 KEYWORD, 40 ADDRESS, 41 PEERADDRESS, 42 FLAG, 43 ASNUM, 44 ASTYPE, 45 PREFIX, 46 PEERDESC, 47 RIBNAME, 48 SHUTDOWN_COMMUNICATION, 49 COMMUNITY, 50 LARGE_COMMUNITY, 51 LOCALPREF, 52 MED, 53 NEXTHOP, 54 PFTABLE, 55 PREPNBR, 56 PREPSELF, 57 WEIGHT, 58 FAMILY, 59 GETOPT, 60 RTABLE, 61 FILENAME, 62 BULK 63 }; 64 65 enum getopts { 66 GETOPT_NONE, 67 GETOPT_IRRFILTER 68 }; 69 70 struct token { 71 enum token_type type; 72 const char *keyword; 73 int value; 74 const struct token *next; 75 }; 76 77 static const struct token t_main[]; 78 static const struct token t_show[]; 79 static const struct token t_show_summary[]; 80 static const struct token t_show_fib[]; 81 static const struct token t_show_rib[]; 82 static const struct token t_show_mrt[]; 83 static const struct token t_show_mrt_file[]; 84 static const struct token t_show_rib_neigh[]; 85 static const struct token t_show_mrt_neigh[]; 86 static const struct token t_show_rib_rib[]; 87 static const struct token t_show_neighbor[]; 88 static const struct token t_show_neighbor_modifiers[]; 89 static const struct token t_fib[]; 90 static const struct token t_neighbor[]; 91 static const struct token t_neighbor_modifiers[]; 92 static const struct token t_show_rib_as[]; 93 static const struct token t_show_mrt_as[]; 94 static const struct token t_show_prefix[]; 95 static const struct token t_show_ip[]; 96 static const struct token t_show_community[]; 97 static const struct token t_show_largecommunity[]; 98 static const struct token t_network[]; 99 static const struct token t_network_show[]; 100 static const struct token t_prefix[]; 101 static const struct token t_set[]; 102 static const struct token t_community[]; 103 static const struct token t_largecommunity[]; 104 static const struct token t_localpref[]; 105 static const struct token t_med[]; 106 static const struct token t_nexthop[]; 107 static const struct token t_pftable[]; 108 static const struct token t_prepnbr[]; 109 static const struct token t_prepself[]; 110 static const struct token t_weight[]; 111 static const struct token t_irrfilter[]; 112 static const struct token t_irrfilter_opts[]; 113 static const struct token t_log[]; 114 static const struct token t_fib_table[]; 115 static const struct token t_show_fib_table[]; 116 117 static const struct token t_main[] = { 118 { KEYWORD, "reload", RELOAD, NULL}, 119 { KEYWORD, "show", SHOW, t_show}, 120 { KEYWORD, "fib", FIB, t_fib}, 121 { KEYWORD, "neighbor", NEIGHBOR, t_neighbor}, 122 { KEYWORD, "network", NONE, t_network}, 123 { KEYWORD, "irrfilter", IRRFILTER, t_irrfilter}, 124 { KEYWORD, "log", NONE, t_log}, 125 { ENDTOKEN, "", NONE, NULL} 126 }; 127 128 static const struct token t_show[] = { 129 { NOTOKEN, "", NONE, NULL}, 130 { KEYWORD, "fib", SHOW_FIB, t_show_fib}, 131 { KEYWORD, "interfaces", SHOW_INTERFACE, NULL}, 132 { KEYWORD, "neighbor", SHOW_NEIGHBOR, t_show_neighbor}, 133 { KEYWORD, "network", NETWORK_SHOW, t_network_show}, 134 { KEYWORD, "nexthop", SHOW_NEXTHOP, NULL}, 135 { KEYWORD, "rib", SHOW_RIB, t_show_rib}, 136 { KEYWORD, "tables", SHOW_FIB_TABLES, NULL}, 137 { KEYWORD, "ip", NONE, t_show_ip}, 138 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 139 { KEYWORD, "mrt", SHOW_MRT, t_show_mrt}, 140 { ENDTOKEN, "", NONE, NULL} 141 }; 142 143 static const struct token t_show_summary[] = { 144 { NOTOKEN, "", NONE, NULL}, 145 { KEYWORD, "terse", SHOW_SUMMARY_TERSE, NULL}, 146 { ENDTOKEN, "", NONE, NULL} 147 }; 148 149 static const struct token t_show_fib[] = { 150 { NOTOKEN, "", NONE, NULL}, 151 { FLAG, "connected", F_CONNECTED, t_show_fib}, 152 { FLAG, "static", F_STATIC, t_show_fib}, 153 { FLAG, "bgp", F_BGPD_INSERTED, t_show_fib}, 154 { FLAG, "nexthop", F_NEXTHOP, t_show_fib}, 155 { KEYWORD, "table", NONE, t_show_fib_table}, 156 { FAMILY, "", NONE, t_show_fib}, 157 { ADDRESS, "", NONE, NULL}, 158 { ENDTOKEN, "", NONE, NULL} 159 }; 160 161 static const struct token t_show_rib[] = { 162 { NOTOKEN, "", NONE, NULL}, 163 { ASTYPE, "as", AS_ALL, t_show_rib_as}, 164 { ASTYPE, "source-as", AS_SOURCE, t_show_rib_as}, 165 { ASTYPE, "transit-as", AS_TRANSIT, t_show_rib_as}, 166 { ASTYPE, "peer-as", AS_PEER, t_show_rib_as}, 167 { ASTYPE, "empty-as", AS_EMPTY, t_show_rib}, 168 { KEYWORD, "community", NONE, t_show_community}, 169 { KEYWORD, "large-community", NONE, t_show_largecommunity}, 170 { FLAG, "best", F_CTL_ACTIVE, t_show_rib}, 171 { FLAG, "selected", F_CTL_ACTIVE, t_show_rib}, 172 { FLAG, "detail", F_CTL_DETAIL, t_show_rib}, 173 { FLAG, "in", F_CTL_ADJ_IN, t_show_rib}, 174 { FLAG, "out", F_CTL_ADJ_OUT, t_show_rib}, 175 { KEYWORD, "neighbor", NONE, t_show_rib_neigh}, 176 { KEYWORD, "table", NONE, t_show_rib_rib}, 177 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 178 { KEYWORD, "memory", SHOW_RIB_MEM, NULL}, 179 { FAMILY, "", NONE, t_show_rib}, 180 { PREFIX, "", NONE, t_show_prefix}, 181 { ENDTOKEN, "", NONE, NULL} 182 }; 183 184 185 static const struct token t_show_mrt[] = { 186 { NOTOKEN, "", NONE, NULL}, 187 { ASTYPE, "as", AS_ALL, t_show_mrt_as}, 188 { ASTYPE, "source-as", AS_SOURCE, t_show_mrt_as}, 189 { ASTYPE, "transit-as", AS_TRANSIT, t_show_mrt_as}, 190 { ASTYPE, "peer-as", AS_PEER, t_show_mrt_as}, 191 { ASTYPE, "empty-as", AS_EMPTY, t_show_mrt}, 192 { FLAG, "detail", F_CTL_DETAIL, t_show_mrt}, 193 { KEYWORD, "neighbor", NONE, t_show_mrt_neigh}, 194 { KEYWORD, "file", NONE, t_show_mrt_file}, 195 { FAMILY, "", NONE, t_show_mrt}, 196 { PREFIX, "", NONE, t_show_prefix}, 197 { ENDTOKEN, "", NONE, NULL} 198 }; 199 200 static const struct token t_show_mrt_file[] = { 201 { FILENAME, "", NONE, t_show_mrt}, 202 { ENDTOKEN, "", NONE, NULL} 203 }; 204 205 static const struct token t_show_rib_neigh[] = { 206 { PEERADDRESS, "", NONE, t_show_rib}, 207 { PEERDESC, "", NONE, t_show_rib}, 208 { ENDTOKEN, "", NONE, NULL} 209 }; 210 211 static const struct token t_show_mrt_neigh[] = { 212 { PEERADDRESS, "", NONE, t_show_mrt}, 213 { ENDTOKEN, "", NONE, NULL} 214 }; 215 216 static const struct token t_show_rib_rib[] = { 217 { RIBNAME, "", NONE, t_show_rib}, 218 { ENDTOKEN, "", NONE, NULL} 219 }; 220 221 static const struct token t_show_neighbor[] = { 222 { NOTOKEN, "", NONE, NULL}, 223 { PEERADDRESS, "", NONE, t_show_neighbor_modifiers}, 224 { PEERDESC, "", NONE, t_show_neighbor_modifiers}, 225 { ENDTOKEN, "", NONE, NULL} 226 }; 227 228 static const struct token t_show_neighbor_modifiers[] = { 229 { NOTOKEN, "", NONE, NULL}, 230 { KEYWORD, "timers", SHOW_NEIGHBOR_TIMERS, NULL}, 231 { KEYWORD, "messages", SHOW_NEIGHBOR, NULL}, 232 { KEYWORD, "terse", SHOW_NEIGHBOR_TERSE, NULL}, 233 { ENDTOKEN, "", NONE, NULL} 234 }; 235 236 static const struct token t_fib[] = { 237 { KEYWORD, "couple", FIB_COUPLE, NULL}, 238 { KEYWORD, "decouple", FIB_DECOUPLE, NULL}, 239 { KEYWORD, "table", NONE, t_fib_table}, 240 { ENDTOKEN, "", NONE, NULL} 241 }; 242 243 static const struct token t_neighbor[] = { 244 { PEERADDRESS, "", NONE, t_neighbor_modifiers}, 245 { PEERDESC, "", NONE, t_neighbor_modifiers}, 246 { ENDTOKEN, "", NONE, NULL} 247 }; 248 249 static const struct token t_nei_mod_shutc[] = { 250 { NOTOKEN, "", NONE, NULL}, 251 { SHUTDOWN_COMMUNICATION, "", NONE, NULL}, 252 { ENDTOKEN, "", NONE, NULL} 253 }; 254 255 static const struct token t_neighbor_modifiers[] = { 256 { KEYWORD, "up", NEIGHBOR_UP, NULL}, 257 { KEYWORD, "down", NEIGHBOR_DOWN, t_nei_mod_shutc}, 258 { KEYWORD, "clear", NEIGHBOR_CLEAR, NULL}, 259 { KEYWORD, "refresh", NEIGHBOR_RREFRESH, NULL}, 260 { KEYWORD, "destroy", NEIGHBOR_DESTROY, NULL}, 261 { ENDTOKEN, "", NONE, NULL} 262 }; 263 264 static const struct token t_show_rib_as[] = { 265 { ASNUM, "", NONE, t_show_rib}, 266 { ENDTOKEN, "", NONE, NULL} 267 }; 268 269 static const struct token t_show_mrt_as[] = { 270 { ASNUM, "", NONE, t_show_mrt}, 271 { ENDTOKEN, "", NONE, NULL} 272 }; 273 274 static const struct token t_show_prefix[] = { 275 { NOTOKEN, "", NONE, NULL}, 276 { FLAG, "all", F_LONGER, NULL}, 277 { FLAG, "longer-prefixes", F_LONGER, NULL}, 278 { ENDTOKEN, "", NONE, NULL} 279 }; 280 281 static const struct token t_show_ip[] = { 282 { KEYWORD, "bgp", SHOW_RIB, t_show_rib}, 283 { ENDTOKEN, "", NONE, NULL} 284 }; 285 286 static const struct token t_show_community[] = { 287 { COMMUNITY, "", NONE, t_show_rib}, 288 { ENDTOKEN, "", NONE, NULL} 289 }; 290 291 static const struct token t_show_largecommunity[] = { 292 { LARGE_COMMUNITY, "", NONE, t_show_rib}, 293 { ENDTOKEN, "", NONE, NULL} 294 }; 295 296 static const struct token t_network[] = { 297 { KEYWORD, "add", NETWORK_ADD, t_prefix}, 298 { KEYWORD, "delete", NETWORK_REMOVE, t_prefix}, 299 { KEYWORD, "flush", NETWORK_FLUSH, NULL}, 300 { KEYWORD, "show", NETWORK_SHOW, t_network_show}, 301 { KEYWORD, "mrt", NETWORK_MRT, t_show_mrt}, 302 { KEYWORD, "bulk", NETWORK_BULK_ADD, t_set}, 303 { ENDTOKEN, "", NONE, NULL} 304 }; 305 306 static const struct token t_prefix[] = { 307 { PREFIX, "", NONE, t_set}, 308 { ENDTOKEN, "", NONE, NULL} 309 }; 310 311 static const struct token t_network_show[] = { 312 { NOTOKEN, "", NONE, NULL}, 313 { FAMILY, "", NONE, NULL}, 314 { ENDTOKEN, "", NONE, NULL} 315 }; 316 317 static const struct token t_set[] = { 318 { NOTOKEN, "", NONE, NULL}, 319 { KEYWORD, "community", NONE, t_community}, 320 { KEYWORD, "large-community", NONE, t_largecommunity}, 321 { KEYWORD, "localpref", NONE, t_localpref}, 322 { KEYWORD, "med", NONE, t_med}, 323 { KEYWORD, "metric", NONE, t_med}, 324 { KEYWORD, "nexthop", NONE, t_nexthop}, 325 { KEYWORD, "pftable", NONE, t_pftable}, 326 { KEYWORD, "prepend-neighbor", NONE, t_prepnbr}, 327 { KEYWORD, "prepend-self", NONE, t_prepself}, 328 { KEYWORD, "weight", NONE, t_weight}, 329 { KEYWORD, "add", NETWORK_BULK_ADD, NULL}, 330 { KEYWORD, "delete", NETWORK_BULK_REMOVE, NULL}, 331 { ENDTOKEN, "", NONE, NULL} 332 }; 333 334 static const struct token t_community[] = { 335 { COMMUNITY, "", NONE, t_set}, 336 { ENDTOKEN, "", NONE, NULL} 337 }; 338 339 static const struct token t_largecommunity[] = { 340 { LARGE_COMMUNITY, "", NONE, t_set}, 341 { ENDTOKEN, "", NONE, NULL} 342 }; 343 344 static const struct token t_localpref[] = { 345 { LOCALPREF, "", NONE, t_set}, 346 { ENDTOKEN, "", NONE, NULL} 347 }; 348 349 static const struct token t_med[] = { 350 { MED, "", NONE, t_set}, 351 { ENDTOKEN, "", NONE, NULL} 352 }; 353 354 static const struct token t_nexthop[] = { 355 { NEXTHOP, "", NONE, t_set}, 356 { ENDTOKEN, "", NONE, NULL} 357 }; 358 359 static const struct token t_pftable[] = { 360 { PFTABLE, "", NONE, t_set}, 361 { ENDTOKEN, "", NONE, NULL} 362 }; 363 364 static const struct token t_prepnbr[] = { 365 { PREPNBR, "", NONE, t_set}, 366 { ENDTOKEN, "", NONE, NULL} 367 }; 368 369 static const struct token t_prepself[] = { 370 { PREPSELF, "", NONE, t_set}, 371 { ENDTOKEN, "", NONE, NULL} 372 }; 373 374 static const struct token t_weight[] = { 375 { WEIGHT, "", NONE, t_set}, 376 { ENDTOKEN, "", NONE, NULL} 377 }; 378 379 static const struct token t_irrfilter[] = { 380 { GETOPT, "", GETOPT_IRRFILTER, t_irrfilter}, 381 { ASNUM, "", NONE, t_irrfilter_opts}, 382 { ENDTOKEN, "", NONE, NULL} 383 }; 384 385 static const struct token t_irrfilter_opts[] = { 386 { NOTOKEN, "", NONE, NULL}, 387 { FLAG, "importonly", F_IMPORTONLY, t_irrfilter_opts}, 388 { ENDTOKEN, "", NONE, NULL} 389 }; 390 391 static const struct token t_log[] = { 392 { KEYWORD, "verbose", LOG_VERBOSE, NULL}, 393 { KEYWORD, "brief", LOG_BRIEF, NULL}, 394 { ENDTOKEN, "", NONE, NULL} 395 }; 396 397 static const struct token t_fib_table[] = { 398 { RTABLE, "", NONE, t_fib}, 399 { ENDTOKEN, "", NONE, NULL} 400 }; 401 402 static const struct token t_show_fib_table[] = { 403 { RTABLE, "", NONE, t_show_fib}, 404 { ENDTOKEN, "", NONE, NULL} 405 }; 406 407 static struct parse_result res; 408 409 const struct token *match_token(int *argc, char **argv[], 410 const struct token []); 411 void show_valid_args(const struct token []); 412 int parse_addr(const char *, struct bgpd_addr *); 413 int parse_asnum(const char *, size_t, u_int32_t *); 414 int parse_number(const char *, struct parse_result *, 415 enum token_type); 416 int getcommunity(const char *); 417 int parse_community(const char *, struct parse_result *); 418 u_int getlargecommunity(const char *); 419 int parse_largecommunity(const char *, struct parse_result *); 420 int parse_nexthop(const char *, struct parse_result *); 421 int bgpctl_getopt(int *, char **[], int); 422 423 struct parse_result * 424 parse(int argc, char *argv[]) 425 { 426 const struct token *table = t_main; 427 const struct token *match; 428 429 bzero(&res, sizeof(res)); 430 res.community.as = COMMUNITY_UNSET; 431 res.community.type = COMMUNITY_UNSET; 432 res.large_community.as = COMMUNITY_UNSET; 433 res.large_community.ld1 = COMMUNITY_UNSET; 434 res.large_community.ld2 = COMMUNITY_UNSET; 435 TAILQ_INIT(&res.set); 436 if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) { 437 fprintf(stderr, "getcwd failed: %s\n", strerror(errno)); 438 return (NULL); 439 } 440 441 while (argc >= 0) { 442 if ((match = match_token(&argc, &argv, table)) == NULL) { 443 fprintf(stderr, "valid commands/args:\n"); 444 show_valid_args(table); 445 return (NULL); 446 } 447 448 argc--; 449 argv++; 450 451 if (match->type == NOTOKEN || match->next == NULL) 452 break; 453 454 table = match->next; 455 } 456 457 if (argc > 0) { 458 fprintf(stderr, "superfluous argument: %s\n", argv[0]); 459 return (NULL); 460 } 461 462 return (&res); 463 } 464 465 const struct token * 466 match_token(int *argc, char **argv[], const struct token table[]) 467 { 468 u_int i, match; 469 const struct token *t = NULL; 470 struct filter_set *fs; 471 const char *word = *argv[0]; 472 size_t wordlen = 0; 473 474 match = 0; 475 if (word != NULL) 476 wordlen = strlen(word); 477 for (i = 0; table[i].type != ENDTOKEN; i++) { 478 switch (table[i].type) { 479 case NOTOKEN: 480 if (word == NULL || wordlen == 0) { 481 match++; 482 t = &table[i]; 483 } 484 break; 485 case KEYWORD: 486 if (word != NULL && strncmp(word, table[i].keyword, 487 wordlen) == 0) { 488 match++; 489 t = &table[i]; 490 if (t->value) 491 res.action = t->value; 492 } 493 break; 494 case FLAG: 495 if (word != NULL && strncmp(word, table[i].keyword, 496 wordlen) == 0) { 497 match++; 498 t = &table[i]; 499 res.flags |= t->value; 500 } 501 break; 502 case FAMILY: 503 if (word == NULL) 504 break; 505 if (!strcmp(word, "inet") || 506 !strcasecmp(word, "IPv4")) { 507 match++; 508 t = &table[i]; 509 res.aid = AID_INET; 510 } 511 if (!strcmp(word, "inet6") || 512 !strcasecmp(word, "IPv6")) { 513 match++; 514 t = &table[i]; 515 res.aid = AID_INET6; 516 } 517 if (!strcasecmp(word, "VPNv4")) { 518 match++; 519 t = &table[i]; 520 res.aid = AID_VPN_IPv4; 521 } 522 break; 523 case ADDRESS: 524 if (parse_addr(word, &res.addr)) { 525 match++; 526 t = &table[i]; 527 if (t->value) 528 res.action = t->value; 529 } 530 break; 531 case PEERADDRESS: 532 if (parse_addr(word, &res.peeraddr)) { 533 match++; 534 t = &table[i]; 535 if (t->value) 536 res.action = t->value; 537 } 538 break; 539 case PREFIX: 540 if (parse_prefix(word, wordlen, &res.addr, &res.prefixlen)) { 541 match++; 542 t = &table[i]; 543 if (t->value) 544 res.action = t->value; 545 } 546 break; 547 case ASTYPE: 548 if (word != NULL && strncmp(word, table[i].keyword, 549 wordlen) == 0) { 550 match++; 551 t = &table[i]; 552 res.as.type = t->value; 553 } 554 break; 555 case ASNUM: 556 if (parse_asnum(word, wordlen, &res.as.as)) { 557 match++; 558 t = &table[i]; 559 } 560 break; 561 case PEERDESC: 562 if (!match && word != NULL && wordlen > 0) { 563 if (strlcpy(res.peerdesc, word, 564 sizeof(res.peerdesc)) >= 565 sizeof(res.peerdesc)) 566 errx(1, "neighbor description too " 567 "long"); 568 match++; 569 t = &table[i]; 570 } 571 break; 572 case RIBNAME: 573 if (!match && word != NULL && wordlen > 0) { 574 if (strlcpy(res.rib, word, sizeof(res.rib)) >= 575 sizeof(res.rib)) 576 errx(1, "rib name too long"); 577 match++; 578 t = &table[i]; 579 } 580 break; 581 case SHUTDOWN_COMMUNICATION: 582 if (!match && word != NULL && wordlen > 0) { 583 if (strlcpy(res.shutcomm, word, 584 sizeof(res.shutcomm)) >= 585 sizeof(res.shutcomm)) 586 errx(1, "shutdown reason too long"); 587 match++; 588 t = &table[i]; 589 } 590 break; 591 case COMMUNITY: 592 if (word != NULL && wordlen > 0 && 593 parse_community(word, &res)) { 594 match++; 595 t = &table[i]; 596 } 597 break; 598 case LARGE_COMMUNITY: 599 if (word != NULL && wordlen > 0 && 600 parse_largecommunity(word, &res)) { 601 match++; 602 t = &table[i]; 603 } 604 break; 605 case LOCALPREF: 606 case MED: 607 case PREPNBR: 608 case PREPSELF: 609 case WEIGHT: 610 case RTABLE: 611 if (word != NULL && wordlen > 0 && 612 parse_number(word, &res, table[i].type)) { 613 match++; 614 t = &table[i]; 615 } 616 break; 617 case NEXTHOP: 618 if (word != NULL && wordlen > 0 && 619 parse_nexthop(word, &res)) { 620 match++; 621 t = &table[i]; 622 } 623 break; 624 case PFTABLE: 625 if (word != NULL && wordlen > 0) { 626 if ((fs = calloc(1, 627 sizeof(struct filter_set))) == NULL) 628 err(1, NULL); 629 if (strlcpy(fs->action.pftable, word, 630 sizeof(fs->action.pftable)) >= 631 sizeof(fs->action.pftable)) 632 errx(1, "pftable name too long"); 633 TAILQ_INSERT_TAIL(&res.set, fs, entry); 634 match++; 635 t = &table[i]; 636 } 637 break; 638 case GETOPT: 639 if (bgpctl_getopt(argc, argv, table[i].value)) { 640 match++; 641 t = &table[i]; 642 } 643 break; 644 case FILENAME: 645 if (word != NULL && wordlen > 0) { 646 if ((res.mrtfd = open(word, O_RDONLY)) == -1) { 647 /* 648 * ignore error if path has no / and 649 * does not exist. In hope to print 650 * usage. 651 */ 652 if (errno == ENOENT && 653 !strchr(word, '/')) 654 break; 655 err(1, "mrt open(%s)", word); 656 } 657 match++; 658 t = &table[i]; 659 } 660 break; 661 case BULK: 662 match++; 663 t = &table[i]; 664 break; 665 case ENDTOKEN: 666 break; 667 } 668 } 669 670 if (match != 1) { 671 if (word == NULL) 672 fprintf(stderr, "missing argument:\n"); 673 else if (match > 1) 674 fprintf(stderr, "ambiguous argument: %s\n", word); 675 else if (match < 1) 676 fprintf(stderr, "unknown argument: %s\n", word); 677 return (NULL); 678 } 679 680 return (t); 681 } 682 683 void 684 show_valid_args(const struct token table[]) 685 { 686 int i; 687 688 for (i = 0; table[i].type != ENDTOKEN; i++) { 689 switch (table[i].type) { 690 case NOTOKEN: 691 fprintf(stderr, " <cr>\n"); 692 break; 693 case KEYWORD: 694 case FLAG: 695 case ASTYPE: 696 fprintf(stderr, " %s\n", table[i].keyword); 697 break; 698 case ADDRESS: 699 case PEERADDRESS: 700 fprintf(stderr, " <address>\n"); 701 break; 702 case PREFIX: 703 fprintf(stderr, " <address>[/<len>]\n"); 704 break; 705 case ASNUM: 706 fprintf(stderr, " <asnum>\n"); 707 break; 708 case PEERDESC: 709 fprintf(stderr, " <neighbor description>\n"); 710 break; 711 case RIBNAME: 712 fprintf(stderr, " <rib name>\n"); 713 break; 714 case SHUTDOWN_COMMUNICATION: 715 fprintf(stderr, " <shutdown reason>\n"); 716 break; 717 case COMMUNITY: 718 fprintf(stderr, " <community>\n"); 719 break; 720 case LARGE_COMMUNITY: 721 fprintf(stderr, " <large-community>\n"); 722 break; 723 case LOCALPREF: 724 case MED: 725 case PREPNBR: 726 case PREPSELF: 727 case WEIGHT: 728 fprintf(stderr, " <number>\n"); 729 break; 730 case RTABLE: 731 fprintf(stderr, " <rtableid>\n"); 732 break; 733 case NEXTHOP: 734 fprintf(stderr, " <address>\n"); 735 break; 736 case PFTABLE: 737 fprintf(stderr, " <pftable>\n"); 738 break; 739 case FAMILY: 740 fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n"); 741 break; 742 case GETOPT: 743 fprintf(stderr, " <options>\n"); 744 break; 745 case FILENAME: 746 fprintf(stderr, " <filename>\n"); 747 break; 748 case BULK: 749 case ENDTOKEN: 750 break; 751 } 752 } 753 } 754 755 int 756 parse_addr(const char *word, struct bgpd_addr *addr) 757 { 758 struct in_addr ina; 759 struct addrinfo hints, *r; 760 761 if (word == NULL) 762 return (0); 763 764 bzero(addr, sizeof(struct bgpd_addr)); 765 bzero(&ina, sizeof(ina)); 766 767 if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) { 768 addr->aid = AID_INET; 769 addr->v4 = ina; 770 return (1); 771 } 772 773 bzero(&hints, sizeof(hints)); 774 hints.ai_family = AF_INET6; 775 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 776 hints.ai_flags = AI_NUMERICHOST; 777 if (getaddrinfo(word, "0", &hints, &r) == 0) { 778 sa2addr(r->ai_addr, addr); 779 freeaddrinfo(r); 780 return (1); 781 } 782 783 return (0); 784 } 785 786 int 787 parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, u_int8_t *prefixlen) 788 { 789 char *p, *ps; 790 const char *errstr; 791 int mask = -1; 792 793 if (word == NULL) 794 return (0); 795 796 bzero(addr, sizeof(struct bgpd_addr)); 797 798 if ((p = strrchr(word, '/')) != NULL) { 799 size_t plen = strlen(p); 800 mask = strtonum(p + 1, 0, 128, &errstr); 801 if (errstr) 802 errx(1, "netmask %s", errstr); 803 804 if ((ps = malloc(wordlen - plen + 1)) == NULL) 805 err(1, "parse_prefix: malloc"); 806 strlcpy(ps, word, wordlen - plen + 1); 807 808 if (parse_addr(ps, addr) == 0) { 809 free(ps); 810 return (0); 811 } 812 813 free(ps); 814 } else 815 if (parse_addr(word, addr) == 0) 816 return (0); 817 818 switch (addr->aid) { 819 case AID_INET: 820 if (mask == -1) 821 mask = 32; 822 if (mask > 32) 823 errx(1, "invalid netmask: too large"); 824 addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask)); 825 break; 826 case AID_INET6: 827 if (mask == -1) 828 mask = 128; 829 inet6applymask(&addr->v6, &addr->v6, mask); 830 break; 831 default: 832 return (0); 833 } 834 835 *prefixlen = mask; 836 return (1); 837 } 838 839 int 840 parse_asnum(const char *word, size_t wordlen, u_int32_t *asnum) 841 { 842 const char *errstr; 843 char *dot; 844 u_int32_t uval, uvalh = 0; 845 846 if (word == NULL) 847 return (0); 848 849 if (wordlen < 1 || word[0] < '0' || word[0] > '9') 850 return (0); 851 852 if ((dot = strchr(word,'.')) != NULL) { 853 *dot++ = '\0'; 854 uvalh = strtonum(word, 0, USHRT_MAX, &errstr); 855 if (errstr) 856 errx(1, "AS number is %s: %s", errstr, word); 857 uval = strtonum(dot, 0, USHRT_MAX, &errstr); 858 if (errstr) 859 errx(1, "AS number is %s: %s", errstr, word); 860 } else { 861 uval = strtonum(word, 0, UINT_MAX, &errstr); 862 if (errstr) 863 errx(1, "AS number is %s: %s", errstr, word); 864 } 865 866 *asnum = uval | (uvalh << 16); 867 return (1); 868 } 869 870 int 871 parse_number(const char *word, struct parse_result *r, enum token_type type) 872 { 873 struct filter_set *fs; 874 const char *errstr; 875 u_int uval; 876 877 if (word == NULL) 878 return (0); 879 880 uval = strtonum(word, 0, UINT_MAX, &errstr); 881 if (errstr) 882 errx(1, "number is %s: %s", errstr, word); 883 884 /* number was parseable */ 885 if (type == RTABLE) { 886 r->rtableid = uval; 887 return (1); 888 } 889 890 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 891 err(1, NULL); 892 switch (type) { 893 case LOCALPREF: 894 fs->type = ACTION_SET_LOCALPREF; 895 fs->action.metric = uval; 896 break; 897 case MED: 898 fs->type = ACTION_SET_MED; 899 fs->action.metric = uval; 900 break; 901 case PREPNBR: 902 if (uval > 128) { 903 free(fs); 904 return (0); 905 } 906 fs->type = ACTION_SET_PREPEND_PEER; 907 fs->action.prepend = uval; 908 break; 909 case PREPSELF: 910 if (uval > 128) { 911 free(fs); 912 return (0); 913 } 914 fs->type = ACTION_SET_PREPEND_SELF; 915 fs->action.prepend = uval; 916 break; 917 case WEIGHT: 918 fs->type = ACTION_SET_WEIGHT; 919 fs->action.metric = uval; 920 break; 921 default: 922 errx(1, "king bula sez bad things happen"); 923 } 924 925 TAILQ_INSERT_TAIL(&r->set, fs, entry); 926 return (1); 927 } 928 929 int 930 getcommunity(const char *s) 931 { 932 const char *errstr; 933 u_int16_t uval; 934 935 if (strcmp(s, "*") == 0) 936 return (COMMUNITY_ANY); 937 938 uval = strtonum(s, 0, USHRT_MAX, &errstr); 939 if (errstr) 940 errx(1, "Community is %s: %s", errstr, s); 941 942 return (uval); 943 } 944 945 int 946 parse_community(const char *word, struct parse_result *r) 947 { 948 struct filter_set *fs; 949 char *p; 950 int as, type; 951 952 /* Well-known communities */ 953 if (strcasecmp(word, "NO_EXPORT") == 0) { 954 as = COMMUNITY_WELLKNOWN; 955 type = COMMUNITY_NO_EXPORT; 956 goto done; 957 } else if (strcasecmp(word, "NO_ADVERTISE") == 0) { 958 as = COMMUNITY_WELLKNOWN; 959 type = COMMUNITY_NO_ADVERTISE; 960 goto done; 961 } else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) { 962 as = COMMUNITY_WELLKNOWN; 963 type = COMMUNITY_NO_EXPSUBCONFED; 964 goto done; 965 } else if (strcasecmp(word, "NO_PEER") == 0) { 966 as = COMMUNITY_WELLKNOWN; 967 type = COMMUNITY_NO_PEER; 968 goto done; 969 } else if (strcasecmp(word, "BLACKHOLE") == 0) { 970 as = COMMUNITY_WELLKNOWN; 971 type = COMMUNITY_BLACKHOLE; 972 goto done; 973 } 974 975 if ((p = strchr(word, ':')) == NULL) { 976 fprintf(stderr, "Bad community syntax\n"); 977 return (0); 978 } 979 *p++ = 0; 980 981 as = getcommunity(word); 982 type = getcommunity(p); 983 984 done: 985 if (as == 0) { 986 fprintf(stderr, "Invalid community\n"); 987 return (0); 988 } 989 if (as == COMMUNITY_WELLKNOWN) 990 switch (type) { 991 case COMMUNITY_NO_EXPORT: 992 case COMMUNITY_NO_ADVERTISE: 993 case COMMUNITY_NO_EXPSUBCONFED: 994 case COMMUNITY_BLACKHOLE: 995 /* valid */ 996 break; 997 default: 998 /* unknown */ 999 fprintf(stderr, "Unknown well-known community\n"); 1000 return (0); 1001 } 1002 1003 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 1004 err(1, NULL); 1005 fs->type = ACTION_SET_COMMUNITY; 1006 fs->action.community.as = as; 1007 fs->action.community.type = type; 1008 1009 r->community.as = as; 1010 r->community.type = type; 1011 1012 TAILQ_INSERT_TAIL(&r->set, fs, entry); 1013 return (1); 1014 } 1015 1016 u_int 1017 getlargecommunity(const char *s) 1018 { 1019 const char *errstr; 1020 u_int32_t uval; 1021 1022 if (strcmp(s, "*") == 0) 1023 return (COMMUNITY_ANY); 1024 1025 uval = strtonum(s, 0, UINT_MAX, &errstr); 1026 if (errstr) 1027 errx(1, "Large Community is %s: %s", errstr, s); 1028 1029 return (uval); 1030 } 1031 1032 int 1033 parse_largecommunity(const char *word, struct parse_result *r) 1034 { 1035 struct filter_set *fs; 1036 char *p = strdup(word); 1037 char *array[3]; 1038 int64_t as, ld1, ld2; 1039 int i = 0; 1040 1041 while (p != NULL) { 1042 array[i++] = p; 1043 p = strchr(p, ':'); 1044 if (p) 1045 *p++ = 0; 1046 } 1047 1048 as = getlargecommunity(array[0]); 1049 ld1 = getlargecommunity(array[1]); 1050 ld2 = getlargecommunity(array[2]); 1051 1052 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 1053 err(1, NULL); 1054 fs->type = ACTION_SET_LARGE_COMMUNITY; 1055 fs->action.large_community.as = as; 1056 fs->action.large_community.ld1 = ld1; 1057 fs->action.large_community.ld2 = ld2; 1058 1059 r->large_community.as = as; 1060 r->large_community.ld1 = ld1; 1061 r->large_community.ld2 = ld2; 1062 1063 TAILQ_INSERT_TAIL(&r->set, fs, entry); 1064 return (1); 1065 } 1066 1067 int 1068 parse_nexthop(const char *word, struct parse_result *r) 1069 { 1070 struct filter_set *fs; 1071 1072 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 1073 err(1, NULL); 1074 1075 if (strcmp(word, "blackhole") == 0) 1076 fs->type = ACTION_SET_NEXTHOP_BLACKHOLE; 1077 else if (strcmp(word, "reject") == 0) 1078 fs->type = ACTION_SET_NEXTHOP_REJECT; 1079 else if (strcmp(word, "no-modify") == 0) 1080 fs->type = ACTION_SET_NEXTHOP_NOMODIFY; 1081 else if (parse_addr(word, &fs->action.nexthop)) { 1082 fs->type = ACTION_SET_NEXTHOP; 1083 } else { 1084 free(fs); 1085 return (0); 1086 } 1087 1088 TAILQ_INSERT_TAIL(&r->set, fs, entry); 1089 return (1); 1090 } 1091 1092 int 1093 bgpctl_getopt(int *argc, char **argv[], int type) 1094 { 1095 int ch; 1096 1097 optind = optreset = 1; 1098 while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) { 1099 switch (ch) { 1100 case '4': 1101 res.flags = (res.flags | F_IPV4) & ~F_IPV6; 1102 break; 1103 case '6': 1104 res.flags = (res.flags | F_IPV6) & ~F_IPV4; 1105 break; 1106 case 'o': 1107 res.irr_outdir = optarg; 1108 break; 1109 default: 1110 usage(); 1111 /* NOTREACHED */ 1112 } 1113 } 1114 1115 if (optind > 1) { 1116 (*argc) -= (optind - 1); 1117 (*argv) += (optind - 1); 1118 1119 /* need to move one backwards as calling code moves forward */ 1120 (*argc)++; 1121 (*argv)--; 1122 return (1); 1123 } else 1124 return (0); 1125 } 1126