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