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