1 /* $OpenBSD: parser.c,v 1.137 2024/08/22 08:17:54 florian 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 ENDTOKEN, 38 NOTOKEN, 39 ANYTOKEN, 40 KEYWORD, 41 ADDRESS, 42 PEERADDRESS, 43 FLAG, 44 ASNUM, 45 ASTYPE, 46 PREFIX, 47 PEERDESC, 48 GROUPDESC, 49 RIBNAME, 50 COMMUNICATION, 51 COMMUNITY, 52 EXTCOMMUNITY, 53 LRGCOMMUNITY, 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 FLOW_PROTO, 67 FLOW_SRC, 68 FLOW_DST, 69 FLOW_SRCPORT, 70 FLOW_DSTPORT, 71 FLOW_ICMPTYPE, 72 FLOW_ICMPCODE, 73 FLOW_LENGTH, 74 FLOW_DSCP, 75 FLOW_FLAGS, 76 FLOW_FRAGS, 77 }; 78 79 struct token { 80 enum token_type type; 81 const char *keyword; 82 int value; 83 const struct token *next; 84 }; 85 86 static const struct token *prevtable; 87 88 static const struct token t_main[]; 89 static const struct token t_show[]; 90 static const struct token t_show_summary[]; 91 static const struct token t_show_fib[]; 92 static const struct token t_show_rib[]; 93 static const struct token t_show_avs[]; 94 static const struct token t_show_ovs[]; 95 static const struct token t_show_mrt[]; 96 static const struct token t_show_mrt_file[]; 97 static const struct token t_show_rib_neigh[]; 98 static const struct token t_show_mrt_neigh[]; 99 static const struct token t_show_rib_rib[]; 100 static const struct token t_show_neighbor[]; 101 static const struct token t_show_neighbor_modifiers[]; 102 static const struct token t_fib[]; 103 static const struct token t_neighbor[]; 104 static const struct token t_neighbor_modifiers[]; 105 static const struct token t_show_rib_as[]; 106 static const struct token t_show_mrt_as[]; 107 static const struct token t_show_prefix[]; 108 static const struct token t_show_ip[]; 109 static const struct token t_network[]; 110 static const struct token t_flowspec[]; 111 static const struct token t_flowfamily[]; 112 static const struct token t_flowrule[]; 113 static const struct token t_flowsrc[]; 114 static const struct token t_flowdst[]; 115 static const struct token t_flowsrcport[]; 116 static const struct token t_flowdstport[]; 117 static const struct token t_flowicmp[]; 118 static const struct token t_bulk[]; 119 static const struct token t_network_show[]; 120 static const struct token t_prefix[]; 121 static const struct token t_set[]; 122 static const struct token t_nexthop[]; 123 static const struct token t_pftable[]; 124 static const struct token t_log[]; 125 static const struct token t_communication[]; 126 127 static const struct token t_main[] = { 128 { KEYWORD, "fib", FIB, t_fib}, 129 { KEYWORD, "flowspec", NONE, t_flowspec}, 130 { KEYWORD, "log", NONE, t_log}, 131 { KEYWORD, "neighbor", NEIGHBOR, t_neighbor}, 132 { KEYWORD, "network", NONE, t_network}, 133 { KEYWORD, "reload", RELOAD, t_communication}, 134 { KEYWORD, "show", SHOW, t_show}, 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, "flowspec", FLOWSPEC_SHOW, t_network_show}, 142 { KEYWORD, "interfaces", SHOW_INTERFACE, NULL}, 143 { KEYWORD, "ip", NONE, t_show_ip}, 144 { KEYWORD, "metrics", SHOW_METRICS, NULL}, 145 { KEYWORD, "mrt", SHOW_MRT, t_show_mrt}, 146 { KEYWORD, "neighbor", SHOW_NEIGHBOR, t_show_neighbor}, 147 { KEYWORD, "network", NETWORK_SHOW, t_network_show}, 148 { KEYWORD, "nexthop", SHOW_NEXTHOP, NULL}, 149 { KEYWORD, "rib", SHOW_RIB, t_show_rib}, 150 { KEYWORD, "rtr", SHOW_RTR, NULL}, 151 { KEYWORD, "sets", SHOW_SET, NULL}, 152 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 153 { KEYWORD, "tables", SHOW_FIB_TABLES, NULL}, 154 { ENDTOKEN, "", NONE, NULL} 155 }; 156 157 static const struct token t_show_summary[] = { 158 { NOTOKEN, "", NONE, NULL}, 159 { KEYWORD, "terse", SHOW_SUMMARY_TERSE, NULL}, 160 { ENDTOKEN, "", NONE, NULL} 161 }; 162 163 static const struct token t_show_fib[] = { 164 { NOTOKEN, "", NONE, NULL}, 165 { FLAG, "bgp", F_BGPD, t_show_fib}, 166 { FLAG, "connected", F_CONNECTED, t_show_fib}, 167 { FLAG, "nexthop", F_NEXTHOP, t_show_fib}, 168 { FLAG, "static", F_STATIC, t_show_fib}, 169 { RTABLE, "table", NONE, t_show_fib}, 170 { FAMILY, "", NONE, t_show_fib}, 171 { ADDRESS, "", NONE, NULL}, 172 { ENDTOKEN, "", NONE, NULL} 173 }; 174 175 static const struct token t_show_rib[] = { 176 { NOTOKEN, "", NONE, NULL}, 177 { ASTYPE, "as", AS_ALL, t_show_rib_as}, 178 { KEYWORD, "avs", NONE, t_show_avs}, 179 { FLAG, "best", F_CTL_BEST, t_show_rib}, 180 { COMMUNITY, "community", NONE, t_show_rib}, 181 { FLAG, "detail", F_CTL_DETAIL, t_show_rib}, 182 { FLAG, "disqualified", F_CTL_INELIGIBLE, t_show_rib}, 183 { ASTYPE, "empty-as", AS_EMPTY, t_show_rib}, 184 { FLAG, "error", F_CTL_INVALID, t_show_rib}, 185 { EXTCOMMUNITY, "ext-community", NONE, t_show_rib}, 186 { FLAG, "filtered", F_CTL_FILTERED, t_show_rib}, 187 { FLAG, "in", F_CTL_ADJ_IN, t_show_rib}, 188 { LRGCOMMUNITY, "large-community", NONE, t_show_rib}, 189 { FLAG, "leaked", F_CTL_LEAKED, t_show_rib}, 190 { KEYWORD, "memory", SHOW_RIB_MEM, NULL}, 191 { KEYWORD, "neighbor", NONE, t_show_rib_neigh}, 192 { FLAG, "out", F_CTL_ADJ_OUT, t_show_rib}, 193 { KEYWORD, "ovs", NONE, t_show_ovs}, 194 { PATHID, "path-id", NONE, t_show_rib}, 195 { ASTYPE, "peer-as", AS_PEER, t_show_rib_as}, 196 { FLAG, "selected", F_CTL_BEST, t_show_rib}, 197 { ASTYPE, "source-as", AS_SOURCE, t_show_rib_as}, 198 { FLAG, "ssv", F_CTL_SSV, t_show_rib}, 199 { KEYWORD, "summary", SHOW_SUMMARY, t_show_summary}, 200 { KEYWORD, "table", NONE, t_show_rib_rib}, 201 { ASTYPE, "transit-as", AS_TRANSIT, t_show_rib_as}, 202 { FAMILY, "", NONE, t_show_rib}, 203 { PREFIX, "", NONE, t_show_prefix}, 204 { ENDTOKEN, "", NONE, NULL} 205 }; 206 207 static const struct token t_show_avs[] = { 208 { FLAG, "invalid", F_CTL_AVS_INVALID, t_show_rib}, 209 { FLAG, "unknown", F_CTL_AVS_UNKNOWN, t_show_rib}, 210 { FLAG, "valid" , F_CTL_AVS_VALID, t_show_rib}, 211 { ENDTOKEN, "", NONE, NULL} 212 }; 213 214 static const struct token t_show_ovs[] = { 215 { FLAG, "invalid", F_CTL_OVS_INVALID, t_show_rib}, 216 { FLAG, "not-found", F_CTL_OVS_NOTFOUND, t_show_rib}, 217 { FLAG, "valid" , F_CTL_OVS_VALID, t_show_rib}, 218 { ENDTOKEN, "", NONE, NULL} 219 }; 220 221 static const struct token t_show_mrt[] = { 222 { NOTOKEN, "", NONE, NULL}, 223 { ASTYPE, "as", AS_ALL, t_show_mrt_as}, 224 { FLAG, "detail", F_CTL_DETAIL, t_show_mrt}, 225 { ASTYPE, "empty-as", AS_EMPTY, t_show_mrt}, 226 { KEYWORD, "file", NONE, t_show_mrt_file}, 227 { KEYWORD, "neighbor", NONE, t_show_mrt_neigh}, 228 { ASTYPE, "peer-as", AS_PEER, t_show_mrt_as}, 229 { FLAG, "peers", F_CTL_NEIGHBORS,t_show_mrt}, 230 { ASTYPE, "source-as", AS_SOURCE, t_show_mrt_as}, 231 { FLAG, "ssv", F_CTL_SSV, t_show_mrt}, 232 { ASTYPE, "transit-as", AS_TRANSIT, t_show_mrt_as}, 233 { FAMILY, "", NONE, t_show_mrt}, 234 { PREFIX, "", NONE, t_show_prefix}, 235 { ENDTOKEN, "", NONE, NULL} 236 }; 237 238 static const struct token t_show_mrt_file[] = { 239 { FILENAME, "", NONE, t_show_mrt}, 240 { ENDTOKEN, "", NONE, NULL} 241 }; 242 243 static const struct token t_show_rib_neigh_group[] = { 244 { GROUPDESC, "", NONE, t_show_rib}, 245 { ENDTOKEN, "", NONE, NULL} 246 }; 247 248 static const struct token t_show_rib_neigh[] = { 249 { KEYWORD, "group", NONE, t_show_rib_neigh_group}, 250 { PEERADDRESS, "", NONE, t_show_rib}, 251 { PEERDESC, "", NONE, t_show_rib}, 252 { ENDTOKEN, "", NONE, NULL} 253 }; 254 255 static const struct token t_show_mrt_neigh[] = { 256 { PEERADDRESS, "", NONE, t_show_mrt}, 257 { ENDTOKEN, "", NONE, NULL} 258 }; 259 260 static const struct token t_show_rib_rib[] = { 261 { RIBNAME, "", NONE, t_show_rib}, 262 { ENDTOKEN, "", NONE, NULL} 263 }; 264 265 static const struct token t_show_neighbor_modifiers[] = { 266 { NOTOKEN, "", NONE, NULL}, 267 { KEYWORD, "messages", SHOW_NEIGHBOR, NULL}, 268 { KEYWORD, "terse", SHOW_NEIGHBOR_TERSE, NULL}, 269 { KEYWORD, "timers", SHOW_NEIGHBOR_TIMERS, NULL}, 270 { ENDTOKEN, "", NONE, NULL} 271 }; 272 273 static const struct token t_show_neighbor_group[] = { 274 { GROUPDESC, "", NONE, t_show_neighbor_modifiers}, 275 { ENDTOKEN, "", NONE, NULL} 276 }; 277 278 static const struct token t_show_neighbor[] = { 279 { NOTOKEN, "", NONE, NULL}, 280 { KEYWORD, "group", NONE, t_show_neighbor_group}, 281 { PEERADDRESS, "", NONE, t_show_neighbor_modifiers}, 282 { PEERDESC, "", NONE, t_show_neighbor_modifiers}, 283 { ENDTOKEN, "", NONE, NULL} 284 }; 285 286 static const struct token t_fib[] = { 287 { KEYWORD, "couple", FIB_COUPLE, NULL}, 288 { KEYWORD, "decouple", FIB_DECOUPLE, NULL}, 289 { RTABLE, "table", NONE, t_fib}, 290 { ENDTOKEN, "", NONE, NULL} 291 }; 292 293 static const struct token t_neighbor_group[] = { 294 { GROUPDESC, "", NONE, t_neighbor_modifiers}, 295 { ENDTOKEN, "", NONE, NULL} 296 }; 297 298 static const struct token t_neighbor[] = { 299 { KEYWORD, "group", NONE, t_neighbor_group}, 300 { PEERADDRESS, "", NONE, t_neighbor_modifiers}, 301 { PEERDESC, "", NONE, t_neighbor_modifiers}, 302 { ENDTOKEN, "", NONE, NULL} 303 }; 304 305 static const struct token t_communication[] = { 306 { NOTOKEN, "", NONE, NULL}, 307 { COMMUNICATION, "", NONE, NULL}, 308 { ENDTOKEN, "", NONE, NULL} 309 }; 310 311 static const struct token t_neighbor_modifiers[] = { 312 { KEYWORD, "clear", NEIGHBOR_CLEAR, t_communication}, 313 { KEYWORD, "destroy", NEIGHBOR_DESTROY, NULL}, 314 { KEYWORD, "down", NEIGHBOR_DOWN, t_communication}, 315 { KEYWORD, "refresh", NEIGHBOR_RREFRESH, NULL}, 316 { KEYWORD, "up", NEIGHBOR_UP, NULL}, 317 { ENDTOKEN, "", NONE, NULL} 318 }; 319 320 static const struct token t_show_rib_as[] = { 321 { ASNUM, "", NONE, t_show_rib}, 322 { ENDTOKEN, "", NONE, NULL} 323 }; 324 325 static const struct token t_show_mrt_as[] = { 326 { ASNUM, "", NONE, t_show_mrt}, 327 { ENDTOKEN, "", NONE, NULL} 328 }; 329 330 static const struct token t_show_prefix[] = { 331 { FLAG, "all", F_LONGER, t_show_rib}, 332 { FLAG, "longer-prefixes", F_LONGER, t_show_rib}, 333 { FLAG, "or-longer", F_LONGER, t_show_rib}, 334 { FLAG, "or-shorter", F_SHORTER, t_show_rib}, 335 { ANYTOKEN, "", NONE, t_show_rib}, 336 { ENDTOKEN, "", NONE, NULL} 337 }; 338 339 static const struct token t_show_ip[] = { 340 { KEYWORD, "bgp", SHOW_RIB, t_show_rib}, 341 { ENDTOKEN, "", NONE, NULL} 342 }; 343 344 static const struct token t_network[] = { 345 { KEYWORD, "add", NETWORK_ADD, t_prefix}, 346 { KEYWORD, "bulk", NONE, t_bulk}, 347 { KEYWORD, "delete", NETWORK_REMOVE, t_prefix}, 348 { KEYWORD, "flush", NETWORK_FLUSH, NULL}, 349 { KEYWORD, "mrt", NETWORK_MRT, t_show_mrt}, 350 { KEYWORD, "show", NETWORK_SHOW, t_network_show}, 351 { ENDTOKEN, "", NONE, NULL} 352 }; 353 354 static const struct token t_flowspec[] = { 355 { KEYWORD, "add", FLOWSPEC_ADD, t_flowfamily}, 356 { KEYWORD, "delete", FLOWSPEC_REMOVE,t_flowfamily}, 357 { KEYWORD, "flush", FLOWSPEC_FLUSH, NULL}, 358 { KEYWORD, "show", FLOWSPEC_SHOW, t_network_show}, 359 { ENDTOKEN, "", NONE, NULL} 360 }; 361 362 static const struct token t_flowfamily[] = { 363 { FAMILY, "", NONE, t_flowrule}, 364 { ENDTOKEN, "", NONE, NULL} 365 }; 366 367 static const struct token t_flowrule[] = { 368 { NOTOKEN, "", NONE, NULL}, 369 { FLOW_FLAGS, "flags", NONE, t_flowrule}, 370 { FLOW_FRAGS, "fragment", NONE, t_flowrule}, 371 { KEYWORD, "from", NONE, t_flowsrc}, 372 { FLOW_ICMPTYPE,"icmp-type", NONE, t_flowicmp}, 373 { FLOW_LENGTH, "length", NONE, t_flowrule}, 374 { FLOW_PROTO, "proto", NONE, t_flowrule}, 375 { KEYWORD, "set", NONE, t_set}, 376 { KEYWORD, "to", NONE, t_flowdst}, 377 { FLOW_DSCP, "dscp", NONE, t_flowrule}, 378 { ENDTOKEN, "", NONE, NULL} 379 }; 380 381 static const struct token t_flowsrc[] = { 382 { KEYWORD, "any", NONE, t_flowsrcport}, 383 { FLOW_SRC, "", NONE, t_flowsrcport}, 384 { ENDTOKEN, "", NONE, NULL} 385 }; 386 387 static const struct token t_flowdst[] = { 388 { KEYWORD, "any", NONE, t_flowdstport}, 389 { FLOW_DST, "", NONE, t_flowdstport}, 390 { ENDTOKEN, "", NONE, NULL} 391 }; 392 393 static const struct token t_flowsrcport[] = { 394 { FLOW_SRCPORT, "port", NONE, t_flowrule}, 395 { ANYTOKEN, "", NONE, t_flowrule}, 396 { ENDTOKEN, "", NONE, NULL} 397 }; 398 399 static const struct token t_flowdstport[] = { 400 { FLOW_DSTPORT, "port", NONE, t_flowrule}, 401 { ANYTOKEN, "", NONE, t_flowrule}, 402 { ENDTOKEN, "", NONE, NULL} 403 }; 404 405 static const struct token t_flowicmp[] = { 406 { FLOW_ICMPCODE,"code", NONE, t_flowrule}, 407 { ANYTOKEN, "", NONE, t_flowrule}, 408 { ENDTOKEN, "", NONE, NULL} 409 }; 410 411 static const struct token t_bulk[] = { 412 { KEYWORD, "add", NETWORK_BULK_ADD, t_set}, 413 { KEYWORD, "delete", NETWORK_BULK_REMOVE, NULL}, 414 { ENDTOKEN, "", NONE, NULL} 415 }; 416 417 static const struct token t_prefix[] = { 418 { PREFIX, "", NONE, t_set}, 419 { ENDTOKEN, "", NONE, NULL} 420 }; 421 422 static const struct token t_network_show[] = { 423 { NOTOKEN, "", NONE, NULL}, 424 { FAMILY, "", NONE, NULL}, 425 { ENDTOKEN, "", NONE, NULL} 426 }; 427 428 static const struct token t_rd[] = { 429 { RD, "", NONE, t_set}, 430 { ENDTOKEN, "", NONE, NULL} 431 }; 432 433 static const struct token t_set[] = { 434 { NOTOKEN, "", NONE, NULL}, 435 { COMMUNITY, "community", NONE, t_set}, 436 { EXTCOMMUNITY, "ext-community", NONE, t_set}, 437 { LRGCOMMUNITY, "large-community", NONE, t_set}, 438 { LOCALPREF, "localpref", NONE, t_set}, 439 { MED, "med", NONE, t_set}, 440 { MED, "metric", NONE, t_set}, 441 { KEYWORD, "nexthop", NONE, t_nexthop}, 442 { KEYWORD, "pftable", NONE, t_pftable}, 443 { PREPNBR, "prepend-neighbor", NONE, t_set}, 444 { PREPSELF, "prepend-self", NONE, t_set}, 445 { KEYWORD, "rd", NONE, t_rd}, 446 { WEIGHT, "weight", NONE, t_set}, 447 { ENDTOKEN, "", NONE, NULL} 448 }; 449 450 static const struct token t_nexthop[] = { 451 { NEXTHOP, "", NONE, t_set}, 452 { ENDTOKEN, "", NONE, NULL} 453 }; 454 455 static const struct token t_pftable[] = { 456 { PFTABLE, "", NONE, t_set}, 457 { ENDTOKEN, "", NONE, NULL} 458 }; 459 460 static const struct token t_log[] = { 461 { KEYWORD, "brief", LOG_BRIEF, NULL}, 462 { KEYWORD, "verbose", LOG_VERBOSE, NULL}, 463 { ENDTOKEN, "", NONE, NULL} 464 }; 465 466 static struct parse_result res; 467 468 const struct token *match_token(int, char *[], const struct token [], 469 int *); 470 void show_valid_args(const struct token []); 471 472 int parse_addr(const char *, struct bgpd_addr *); 473 int parse_asnum(const char *, size_t, uint32_t *); 474 int parse_number(const char *, struct parse_result *, enum token_type); 475 void parsecommunity(struct community *c, char *s); 476 void parselargecommunity(struct community *c, char *s); 477 void parseextcommunity(struct community *c, const char *t, char *s); 478 int parse_nexthop(const char *, struct parse_result *); 479 int parse_flow_numop(int, char *[], struct parse_result *, enum token_type); 480 481 struct parse_result * 482 parse(int argc, char *argv[]) 483 { 484 const struct token *table = t_main; 485 const struct token *match; 486 int used; 487 488 memset(&res, 0, sizeof(res)); 489 res.rtableid = getrtable(); 490 TAILQ_INIT(&res.set); 491 492 while (argc >= 0) { 493 if ((match = match_token(argc, argv, table, &used)) == NULL) { 494 fprintf(stderr, "valid commands/args:\n"); 495 show_valid_args(table); 496 return (NULL); 497 } 498 if (match->type == ANYTOKEN) { 499 if (prevtable == NULL) 500 prevtable = table; 501 table = match->next; 502 continue; 503 } 504 505 argc -= used; 506 argv += used; 507 508 if (match->type == NOTOKEN || match->next == NULL) 509 break; 510 table = match->next; 511 } 512 513 if (argc > 0) { 514 fprintf(stderr, "superfluous argument: %s\n", argv[0]); 515 return (NULL); 516 } 517 518 return (&res); 519 } 520 521 const struct token * 522 match_token(int argc, char *argv[], const struct token table[], int *argsused) 523 { 524 u_int i, match; 525 const struct token *t = NULL; 526 struct filter_set *fs; 527 const char *word = argv[0]; 528 size_t wordlen = 0; 529 530 *argsused = 1; 531 match = 0; 532 if (word != NULL) 533 wordlen = strlen(word); 534 for (i = 0; table[i].type != ENDTOKEN; i++) { 535 switch (table[i].type) { 536 case NOTOKEN: 537 if (word == NULL || wordlen == 0) { 538 match++; 539 t = &table[i]; 540 } 541 break; 542 case ANYTOKEN: 543 /* match anything if nothing else matched before */ 544 if (match == 0) { 545 match++; 546 t = &table[i]; 547 } 548 break; 549 case KEYWORD: 550 if (word != NULL && strncmp(word, table[i].keyword, 551 wordlen) == 0) { 552 match++; 553 t = &table[i]; 554 if (t->value) 555 res.action = t->value; 556 } 557 break; 558 case FLAG: 559 if (word != NULL && strncmp(word, table[i].keyword, 560 wordlen) == 0) { 561 match++; 562 t = &table[i]; 563 res.flags |= t->value; 564 } 565 break; 566 case FAMILY: 567 if (word == NULL) 568 break; 569 if (!strcmp(word, "inet") || 570 !strcasecmp(word, "IPv4")) { 571 match++; 572 t = &table[i]; 573 res.aid = AID_INET; 574 } 575 if (!strcmp(word, "inet6") || 576 !strcasecmp(word, "IPv6")) { 577 match++; 578 t = &table[i]; 579 res.aid = AID_INET6; 580 } 581 if (!strcasecmp(word, "VPNv4")) { 582 match++; 583 t = &table[i]; 584 res.aid = AID_VPN_IPv4; 585 } 586 if (!strcasecmp(word, "VPNv6")) { 587 match++; 588 t = &table[i]; 589 res.aid = AID_VPN_IPv6; 590 } 591 break; 592 case ADDRESS: 593 if (parse_addr(word, &res.addr)) { 594 match++; 595 t = &table[i]; 596 } 597 break; 598 case PEERADDRESS: 599 if (parse_addr(word, &res.peeraddr)) { 600 match++; 601 t = &table[i]; 602 } 603 break; 604 case FLOW_SRC: 605 if (parse_prefix(word, wordlen, &res.flow.src, 606 &res.flow.srclen)) { 607 match++; 608 t = &table[i]; 609 if (res.aid != res.flow.src.aid) 610 errx(1, "wrong address family in " 611 "flowspec rule"); 612 } 613 break; 614 case FLOW_DST: 615 if (parse_prefix(word, wordlen, &res.flow.dst, 616 &res.flow.dstlen)) { 617 match++; 618 t = &table[i]; 619 if (res.aid != res.flow.dst.aid) 620 errx(1, "wrong address family in " 621 "flowspec rule"); 622 } 623 break; 624 case PREFIX: 625 if (parse_prefix(word, wordlen, &res.addr, 626 &res.prefixlen)) { 627 match++; 628 t = &table[i]; 629 } 630 break; 631 case ASTYPE: 632 if (word != NULL && strncmp(word, table[i].keyword, 633 wordlen) == 0) { 634 match++; 635 t = &table[i]; 636 res.as.type = t->value; 637 } 638 break; 639 case ASNUM: 640 if (parse_asnum(word, wordlen, &res.as.as_min)) { 641 res.as.as_max = res.as.as_min; 642 match++; 643 t = &table[i]; 644 } 645 break; 646 case GROUPDESC: 647 res.is_group = 1; 648 /* FALLTHROUGH */ 649 case PEERDESC: 650 if (!match && word != NULL && wordlen > 0) { 651 if (strlcpy(res.peerdesc, word, 652 sizeof(res.peerdesc)) >= 653 sizeof(res.peerdesc)) 654 errx(1, "neighbor description too " 655 "long"); 656 match++; 657 t = &table[i]; 658 } 659 break; 660 case RIBNAME: 661 if (!match && word != NULL && wordlen > 0) { 662 if (strlcpy(res.rib, word, sizeof(res.rib)) >= 663 sizeof(res.rib)) 664 errx(1, "rib name too long"); 665 match++; 666 t = &table[i]; 667 } 668 break; 669 case COMMUNICATION: 670 if (!match && word != NULL && wordlen > 0) { 671 if (strlcpy(res.reason, word, 672 sizeof(res.reason)) >= 673 sizeof(res.reason)) 674 errx(1, "shutdown reason too long"); 675 match++; 676 t = &table[i]; 677 } 678 break; 679 case COMMUNITY: 680 if (word != NULL && strncmp(word, table[i].keyword, 681 wordlen) == 0 && argc > 1) { 682 parsecommunity(&res.community, argv[1]); 683 *argsused += 1; 684 685 if ((fs = calloc(1, sizeof(*fs))) == NULL) 686 err(1, NULL); 687 fs->type = ACTION_SET_COMMUNITY; 688 fs->action.community = res.community; 689 TAILQ_INSERT_TAIL(&res.set, fs, entry); 690 691 match++; 692 t = &table[i]; 693 } 694 break; 695 case LRGCOMMUNITY: 696 if (word != NULL && strncmp(word, table[i].keyword, 697 wordlen) == 0 && argc > 1) { 698 parselargecommunity(&res.community, argv[1]); 699 *argsused += 1; 700 701 if ((fs = calloc(1, sizeof(*fs))) == NULL) 702 err(1, NULL); 703 fs->type = ACTION_SET_COMMUNITY; 704 fs->action.community = res.community; 705 TAILQ_INSERT_TAIL(&res.set, fs, entry); 706 707 match++; 708 t = &table[i]; 709 } 710 break; 711 case EXTCOMMUNITY: 712 if (word != NULL && strncmp(word, table[i].keyword, 713 wordlen) == 0 && argc > 2) { 714 parseextcommunity(&res.community, 715 argv[1], argv[2]); 716 *argsused += 2; 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 && strncmp(word, table[i].keyword, 772 wordlen) == 0 && argc > 1 && 773 parse_number(argv[1], &res, table[i].type)) { 774 *argsused += 1; 775 match++; 776 t = &table[i]; 777 } 778 break; 779 case NEXTHOP: 780 if (word != NULL && wordlen > 0 && 781 parse_nexthop(word, &res)) { 782 match++; 783 t = &table[i]; 784 } 785 break; 786 case PFTABLE: 787 if (word != NULL && wordlen > 0) { 788 if ((fs = calloc(1, 789 sizeof(struct filter_set))) == NULL) 790 err(1, NULL); 791 if (strlcpy(fs->action.pftable, word, 792 sizeof(fs->action.pftable)) >= 793 sizeof(fs->action.pftable)) 794 errx(1, "pftable name too long"); 795 TAILQ_INSERT_TAIL(&res.set, fs, entry); 796 match++; 797 t = &table[i]; 798 } 799 break; 800 case FILENAME: 801 if (word != NULL && wordlen > 0) { 802 if ((res.mrtfd = open(word, O_RDONLY)) == -1) { 803 /* 804 * ignore error if path has no / and 805 * does not exist. In hope to print 806 * usage. 807 */ 808 if (errno == ENOENT && 809 !strchr(word, '/')) 810 break; 811 err(1, "mrt open(%s)", word); 812 } 813 match++; 814 t = &table[i]; 815 } 816 break; 817 case FLOW_SRCPORT: 818 case FLOW_DSTPORT: 819 case FLOW_PROTO: 820 case FLOW_ICMPTYPE: 821 case FLOW_ICMPCODE: 822 case FLOW_LENGTH: 823 case FLOW_DSCP: 824 if (word != NULL && strncmp(word, table[i].keyword, 825 wordlen) == 0 && argc > 1) { 826 *argsused += parse_flow_numop(argc, argv, &res, 827 table[i].type); 828 829 match++; 830 t = &table[i]; 831 } 832 break; 833 case FLOW_FLAGS: 834 case FLOW_FRAGS: 835 if (word != NULL && strncmp(word, table[i].keyword, 836 wordlen) == 0) { 837 errx(1, "%s not yet implemented", word); 838 } 839 break; 840 case ENDTOKEN: 841 break; 842 } 843 } 844 845 if (match != 1) { 846 if (word == NULL) 847 fprintf(stderr, "missing argument:\n"); 848 else if (match > 1) 849 fprintf(stderr, "ambiguous argument: %s\n", word); 850 else if (match < 1) 851 fprintf(stderr, "unknown argument: %s\n", word); 852 return (NULL); 853 } 854 855 return (t); 856 } 857 858 void 859 show_valid_args(const struct token table[]) 860 { 861 int i; 862 863 if (prevtable != NULL) { 864 const struct token *t = prevtable; 865 prevtable = NULL; 866 show_valid_args(t); 867 fprintf(stderr, "or any of\n"); 868 } 869 870 for (i = 0; table[i].type != ENDTOKEN; i++) { 871 switch (table[i].type) { 872 case NOTOKEN: 873 fprintf(stderr, " <cr>\n"); 874 break; 875 case ANYTOKEN: 876 break; 877 case KEYWORD: 878 case FLAG: 879 case ASTYPE: 880 fprintf(stderr, " %s\n", table[i].keyword); 881 break; 882 case ADDRESS: 883 case PEERADDRESS: 884 fprintf(stderr, " <address>\n"); 885 break; 886 case PREFIX: 887 case FLOW_SRC: 888 case FLOW_DST: 889 fprintf(stderr, " <address>[/<len>]\n"); 890 break; 891 case ASNUM: 892 fprintf(stderr, " <asnum>\n"); 893 break; 894 case GROUPDESC: 895 case PEERDESC: 896 fprintf(stderr, " <neighbor description>\n"); 897 break; 898 case RIBNAME: 899 fprintf(stderr, " <rib name>\n"); 900 break; 901 case COMMUNICATION: 902 fprintf(stderr, " <reason>\n"); 903 break; 904 case COMMUNITY: 905 fprintf(stderr, " %s <community>\n", 906 table[i].keyword); 907 break; 908 case LRGCOMMUNITY: 909 fprintf(stderr, " %s <large-community>\n", 910 table[i].keyword); 911 break; 912 case EXTCOMMUNITY: 913 fprintf(stderr, " %s <extended-community>\n", 914 table[i].keyword); 915 break; 916 case RD: 917 fprintf(stderr, " <route-distinguisher>\n"); 918 break; 919 case LOCALPREF: 920 case MED: 921 case PREPNBR: 922 case PREPSELF: 923 case WEIGHT: 924 case RTABLE: 925 case PATHID: 926 fprintf(stderr, " %s <number>\n", table[i].keyword); 927 break; 928 case NEXTHOP: 929 fprintf(stderr, " <address>\n"); 930 break; 931 case PFTABLE: 932 fprintf(stderr, " <pftable>\n"); 933 break; 934 case FAMILY: 935 fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 | " 936 "VPNv4 | VPNv6 ]\n"); 937 break; 938 case FILENAME: 939 fprintf(stderr, " <filename>\n"); 940 break; 941 case FLOW_SRCPORT: 942 case FLOW_DSTPORT: 943 case FLOW_PROTO: 944 case FLOW_ICMPTYPE: 945 case FLOW_ICMPCODE: 946 case FLOW_LENGTH: 947 case FLOW_DSCP: 948 fprintf(stderr, " %s <numberspec>\n", 949 table[i].keyword); 950 break; 951 case FLOW_FLAGS: 952 case FLOW_FRAGS: 953 fprintf(stderr, " %s <flagspec>\n", 954 table[i].keyword); 955 break; 956 case ENDTOKEN: 957 break; 958 } 959 } 960 } 961 962 int 963 parse_addr(const char *word, struct bgpd_addr *addr) 964 { 965 struct in_addr ina; 966 struct addrinfo hints, *r; 967 968 if (word == NULL) 969 return (0); 970 971 memset(&ina, 0, sizeof(ina)); 972 973 if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) { 974 memset(addr, 0, sizeof(*addr)); 975 addr->aid = AID_INET; 976 addr->v4 = ina; 977 return (1); 978 } 979 980 memset(&hints, 0, sizeof(hints)); 981 hints.ai_family = AF_INET6; 982 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 983 hints.ai_flags = AI_NUMERICHOST; 984 if (getaddrinfo(word, "0", &hints, &r) == 0) { 985 sa2addr(r->ai_addr, addr, NULL); 986 freeaddrinfo(r); 987 return (1); 988 } 989 990 return (0); 991 } 992 993 int 994 parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, 995 uint8_t *prefixlen) 996 { 997 struct bgpd_addr tmp; 998 char *p, *ps; 999 const char *errstr; 1000 int mask = -1; 1001 1002 if (word == NULL) 1003 return (0); 1004 1005 memset(&tmp, 0, sizeof(tmp)); 1006 1007 if ((p = strrchr(word, '/')) != NULL) { 1008 size_t plen = strlen(p); 1009 mask = strtonum(p + 1, 0, 128, &errstr); 1010 if (errstr) 1011 errx(1, "netmask %s", errstr); 1012 1013 if ((ps = malloc(wordlen - plen + 1)) == NULL) 1014 err(1, "parse_prefix: malloc"); 1015 strlcpy(ps, word, wordlen - plen + 1); 1016 1017 if (parse_addr(ps, &tmp) == 0) { 1018 free(ps); 1019 return (0); 1020 } 1021 1022 free(ps); 1023 } else 1024 if (parse_addr(word, &tmp) == 0) 1025 return (0); 1026 1027 switch (tmp.aid) { 1028 case AID_INET: 1029 if (mask == -1) 1030 mask = 32; 1031 if (mask > 32) 1032 errx(1, "invalid netmask: too large"); 1033 break; 1034 case AID_INET6: 1035 if (mask == -1) 1036 mask = 128; 1037 break; 1038 default: 1039 return (0); 1040 } 1041 1042 applymask(addr, &tmp, mask); 1043 *prefixlen = mask; 1044 return (1); 1045 } 1046 1047 int 1048 parse_asnum(const char *word, size_t wordlen, uint32_t *asnum) 1049 { 1050 const char *errstr; 1051 char *dot, *parseword; 1052 uint32_t uval, uvalh = 0; 1053 1054 if (word == NULL) 1055 return (0); 1056 1057 if (wordlen < 1 || word[0] < '0' || word[0] > '9') 1058 return (0); 1059 1060 parseword = strdup(word); 1061 if ((dot = strchr(parseword, '.')) != NULL) { 1062 *dot++ = '\0'; 1063 uvalh = strtonum(parseword, 0, USHRT_MAX, &errstr); 1064 if (errstr) 1065 errx(1, "AS number is %s: %s", errstr, word); 1066 uval = strtonum(dot, 0, USHRT_MAX, &errstr); 1067 if (errstr) 1068 errx(1, "AS number is %s: %s", errstr, word); 1069 } else { 1070 uval = strtonum(parseword, 0, UINT_MAX, &errstr); 1071 if (errstr) 1072 errx(1, "AS number is %s: %s", errstr, word); 1073 } 1074 1075 free(parseword); 1076 *asnum = uval | (uvalh << 16); 1077 return (1); 1078 } 1079 1080 int 1081 parse_number(const char *word, struct parse_result *r, enum token_type type) 1082 { 1083 struct filter_set *fs; 1084 const char *errstr; 1085 u_int uval; 1086 1087 if (word == NULL) 1088 return (0); 1089 1090 uval = strtonum(word, 0, UINT_MAX, &errstr); 1091 if (errstr) 1092 errx(1, "number is %s: %s", errstr, word); 1093 1094 /* number was parseable */ 1095 switch (type) { 1096 case RTABLE: 1097 r->rtableid = uval; 1098 return (1); 1099 case PATHID: 1100 r->pathid = uval; 1101 r->flags |= F_CTL_HAS_PATHID; 1102 return (1); 1103 default: 1104 break; 1105 } 1106 1107 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 1108 err(1, NULL); 1109 switch (type) { 1110 case LOCALPREF: 1111 fs->type = ACTION_SET_LOCALPREF; 1112 fs->action.metric = uval; 1113 break; 1114 case MED: 1115 fs->type = ACTION_SET_MED; 1116 fs->action.metric = uval; 1117 break; 1118 case PREPNBR: 1119 if (uval > 128) { 1120 free(fs); 1121 return (0); 1122 } 1123 fs->type = ACTION_SET_PREPEND_PEER; 1124 fs->action.prepend = uval; 1125 break; 1126 case PREPSELF: 1127 if (uval > 128) { 1128 free(fs); 1129 return (0); 1130 } 1131 fs->type = ACTION_SET_PREPEND_SELF; 1132 fs->action.prepend = uval; 1133 break; 1134 case WEIGHT: 1135 fs->type = ACTION_SET_WEIGHT; 1136 fs->action.metric = uval; 1137 break; 1138 default: 1139 errx(1, "king bula sez bad things happen"); 1140 } 1141 1142 TAILQ_INSERT_TAIL(&r->set, fs, entry); 1143 return (1); 1144 } 1145 1146 static void 1147 getcommunity(char *s, int large, uint32_t *val, uint32_t *flag) 1148 { 1149 long long max = USHRT_MAX; 1150 const char *errstr; 1151 1152 *flag = 0; 1153 *val = 0; 1154 if (strcmp(s, "*") == 0) { 1155 *flag = COMMUNITY_ANY; 1156 return; 1157 } else if (strcmp(s, "neighbor-as") == 0) { 1158 *flag = COMMUNITY_NEIGHBOR_AS; 1159 return; 1160 } else if (strcmp(s, "local-as") == 0) { 1161 *flag = COMMUNITY_LOCAL_AS; 1162 return; 1163 } 1164 if (large) 1165 max = UINT_MAX; 1166 *val = strtonum(s, 0, max, &errstr); 1167 if (errstr) 1168 errx(1, "Community %s is %s (max: %llu)", s, errstr, max); 1169 } 1170 1171 static void 1172 setcommunity(struct community *c, uint32_t as, uint32_t data, 1173 uint32_t asflag, uint32_t dataflag) 1174 { 1175 c->flags = COMMUNITY_TYPE_BASIC; 1176 c->flags |= asflag << 8; 1177 c->flags |= dataflag << 16; 1178 c->data1 = as; 1179 c->data2 = data; 1180 c->data3 = 0; 1181 } 1182 1183 void 1184 parsecommunity(struct community *c, char *s) 1185 { 1186 char *p; 1187 uint32_t as, data, asflag, dataflag; 1188 1189 /* Well-known communities */ 1190 if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) { 1191 setcommunity(c, COMMUNITY_WELLKNOWN, 1192 COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0); 1193 return; 1194 } else if (strcasecmp(s, "NO_EXPORT") == 0) { 1195 setcommunity(c, COMMUNITY_WELLKNOWN, 1196 COMMUNITY_NO_EXPORT, 0, 0); 1197 return; 1198 } else if (strcasecmp(s, "NO_ADVERTISE") == 0) { 1199 setcommunity(c, COMMUNITY_WELLKNOWN, 1200 COMMUNITY_NO_ADVERTISE, 0, 0); 1201 return; 1202 } else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) { 1203 setcommunity(c, COMMUNITY_WELLKNOWN, 1204 COMMUNITY_NO_EXPSUBCONFED, 0, 0); 1205 return; 1206 } else if (strcasecmp(s, "NO_PEER") == 0) { 1207 setcommunity(c, COMMUNITY_WELLKNOWN, 1208 COMMUNITY_NO_PEER, 0, 0); 1209 return; 1210 } else if (strcasecmp(s, "BLACKHOLE") == 0) { 1211 setcommunity(c, COMMUNITY_WELLKNOWN, 1212 COMMUNITY_BLACKHOLE, 0, 0); 1213 return; 1214 } 1215 1216 if ((p = strchr(s, ':')) == NULL) 1217 errx(1, "Bad community syntax"); 1218 *p++ = 0; 1219 1220 getcommunity(s, 0, &as, &asflag); 1221 getcommunity(p, 0, &data, &dataflag); 1222 setcommunity(c, as, data, asflag, dataflag); 1223 } 1224 1225 void 1226 parselargecommunity(struct community *c, char *s) 1227 { 1228 char *p, *q; 1229 uint32_t dflag1, dflag2, dflag3; 1230 1231 if ((p = strchr(s, ':')) == NULL) 1232 errx(1, "Bad community syntax"); 1233 *p++ = 0; 1234 1235 if ((q = strchr(p, ':')) == NULL) 1236 errx(1, "Bad community syntax"); 1237 *q++ = 0; 1238 1239 getcommunity(s, 1, &c->data1, &dflag1); 1240 getcommunity(p, 1, &c->data2, &dflag2); 1241 getcommunity(q, 1, &c->data3, &dflag3); 1242 1243 c->flags = COMMUNITY_TYPE_LARGE; 1244 c->flags |= dflag1 << 8; 1245 c->flags |= dflag2 << 16; 1246 c->flags |= dflag3 << 24; 1247 } 1248 1249 static int 1250 parsesubtype(const char *name, int *type, int *subtype) 1251 { 1252 const struct ext_comm_pairs *cp; 1253 int found = 0; 1254 1255 for (cp = iana_ext_comms; cp->subname != NULL; cp++) { 1256 if (strcmp(name, cp->subname) == 0) { 1257 if (found == 0) { 1258 *type = cp->type; 1259 *subtype = cp->subtype; 1260 } 1261 found++; 1262 } 1263 } 1264 if (found > 1) 1265 *type = -1; 1266 return (found); 1267 } 1268 1269 static int 1270 parseextvalue(int type, char *s, uint32_t *v, uint32_t *flag) 1271 { 1272 const char *errstr; 1273 char *p; 1274 struct in_addr ip; 1275 uint32_t uvalh, uval; 1276 1277 if (type != -1) { 1278 /* nothing */ 1279 } else if (strcmp(s, "neighbor-as") == 0) { 1280 *flag = COMMUNITY_NEIGHBOR_AS; 1281 *v = 0; 1282 return EXT_COMMUNITY_TRANS_TWO_AS; 1283 } else if (strcmp(s, "local-as") == 0) { 1284 *flag = COMMUNITY_LOCAL_AS; 1285 *v = 0; 1286 return EXT_COMMUNITY_TRANS_TWO_AS; 1287 } else if ((p = strchr(s, '.')) == NULL) { 1288 /* AS_PLAIN number (4 or 2 byte) */ 1289 strtonum(s, 0, USHRT_MAX, &errstr); 1290 if (errstr == NULL) 1291 type = EXT_COMMUNITY_TRANS_TWO_AS; 1292 else 1293 type = EXT_COMMUNITY_TRANS_FOUR_AS; 1294 } else if (strchr(p + 1, '.') == NULL) { 1295 /* AS_DOT number (4-byte) */ 1296 type = EXT_COMMUNITY_TRANS_FOUR_AS; 1297 } else { 1298 /* more than one dot -> IP address */ 1299 type = EXT_COMMUNITY_TRANS_IPV4; 1300 } 1301 1302 switch (type & EXT_COMMUNITY_VALUE) { 1303 case EXT_COMMUNITY_TRANS_TWO_AS: 1304 uval = strtonum(s, 0, USHRT_MAX, &errstr); 1305 if (errstr) 1306 errx(1, "Bad ext-community %s is %s", s, errstr); 1307 *v = uval; 1308 break; 1309 case EXT_COMMUNITY_TRANS_FOUR_AS: 1310 if ((p = strchr(s, '.')) == NULL) { 1311 uval = strtonum(s, 0, UINT_MAX, &errstr); 1312 if (errstr) 1313 errx(1, "Bad ext-community %s is %s", s, 1314 errstr); 1315 *v = uval; 1316 break; 1317 } 1318 *p++ = '\0'; 1319 uvalh = strtonum(s, 0, USHRT_MAX, &errstr); 1320 if (errstr) 1321 errx(1, "Bad ext-community %s is %s", s, errstr); 1322 uval = strtonum(p, 0, USHRT_MAX, &errstr); 1323 if (errstr) 1324 errx(1, "Bad ext-community %s is %s", p, errstr); 1325 *v = uval | (uvalh << 16); 1326 break; 1327 case EXT_COMMUNITY_TRANS_IPV4: 1328 if (inet_pton(AF_INET, s, &ip) != 1) 1329 errx(1, "Bad ext-community %s not parseable", s); 1330 *v = ntohl(ip.s_addr); 1331 break; 1332 default: 1333 errx(1, "%s: unexpected type %d", __func__, type); 1334 } 1335 return (type); 1336 } 1337 1338 void 1339 parseextcommunity(struct community *c, const char *t, char *s) 1340 { 1341 const struct ext_comm_pairs *cp; 1342 char *p, *ep; 1343 uint64_t ullval; 1344 uint32_t uval, uval2, dflag1 = 0, dflag2 = 0; 1345 int type = 0, subtype = 0; 1346 1347 if (strcmp(t, "*") == 0 && strcmp(s, "*") == 0) { 1348 c->flags = COMMUNITY_TYPE_EXT; 1349 c->flags |= COMMUNITY_ANY << 24; 1350 return; 1351 } 1352 if (parsesubtype(t, &type, &subtype) == 0) 1353 errx(1, "Bad ext-community unknown type"); 1354 1355 switch (type) { 1356 case EXT_COMMUNITY_TRANS_TWO_AS: 1357 case EXT_COMMUNITY_TRANS_FOUR_AS: 1358 case EXT_COMMUNITY_TRANS_IPV4: 1359 case EXT_COMMUNITY_GEN_TWO_AS: 1360 case EXT_COMMUNITY_GEN_FOUR_AS: 1361 case EXT_COMMUNITY_GEN_IPV4: 1362 case -1: 1363 if (strcmp(s, "*") == 0) { 1364 dflag1 = COMMUNITY_ANY; 1365 break; 1366 } 1367 if ((p = strchr(s, ':')) == NULL) 1368 errx(1, "Bad ext-community %s", s); 1369 *p++ = '\0'; 1370 type = parseextvalue(type, s, &uval, &dflag1); 1371 1372 switch (type) { 1373 case EXT_COMMUNITY_TRANS_TWO_AS: 1374 case EXT_COMMUNITY_GEN_TWO_AS: 1375 getcommunity(p, 1, &uval2, &dflag2); 1376 break; 1377 case EXT_COMMUNITY_TRANS_IPV4: 1378 case EXT_COMMUNITY_TRANS_FOUR_AS: 1379 case EXT_COMMUNITY_GEN_IPV4: 1380 case EXT_COMMUNITY_GEN_FOUR_AS: 1381 getcommunity(p, 0, &uval2, &dflag2); 1382 break; 1383 default: 1384 errx(1, "parseextcommunity: unexpected result"); 1385 } 1386 1387 c->data1 = uval; 1388 c->data2 = uval2; 1389 break; 1390 case EXT_COMMUNITY_TRANS_OPAQUE: 1391 case EXT_COMMUNITY_TRANS_EVPN: 1392 if (strcmp(s, "*") == 0) { 1393 dflag1 = COMMUNITY_ANY; 1394 break; 1395 } 1396 errno = 0; 1397 ullval = strtoull(s, &ep, 0); 1398 if (s[0] == '\0' || *ep != '\0') 1399 errx(1, "Bad ext-community bad value"); 1400 if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) 1401 errx(1, "Bad ext-community value too big"); 1402 c->data1 = ullval >> 32; 1403 c->data2 = ullval; 1404 break; 1405 case EXT_COMMUNITY_NON_TRANS_OPAQUE: 1406 if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) { 1407 if (strcmp(s, "valid") == 0) { 1408 c->data2 = EXT_COMMUNITY_OVS_VALID; 1409 break; 1410 } else if (strcmp(s, "invalid") == 0) { 1411 c->data2 = EXT_COMMUNITY_OVS_INVALID; 1412 break; 1413 } else if (strcmp(s, "not-found") == 0) { 1414 c->data2 = EXT_COMMUNITY_OVS_NOTFOUND; 1415 break; 1416 } else if (strcmp(s, "*") == 0) { 1417 dflag1 = COMMUNITY_ANY; 1418 break; 1419 } 1420 } 1421 errx(1, "Bad ext-community %s", s); 1422 } 1423 1424 c->data3 = type << 8 | subtype; 1425 1426 /* special handling of ext-community rt * since type is not known */ 1427 if (dflag1 == COMMUNITY_ANY && type == -1) { 1428 c->flags = COMMUNITY_TYPE_EXT; 1429 c->flags |= dflag1 << 8; 1430 return; 1431 } 1432 1433 /* verify type/subtype combo */ 1434 for (cp = iana_ext_comms; cp->subname != NULL; cp++) { 1435 if (cp->type == type && cp->subtype == subtype) { 1436 c->flags = COMMUNITY_TYPE_EXT; 1437 c->flags |= dflag1 << 8; 1438 c->flags |= dflag2 << 16; 1439 return; 1440 } 1441 } 1442 1443 errx(1, "Bad ext-community bad format for type"); 1444 } 1445 1446 int 1447 parse_nexthop(const char *word, struct parse_result *r) 1448 { 1449 struct filter_set *fs; 1450 1451 if ((fs = calloc(1, sizeof(struct filter_set))) == NULL) 1452 err(1, NULL); 1453 1454 if (strcmp(word, "blackhole") == 0) 1455 fs->type = ACTION_SET_NEXTHOP_BLACKHOLE; 1456 else if (strcmp(word, "reject") == 0) 1457 fs->type = ACTION_SET_NEXTHOP_REJECT; 1458 else if (strcmp(word, "no-modify") == 0) 1459 fs->type = ACTION_SET_NEXTHOP_NOMODIFY; 1460 else if (parse_addr(word, &fs->action.nexthop)) { 1461 fs->type = ACTION_SET_NEXTHOP; 1462 } else { 1463 free(fs); 1464 return (0); 1465 } 1466 1467 TAILQ_INSERT_TAIL(&r->set, fs, entry); 1468 return (1); 1469 } 1470 1471 static int 1472 unary_op(const char *op) 1473 { 1474 if (strcmp(op, "=") == 0) 1475 return FLOWSPEC_OP_NUM_EQ; 1476 if (strcmp(op, "!=") == 0) 1477 return FLOWSPEC_OP_NUM_NOT; 1478 if (strcmp(op, ">") == 0) 1479 return FLOWSPEC_OP_NUM_GT; 1480 if (strcmp(op, ">=") == 0) 1481 return FLOWSPEC_OP_NUM_GE; 1482 if (strcmp(op, "<") == 0) 1483 return FLOWSPEC_OP_NUM_LT; 1484 if (strcmp(op, "<=") == 0) 1485 return FLOWSPEC_OP_NUM_LE; 1486 return -1; 1487 } 1488 1489 static enum comp_ops 1490 binary_op(const char *op) 1491 { 1492 if (strcmp(op, "-") == 0) 1493 return OP_RANGE; 1494 if (strcmp(op, "><") == 0) 1495 return OP_XRANGE; 1496 return OP_NONE; 1497 } 1498 1499 static void 1500 push_numop(struct parse_result *r, int type, uint8_t op, int and, long long val) 1501 { 1502 uint8_t *comp; 1503 void *data; 1504 uint32_t u32; 1505 uint16_t u16; 1506 uint8_t u8, flag = 0; 1507 int len, complen; 1508 1509 flag |= op; 1510 if (and) 1511 flag |= FLOWSPEC_OP_AND; 1512 1513 if (val < 0 || val > 0xffffffff) { 1514 errx(1, "unsupported value for flowspec num_op"); 1515 } else if (val <= 255) { 1516 len = 1; 1517 u8 = val; 1518 data = &u8; 1519 } else if (val <= 0xffff) { 1520 len = 2; 1521 u16 = htons(val); 1522 data = &u16; 1523 flag |= 1 << FLOWSPEC_OP_LEN_SHIFT; 1524 } else { 1525 len = 4; 1526 u32 = htonl(val); 1527 data = &u32; 1528 flag |= 2 << FLOWSPEC_OP_LEN_SHIFT; 1529 } 1530 1531 complen = r->flow.complen[type]; 1532 comp = realloc(r->flow.components[type], complen + len + 1); 1533 if (comp == NULL) 1534 err(1, NULL); 1535 1536 comp[complen++] = flag; 1537 memcpy(comp + complen, data, len); 1538 complen += len; 1539 r->flow.complen[type] = complen; 1540 r->flow.components[type] = comp; 1541 } 1542 1543 int 1544 parse_flow_numop(int argc, char *argv[], struct parse_result *r, 1545 enum token_type toktype) 1546 { 1547 const char *errstr; 1548 long long val, val2; 1549 int numargs, type; 1550 int is_list = 0; 1551 int op; 1552 1553 switch (toktype) { 1554 case FLOW_PROTO: 1555 type = FLOWSPEC_TYPE_PROTO; 1556 break; 1557 case FLOW_SRCPORT: 1558 type = FLOWSPEC_TYPE_SRC_PORT; 1559 break; 1560 case FLOW_DSTPORT: 1561 type = FLOWSPEC_TYPE_DST_PORT; 1562 break; 1563 case FLOW_ICMPTYPE: 1564 type = FLOWSPEC_TYPE_ICMP_TYPE; 1565 break; 1566 case FLOW_ICMPCODE: 1567 type = FLOWSPEC_TYPE_ICMP_CODE; 1568 break; 1569 case FLOW_LENGTH: 1570 type = FLOWSPEC_TYPE_PKT_LEN; 1571 break; 1572 case FLOW_DSCP: 1573 type = FLOWSPEC_TYPE_DSCP; 1574 break; 1575 default: 1576 errx(1, "parse_flow_numop called with unsupported type"); 1577 } 1578 1579 /* skip keyword (which is already accounted for) */ 1580 argc--; 1581 argv++; 1582 numargs = argc; 1583 1584 while (argc > 0) { 1585 if (strcmp(argv[0], "{") == 0) { 1586 is_list = 1; 1587 argc--; 1588 argv++; 1589 } else if (is_list && strcmp(argv[0], "}") == 0) { 1590 is_list = 0; 1591 argc--; 1592 argv++; 1593 } else if ((op = unary_op(argv[0])) != -1) { 1594 if (argc < 2) 1595 errx(1, "missing argument in flowspec " 1596 "definition"); 1597 1598 val = strtonum(argv[1], LLONG_MIN, LLONG_MAX, &errstr); 1599 if (errstr) 1600 errx(1, "\"%s\" invalid number: %s", argv[0], 1601 errstr); 1602 push_numop(r, type, op, 0, val); 1603 argc -= 2; 1604 argv += 2; 1605 } else { 1606 val = strtonum(argv[0], LLONG_MIN, LLONG_MAX, &errstr); 1607 if (errstr) 1608 errx(1, "\"%s\" invalid number: %s", argv[0], 1609 errstr); 1610 if (argc >= 3 && (op = binary_op(argv[1])) != OP_NONE) { 1611 val2 = strtonum(argv[2], LLONG_MIN, LLONG_MAX, 1612 &errstr); 1613 if (errstr) 1614 errx(1, "\"%s\" invalid number: %s", 1615 argv[2], errstr); 1616 switch (op) { 1617 case OP_RANGE: 1618 push_numop(r, type, FLOWSPEC_OP_NUM_GE, 1619 0, val); 1620 push_numop(r, type, FLOWSPEC_OP_NUM_LE, 1621 1, val2); 1622 break; 1623 case OP_XRANGE: 1624 push_numop(r, type, FLOWSPEC_OP_NUM_LT, 1625 0, val); 1626 push_numop(r, type, FLOWSPEC_OP_NUM_GT, 1627 0, val2); 1628 break; 1629 } 1630 argc -= 3; 1631 argv += 3; 1632 } else { 1633 push_numop(r, type, FLOWSPEC_OP_NUM_EQ, 0, val); 1634 argc--; 1635 argv++; 1636 } 1637 } 1638 if (is_list == 0) 1639 break; 1640 } 1641 1642 return numargs - argc; 1643 } 1644