1 /* $OpenBSD: parser.c,v 1.68 2014/01/05 20:53:56 deraadt 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 *, struct bgpd_addr *, 390 u_int8_t *); 391 int parse_asnum(const char *, 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 446 match = 0; 447 448 for (i = 0; table[i].type != ENDTOKEN; i++) { 449 switch (table[i].type) { 450 case NOTOKEN: 451 if (word == NULL || strlen(word) == 0) { 452 match++; 453 t = &table[i]; 454 } 455 break; 456 case KEYWORD: 457 if (word != NULL && strncmp(word, table[i].keyword, 458 strlen(word)) == 0) { 459 match++; 460 t = &table[i]; 461 if (t->value) 462 res.action = t->value; 463 } 464 break; 465 case FLAG: 466 if (word != NULL && strncmp(word, table[i].keyword, 467 strlen(word)) == 0) { 468 match++; 469 t = &table[i]; 470 res.flags |= t->value; 471 } 472 break; 473 case FAMILY: 474 if (word == NULL) 475 break; 476 if (!strcmp(word, "inet") || 477 !strcasecmp(word, "IPv4")) { 478 match++; 479 t = &table[i]; 480 res.aid = AID_INET; 481 } 482 if (!strcmp(word, "inet6") || 483 !strcasecmp(word, "IPv6")) { 484 match++; 485 t = &table[i]; 486 res.aid = AID_INET6; 487 } 488 if (!strcasecmp(word, "VPNv4")) { 489 match++; 490 t = &table[i]; 491 res.aid = AID_VPN_IPv4; 492 } 493 break; 494 case ADDRESS: 495 if (parse_addr(word, &res.addr)) { 496 match++; 497 t = &table[i]; 498 if (t->value) 499 res.action = t->value; 500 } 501 break; 502 case PEERADDRESS: 503 if (parse_addr(word, &res.peeraddr)) { 504 match++; 505 t = &table[i]; 506 if (t->value) 507 res.action = t->value; 508 } 509 break; 510 case PREFIX: 511 if (parse_prefix(word, &res.addr, &res.prefixlen)) { 512 match++; 513 t = &table[i]; 514 if (t->value) 515 res.action = t->value; 516 } 517 break; 518 case ASTYPE: 519 if (word != NULL && strncmp(word, table[i].keyword, 520 strlen(word)) == 0) { 521 match++; 522 t = &table[i]; 523 res.as.type = t->value; 524 } 525 break; 526 case ASNUM: 527 if (parse_asnum(word, &res.as.as)) { 528 match++; 529 t = &table[i]; 530 } 531 break; 532 case PEERDESC: 533 if (!match && word != NULL && strlen(word) > 0) { 534 if (strlcpy(res.peerdesc, word, 535 sizeof(res.peerdesc)) >= 536 sizeof(res.peerdesc)) 537 errx(1, "neighbor description too " 538 "long"); 539 match++; 540 t = &table[i]; 541 } 542 break; 543 case RIBNAME: 544 if (!match && word != NULL && strlen(word) > 0) { 545 if (strlcpy(res.rib, word, sizeof(res.rib)) >= 546 sizeof(res.rib)) 547 errx(1, "rib name too long"); 548 match++; 549 t = &table[i]; 550 } 551 break; 552 case COMMUNITY: 553 if (word != NULL && strlen(word) > 0 && 554 parse_community(word, &res)) { 555 match++; 556 t = &table[i]; 557 } 558 break; 559 case LOCALPREF: 560 case MED: 561 case PREPNBR: 562 case PREPSELF: 563 case WEIGHT: 564 case RTABLE: 565 if (word != NULL && strlen(word) > 0 && 566 parse_number(word, &res, table[i].type)) { 567 match++; 568 t = &table[i]; 569 } 570 break; 571 case NEXTHOP: 572 if (word != NULL && strlen(word) > 0 && 573 parse_nexthop(word, &res)) { 574 match++; 575 t = &table[i]; 576 } 577 break; 578 case PFTABLE: 579 if (word != NULL && strlen(word) > 0) { 580 if ((fs = calloc(1, 581 sizeof(struct filter_set))) == NULL) 582 err(1, NULL); 583 if (strlcpy(fs->action.pftable, word, 584 sizeof(fs->action.pftable)) >= 585 sizeof(fs->action.pftable)) 586 errx(1, "pftable name too long"); 587 TAILQ_INSERT_TAIL(&res.set, fs, entry); 588 match++; 589 t = &table[i]; 590 } 591 break; 592 case GETOPT: 593 if (bgpctl_getopt(argc, argv, table[i].value)) { 594 match++; 595 t = &table[i]; 596 } 597 break; 598 case FILENAME: 599 if (word != NULL && strlen(word) > 0) { 600 if ((res.mrtfd = open(word, O_RDONLY)) == -1) { 601 /* 602 * ignore error if path has no / and 603 * does not exist. In hope to print 604 * usage. 605 */ 606 if (errno == ENOENT && 607 !strchr(word, '/')) 608 break; 609 err(1, "mrt open(%s)", word); 610 } 611 match++; 612 t = &table[i]; 613 } 614 break; 615 case BULK: 616 match++; 617 t = &table[i]; 618 break; 619 case ENDTOKEN: 620 break; 621 } 622 } 623 624 if (match != 1) { 625 if (word == NULL) 626 fprintf(stderr, "missing argument:\n"); 627 else if (match > 1) 628 fprintf(stderr, "ambiguous argument: %s\n", word); 629 else if (match < 1) 630 fprintf(stderr, "unknown argument: %s\n", word); 631 return (NULL); 632 } 633 634 return (t); 635 } 636 637 void 638 show_valid_args(const struct token table[]) 639 { 640 int i; 641 642 for (i = 0; table[i].type != ENDTOKEN; i++) { 643 switch (table[i].type) { 644 case NOTOKEN: 645 fprintf(stderr, " <cr>\n"); 646 break; 647 case KEYWORD: 648 case FLAG: 649 case ASTYPE: 650 fprintf(stderr, " %s\n", table[i].keyword); 651 break; 652 case ADDRESS: 653 case PEERADDRESS: 654 fprintf(stderr, " <address>\n"); 655 break; 656 case PREFIX: 657 fprintf(stderr, " <address>[/<len>]\n"); 658 break; 659 case ASNUM: 660 fprintf(stderr, " <asnum>\n"); 661 break; 662 case PEERDESC: 663 fprintf(stderr, " <neighbor description>\n"); 664 break; 665 case RIBNAME: 666 fprintf(stderr, " <rib name>\n"); 667 break; 668 case COMMUNITY: 669 fprintf(stderr, " <community>\n"); 670 break; 671 case LOCALPREF: 672 case MED: 673 case PREPNBR: 674 case PREPSELF: 675 case WEIGHT: 676 fprintf(stderr, " <number>\n"); 677 break; 678 case RTABLE: 679 fprintf(stderr, " <rtableid>\n"); 680 break; 681 case NEXTHOP: 682 fprintf(stderr, " <address>\n"); 683 break; 684 case PFTABLE: 685 fprintf(stderr, " <pftable>\n"); 686 break; 687 case FAMILY: 688 fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n"); 689 break; 690 case GETOPT: 691 fprintf(stderr, " <options>\n"); 692 break; 693 case FILENAME: 694 fprintf(stderr, " <filename>\n"); 695 break; 696 case BULK: 697 case ENDTOKEN: 698 break; 699 } 700 } 701 } 702 703 int 704 parse_addr(const char *word, struct bgpd_addr *addr) 705 { 706 struct in_addr ina; 707 struct addrinfo hints, *r; 708 709 if (word == NULL) 710 return (0); 711 712 bzero(addr, sizeof(struct bgpd_addr)); 713 bzero(&ina, sizeof(ina)); 714 715 if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) { 716 addr->aid = AID_INET; 717 addr->v4 = ina; 718 return (1); 719 } 720 721 bzero(&hints, sizeof(hints)); 722 hints.ai_family = AF_INET6; 723 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 724 hints.ai_flags = AI_NUMERICHOST; 725 if (getaddrinfo(word, "0", &hints, &r) == 0) { 726 sa2addr(r->ai_addr, addr); 727 freeaddrinfo(r); 728 return (1); 729 } 730 731 return (0); 732 } 733 734 int 735 parse_prefix(const char *word, struct bgpd_addr *addr, u_int8_t *prefixlen) 736 { 737 char *p, *ps; 738 const char *errstr; 739 int mask = -1; 740 741 if (word == NULL) 742 return (0); 743 744 bzero(addr, sizeof(struct bgpd_addr)); 745 746 if ((p = strrchr(word, '/')) != NULL) { 747 mask = strtonum(p + 1, 0, 128, &errstr); 748 if (errstr) 749 errx(1, "netmask %s", errstr); 750 751 if ((ps = malloc(strlen(word) - strlen(p) + 1)) == NULL) 752 err(1, "parse_prefix: malloc"); 753 strlcpy(ps, word, strlen(word) - strlen(p) + 1); 754 755 if (parse_addr(ps, addr) == 0) { 756 free(ps); 757 return (0); 758 } 759 760 free(ps); 761 } else 762 if (parse_addr(word, addr) == 0) 763 return (0); 764 765 switch (addr->aid) { 766 case AID_INET: 767 if (mask == -1) 768 mask = 32; 769 if (mask > 32) 770 errx(1, "invalid netmask: too large"); 771 addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask)); 772 break; 773 case AID_INET6: 774 if (mask == -1) 775 mask = 128; 776 inet6applymask(&addr->v6, &addr->v6, mask); 777 break; 778 default: 779 return (0); 780 } 781 782 *prefixlen = mask; 783 return (1); 784 } 785 786 int 787 parse_asnum(const char *word, u_int32_t *asnum) 788 { 789 const char *errstr; 790 char *dot; 791 u_int32_t uval, uvalh = 0; 792 793 if (word == NULL) 794 return (0); 795 796 if (strlen(word) < 1 || word[0] < '0' || word[0] > '9') 797 return (0); 798 799 if ((dot = strchr(word,'.')) != NULL) { 800 *dot++ = '\0'; 801 uvalh = strtonum(word, 0, USHRT_MAX, &errstr); 802 if (errstr) 803 errx(1, "AS number is %s: %s", errstr, word); 804 uval = strtonum(dot, 0, USHRT_MAX, &errstr); 805 if (errstr) 806 errx(1, "AS number is %s: %s", errstr, word); 807 } else { 808 uval = strtonum(word, 0, UINT_MAX, &errstr); 809 if (errstr) 810 errx(1, "AS number is %s: %s", errstr, word); 811 } 812 813 *asnum = uval | (uvalh << 16); 814 return (1); 815 } 816 817 int 818 parse_number(const char *word, struct parse_result *r, enum token_type type) 819 { 820 struct filter_set *fs; 821 const char *errstr; 822 u_int uval; 823 824 if (word == NULL) 825 return (0); 826 827 uval = strtonum(word, 0, UINT_MAX, &errstr); 828 if (errstr) 829 errx(1, "number is %s: %s", errstr, word); 830 831 /* number was parseable */ 832 if (type == RTABLE) { 833 r->rtableid = uval; 834 return (1); 835 } 836 837 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 838 err(1, NULL); 839 switch (type) { 840 case LOCALPREF: 841 fs->type = ACTION_SET_LOCALPREF; 842 fs->action.metric = uval; 843 break; 844 case MED: 845 fs->type = ACTION_SET_MED; 846 fs->action.metric = uval; 847 break; 848 case PREPNBR: 849 if (uval > 128) { 850 free(fs); 851 return (0); 852 } 853 fs->type = ACTION_SET_PREPEND_PEER; 854 fs->action.prepend = uval; 855 break; 856 case PREPSELF: 857 if (uval > 128) { 858 free(fs); 859 return (0); 860 } 861 fs->type = ACTION_SET_PREPEND_SELF; 862 fs->action.prepend = uval; 863 break; 864 case WEIGHT: 865 fs->type = ACTION_SET_WEIGHT; 866 fs->action.metric = uval; 867 break; 868 default: 869 errx(1, "king bula sez bad things happen"); 870 } 871 872 TAILQ_INSERT_TAIL(&r->set, fs, entry); 873 return (1); 874 } 875 876 int 877 getcommunity(const char *s) 878 { 879 const char *errstr; 880 u_int16_t uval; 881 882 if (strcmp(s, "*") == 0) 883 return (COMMUNITY_ANY); 884 885 uval = strtonum(s, 0, USHRT_MAX, &errstr); 886 if (errstr) 887 errx(1, "Community is %s: %s", errstr, s); 888 889 return (uval); 890 } 891 892 int 893 parse_community(const char *word, struct parse_result *r) 894 { 895 struct filter_set *fs; 896 char *p; 897 int as, type; 898 899 /* Well-known communities */ 900 if (strcasecmp(word, "NO_EXPORT") == 0) { 901 as = COMMUNITY_WELLKNOWN; 902 type = COMMUNITY_NO_EXPORT; 903 goto done; 904 } else if (strcasecmp(word, "NO_ADVERTISE") == 0) { 905 as = COMMUNITY_WELLKNOWN; 906 type = COMMUNITY_NO_ADVERTISE; 907 goto done; 908 } else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) { 909 as = COMMUNITY_WELLKNOWN; 910 type = COMMUNITY_NO_EXPSUBCONFED; 911 goto done; 912 } else if (strcasecmp(word, "NO_PEER") == 0) { 913 as = COMMUNITY_WELLKNOWN; 914 type = COMMUNITY_NO_PEER; 915 goto done; 916 } 917 918 if ((p = strchr(word, ':')) == NULL) { 919 fprintf(stderr, "Bad community syntax\n"); 920 return (0); 921 } 922 *p++ = 0; 923 924 as = getcommunity(word); 925 type = getcommunity(p); 926 927 done: 928 if (as == 0) { 929 fprintf(stderr, "Invalid community\n"); 930 return (0); 931 } 932 if (as == COMMUNITY_WELLKNOWN) 933 switch (type) { 934 case COMMUNITY_NO_EXPORT: 935 case COMMUNITY_NO_ADVERTISE: 936 case COMMUNITY_NO_EXPSUBCONFED: 937 /* valid */ 938 break; 939 default: 940 /* unknown */ 941 fprintf(stderr, "Unknown well-known community\n"); 942 return (0); 943 } 944 945 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 946 err(1, NULL); 947 fs->type = ACTION_SET_COMMUNITY; 948 fs->action.community.as = as; 949 fs->action.community.type = type; 950 951 r->community.as = as; 952 r->community.type = type; 953 954 TAILQ_INSERT_TAIL(&r->set, fs, entry); 955 return (1); 956 } 957 958 int 959 parse_nexthop(const char *word, struct parse_result *r) 960 { 961 struct filter_set *fs; 962 963 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 964 err(1, NULL); 965 966 if (strcmp(word, "blackhole") == 0) 967 fs->type = ACTION_SET_NEXTHOP_BLACKHOLE; 968 else if (strcmp(word, "reject") == 0) 969 fs->type = ACTION_SET_NEXTHOP_REJECT; 970 else if (strcmp(word, "no-modify") == 0) 971 fs->type = ACTION_SET_NEXTHOP_NOMODIFY; 972 else if (parse_addr(word, &fs->action.nexthop)) { 973 fs->type = ACTION_SET_NEXTHOP; 974 } else { 975 free(fs); 976 return (0); 977 } 978 979 TAILQ_INSERT_TAIL(&r->set, fs, entry); 980 return (1); 981 } 982 983 int 984 bgpctl_getopt(int *argc, char **argv[], int type) 985 { 986 int ch; 987 988 optind = optreset = 1; 989 while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) { 990 switch (ch) { 991 case '4': 992 res.flags = (res.flags | F_IPV4) & ~F_IPV6; 993 break; 994 case '6': 995 res.flags = (res.flags | F_IPV6) & ~F_IPV4; 996 break; 997 case 'o': 998 res.irr_outdir = optarg; 999 break; 1000 default: 1001 usage(); 1002 /* NOTREACHED */ 1003 } 1004 } 1005 1006 if (optind > 1) { 1007 (*argc) -= (optind - 1); 1008 (*argv) += (optind - 1); 1009 1010 /* need to move one backwards as calling code moves forward */ 1011 (*argc)++; 1012 (*argv)--; 1013 return (1); 1014 } else 1015 return (0); 1016 } 1017