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