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