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