1 /* $OpenBSD: parser.c,v 1.93 2019/04/10 15:22:18 claudio 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 <endian.h> 24 #include <err.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <limits.h> 28 #include <netdb.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "parser.h" 35 #include "irrfilter.h" 36 37 enum token_type { 38 NOTOKEN, 39 ENDTOKEN, 40 KEYWORD, 41 ADDRESS, 42 PEERADDRESS, 43 FLAG, 44 ASNUM, 45 ASTYPE, 46 PREFIX, 47 PEERDESC, 48 GROUPDESC, 49 RIBNAME, 50 SHUTDOWN_COMMUNICATION, 51 COMMUNITY, 52 EXTCOMMUNITY, 53 EXTCOM_SUBTYPE, 54 LARGE_COMMUNITY, 55 LOCALPREF, 56 MED, 57 NEXTHOP, 58 PFTABLE, 59 PREPNBR, 60 PREPSELF, 61 WEIGHT, 62 RD, 63 FAMILY, 64 GETOPT, 65 RTABLE, 66 FILENAME, 67 BULK 68 }; 69 70 enum getopts { 71 GETOPT_NONE, 72 GETOPT_IRRFILTER 73 }; 74 75 struct token { 76 enum token_type type; 77 const char *keyword; 78 int value; 79 const struct token *next; 80 }; 81 82 static const struct token t_main[]; 83 static const struct token t_show[]; 84 static const struct token t_show_summary[]; 85 static const struct token t_show_fib[]; 86 static const struct token t_show_rib[]; 87 static const struct token t_show_ovs[]; 88 static const struct token t_show_mrt[]; 89 static const struct token t_show_mrt_file[]; 90 static const struct token t_show_rib_neigh[]; 91 static const struct token t_show_mrt_neigh[]; 92 static const struct token t_show_rib_rib[]; 93 static const struct token t_show_neighbor[]; 94 static const struct token t_show_neighbor_modifiers[]; 95 static const struct token t_fib[]; 96 static const struct token t_neighbor[]; 97 static const struct token t_neighbor_modifiers[]; 98 static const struct token t_show_rib_as[]; 99 static const struct token t_show_mrt_as[]; 100 static const struct token t_show_prefix[]; 101 static const struct token t_show_ip[]; 102 static const struct token t_show_community[]; 103 static const struct token t_show_extcommunity[]; 104 static const struct token t_show_ext_subtype[]; 105 static const struct token t_show_largecommunity[]; 106 static const struct token t_network[]; 107 static const struct token t_network_show[]; 108 static const struct token t_prefix[]; 109 static const struct token t_set[]; 110 static const struct token t_community[]; 111 static const struct token t_extcommunity[]; 112 static const struct token t_ext_subtype[]; 113 static const struct token t_largecommunity[]; 114 static const struct token t_localpref[]; 115 static const struct token t_med[]; 116 static const struct token t_nexthop[]; 117 static const struct token t_pftable[]; 118 static const struct token t_prepnbr[]; 119 static const struct token t_prepself[]; 120 static const struct token t_weight[]; 121 static const struct token t_irrfilter[]; 122 static const struct token t_irrfilter_opts[]; 123 static const struct token t_log[]; 124 static const struct token t_fib_table[]; 125 static const struct token t_show_fib_table[]; 126 127 static const struct token t_main[] = { 128 { KEYWORD, "reload", RELOAD, NULL}, 129 { KEYWORD, "show", SHOW, t_show}, 130 { KEYWORD, "fib", FIB, t_fib}, 131 { KEYWORD, "neighbor", NEIGHBOR, t_neighbor}, 132 { KEYWORD, "network", NONE, t_network}, 133 { KEYWORD, "irrfilter", IRRFILTER, t_irrfilter}, 134 { KEYWORD, "log", NONE, t_log}, 135 { ENDTOKEN, "", NONE, NULL} 136 }; 137 138 static const struct token t_show[] = { 139 { NOTOKEN, "", NONE, NULL}, 140 { KEYWORD, "fib", SHOW_FIB, t_show_fib}, 141 { KEYWORD, "interfaces", SHOW_INTERFACE, NULL}, 142 { KEYWORD, "neighbor", SHOW_NEIGHBOR, t_show_neighbor}, 143 { KEYWORD, "network", NETWORK_SHOW, t_network_show}, 144 { KEYWORD, "nexthop", SHOW_NEXTHOP, NULL}, 145 { KEYWORD, "rib", SHOW_RIB, t_show_rib}, 146 { KEYWORD, "tables", SHOW_FIB_TABLES, NULL}, 147 { KEYWORD, "ip", NONE, t_show_ip}, 148 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 149 { KEYWORD, "mrt", SHOW_MRT, t_show_mrt}, 150 { ENDTOKEN, "", NONE, NULL} 151 }; 152 153 static const struct token t_show_summary[] = { 154 { NOTOKEN, "", NONE, NULL}, 155 { KEYWORD, "terse", SHOW_SUMMARY_TERSE, NULL}, 156 { ENDTOKEN, "", NONE, NULL} 157 }; 158 159 static const struct token t_show_fib[] = { 160 { NOTOKEN, "", NONE, NULL}, 161 { FLAG, "connected", F_CONNECTED, t_show_fib}, 162 { FLAG, "static", F_STATIC, t_show_fib}, 163 { FLAG, "bgp", F_BGPD_INSERTED, t_show_fib}, 164 { FLAG, "nexthop", F_NEXTHOP, t_show_fib}, 165 { KEYWORD, "table", NONE, t_show_fib_table}, 166 { FAMILY, "", NONE, t_show_fib}, 167 { ADDRESS, "", NONE, NULL}, 168 { ENDTOKEN, "", NONE, NULL} 169 }; 170 171 static const struct token t_show_rib[] = { 172 { NOTOKEN, "", NONE, NULL}, 173 { ASTYPE, "as", AS_ALL, t_show_rib_as}, 174 { ASTYPE, "source-as", AS_SOURCE, t_show_rib_as}, 175 { ASTYPE, "transit-as", AS_TRANSIT, t_show_rib_as}, 176 { ASTYPE, "peer-as", AS_PEER, t_show_rib_as}, 177 { ASTYPE, "empty-as", AS_EMPTY, t_show_rib}, 178 { KEYWORD, "community", NONE, t_show_community}, 179 { KEYWORD, "ext-community", NONE, t_show_extcommunity}, 180 { KEYWORD, "large-community", NONE, t_show_largecommunity}, 181 { FLAG, "best", F_CTL_ACTIVE, t_show_rib}, 182 { FLAG, "selected", F_CTL_ACTIVE, t_show_rib}, 183 { FLAG, "detail", F_CTL_DETAIL, t_show_rib}, 184 { FLAG, "error", F_CTL_INVALID, t_show_rib}, 185 { FLAG, "ssv" , F_CTL_SSV, t_show_rib}, 186 { FLAG, "in", F_CTL_ADJ_IN, t_show_rib}, 187 { FLAG, "out", F_CTL_ADJ_OUT, t_show_rib}, 188 { KEYWORD, "neighbor", NONE, t_show_rib_neigh}, 189 { KEYWORD, "table", NONE, t_show_rib_rib}, 190 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 191 { KEYWORD, "memory", SHOW_RIB_MEM, NULL}, 192 { KEYWORD, "ovs", NONE, t_show_ovs}, 193 { FAMILY, "", NONE, t_show_rib}, 194 { PREFIX, "", NONE, t_show_prefix}, 195 { ENDTOKEN, "", NONE, NULL} 196 }; 197 198 static const struct token t_show_ovs[] = { 199 { FLAG, "valid" , F_CTL_OVS_VALID, t_show_rib}, 200 { FLAG, "invalid", F_CTL_OVS_INVALID, t_show_rib}, 201 { FLAG, "not-found", F_CTL_OVS_NOTFOUND, t_show_rib}, 202 { ENDTOKEN, "", NONE, NULL} 203 }; 204 205 static const struct token t_show_mrt[] = { 206 { NOTOKEN, "", NONE, NULL}, 207 { ASTYPE, "as", AS_ALL, t_show_mrt_as}, 208 { ASTYPE, "source-as", AS_SOURCE, t_show_mrt_as}, 209 { ASTYPE, "transit-as", AS_TRANSIT, t_show_mrt_as}, 210 { ASTYPE, "peer-as", AS_PEER, t_show_mrt_as}, 211 { ASTYPE, "empty-as", AS_EMPTY, t_show_mrt}, 212 { FLAG, "detail", F_CTL_DETAIL, t_show_mrt}, 213 { FLAG, "ssv" , F_CTL_SSV, t_show_mrt}, 214 { KEYWORD, "neighbor", NONE, t_show_mrt_neigh}, 215 { KEYWORD, "file", NONE, t_show_mrt_file}, 216 { FAMILY, "", NONE, t_show_mrt}, 217 { PREFIX, "", NONE, t_show_prefix}, 218 { ENDTOKEN, "", NONE, NULL} 219 }; 220 221 static const struct token t_show_mrt_file[] = { 222 { FILENAME, "", NONE, t_show_mrt}, 223 { ENDTOKEN, "", NONE, NULL} 224 }; 225 226 static const struct token t_show_rib_neigh_group[] = { 227 { GROUPDESC, "", NONE, t_show_rib}, 228 { ENDTOKEN, "", NONE, NULL} 229 }; 230 231 static const struct token t_show_rib_neigh[] = { 232 { KEYWORD, "group", NONE, t_show_rib_neigh_group}, 233 { PEERADDRESS, "", NONE, t_show_rib}, 234 { PEERDESC, "", NONE, t_show_rib}, 235 { ENDTOKEN, "", NONE, NULL} 236 }; 237 238 static const struct token t_show_mrt_neigh[] = { 239 { PEERADDRESS, "", NONE, t_show_mrt}, 240 { ENDTOKEN, "", NONE, NULL} 241 }; 242 243 static const struct token t_show_rib_rib[] = { 244 { RIBNAME, "", NONE, t_show_rib}, 245 { ENDTOKEN, "", NONE, NULL} 246 }; 247 248 static const struct token t_show_neighbor_modifiers[] = { 249 { NOTOKEN, "", NONE, NULL}, 250 { KEYWORD, "timers", SHOW_NEIGHBOR_TIMERS, NULL}, 251 { KEYWORD, "messages", SHOW_NEIGHBOR, NULL}, 252 { KEYWORD, "terse", SHOW_NEIGHBOR_TERSE, NULL}, 253 { ENDTOKEN, "", NONE, NULL} 254 }; 255 256 static const struct token t_show_neighbor_group[] = { 257 { GROUPDESC, "", NONE, t_show_neighbor_modifiers}, 258 { ENDTOKEN, "", NONE, NULL} 259 }; 260 261 static const struct token t_show_neighbor[] = { 262 { NOTOKEN, "", NONE, NULL}, 263 { KEYWORD, "group", NONE, t_show_neighbor_group}, 264 { PEERADDRESS, "", NONE, t_show_neighbor_modifiers}, 265 { PEERDESC, "", NONE, t_show_neighbor_modifiers}, 266 { ENDTOKEN, "", NONE, NULL} 267 }; 268 269 static const struct token t_fib[] = { 270 { KEYWORD, "couple", FIB_COUPLE, NULL}, 271 { KEYWORD, "decouple", FIB_DECOUPLE, NULL}, 272 { KEYWORD, "table", NONE, t_fib_table}, 273 { ENDTOKEN, "", NONE, NULL} 274 }; 275 276 static const struct token t_neighbor_group[] = { 277 { GROUPDESC, "", NONE, t_neighbor_modifiers}, 278 { ENDTOKEN, "", NONE, NULL} 279 }; 280 281 static const struct token t_neighbor[] = { 282 { KEYWORD, "group", NONE, t_neighbor_group}, 283 { PEERADDRESS, "", NONE, t_neighbor_modifiers}, 284 { PEERDESC, "", NONE, t_neighbor_modifiers}, 285 { ENDTOKEN, "", NONE, NULL} 286 }; 287 288 static const struct token t_nei_mod_shutc[] = { 289 { NOTOKEN, "", NONE, NULL}, 290 { SHUTDOWN_COMMUNICATION, "", NONE, NULL}, 291 { ENDTOKEN, "", NONE, NULL} 292 }; 293 294 static const struct token t_neighbor_modifiers[] = { 295 { KEYWORD, "up", NEIGHBOR_UP, NULL}, 296 { KEYWORD, "down", NEIGHBOR_DOWN, t_nei_mod_shutc}, 297 { KEYWORD, "clear", NEIGHBOR_CLEAR, t_nei_mod_shutc}, 298 { KEYWORD, "refresh", NEIGHBOR_RREFRESH, NULL}, 299 { KEYWORD, "destroy", NEIGHBOR_DESTROY, NULL}, 300 { ENDTOKEN, "", NONE, NULL} 301 }; 302 303 static const struct token t_show_rib_as[] = { 304 { ASNUM, "", NONE, t_show_rib}, 305 { ENDTOKEN, "", NONE, NULL} 306 }; 307 308 static const struct token t_show_mrt_as[] = { 309 { ASNUM, "", NONE, t_show_mrt}, 310 { ENDTOKEN, "", NONE, NULL} 311 }; 312 313 static const struct token t_show_prefix[] = { 314 { NOTOKEN, "", NONE, NULL}, 315 { FLAG, "all", F_LONGER, NULL}, 316 { FLAG, "longer-prefixes", F_LONGER, NULL}, 317 { ENDTOKEN, "", NONE, NULL} 318 }; 319 320 static const struct token t_show_ip[] = { 321 { KEYWORD, "bgp", SHOW_RIB, t_show_rib}, 322 { ENDTOKEN, "", NONE, NULL} 323 }; 324 325 static const struct token t_show_community[] = { 326 { COMMUNITY, "", NONE, t_show_rib}, 327 { ENDTOKEN, "", NONE, NULL} 328 }; 329 330 static const struct token t_show_extcommunity[] = { 331 { EXTCOM_SUBTYPE, "bdc", NONE, t_show_ext_subtype}, 332 { EXTCOM_SUBTYPE, "defgw", NONE, t_show_ext_subtype}, 333 { EXTCOM_SUBTYPE, "esi-lab", NONE, t_show_ext_subtype}, 334 { EXTCOM_SUBTYPE, "esi-rt", NONE, t_show_ext_subtype}, 335 { EXTCOM_SUBTYPE, "l2vid", NONE, t_show_ext_subtype}, 336 { EXTCOM_SUBTYPE, "mac-mob", NONE, t_show_ext_subtype}, 337 { EXTCOM_SUBTYPE, "odi", NONE, t_show_ext_subtype}, 338 { EXTCOM_SUBTYPE, "ort", NONE, t_show_ext_subtype}, 339 { EXTCOM_SUBTYPE, "ori", NONE, t_show_ext_subtype}, 340 { EXTCOM_SUBTYPE, "ovs", NONE, t_show_ext_subtype}, 341 { EXTCOM_SUBTYPE, "rt", NONE, t_show_ext_subtype}, 342 { EXTCOM_SUBTYPE, "soo", NONE, t_show_ext_subtype}, 343 { EXTCOM_SUBTYPE, "srcas", NONE, t_show_ext_subtype}, 344 { EXTCOM_SUBTYPE, "vrfri", NONE, t_show_ext_subtype}, 345 { ENDTOKEN, "", NONE, NULL} 346 }; 347 348 static const struct token t_show_ext_subtype[] = { 349 { EXTCOMMUNITY, "", NONE, t_show_rib}, 350 { ENDTOKEN, "", NONE, NULL} 351 }; 352 353 static const struct token t_show_largecommunity[] = { 354 { LARGE_COMMUNITY, "", NONE, t_show_rib}, 355 { ENDTOKEN, "", NONE, NULL} 356 }; 357 358 static const struct token t_network[] = { 359 { KEYWORD, "add", NETWORK_ADD, t_prefix}, 360 { KEYWORD, "delete", NETWORK_REMOVE, t_prefix}, 361 { KEYWORD, "flush", NETWORK_FLUSH, NULL}, 362 { KEYWORD, "show", NETWORK_SHOW, t_network_show}, 363 { KEYWORD, "mrt", NETWORK_MRT, t_show_mrt}, 364 { KEYWORD, "bulk", NETWORK_BULK_ADD, t_set}, 365 { ENDTOKEN, "", NONE, NULL} 366 }; 367 368 static const struct token t_prefix[] = { 369 { PREFIX, "", NONE, t_set}, 370 { ENDTOKEN, "", NONE, NULL} 371 }; 372 373 static const struct token t_network_show[] = { 374 { NOTOKEN, "", NONE, NULL}, 375 { FAMILY, "", NONE, NULL}, 376 { ENDTOKEN, "", NONE, NULL} 377 }; 378 379 static const struct token t_rd[] = { 380 { RD, "", NONE, t_set}, 381 { ENDTOKEN, "", NONE, NULL} 382 }; 383 384 static const struct token t_set[] = { 385 { NOTOKEN, "", NONE, NULL}, 386 { KEYWORD, "community", NONE, t_community}, 387 { KEYWORD, "ext-community", NONE, t_extcommunity}, 388 { KEYWORD, "large-community", NONE, t_largecommunity}, 389 { KEYWORD, "localpref", NONE, t_localpref}, 390 { KEYWORD, "med", NONE, t_med}, 391 { KEYWORD, "metric", NONE, t_med}, 392 { KEYWORD, "nexthop", NONE, t_nexthop}, 393 { KEYWORD, "pftable", NONE, t_pftable}, 394 { KEYWORD, "prepend-neighbor", NONE, t_prepnbr}, 395 { KEYWORD, "prepend-self", NONE, t_prepself}, 396 { KEYWORD, "rd", NONE, t_rd}, 397 { KEYWORD, "weight", NONE, t_weight}, 398 { KEYWORD, "add", NETWORK_BULK_ADD, NULL}, 399 { KEYWORD, "delete", NETWORK_BULK_REMOVE, NULL}, 400 { ENDTOKEN, "", NONE, NULL} 401 }; 402 403 static const struct token t_community[] = { 404 { COMMUNITY, "", NONE, t_set}, 405 { ENDTOKEN, "", NONE, NULL} 406 }; 407 408 static const struct token t_extcommunity[] = { 409 { EXTCOM_SUBTYPE, "bdc", NONE, t_ext_subtype}, 410 { EXTCOM_SUBTYPE, "defgw", NONE, t_ext_subtype}, 411 { EXTCOM_SUBTYPE, "esi-lab", NONE, t_ext_subtype}, 412 { EXTCOM_SUBTYPE, "esi-rt", NONE, t_ext_subtype}, 413 { EXTCOM_SUBTYPE, "l2vid", NONE, t_ext_subtype}, 414 { EXTCOM_SUBTYPE, "mac-mob", NONE, t_ext_subtype}, 415 { EXTCOM_SUBTYPE, "odi", NONE, t_ext_subtype}, 416 { EXTCOM_SUBTYPE, "ort", NONE, t_ext_subtype}, 417 { EXTCOM_SUBTYPE, "ori", NONE, t_ext_subtype}, 418 { EXTCOM_SUBTYPE, "ovs", NONE, t_ext_subtype}, 419 { EXTCOM_SUBTYPE, "rt", NONE, t_ext_subtype}, 420 { EXTCOM_SUBTYPE, "soo", NONE, t_ext_subtype}, 421 { EXTCOM_SUBTYPE, "srcas", NONE, t_ext_subtype}, 422 { EXTCOM_SUBTYPE, "vrfri", NONE, t_ext_subtype}, 423 { ENDTOKEN, "", NONE, NULL} 424 }; 425 426 static const struct token t_ext_subtype[] = { 427 { EXTCOMMUNITY, "", NONE, t_set}, 428 { ENDTOKEN, "", NONE, NULL} 429 }; 430 431 static const struct token t_largecommunity[] = { 432 { LARGE_COMMUNITY, "", NONE, t_set}, 433 { ENDTOKEN, "", NONE, NULL} 434 }; 435 436 static const struct token t_localpref[] = { 437 { LOCALPREF, "", NONE, t_set}, 438 { ENDTOKEN, "", NONE, NULL} 439 }; 440 441 static const struct token t_med[] = { 442 { MED, "", NONE, t_set}, 443 { ENDTOKEN, "", NONE, NULL} 444 }; 445 446 static const struct token t_nexthop[] = { 447 { NEXTHOP, "", NONE, t_set}, 448 { ENDTOKEN, "", NONE, NULL} 449 }; 450 451 static const struct token t_pftable[] = { 452 { PFTABLE, "", NONE, t_set}, 453 { ENDTOKEN, "", NONE, NULL} 454 }; 455 456 static const struct token t_prepnbr[] = { 457 { PREPNBR, "", NONE, t_set}, 458 { ENDTOKEN, "", NONE, NULL} 459 }; 460 461 static const struct token t_prepself[] = { 462 { PREPSELF, "", NONE, t_set}, 463 { ENDTOKEN, "", NONE, NULL} 464 }; 465 466 static const struct token t_weight[] = { 467 { WEIGHT, "", NONE, t_set}, 468 { ENDTOKEN, "", NONE, NULL} 469 }; 470 471 static const struct token t_irrfilter[] = { 472 { GETOPT, "", GETOPT_IRRFILTER, t_irrfilter}, 473 { ASNUM, "", NONE, t_irrfilter_opts}, 474 { ENDTOKEN, "", NONE, NULL} 475 }; 476 477 static const struct token t_irrfilter_opts[] = { 478 { NOTOKEN, "", NONE, NULL}, 479 { FLAG, "importonly", F_IMPORTONLY, t_irrfilter_opts}, 480 { ENDTOKEN, "", NONE, NULL} 481 }; 482 483 static const struct token t_log[] = { 484 { KEYWORD, "verbose", LOG_VERBOSE, NULL}, 485 { KEYWORD, "brief", LOG_BRIEF, NULL}, 486 { ENDTOKEN, "", NONE, NULL} 487 }; 488 489 static const struct token t_fib_table[] = { 490 { RTABLE, "", NONE, t_fib}, 491 { ENDTOKEN, "", NONE, NULL} 492 }; 493 494 static const struct token t_show_fib_table[] = { 495 { RTABLE, "", NONE, t_show_fib}, 496 { ENDTOKEN, "", NONE, NULL} 497 }; 498 499 static struct parse_result res; 500 501 const struct token *match_token(int *argc, char **argv[], 502 const struct token []); 503 void show_valid_args(const struct token []); 504 505 int parse_addr(const char *, struct bgpd_addr *); 506 int parse_asnum(const char *, size_t, u_int32_t *); 507 int parse_number(const char *, struct parse_result *, enum token_type); 508 void parsecommunity(struct filter_community *c, int type, char *s); 509 int parseextcommunity(struct filter_community *c, const char *t, char *s); 510 int parse_nexthop(const char *, struct parse_result *); 511 int bgpctl_getopt(int *, char **[], int); 512 513 struct parse_result * 514 parse(int argc, char *argv[]) 515 { 516 const struct token *table = t_main; 517 const struct token *match; 518 519 bzero(&res, sizeof(res)); 520 res.rtableid = getrtable(); 521 TAILQ_INIT(&res.set); 522 if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) { 523 fprintf(stderr, "getcwd failed: %s\n", strerror(errno)); 524 return (NULL); 525 } 526 527 while (argc >= 0) { 528 if ((match = match_token(&argc, &argv, table)) == NULL) { 529 fprintf(stderr, "valid commands/args:\n"); 530 show_valid_args(table); 531 return (NULL); 532 } 533 534 argc--; 535 argv++; 536 537 if (match->type == NOTOKEN || match->next == NULL) 538 break; 539 540 table = match->next; 541 } 542 543 if (argc > 0) { 544 fprintf(stderr, "superfluous argument: %s\n", argv[0]); 545 return (NULL); 546 } 547 548 return (&res); 549 } 550 551 const struct token * 552 match_token(int *argc, char **argv[], const struct token table[]) 553 { 554 u_int i, match; 555 const struct token *t = NULL; 556 struct filter_set *fs; 557 const char *word = *argv[0]; 558 size_t wordlen = 0; 559 560 match = 0; 561 if (word != NULL) 562 wordlen = strlen(word); 563 for (i = 0; table[i].type != ENDTOKEN; i++) { 564 switch (table[i].type) { 565 case NOTOKEN: 566 if (word == NULL || wordlen == 0) { 567 match++; 568 t = &table[i]; 569 } 570 break; 571 case KEYWORD: 572 if (word != NULL && strncmp(word, table[i].keyword, 573 wordlen) == 0) { 574 match++; 575 t = &table[i]; 576 if (t->value) 577 res.action = t->value; 578 } 579 break; 580 case FLAG: 581 if (word != NULL && strncmp(word, table[i].keyword, 582 wordlen) == 0) { 583 match++; 584 t = &table[i]; 585 res.flags |= t->value; 586 } 587 break; 588 case FAMILY: 589 if (word == NULL) 590 break; 591 if (!strcmp(word, "inet") || 592 !strcasecmp(word, "IPv4")) { 593 match++; 594 t = &table[i]; 595 res.aid = AID_INET; 596 } 597 if (!strcmp(word, "inet6") || 598 !strcasecmp(word, "IPv6")) { 599 match++; 600 t = &table[i]; 601 res.aid = AID_INET6; 602 } 603 if (!strcasecmp(word, "VPNv4")) { 604 match++; 605 t = &table[i]; 606 res.aid = AID_VPN_IPv4; 607 } 608 break; 609 case ADDRESS: 610 if (parse_addr(word, &res.addr)) { 611 match++; 612 t = &table[i]; 613 if (t->value) 614 res.action = t->value; 615 } 616 break; 617 case PEERADDRESS: 618 if (parse_addr(word, &res.peeraddr)) { 619 match++; 620 t = &table[i]; 621 if (t->value) 622 res.action = t->value; 623 } 624 break; 625 case PREFIX: 626 if (parse_prefix(word, wordlen, &res.addr, &res.prefixlen)) { 627 match++; 628 t = &table[i]; 629 if (t->value) 630 res.action = t->value; 631 } 632 break; 633 case ASTYPE: 634 if (word != NULL && strncmp(word, table[i].keyword, 635 wordlen) == 0) { 636 match++; 637 t = &table[i]; 638 res.as.type = t->value; 639 } 640 break; 641 case ASNUM: 642 if (parse_asnum(word, wordlen, &res.as.as_min)) { 643 res.as.as_max = res.as.as_min; 644 match++; 645 t = &table[i]; 646 } 647 break; 648 case GROUPDESC: 649 res.is_group = 1; 650 /* FALLTHROUGH */ 651 case PEERDESC: 652 if (!match && word != NULL && wordlen > 0) { 653 if (strlcpy(res.peerdesc, word, 654 sizeof(res.peerdesc)) >= 655 sizeof(res.peerdesc)) 656 errx(1, "neighbor description too " 657 "long"); 658 match++; 659 t = &table[i]; 660 } 661 break; 662 case RIBNAME: 663 if (!match && word != NULL && wordlen > 0) { 664 if (strlcpy(res.rib, word, sizeof(res.rib)) >= 665 sizeof(res.rib)) 666 errx(1, "rib name too long"); 667 match++; 668 t = &table[i]; 669 } 670 break; 671 case SHUTDOWN_COMMUNICATION: 672 if (!match && word != NULL && wordlen > 0) { 673 if (strlcpy(res.shutcomm, word, 674 sizeof(res.shutcomm)) >= 675 sizeof(res.shutcomm)) 676 errx(1, "shutdown reason too long"); 677 match++; 678 t = &table[i]; 679 } 680 break; 681 case COMMUNITY: 682 case LARGE_COMMUNITY: 683 if (word != NULL && wordlen > 0) { 684 int type = COMMUNITY_TYPE_BASIC; 685 char *p = strdup(word); 686 687 if (p == NULL) 688 err(1, NULL); 689 if (table[i].type == LARGE_COMMUNITY) 690 type = COMMUNITY_TYPE_LARGE; 691 parsecommunity(&res.community, type, p); 692 free(p); 693 694 if ((fs = calloc(1, sizeof(*fs))) == NULL) 695 err(1, NULL); 696 fs->type = ACTION_SET_COMMUNITY; 697 fs->action.community = res.community; 698 TAILQ_INSERT_TAIL(&res.set, fs, entry); 699 700 match++; 701 t = &table[i]; 702 } 703 break; 704 case EXTCOM_SUBTYPE: 705 if (word != NULL && strncmp(word, table[i].keyword, 706 wordlen) == 0) { 707 res.ext_comm_subtype = table[i].keyword; 708 match++; 709 t = &table[i]; 710 } 711 break; 712 case EXTCOMMUNITY: 713 if (word != NULL && wordlen > 0) { 714 char *p = strdup(word); 715 716 if (p == NULL) 717 err(1, NULL); 718 parseextcommunity(&res.community, 719 res.ext_comm_subtype, p); 720 free(p); 721 722 if ((fs = calloc(1, sizeof(*fs))) == NULL) 723 err(1, NULL); 724 fs->type = ACTION_SET_COMMUNITY; 725 fs->action.community = res.community; 726 TAILQ_INSERT_TAIL(&res.set, fs, entry); 727 728 match++; 729 t = &table[i]; 730 } 731 break; 732 case RD: 733 if (word != NULL && wordlen > 0) { 734 char *p = strdup(word); 735 struct filter_community ext; 736 u_int64_t rd; 737 738 if (p == NULL) 739 err(1, NULL); 740 parseextcommunity(&ext, "rt", p); 741 free(p); 742 743 switch (ext.c.e.type) { 744 case EXT_COMMUNITY_TRANS_TWO_AS: 745 rd = (0ULL << 48); 746 rd |= (u_int64_t)ext.c.e.data1 << 32; 747 rd |= ext.c.e.data2 & 0xffffffff; 748 break; 749 case EXT_COMMUNITY_TRANS_IPV4: 750 rd = (1ULL << 48); 751 rd |= (u_int64_t)ext.c.e.data1 << 16; 752 rd |= ext.c.e.data2 & 0xffff; 753 break; 754 case EXT_COMMUNITY_TRANS_FOUR_AS: 755 rd = (2ULL << 48); 756 rd |= (u_int64_t)ext.c.e.data1 << 16; 757 rd |= ext.c.e.data2 & 0xffff; 758 break; 759 default: 760 errx(1, "bad encoding of rd"); 761 } 762 res.rd = htobe64(rd); 763 match++; 764 t = &table[i]; 765 } 766 break; 767 case LOCALPREF: 768 case MED: 769 case PREPNBR: 770 case PREPSELF: 771 case WEIGHT: 772 case RTABLE: 773 if (word != NULL && wordlen > 0 && 774 parse_number(word, &res, table[i].type)) { 775 match++; 776 t = &table[i]; 777 } 778 break; 779 case NEXTHOP: 780 if (word != NULL && wordlen > 0 && 781 parse_nexthop(word, &res)) { 782 match++; 783 t = &table[i]; 784 } 785 break; 786 case PFTABLE: 787 if (word != NULL && wordlen > 0) { 788 if ((fs = calloc(1, 789 sizeof(struct filter_set))) == NULL) 790 err(1, NULL); 791 if (strlcpy(fs->action.pftable, word, 792 sizeof(fs->action.pftable)) >= 793 sizeof(fs->action.pftable)) 794 errx(1, "pftable name too long"); 795 TAILQ_INSERT_TAIL(&res.set, fs, entry); 796 match++; 797 t = &table[i]; 798 } 799 break; 800 case GETOPT: 801 if (bgpctl_getopt(argc, argv, table[i].value)) { 802 match++; 803 t = &table[i]; 804 } 805 break; 806 case FILENAME: 807 if (word != NULL && wordlen > 0) { 808 if ((res.mrtfd = open(word, O_RDONLY)) == -1) { 809 /* 810 * ignore error if path has no / and 811 * does not exist. In hope to print 812 * usage. 813 */ 814 if (errno == ENOENT && 815 !strchr(word, '/')) 816 break; 817 err(1, "mrt open(%s)", word); 818 } 819 match++; 820 t = &table[i]; 821 } 822 break; 823 case BULK: 824 match++; 825 t = &table[i]; 826 break; 827 case ENDTOKEN: 828 break; 829 } 830 } 831 832 if (match != 1) { 833 if (word == NULL) 834 fprintf(stderr, "missing argument:\n"); 835 else if (match > 1) 836 fprintf(stderr, "ambiguous argument: %s\n", word); 837 else if (match < 1) 838 fprintf(stderr, "unknown argument: %s\n", word); 839 return (NULL); 840 } 841 842 return (t); 843 } 844 845 void 846 show_valid_args(const struct token table[]) 847 { 848 int i; 849 850 for (i = 0; table[i].type != ENDTOKEN; i++) { 851 switch (table[i].type) { 852 case NOTOKEN: 853 fprintf(stderr, " <cr>\n"); 854 break; 855 case KEYWORD: 856 case FLAG: 857 case ASTYPE: 858 case EXTCOM_SUBTYPE: 859 fprintf(stderr, " %s\n", table[i].keyword); 860 break; 861 case ADDRESS: 862 case PEERADDRESS: 863 fprintf(stderr, " <address>\n"); 864 break; 865 case PREFIX: 866 fprintf(stderr, " <address>[/<len>]\n"); 867 break; 868 case ASNUM: 869 fprintf(stderr, " <asnum>\n"); 870 break; 871 case GROUPDESC: 872 case PEERDESC: 873 fprintf(stderr, " <neighbor description>\n"); 874 break; 875 case RIBNAME: 876 fprintf(stderr, " <rib name>\n"); 877 break; 878 case SHUTDOWN_COMMUNICATION: 879 fprintf(stderr, " <shutdown reason>\n"); 880 break; 881 case COMMUNITY: 882 fprintf(stderr, " <community>\n"); 883 break; 884 case LARGE_COMMUNITY: 885 fprintf(stderr, " <large-community>\n"); 886 break; 887 case EXTCOMMUNITY: 888 fprintf(stderr, " <extended-community>\n"); 889 break; 890 case RD: 891 fprintf(stderr, " <route-distinguisher>\n"); 892 break; 893 case LOCALPREF: 894 case MED: 895 case PREPNBR: 896 case PREPSELF: 897 case WEIGHT: 898 fprintf(stderr, " <number>\n"); 899 break; 900 case RTABLE: 901 fprintf(stderr, " <rtableid>\n"); 902 break; 903 case NEXTHOP: 904 fprintf(stderr, " <address>\n"); 905 break; 906 case PFTABLE: 907 fprintf(stderr, " <pftable>\n"); 908 break; 909 case FAMILY: 910 fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n"); 911 break; 912 case GETOPT: 913 fprintf(stderr, " <options>\n"); 914 break; 915 case FILENAME: 916 fprintf(stderr, " <filename>\n"); 917 break; 918 case BULK: 919 case ENDTOKEN: 920 break; 921 } 922 } 923 } 924 925 int 926 parse_addr(const char *word, struct bgpd_addr *addr) 927 { 928 struct in_addr ina; 929 struct addrinfo hints, *r; 930 931 if (word == NULL) 932 return (0); 933 934 bzero(addr, sizeof(struct bgpd_addr)); 935 bzero(&ina, sizeof(ina)); 936 937 if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) { 938 addr->aid = AID_INET; 939 addr->v4 = ina; 940 return (1); 941 } 942 943 bzero(&hints, sizeof(hints)); 944 hints.ai_family = AF_INET6; 945 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 946 hints.ai_flags = AI_NUMERICHOST; 947 if (getaddrinfo(word, "0", &hints, &r) == 0) { 948 sa2addr(r->ai_addr, addr, NULL); 949 freeaddrinfo(r); 950 return (1); 951 } 952 953 return (0); 954 } 955 956 int 957 parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, u_int8_t *prefixlen) 958 { 959 char *p, *ps; 960 const char *errstr; 961 int mask = -1; 962 963 if (word == NULL) 964 return (0); 965 966 bzero(addr, sizeof(struct bgpd_addr)); 967 968 if ((p = strrchr(word, '/')) != NULL) { 969 size_t plen = strlen(p); 970 mask = strtonum(p + 1, 0, 128, &errstr); 971 if (errstr) 972 errx(1, "netmask %s", errstr); 973 974 if ((ps = malloc(wordlen - plen + 1)) == NULL) 975 err(1, "parse_prefix: malloc"); 976 strlcpy(ps, word, wordlen - plen + 1); 977 978 if (parse_addr(ps, addr) == 0) { 979 free(ps); 980 return (0); 981 } 982 983 free(ps); 984 } else 985 if (parse_addr(word, addr) == 0) 986 return (0); 987 988 switch (addr->aid) { 989 case AID_INET: 990 if (mask == -1) 991 mask = 32; 992 if (mask > 32) 993 errx(1, "invalid netmask: too large"); 994 addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask)); 995 break; 996 case AID_INET6: 997 if (mask == -1) 998 mask = 128; 999 inet6applymask(&addr->v6, &addr->v6, mask); 1000 break; 1001 default: 1002 return (0); 1003 } 1004 1005 *prefixlen = mask; 1006 return (1); 1007 } 1008 1009 int 1010 parse_asnum(const char *word, size_t wordlen, u_int32_t *asnum) 1011 { 1012 const char *errstr; 1013 char *dot; 1014 u_int32_t uval, uvalh = 0; 1015 1016 if (word == NULL) 1017 return (0); 1018 1019 if (wordlen < 1 || word[0] < '0' || word[0] > '9') 1020 return (0); 1021 1022 if ((dot = strchr(word,'.')) != NULL) { 1023 *dot++ = '\0'; 1024 uvalh = strtonum(word, 0, USHRT_MAX, &errstr); 1025 if (errstr) 1026 errx(1, "AS number is %s: %s", errstr, word); 1027 uval = strtonum(dot, 0, USHRT_MAX, &errstr); 1028 if (errstr) 1029 errx(1, "AS number is %s: %s", errstr, word); 1030 } else { 1031 uval = strtonum(word, 0, UINT_MAX, &errstr); 1032 if (errstr) 1033 errx(1, "AS number is %s: %s", errstr, word); 1034 } 1035 1036 *asnum = uval | (uvalh << 16); 1037 return (1); 1038 } 1039 1040 int 1041 parse_number(const char *word, struct parse_result *r, enum token_type type) 1042 { 1043 struct filter_set *fs; 1044 const char *errstr; 1045 u_int uval; 1046 1047 if (word == NULL) 1048 return (0); 1049 1050 uval = strtonum(word, 0, UINT_MAX, &errstr); 1051 if (errstr) 1052 errx(1, "number is %s: %s", errstr, word); 1053 1054 /* number was parseable */ 1055 if (type == RTABLE) { 1056 r->rtableid = uval; 1057 return (1); 1058 } 1059 1060 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 1061 err(1, NULL); 1062 switch (type) { 1063 case LOCALPREF: 1064 fs->type = ACTION_SET_LOCALPREF; 1065 fs->action.metric = uval; 1066 break; 1067 case MED: 1068 fs->type = ACTION_SET_MED; 1069 fs->action.metric = uval; 1070 break; 1071 case PREPNBR: 1072 if (uval > 128) { 1073 free(fs); 1074 return (0); 1075 } 1076 fs->type = ACTION_SET_PREPEND_PEER; 1077 fs->action.prepend = uval; 1078 break; 1079 case PREPSELF: 1080 if (uval > 128) { 1081 free(fs); 1082 return (0); 1083 } 1084 fs->type = ACTION_SET_PREPEND_SELF; 1085 fs->action.prepend = uval; 1086 break; 1087 case WEIGHT: 1088 fs->type = ACTION_SET_WEIGHT; 1089 fs->action.metric = uval; 1090 break; 1091 default: 1092 errx(1, "king bula sez bad things happen"); 1093 } 1094 1095 TAILQ_INSERT_TAIL(&r->set, fs, entry); 1096 return (1); 1097 } 1098 1099 static void 1100 getcommunity(char *s, int large, u_int32_t *val, u_int8_t *flag) 1101 { 1102 long long max = USHRT_MAX; 1103 const char *errstr; 1104 1105 *flag = 0; 1106 *val = 0; 1107 if (strcmp(s, "*") == 0) { 1108 *flag = COMMUNITY_ANY; 1109 return; 1110 } else if (strcmp(s, "neighbor-as") == 0) { 1111 *flag = COMMUNITY_NEIGHBOR_AS; 1112 return; 1113 } else if (strcmp(s, "local-as") == 0) { 1114 *flag = COMMUNITY_LOCAL_AS; 1115 return; 1116 } 1117 if (large) 1118 max = UINT_MAX; 1119 *val = strtonum(s, 0, max, &errstr); 1120 if (errstr) 1121 errx(1, "Community %s is %s (max: %llu)", s, errstr, max); 1122 } 1123 1124 static void 1125 setcommunity(struct filter_community *c, u_int32_t as, u_int32_t data, 1126 u_int8_t asflag, u_int8_t dataflag) 1127 { 1128 memset(c, 0, sizeof(*c)); 1129 c->type = COMMUNITY_TYPE_BASIC; 1130 c->dflag1 = asflag; 1131 c->dflag2 = dataflag; 1132 c->c.b.data1 = as; 1133 c->c.b.data2 = data; 1134 } 1135 1136 static void 1137 parselargecommunity(struct filter_community *c, char *s) 1138 { 1139 char *p, *q; 1140 1141 if ((p = strchr(s, ':')) == NULL) 1142 errx(1, "Bad community syntax"); 1143 *p++ = 0; 1144 1145 if ((q = strchr(p, ':')) == NULL) 1146 errx(1, "Bad community syntax"); 1147 *q++ = 0; 1148 1149 getcommunity(s, 1, &c->c.l.data1, &c->dflag1); 1150 getcommunity(p, 1, &c->c.l.data2, &c->dflag2); 1151 getcommunity(q, 1, &c->c.l.data3, &c->dflag3); 1152 1153 c->type = COMMUNITY_TYPE_LARGE; 1154 } 1155 1156 void 1157 parsecommunity(struct filter_community *c, int type, char *s) 1158 { 1159 char *p; 1160 u_int32_t as, data; 1161 u_int8_t asflag, dataflag; 1162 1163 if (type == COMMUNITY_TYPE_LARGE) { 1164 parselargecommunity(c, s); 1165 return; 1166 } 1167 1168 /* Well-known communities */ 1169 if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) { 1170 setcommunity(c, COMMUNITY_WELLKNOWN, 1171 COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0); 1172 return; 1173 } else if (strcasecmp(s, "NO_EXPORT") == 0) { 1174 setcommunity(c, COMMUNITY_WELLKNOWN, 1175 COMMUNITY_NO_EXPORT, 0, 0); 1176 return; 1177 } else if (strcasecmp(s, "NO_ADVERTISE") == 0) { 1178 setcommunity(c, COMMUNITY_WELLKNOWN, 1179 COMMUNITY_NO_ADVERTISE, 0, 0); 1180 return; 1181 } else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) { 1182 setcommunity(c, COMMUNITY_WELLKNOWN, 1183 COMMUNITY_NO_EXPSUBCONFED, 0, 0); 1184 return; 1185 } else if (strcasecmp(s, "NO_PEER") == 0) { 1186 setcommunity(c, COMMUNITY_WELLKNOWN, 1187 COMMUNITY_NO_PEER, 0, 0); 1188 return; 1189 } else if (strcasecmp(s, "BLACKHOLE") == 0) { 1190 setcommunity(c, COMMUNITY_WELLKNOWN, 1191 COMMUNITY_BLACKHOLE, 0, 0); 1192 return; 1193 } 1194 1195 if ((p = strchr(s, ':')) == NULL) 1196 errx(1, "Bad community syntax"); 1197 *p++ = 0; 1198 1199 getcommunity(s, 0, &as, &asflag); 1200 getcommunity(p, 0, &data, &dataflag); 1201 setcommunity(c, as, data, asflag, dataflag); 1202 } 1203 1204 static int 1205 parsesubtype(const char *name, int *type, int *subtype) 1206 { 1207 const struct ext_comm_pairs *cp; 1208 int found = 0; 1209 1210 printf("%s: looking for %s\n", __func__, name); 1211 for (cp = iana_ext_comms; cp->subname != NULL; cp++) { 1212 if (strcmp(name, cp->subname) == 0) { 1213 if (found == 0) { 1214 *type = cp->type; 1215 *subtype = cp->subtype; 1216 } 1217 found++; 1218 } 1219 } 1220 if (found > 1) 1221 *type = -1; 1222 return (found); 1223 } 1224 1225 static int 1226 parseextvalue(int type, char *s, u_int32_t *v) 1227 { 1228 const char *errstr; 1229 char *p; 1230 struct in_addr ip; 1231 u_int32_t uvalh, uval; 1232 1233 if (type != -1) { 1234 /* nothing */ 1235 } else if ((p = strchr(s, '.')) == NULL) { 1236 /* AS_PLAIN number (4 or 2 byte) */ 1237 strtonum(s, 0, USHRT_MAX, &errstr); 1238 if (errstr == NULL) 1239 type = EXT_COMMUNITY_TRANS_TWO_AS; 1240 else 1241 type = EXT_COMMUNITY_TRANS_FOUR_AS; 1242 } else if (strchr(p + 1, '.') == NULL) { 1243 /* AS_DOT number (4-byte) */ 1244 type = EXT_COMMUNITY_TRANS_FOUR_AS; 1245 } else { 1246 /* more than one dot -> IP address */ 1247 type = EXT_COMMUNITY_TRANS_IPV4; 1248 } 1249 1250 switch (type) { 1251 case EXT_COMMUNITY_TRANS_TWO_AS: 1252 uval = strtonum(s, 0, USHRT_MAX, &errstr); 1253 if (errstr) 1254 errx(1, "Bad ext-community %s is %s", s, errstr); 1255 *v = uval; 1256 break; 1257 case EXT_COMMUNITY_TRANS_FOUR_AS: 1258 if ((p = strchr(s, '.')) == NULL) { 1259 uval = strtonum(s, 0, UINT_MAX, &errstr); 1260 if (errstr) 1261 errx(1, "Bad ext-community %s is %s", s, 1262 errstr); 1263 *v = uval; 1264 break; 1265 } 1266 *p++ = '\0'; 1267 uvalh = strtonum(s, 0, USHRT_MAX, &errstr); 1268 if (errstr) 1269 errx(1, "Bad ext-community %s is %s", s, errstr); 1270 uval = strtonum(p, 0, USHRT_MAX, &errstr); 1271 if (errstr) 1272 errx(1, "Bad ext-community %s is %s", p, errstr); 1273 *v = uval | (uvalh << 16); 1274 break; 1275 case EXT_COMMUNITY_TRANS_IPV4: 1276 if (inet_aton(s, &ip) == 0) 1277 errx(1, "Bad ext-community %s not parseable", s); 1278 *v = ntohl(ip.s_addr); 1279 break; 1280 default: 1281 errx(1, "%s: unexpected type %d", __func__, type); 1282 } 1283 return (type); 1284 } 1285 1286 int 1287 parseextcommunity(struct filter_community *c, const char *t, char *s) 1288 { 1289 const struct ext_comm_pairs *cp; 1290 const char *errstr; 1291 u_int64_t ullval; 1292 u_int32_t uval; 1293 char *p, *ep; 1294 int type = 0, subtype = 0; 1295 1296 if (parsesubtype(t, &type, &subtype) == 0) 1297 errx(1, "Bad ext-community unknown type"); 1298 1299 switch (type) { 1300 case EXT_COMMUNITY_TRANS_TWO_AS: 1301 case EXT_COMMUNITY_TRANS_FOUR_AS: 1302 case EXT_COMMUNITY_TRANS_IPV4: 1303 case -1: 1304 if ((p = strchr(s, ':')) == NULL) 1305 errx(1, "Bad ext-community %s", s); 1306 *p++ = '\0'; 1307 type = parseextvalue(type, s, &uval); 1308 switch (type) { 1309 case EXT_COMMUNITY_TRANS_TWO_AS: 1310 ullval = strtonum(p, 0, UINT_MAX, &errstr); 1311 break; 1312 case EXT_COMMUNITY_TRANS_IPV4: 1313 case EXT_COMMUNITY_TRANS_FOUR_AS: 1314 ullval = strtonum(p, 0, USHRT_MAX, &errstr); 1315 break; 1316 default: 1317 errx(1, "parseextcommunity: unexpected result"); 1318 } 1319 if (errstr) 1320 errx(1, "Bad ext-community %s is %s", p, errstr); 1321 c->c.e.data1 = uval; 1322 c->c.e.data2 = ullval; 1323 break; 1324 case EXT_COMMUNITY_TRANS_OPAQUE: 1325 case EXT_COMMUNITY_TRANS_EVPN: 1326 errno = 0; 1327 ullval = strtoull(s, &ep, 0); 1328 if (s[0] == '\0' || *ep != '\0') 1329 errx(1, "Bad ext-community bad value"); 1330 if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) 1331 errx(1, "Bad ext-community value too big"); 1332 c->c.e.data2 = ullval; 1333 break; 1334 case EXT_COMMUNITY_NON_TRANS_OPAQUE: 1335 if (strcmp(s, "valid") == 0) 1336 c->c.e.data2 = EXT_COMMUNITY_OVS_VALID; 1337 else if (strcmp(s, "invalid") == 0) 1338 c->c.e.data2 = EXT_COMMUNITY_OVS_INVALID; 1339 else if (strcmp(s, "not-found") == 0) 1340 c->c.e.data2 = EXT_COMMUNITY_OVS_NOTFOUND; 1341 else 1342 errx(1, "Bad ext-community %s", s); 1343 break; 1344 } 1345 c->c.e.type = type; 1346 c->c.e.subtype = subtype; 1347 1348 /* verify type/subtype combo */ 1349 for (cp = iana_ext_comms; cp->subname != NULL; cp++) { 1350 if (cp->type == type && cp->subtype == subtype) { 1351 c->type = COMMUNITY_TYPE_EXT; 1352 return (0); 1353 } 1354 } 1355 1356 errx(1, "Bad ext-community bad format for type"); 1357 } 1358 1359 int 1360 parse_nexthop(const char *word, struct parse_result *r) 1361 { 1362 struct filter_set *fs; 1363 1364 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 1365 err(1, NULL); 1366 1367 if (strcmp(word, "blackhole") == 0) 1368 fs->type = ACTION_SET_NEXTHOP_BLACKHOLE; 1369 else if (strcmp(word, "reject") == 0) 1370 fs->type = ACTION_SET_NEXTHOP_REJECT; 1371 else if (strcmp(word, "no-modify") == 0) 1372 fs->type = ACTION_SET_NEXTHOP_NOMODIFY; 1373 else if (parse_addr(word, &fs->action.nexthop)) { 1374 fs->type = ACTION_SET_NEXTHOP; 1375 } else { 1376 free(fs); 1377 return (0); 1378 } 1379 1380 TAILQ_INSERT_TAIL(&r->set, fs, entry); 1381 return (1); 1382 } 1383 1384 int 1385 bgpctl_getopt(int *argc, char **argv[], int type) 1386 { 1387 int ch; 1388 1389 optind = optreset = 1; 1390 while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) { 1391 switch (ch) { 1392 case '4': 1393 res.flags = (res.flags | F_IPV4) & ~F_IPV6; 1394 break; 1395 case '6': 1396 res.flags = (res.flags | F_IPV6) & ~F_IPV4; 1397 break; 1398 case 'o': 1399 res.irr_outdir = optarg; 1400 break; 1401 default: 1402 usage(); 1403 /* NOTREACHED */ 1404 } 1405 } 1406 1407 if (optind > 1) { 1408 (*argc) -= (optind - 1); 1409 (*argv) += (optind - 1); 1410 1411 /* need to move one backwards as calling code moves forward */ 1412 (*argc)++; 1413 (*argv)--; 1414 return (1); 1415 } else 1416 return (0); 1417 } 1418