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