1 /* $OpenBSD: bgpctl.c,v 1.314 2024/12/16 16:10:46 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2004-2019 Claudio Jeker <claudio@openbsd.org> 6 * Copyright (c) 2016 Job Snijders <job@instituut.net> 7 * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/time.h> 23 #include <sys/types.h> 24 #include <sys/socket.h> 25 #include <sys/stat.h> 26 #include <sys/un.h> 27 28 #include <endian.h> 29 #include <err.h> 30 #include <errno.h> 31 #include <fcntl.h> 32 #include <math.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <time.h> 37 #include <unistd.h> 38 #include <util.h> 39 40 #include "bgpd.h" 41 #include "session.h" 42 #include "rde.h" 43 #include "version.h" 44 45 #include "bgpctl.h" 46 #include "parser.h" 47 #include "mrtparser.h" 48 49 int main(int, char *[]); 50 int show(struct imsg *, struct parse_result *); 51 void send_filterset(struct imsgbuf *, struct filter_set_head *); 52 void show_mrt_dump_neighbors(struct mrt_rib *, struct mrt_peer *, 53 void *); 54 void show_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *); 55 void network_mrt_dump(struct mrt_rib *, struct mrt_peer *, void *); 56 void show_mrt_state(struct mrt_bgp_state *, void *); 57 void show_mrt_msg(struct mrt_bgp_msg *, void *); 58 const char *msg_type(uint8_t); 59 void network_bulk(struct parse_result *); 60 int match_aspath(void *, uint16_t, struct filter_as *); 61 struct flowspec *res_to_flowspec(struct parse_result *); 62 63 struct imsgbuf *imsgbuf; 64 struct mrt_parser show_mrt = { show_mrt_dump, show_mrt_state, show_mrt_msg }; 65 struct mrt_parser net_mrt = { network_mrt_dump, NULL, NULL }; 66 const struct output *output = &show_output; 67 int tableid; 68 int nodescr; 69 70 __dead void 71 usage(void) 72 { 73 extern char *__progname; 74 75 fprintf(stderr, "usage: %s [-jnV] [-s socket] command [argument ...]\n", 76 __progname); 77 exit(1); 78 } 79 80 int 81 main(int argc, char *argv[]) 82 { 83 struct sockaddr_un sa_un; 84 int fd, n, done, numdone, ch, verbose = 0; 85 struct imsg imsg; 86 struct network_config net; 87 struct parse_result *res; 88 struct ctl_neighbor neighbor; 89 struct ctl_show_rib_request ribreq; 90 struct flowspec *f; 91 char *sockname; 92 enum imsg_type type; 93 94 if (pledge("stdio rpath wpath cpath unix inet dns", NULL) == -1) 95 err(1, "pledge"); 96 97 tableid = getrtable(); 98 if (asprintf(&sockname, "%s.%d", SOCKET_NAME, tableid) == -1) 99 err(1, "asprintf"); 100 101 while ((ch = getopt(argc, argv, "jns:V")) != -1) { 102 switch (ch) { 103 case 'n': 104 if (++nodescr > 1) 105 usage(); 106 break; 107 case 'j': 108 output = &json_output; 109 break; 110 case 's': 111 sockname = optarg; 112 break; 113 case 'V': 114 fprintf(stderr, "OpenBGPD %s\n", BGPD_VERSION); 115 return 0; 116 default: 117 usage(); 118 /* NOTREACHED */ 119 } 120 } 121 argc -= optind; 122 argv += optind; 123 124 if ((res = parse(argc, argv)) == NULL) 125 exit(1); 126 127 memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr)); 128 strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr)); 129 neighbor.is_group = res->is_group; 130 strlcpy(neighbor.reason, res->reason, sizeof(neighbor.reason)); 131 132 switch (res->action) { 133 case SHOW_MRT: 134 if (pledge("stdio", NULL) == -1) 135 err(1, "pledge"); 136 137 memset(&ribreq, 0, sizeof(ribreq)); 138 if (res->as.type != AS_UNDEF) 139 ribreq.as = res->as; 140 if (res->addr.aid) { 141 ribreq.prefix = res->addr; 142 ribreq.prefixlen = res->prefixlen; 143 } 144 /* XXX currently no communities support */ 145 ribreq.neighbor = neighbor; 146 ribreq.aid = res->aid; 147 ribreq.flags = res->flags; 148 ribreq.validation_state = res->validation_state; 149 show_mrt.arg = &ribreq; 150 if (res->flags & F_CTL_NEIGHBORS) 151 show_mrt.dump = show_mrt_dump_neighbors; 152 else 153 output->head(res); 154 mrt_parse(res->mrtfd, &show_mrt, 1); 155 exit(0); 156 default: 157 break; 158 } 159 160 if (pledge("stdio unix", NULL) == -1) 161 err(1, "pledge"); 162 163 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 164 err(1, "control_init: socket"); 165 166 memset(&sa_un, 0, sizeof(sa_un)); 167 sa_un.sun_family = AF_UNIX; 168 if (strlcpy(sa_un.sun_path, sockname, sizeof(sa_un.sun_path)) >= 169 sizeof(sa_un.sun_path)) 170 errx(1, "socket name too long"); 171 if (connect(fd, (struct sockaddr *)&sa_un, sizeof(sa_un)) == -1) 172 err(1, "connect: %s", sockname); 173 174 if (pledge("stdio", NULL) == -1) 175 err(1, "pledge"); 176 177 if ((imsgbuf = malloc(sizeof(struct imsgbuf))) == NULL) 178 err(1, NULL); 179 if (imsgbuf_init(imsgbuf, fd) == -1 || 180 imsgbuf_set_maxsize(imsgbuf, MAX_BGPD_IMSGSIZE) == -1) 181 err(1, NULL); 182 done = 0; 183 184 switch (res->action) { 185 case NONE: 186 case SHOW_MRT: 187 usage(); 188 /* NOTREACHED */ 189 case SHOW: 190 case SHOW_SUMMARY: 191 imsg_compose(imsgbuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 192 NULL, 0); 193 break; 194 case SHOW_SUMMARY_TERSE: 195 imsg_compose(imsgbuf, IMSG_CTL_SHOW_TERSE, 0, 0, -1, NULL, 0); 196 break; 197 case SHOW_FIB: 198 if (!res->addr.aid) { 199 struct ctl_kroute_req req = { 0 }; 200 201 req.af = aid2af(res->aid); 202 req.flags = res->flags; 203 204 imsg_compose(imsgbuf, IMSG_CTL_KROUTE, res->rtableid, 205 0, -1, &req, sizeof(req)); 206 } else 207 imsg_compose(imsgbuf, IMSG_CTL_KROUTE_ADDR, 208 res->rtableid, 0, -1, 209 &res->addr, sizeof(res->addr)); 210 break; 211 case SHOW_FIB_TABLES: 212 imsg_compose(imsgbuf, IMSG_CTL_SHOW_FIB_TABLES, 0, 0, -1, 213 NULL, 0); 214 break; 215 case SHOW_NEXTHOP: 216 imsg_compose(imsgbuf, IMSG_CTL_SHOW_NEXTHOP, res->rtableid, 217 0, -1, NULL, 0); 218 break; 219 case SHOW_INTERFACE: 220 imsg_compose(imsgbuf, IMSG_CTL_SHOW_INTERFACE, 0, 0, -1, 221 NULL, 0); 222 break; 223 case SHOW_SET: 224 imsg_compose(imsgbuf, IMSG_CTL_SHOW_SET, 0, 0, -1, NULL, 0); 225 break; 226 case SHOW_RTR: 227 imsg_compose(imsgbuf, IMSG_CTL_SHOW_RTR, 0, 0, -1, NULL, 0); 228 break; 229 case SHOW_NEIGHBOR: 230 case SHOW_NEIGHBOR_TIMERS: 231 case SHOW_NEIGHBOR_TERSE: 232 neighbor.show_timers = (res->action == SHOW_NEIGHBOR_TIMERS); 233 if (res->peeraddr.aid || res->peerdesc[0]) 234 imsg_compose(imsgbuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 235 &neighbor, sizeof(neighbor)); 236 else 237 imsg_compose(imsgbuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 238 NULL, 0); 239 break; 240 case SHOW_RIB: 241 memset(&ribreq, 0, sizeof(ribreq)); 242 type = IMSG_CTL_SHOW_RIB; 243 if (res->addr.aid) { 244 ribreq.prefix = res->addr; 245 ribreq.prefixlen = res->prefixlen; 246 type = IMSG_CTL_SHOW_RIB_PREFIX; 247 } 248 if (res->as.type != AS_UNDEF) 249 ribreq.as = res->as; 250 if (res->community.flags != 0) 251 ribreq.community = res->community; 252 ribreq.neighbor = neighbor; 253 strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); 254 ribreq.aid = res->aid; 255 ribreq.path_id = res->pathid; 256 ribreq.flags = res->flags; 257 imsg_compose(imsgbuf, type, 0, 0, -1, &ribreq, sizeof(ribreq)); 258 break; 259 case SHOW_RIB_MEM: 260 imsg_compose(imsgbuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0); 261 break; 262 case SHOW_METRICS: 263 output = &ometric_output; 264 numdone = 2; 265 imsg_compose(imsgbuf, IMSG_CTL_SHOW_NEIGHBOR, 0, 0, -1, 266 NULL, 0); 267 imsg_compose(imsgbuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0); 268 break; 269 case RELOAD: 270 imsg_compose(imsgbuf, IMSG_CTL_RELOAD, 0, 0, -1, 271 res->reason, sizeof(res->reason)); 272 if (res->reason[0]) 273 printf("reload request sent: %s\n", res->reason); 274 else 275 printf("reload request sent.\n"); 276 break; 277 case FIB: 278 errx(1, "action==FIB"); 279 break; 280 case FIB_COUPLE: 281 imsg_compose(imsgbuf, IMSG_CTL_FIB_COUPLE, res->rtableid, 0, -1, 282 NULL, 0); 283 printf("couple request sent.\n"); 284 done = 1; 285 break; 286 case FIB_DECOUPLE: 287 imsg_compose(imsgbuf, IMSG_CTL_FIB_DECOUPLE, res->rtableid, 288 0, -1, NULL, 0); 289 printf("decouple request sent.\n"); 290 done = 1; 291 break; 292 case NEIGHBOR: 293 errx(1, "action==NEIGHBOR"); 294 break; 295 case NEIGHBOR_UP: 296 imsg_compose(imsgbuf, IMSG_CTL_NEIGHBOR_UP, 0, 0, -1, 297 &neighbor, sizeof(neighbor)); 298 break; 299 case NEIGHBOR_DOWN: 300 imsg_compose(imsgbuf, IMSG_CTL_NEIGHBOR_DOWN, 0, 0, -1, 301 &neighbor, sizeof(neighbor)); 302 break; 303 case NEIGHBOR_CLEAR: 304 imsg_compose(imsgbuf, IMSG_CTL_NEIGHBOR_CLEAR, 0, 0, -1, 305 &neighbor, sizeof(neighbor)); 306 break; 307 case NEIGHBOR_RREFRESH: 308 imsg_compose(imsgbuf, IMSG_CTL_NEIGHBOR_RREFRESH, 0, 0, -1, 309 &neighbor, sizeof(neighbor)); 310 break; 311 case NEIGHBOR_DESTROY: 312 imsg_compose(imsgbuf, IMSG_CTL_NEIGHBOR_DESTROY, 0, 0, -1, 313 &neighbor, sizeof(neighbor)); 314 break; 315 case NETWORK_BULK_ADD: 316 case NETWORK_BULK_REMOVE: 317 network_bulk(res); 318 printf("requests sent.\n"); 319 done = 1; 320 break; 321 case NETWORK_ADD: 322 case NETWORK_REMOVE: 323 memset(&net, 0, sizeof(net)); 324 net.prefix = res->addr; 325 net.prefixlen = res->prefixlen; 326 net.rd = res->rd; 327 /* attribute sets are not supported */ 328 if (res->action == NETWORK_ADD) { 329 imsg_compose(imsgbuf, IMSG_NETWORK_ADD, 0, 0, -1, 330 &net, sizeof(net)); 331 send_filterset(imsgbuf, &res->set); 332 imsg_compose(imsgbuf, IMSG_NETWORK_DONE, 0, 0, -1, 333 NULL, 0); 334 } else 335 imsg_compose(imsgbuf, IMSG_NETWORK_REMOVE, 0, 0, -1, 336 &net, sizeof(net)); 337 printf("request sent.\n"); 338 done = 1; 339 break; 340 case NETWORK_FLUSH: 341 imsg_compose(imsgbuf, IMSG_NETWORK_FLUSH, 0, 0, -1, NULL, 0); 342 printf("request sent.\n"); 343 done = 1; 344 break; 345 case NETWORK_SHOW: 346 memset(&ribreq, 0, sizeof(ribreq)); 347 ribreq.aid = res->aid; 348 strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); 349 imsg_compose(imsgbuf, IMSG_CTL_SHOW_NETWORK, 0, 0, -1, 350 &ribreq, sizeof(ribreq)); 351 break; 352 case NETWORK_MRT: 353 memset(&ribreq, 0, sizeof(ribreq)); 354 if (res->as.type != AS_UNDEF) 355 ribreq.as = res->as; 356 if (res->addr.aid) { 357 ribreq.prefix = res->addr; 358 ribreq.prefixlen = res->prefixlen; 359 } 360 /* XXX currently no community support */ 361 ribreq.neighbor = neighbor; 362 ribreq.aid = res->aid; 363 ribreq.flags = res->flags; 364 net_mrt.arg = &ribreq; 365 mrt_parse(res->mrtfd, &net_mrt, 1); 366 done = 1; 367 break; 368 case FLOWSPEC_ADD: 369 case FLOWSPEC_REMOVE: 370 f = res_to_flowspec(res); 371 /* attribute sets are not supported */ 372 if (res->action == FLOWSPEC_ADD) { 373 imsg_compose(imsgbuf, IMSG_FLOWSPEC_ADD, 0, 0, -1, 374 f, FLOWSPEC_SIZE + f->len); 375 send_filterset(imsgbuf, &res->set); 376 imsg_compose(imsgbuf, IMSG_FLOWSPEC_DONE, 0, 0, -1, 377 NULL, 0); 378 } else 379 imsg_compose(imsgbuf, IMSG_FLOWSPEC_REMOVE, 0, 0, -1, 380 f, FLOWSPEC_SIZE + f->len); 381 printf("request sent.\n"); 382 done = 1; 383 break; 384 case FLOWSPEC_FLUSH: 385 imsg_compose(imsgbuf, IMSG_FLOWSPEC_FLUSH, 0, 0, -1, NULL, 0); 386 printf("request sent.\n"); 387 done = 1; 388 break; 389 case FLOWSPEC_SHOW: 390 memset(&ribreq, 0, sizeof(ribreq)); 391 switch (res->aid) { 392 case AID_INET: 393 ribreq.aid = AID_FLOWSPECv4; 394 break; 395 case AID_INET6: 396 ribreq.aid = AID_FLOWSPECv6; 397 break; 398 case AID_UNSPEC: 399 ribreq.aid = res->aid; 400 break; 401 default: 402 errx(1, "flowspec family %s currently not supported", 403 aid2str(res->aid)); 404 } 405 strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); 406 imsg_compose(imsgbuf, IMSG_CTL_SHOW_FLOWSPEC, 0, 0, -1, 407 &ribreq, sizeof(ribreq)); 408 break; 409 case LOG_VERBOSE: 410 verbose = 1; 411 /* FALLTHROUGH */ 412 case LOG_BRIEF: 413 imsg_compose(imsgbuf, IMSG_CTL_LOG_VERBOSE, 0, 0, -1, 414 &verbose, sizeof(verbose)); 415 printf("logging request sent.\n"); 416 done = 1; 417 break; 418 } 419 420 output->head(res); 421 422 again: 423 if (imsgbuf_flush(imsgbuf) == -1) 424 err(1, "write error"); 425 426 while (!done) { 427 while (!done) { 428 if ((n = imsg_get(imsgbuf, &imsg)) == -1) 429 err(1, "imsg_get error"); 430 if (n == 0) 431 break; 432 433 done = show(&imsg, res); 434 imsg_free(&imsg); 435 } 436 437 if (done) 438 break; 439 440 if ((n = imsgbuf_read(imsgbuf)) == -1) 441 err(1, "read error"); 442 if (n == 0) 443 errx(1, "pipe closed"); 444 445 } 446 447 if (res->action == SHOW_METRICS && --numdone > 0) { 448 done = 0; 449 goto again; 450 } 451 452 output->tail(); 453 454 close(fd); 455 free(imsgbuf); 456 457 exit(0); 458 } 459 460 int 461 show(struct imsg *imsg, struct parse_result *res) 462 { 463 struct peer p; 464 struct ctl_timer t; 465 struct ctl_show_interface iface; 466 struct ctl_show_nexthop nh; 467 struct ctl_show_set set; 468 struct ctl_show_rtr rtr; 469 struct kroute_full kf; 470 struct ktable kt; 471 struct flowspec f; 472 struct ctl_show_rib rib; 473 struct rde_memstats stats; 474 struct ibuf ibuf; 475 u_int rescode; 476 477 switch (imsg->hdr.type) { 478 case IMSG_CTL_SHOW_NEIGHBOR: 479 if (output->neighbor == NULL) 480 break; 481 if (imsg_get_data(imsg, &p, sizeof(p)) == -1) 482 err(1, "imsg_get_data"); 483 output->neighbor(&p, res); 484 break; 485 case IMSG_CTL_SHOW_TIMER: 486 if (output->timer == NULL) 487 break; 488 if (imsg_get_data(imsg, &t, sizeof(t)) == -1) 489 err(1, "imsg_get_data"); 490 if (t.type > 0 && t.type < Timer_Max) 491 output->timer(&t); 492 break; 493 case IMSG_CTL_SHOW_INTERFACE: 494 if (output->interface == NULL) 495 break; 496 if (imsg_get_data(imsg, &iface, sizeof(iface)) == -1) 497 err(1, "imsg_get_data"); 498 output->interface(&iface); 499 break; 500 case IMSG_CTL_SHOW_NEXTHOP: 501 if (output->nexthop == NULL) 502 break; 503 if (imsg_get_data(imsg, &nh, sizeof(nh)) == -1) 504 err(1, "imsg_get_data"); 505 output->nexthop(&nh); 506 break; 507 case IMSG_CTL_KROUTE: 508 case IMSG_CTL_SHOW_NETWORK: 509 if (output->fib == NULL) 510 break; 511 if (imsg_get_data(imsg, &kf, sizeof(kf)) == -1) 512 err(1, "imsg_get_data"); 513 output->fib(&kf); 514 break; 515 case IMSG_CTL_SHOW_FLOWSPEC: 516 if (output->flowspec == NULL) 517 break; 518 if (imsg_get_data(imsg, &f, sizeof(f)) == -1) 519 err(1, "imsg_get_data"); 520 output->flowspec(&f); 521 break; 522 case IMSG_CTL_SHOW_FIB_TABLES: 523 if (output->fib_table == NULL) 524 break; 525 if (imsg_get_data(imsg, &kt, sizeof(kt)) == -1) 526 err(1, "imsg_get_data"); 527 output->fib_table(&kt); 528 break; 529 case IMSG_CTL_SHOW_RIB: 530 if (output->rib == NULL) 531 break; 532 if (imsg_get_ibuf(imsg, &ibuf) == -1) 533 err(1, "imsg_get_ibuf"); 534 if (ibuf_get(&ibuf, &rib, sizeof(rib)) == -1) 535 err(1, "imsg_get_ibuf"); 536 output->rib(&rib, &ibuf, res); 537 break; 538 case IMSG_CTL_SHOW_RIB_COMMUNITIES: 539 if (output->communities == NULL) 540 break; 541 if (imsg_get_ibuf(imsg, &ibuf) == -1) 542 err(1, "imsg_get_ibuf"); 543 output->communities(&ibuf, res); 544 break; 545 case IMSG_CTL_SHOW_RIB_ATTR: 546 if (output->attr == NULL) 547 break; 548 if (imsg_get_ibuf(imsg, &ibuf) == -1) 549 err(1, "imsg_get_ibuf"); 550 output->attr(&ibuf, res->flags, 0); 551 break; 552 case IMSG_CTL_SHOW_RIB_MEM: 553 if (output->rib_mem == NULL) 554 break; 555 if (imsg_get_data(imsg, &stats, sizeof(stats)) == -1) 556 err(1, "imsg_get_data"); 557 output->rib_mem(&stats); 558 return (1); 559 case IMSG_CTL_SHOW_SET: 560 if (output->set == NULL) 561 break; 562 if (imsg_get_data(imsg, &set, sizeof(set)) == -1) 563 err(1, "imsg_get_data"); 564 output->set(&set); 565 break; 566 case IMSG_CTL_SHOW_RTR: 567 if (output->rtr == NULL) 568 break; 569 if (imsg_get_data(imsg, &rtr, sizeof(rtr)) == -1) 570 err(1, "imsg_get_data"); 571 output->rtr(&rtr); 572 break; 573 case IMSG_CTL_RESULT: 574 if (output->result == NULL) 575 break; 576 if (imsg_get_data(imsg, &rescode, sizeof(rescode)) == -1) 577 err(1, "imsg_get_data"); 578 output->result(rescode); 579 return (1); 580 case IMSG_CTL_END: 581 return (1); 582 default: 583 warnx("unknown imsg %d received", imsg->hdr.type); 584 break; 585 } 586 587 return (0); 588 } 589 590 time_t 591 get_monotime(time_t t) 592 { 593 struct timespec ts; 594 595 if (t == 0) 596 return -1; 597 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) 598 err(1, "clock_gettime"); 599 if (t > ts.tv_sec) /* time in the future is not possible */ 600 t = ts.tv_sec; 601 return (ts.tv_sec - t); 602 } 603 604 char * 605 fmt_peer(const char *descr, const struct bgpd_addr *remote_addr, 606 int masklen) 607 { 608 const char *ip; 609 char *p; 610 611 if (descr && descr[0] && !nodescr) { 612 if ((p = strdup(descr)) == NULL) 613 err(1, NULL); 614 return (p); 615 } 616 617 ip = log_addr(remote_addr); 618 if (masklen != -1 && ((remote_addr->aid == AID_INET && masklen != 32) || 619 (remote_addr->aid == AID_INET6 && masklen != 128))) { 620 if (asprintf(&p, "%s/%u", ip, masklen) == -1) 621 err(1, NULL); 622 } else { 623 if ((p = strdup(ip)) == NULL) 624 err(1, NULL); 625 } 626 627 return (p); 628 } 629 630 const char * 631 fmt_auth_method(enum auth_method method) 632 { 633 switch (method) { 634 case AUTH_MD5SIG: 635 return ", using md5sig"; 636 case AUTH_IPSEC_MANUAL_ESP: 637 return ", using ipsec manual esp"; 638 case AUTH_IPSEC_MANUAL_AH: 639 return ", using ipsec manual ah"; 640 case AUTH_IPSEC_IKE_ESP: 641 return ", using ipsec ike esp"; 642 case AUTH_IPSEC_IKE_AH: 643 return ", using ipsec ike ah"; 644 case AUTH_NONE: /* FALLTHROUGH */ 645 default: 646 return ""; 647 } 648 } 649 650 #define TF_LEN 16 651 652 const char * 653 fmt_timeframe(time_t t) 654 { 655 static char buf[TF_LEN]; 656 unsigned int sec, min, hrs, day; 657 unsigned long long week; 658 659 if (t < 0) 660 t = 0; 661 week = t; 662 663 sec = week % 60; 664 week /= 60; 665 min = week % 60; 666 week /= 60; 667 hrs = week % 24; 668 week /= 24; 669 day = week % 7; 670 week /= 7; 671 672 if (week >= 1000) 673 snprintf(buf, TF_LEN, "%02lluw", week); 674 else if (week > 0) 675 snprintf(buf, TF_LEN, "%02lluw%01ud%02uh", week, day, hrs); 676 else if (day > 0) 677 snprintf(buf, TF_LEN, "%01ud%02uh%02um", day, hrs, min); 678 else 679 snprintf(buf, TF_LEN, "%02u:%02u:%02u", hrs, min, sec); 680 681 return (buf); 682 } 683 684 const char * 685 fmt_monotime(time_t t) 686 { 687 t = get_monotime(t); 688 689 if (t == -1) 690 return ("Never"); 691 692 return (fmt_timeframe(t)); 693 } 694 695 const char * 696 fmt_fib_flags(uint16_t flags) 697 { 698 static char buf[8]; 699 700 if (flags & F_BGPD) 701 strlcpy(buf, "B", sizeof(buf)); 702 else if (flags & F_CONNECTED) 703 strlcpy(buf, "C", sizeof(buf)); 704 else if (flags & F_STATIC) 705 strlcpy(buf, "S", sizeof(buf)); 706 else 707 strlcpy(buf, " ", sizeof(buf)); 708 709 if (flags & F_NEXTHOP) 710 strlcat(buf, "N", sizeof(buf)); 711 else 712 strlcat(buf, " ", sizeof(buf)); 713 714 if (flags & F_REJECT && flags & F_BLACKHOLE) 715 strlcat(buf, "f", sizeof(buf)); 716 else if (flags & F_REJECT) 717 strlcat(buf, "r", sizeof(buf)); 718 else if (flags & F_BLACKHOLE) 719 strlcat(buf, "b", sizeof(buf)); 720 else 721 strlcat(buf, " ", sizeof(buf)); 722 723 return buf; 724 } 725 726 const char * 727 fmt_origin(uint8_t origin, int sum) 728 { 729 switch (origin) { 730 case ORIGIN_IGP: 731 return (sum ? "i" : "IGP"); 732 case ORIGIN_EGP: 733 return (sum ? "e" : "EGP"); 734 case ORIGIN_INCOMPLETE: 735 return (sum ? "?" : "incomplete"); 736 default: 737 return (sum ? "X" : "bad origin"); 738 } 739 } 740 741 const char * 742 fmt_flags(uint32_t flags, int sum) 743 { 744 static char buf[80]; 745 char flagstr[5]; 746 char *p = flagstr; 747 748 if (sum) { 749 if (flags & F_PREF_FILTERED) 750 *p++ = 'F'; 751 if (flags & F_PREF_INVALID) 752 *p++ = 'E'; 753 if (flags & F_PREF_OTC_LEAK) 754 *p++ = 'L'; 755 if (flags & F_PREF_ANNOUNCE) 756 *p++ = 'A'; 757 if (flags & F_PREF_INTERNAL) 758 *p++ = 'I'; 759 if (flags & F_PREF_STALE) 760 *p++ = 'S'; 761 if (flags & F_PREF_ELIGIBLE) 762 *p++ = '*'; 763 if (flags & F_PREF_BEST) 764 *p++ = '>'; 765 if (flags & F_PREF_ECMP) 766 *p++ = 'm'; 767 if (flags & F_PREF_AS_WIDE) 768 *p++ = 'w'; 769 *p = '\0'; 770 snprintf(buf, sizeof(buf), "%-5s", flagstr); 771 } else { 772 if (flags & F_PREF_INTERNAL) 773 strlcpy(buf, "internal", sizeof(buf)); 774 else 775 strlcpy(buf, "external", sizeof(buf)); 776 777 if (flags & F_PREF_FILTERED) 778 strlcat(buf, ", filtered", sizeof(buf)); 779 if (flags & F_PREF_INVALID) 780 strlcat(buf, ", invalid", sizeof(buf)); 781 if (flags & F_PREF_OTC_LEAK) 782 strlcat(buf, ", otc leak", sizeof(buf)); 783 if (flags & F_PREF_STALE) 784 strlcat(buf, ", stale", sizeof(buf)); 785 if (flags & F_PREF_ELIGIBLE) 786 strlcat(buf, ", valid", sizeof(buf)); 787 if (flags & F_PREF_BEST) 788 strlcat(buf, ", best", sizeof(buf)); 789 if (flags & F_PREF_ECMP) 790 strlcat(buf, ", ecmp", sizeof(buf)); 791 if (flags & F_PREF_AS_WIDE) 792 strlcat(buf, ", as-wide", sizeof(buf)); 793 if (flags & F_PREF_ANNOUNCE) 794 strlcat(buf, ", announced", sizeof(buf)); 795 if (strlen(buf) >= sizeof(buf) - 1) 796 errx(1, "%s buffer too small", __func__); 797 } 798 799 return buf; 800 } 801 802 const char * 803 fmt_ovs(uint8_t validation_state, int sum) 804 { 805 switch (validation_state) { 806 case ROA_INVALID: 807 return (sum ? "!" : "invalid"); 808 case ROA_VALID: 809 return (sum ? "V" : "valid"); 810 default: 811 return (sum ? "N" : "not-found"); 812 } 813 } 814 815 const char * 816 fmt_avs(uint8_t validation_state, int sum) 817 { 818 switch (validation_state) { 819 case ASPA_INVALID: 820 return (sum ? "!" : "invalid"); 821 case ASPA_VALID: 822 return (sum ? "V" : "valid"); 823 default: 824 return (sum ? "?" : "unknown"); 825 } 826 } 827 828 const char * 829 fmt_mem(long long num) 830 { 831 static char buf[16]; 832 833 if (fmt_scaled(num, buf) == -1) 834 snprintf(buf, sizeof(buf), "%lldB", num); 835 836 return (buf); 837 } 838 839 const char * 840 fmt_errstr(uint8_t errcode, uint8_t subcode) 841 { 842 static char errbuf[256]; 843 const char *errstr = NULL; 844 const char *suberr = NULL; 845 int uk = 0; 846 847 if (errcode == 0) /* no error */ 848 return NULL; 849 850 if (errcode < sizeof(errnames)/sizeof(char *)) 851 errstr = errnames[errcode]; 852 853 switch (errcode) { 854 case ERR_HEADER: 855 if (subcode < sizeof(suberr_header_names)/sizeof(char *)) 856 suberr = suberr_header_names[subcode]; 857 else 858 uk = 1; 859 break; 860 case ERR_OPEN: 861 if (subcode < sizeof(suberr_open_names)/sizeof(char *)) 862 suberr = suberr_open_names[subcode]; 863 else 864 uk = 1; 865 break; 866 case ERR_UPDATE: 867 if (subcode < sizeof(suberr_update_names)/sizeof(char *)) 868 suberr = suberr_update_names[subcode]; 869 else 870 uk = 1; 871 break; 872 case ERR_HOLDTIMEREXPIRED: 873 if (subcode != 0) 874 uk = 1; 875 break; 876 case ERR_FSM: 877 if (subcode < sizeof(suberr_fsm_names)/sizeof(char *)) 878 suberr = suberr_fsm_names[subcode]; 879 else 880 uk = 1; 881 break; 882 case ERR_CEASE: 883 if (subcode < sizeof(suberr_cease_names)/sizeof(char *)) 884 suberr = suberr_cease_names[subcode]; 885 else 886 uk = 1; 887 break; 888 default: 889 snprintf(errbuf, sizeof(errbuf), 890 "unknown error code %u subcode %u", errcode, subcode); 891 return (errbuf); 892 } 893 894 if (uk) 895 snprintf(errbuf, sizeof(errbuf), 896 "%s, unknown subcode %u", errstr, subcode); 897 else if (suberr == NULL) 898 return (errstr); 899 else 900 snprintf(errbuf, sizeof(errbuf), 901 "%s, %s", errstr, suberr); 902 903 return (errbuf); 904 } 905 906 const char * 907 fmt_attr(uint8_t type, int flags) 908 { 909 #define CHECK_FLAGS(s, t, m) \ 910 if (((s) & ~(ATTR_DEFMASK | (m))) != (t)) pflags = 1 911 912 static char cstr[48]; 913 int pflags = 0; 914 915 switch (type) { 916 case ATTR_ORIGIN: 917 CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); 918 strlcpy(cstr, "Origin", sizeof(cstr)); 919 break; 920 case ATTR_ASPATH: 921 CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); 922 strlcpy(cstr, "AS-Path", sizeof(cstr)); 923 break; 924 case ATTR_AS4_PATH: 925 CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); 926 strlcpy(cstr, "AS4-Path", sizeof(cstr)); 927 break; 928 case ATTR_NEXTHOP: 929 CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); 930 strlcpy(cstr, "Nexthop", sizeof(cstr)); 931 break; 932 case ATTR_MED: 933 CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); 934 strlcpy(cstr, "Med", sizeof(cstr)); 935 break; 936 case ATTR_LOCALPREF: 937 CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); 938 strlcpy(cstr, "Localpref", sizeof(cstr)); 939 break; 940 case ATTR_ATOMIC_AGGREGATE: 941 CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); 942 strlcpy(cstr, "Atomic Aggregate", sizeof(cstr)); 943 break; 944 case ATTR_AGGREGATOR: 945 CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); 946 strlcpy(cstr, "Aggregator", sizeof(cstr)); 947 break; 948 case ATTR_AS4_AGGREGATOR: 949 CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); 950 strlcpy(cstr, "AS4-Aggregator", sizeof(cstr)); 951 break; 952 case ATTR_COMMUNITIES: 953 CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); 954 strlcpy(cstr, "Communities", sizeof(cstr)); 955 break; 956 case ATTR_ORIGINATOR_ID: 957 CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); 958 strlcpy(cstr, "Originator Id", sizeof(cstr)); 959 break; 960 case ATTR_CLUSTER_LIST: 961 CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); 962 strlcpy(cstr, "Cluster Id List", sizeof(cstr)); 963 break; 964 case ATTR_MP_REACH_NLRI: 965 CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); 966 strlcpy(cstr, "MP Reach NLRI", sizeof(cstr)); 967 break; 968 case ATTR_MP_UNREACH_NLRI: 969 CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); 970 strlcpy(cstr, "MP Unreach NLRI", sizeof(cstr)); 971 break; 972 case ATTR_EXT_COMMUNITIES: 973 CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); 974 strlcpy(cstr, "Ext. Communities", sizeof(cstr)); 975 break; 976 case ATTR_LARGE_COMMUNITIES: 977 CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); 978 strlcpy(cstr, "Large Communities", sizeof(cstr)); 979 break; 980 case ATTR_OTC: 981 CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); 982 strlcpy(cstr, "OTC", sizeof(cstr)); 983 break; 984 default: 985 /* ignore unknown attributes */ 986 snprintf(cstr, sizeof(cstr), "Unknown Attribute #%u", type); 987 pflags = 1; 988 break; 989 } 990 if (flags != -1 && pflags) { 991 strlcat(cstr, " flags [", sizeof(cstr)); 992 if (flags & ATTR_OPTIONAL) 993 strlcat(cstr, "O", sizeof(cstr)); 994 if (flags & ATTR_TRANSITIVE) 995 strlcat(cstr, "T", sizeof(cstr)); 996 if (flags & ATTR_PARTIAL) 997 strlcat(cstr, "P", sizeof(cstr)); 998 strlcat(cstr, "]", sizeof(cstr)); 999 } 1000 return (cstr); 1001 1002 #undef CHECK_FLAGS 1003 } 1004 1005 const char * 1006 fmt_community(uint16_t a, uint16_t v) 1007 { 1008 static char buf[12]; 1009 1010 if (a == COMMUNITY_WELLKNOWN) 1011 switch (v) { 1012 case COMMUNITY_GRACEFUL_SHUTDOWN: 1013 return "GRACEFUL_SHUTDOWN"; 1014 case COMMUNITY_NO_EXPORT: 1015 return "NO_EXPORT"; 1016 case COMMUNITY_NO_ADVERTISE: 1017 return "NO_ADVERTISE"; 1018 case COMMUNITY_NO_EXPSUBCONFED: 1019 return "NO_EXPORT_SUBCONFED"; 1020 case COMMUNITY_NO_PEER: 1021 return "NO_PEER"; 1022 case COMMUNITY_BLACKHOLE: 1023 return "BLACKHOLE"; 1024 default: 1025 break; 1026 } 1027 1028 snprintf(buf, sizeof(buf), "%hu:%hu", a, v); 1029 return buf; 1030 } 1031 1032 const char * 1033 fmt_large_community(uint32_t d1, uint32_t d2, uint32_t d3) 1034 { 1035 static char buf[33]; 1036 1037 snprintf(buf, sizeof(buf), "%u:%u:%u", d1, d2, d3); 1038 return buf; 1039 } 1040 1041 const char * 1042 fmt_ext_community(uint64_t ext) 1043 { 1044 static char buf[32]; 1045 struct in_addr ip; 1046 uint32_t as4, u32; 1047 uint16_t as2, u16; 1048 uint8_t type, subtype; 1049 1050 type = ext >> 56; 1051 subtype = ext >> 48; 1052 1053 switch (type) { 1054 case EXT_COMMUNITY_TRANS_TWO_AS: 1055 case EXT_COMMUNITY_GEN_TWO_AS: 1056 as2 = ext >> 32; 1057 u32 = ext; 1058 snprintf(buf, sizeof(buf), "%s %s:%u", 1059 log_ext_subtype(type, subtype), log_as(as2), u32); 1060 return buf; 1061 case EXT_COMMUNITY_TRANS_IPV4: 1062 case EXT_COMMUNITY_GEN_IPV4: 1063 ip.s_addr = htonl(ext >> 16); 1064 u16 = ext; 1065 snprintf(buf, sizeof(buf), "%s %s:%hu", 1066 log_ext_subtype(type, subtype), inet_ntoa(ip), u16); 1067 return buf; 1068 case EXT_COMMUNITY_TRANS_FOUR_AS: 1069 case EXT_COMMUNITY_GEN_FOUR_AS: 1070 as4 = ext >> 16; 1071 u16 = ext; 1072 snprintf(buf, sizeof(buf), "%s %s:%hu", 1073 log_ext_subtype(type, subtype), log_as(as4), u16); 1074 return buf; 1075 case EXT_COMMUNITY_TRANS_OPAQUE: 1076 case EXT_COMMUNITY_TRANS_EVPN: 1077 ext &= 0xffffffffffffULL; 1078 snprintf(buf, sizeof(buf), "%s 0x%llx", 1079 log_ext_subtype(type, subtype), (unsigned long long)ext); 1080 return buf; 1081 case EXT_COMMUNITY_NON_TRANS_OPAQUE: 1082 ext &= 0xffffffffffffULL; 1083 if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) { 1084 switch (ext) { 1085 case EXT_COMMUNITY_OVS_VALID: 1086 snprintf(buf, sizeof(buf), "%s valid", 1087 log_ext_subtype(type, subtype)); 1088 return buf; 1089 case EXT_COMMUNITY_OVS_NOTFOUND: 1090 snprintf(buf, sizeof(buf), "%s not-found", 1091 log_ext_subtype(type, subtype)); 1092 return buf; 1093 case EXT_COMMUNITY_OVS_INVALID: 1094 snprintf(buf, sizeof(buf), "%s invalid", 1095 log_ext_subtype(type, subtype)); 1096 return buf; 1097 default: 1098 snprintf(buf, sizeof(buf), "%s 0x%llx", 1099 log_ext_subtype(type, subtype), 1100 (unsigned long long)ext); 1101 return buf; 1102 } 1103 } else { 1104 snprintf(buf, sizeof(buf), "%s 0x%llx", 1105 log_ext_subtype(type, subtype), 1106 (unsigned long long)ext); 1107 return buf; 1108 } 1109 break; 1110 default: 1111 snprintf(buf, sizeof(buf), "0x%llx", (unsigned long long)ext); 1112 return buf; 1113 } 1114 } 1115 1116 const char * 1117 fmt_set_type(struct ctl_show_set *set) 1118 { 1119 switch (set->type) { 1120 case ASPA_SET: 1121 return "ASPA"; 1122 case ROA_SET: 1123 return "ROA"; 1124 case PREFIX_SET: 1125 return "PREFIX"; 1126 case ORIGIN_SET: 1127 return "ORIGIN"; 1128 case ASNUM_SET: 1129 return "ASNUM"; 1130 default: 1131 return "BULA"; 1132 } 1133 } 1134 1135 void 1136 send_filterset(struct imsgbuf *i, struct filter_set_head *set) 1137 { 1138 struct filter_set *s; 1139 1140 while ((s = TAILQ_FIRST(set)) != NULL) { 1141 imsg_compose(i, IMSG_FILTER_SET, 0, 0, -1, s, 1142 sizeof(struct filter_set)); 1143 TAILQ_REMOVE(set, s, entry); 1144 free(s); 1145 } 1146 } 1147 1148 void 1149 network_bulk(struct parse_result *res) 1150 { 1151 struct network_config net; 1152 struct filter_set *s = NULL; 1153 struct bgpd_addr h; 1154 char *line = NULL; 1155 size_t linesize = 0; 1156 ssize_t linelen; 1157 uint8_t len; 1158 FILE *f; 1159 1160 if ((f = fdopen(STDIN_FILENO, "r")) == NULL) 1161 err(1, "Failed to open stdin\n"); 1162 1163 while ((linelen = getline(&line, &linesize, f)) != -1) { 1164 char *b, *buf = line; 1165 while ((b = strsep(&buf, " \t\n")) != NULL) { 1166 if (*b == '\0') /* skip empty tokens */ 1167 continue; 1168 /* Stop processing after a comment */ 1169 if (*b == '#') 1170 break; 1171 memset(&net, 0, sizeof(net)); 1172 if (parse_prefix(b, strlen(b), &h, &len) != 1) 1173 errx(1, "bad prefix: %s", b); 1174 net.prefix = h; 1175 net.prefixlen = len; 1176 net.rd = res->rd; 1177 1178 if (res->action == NETWORK_BULK_ADD) { 1179 imsg_compose(imsgbuf, IMSG_NETWORK_ADD, 1180 0, 0, -1, &net, sizeof(net)); 1181 /* 1182 * can't use send_filterset since that 1183 * would free the set. 1184 */ 1185 TAILQ_FOREACH(s, &res->set, entry) { 1186 imsg_compose(imsgbuf, 1187 IMSG_FILTER_SET, 1188 0, 0, -1, s, sizeof(*s)); 1189 } 1190 imsg_compose(imsgbuf, IMSG_NETWORK_DONE, 1191 0, 0, -1, NULL, 0); 1192 } else 1193 imsg_compose(imsgbuf, IMSG_NETWORK_REMOVE, 1194 0, 0, -1, &net, sizeof(net)); 1195 } 1196 } 1197 free(line); 1198 if (ferror(f)) 1199 err(1, "getline"); 1200 fclose(f); 1201 } 1202 1203 void 1204 show_mrt_dump_neighbors(struct mrt_rib *mr, struct mrt_peer *mp, void *arg) 1205 { 1206 struct mrt_peer_entry *p; 1207 struct in_addr ina; 1208 uint16_t i; 1209 1210 ina.s_addr = htonl(mp->bgp_id); 1211 printf("view: %s BGP ID: %s Number of peers: %u\n\n", 1212 mp->view, inet_ntoa(ina), mp->npeers); 1213 printf("%-30s %8s %15s\n", "Neighbor", "AS", "BGP ID"); 1214 for (i = 0; i < mp->npeers; i++) { 1215 p = &mp->peers[i]; 1216 ina.s_addr = htonl(p->bgp_id); 1217 printf("%-30s %8u %15s\n", log_addr(&p->addr), p->asnum, 1218 inet_ntoa(ina)); 1219 } 1220 /* we only print the first message */ 1221 exit(0); 1222 } 1223 1224 void 1225 show_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg) 1226 { 1227 struct ctl_show_rib ctl; 1228 struct parse_result res; 1229 struct ctl_show_rib_request *req = arg; 1230 struct mrt_rib_entry *mre; 1231 struct ibuf ibuf; 1232 time_t now; 1233 uint16_t i, j; 1234 1235 memset(&res, 0, sizeof(res)); 1236 res.flags = req->flags; 1237 now = time(NULL); 1238 1239 for (i = 0; i < mr->nentries; i++) { 1240 mre = &mr->entries[i]; 1241 memset(&ctl, 0, sizeof(ctl)); 1242 ctl.prefix = mr->prefix; 1243 ctl.prefixlen = mr->prefixlen; 1244 if (mre->originated <= now) 1245 ctl.age = now - mre->originated; 1246 ctl.true_nexthop = mre->nexthop; 1247 ctl.exit_nexthop = mre->nexthop; 1248 ctl.origin = mre->origin; 1249 ctl.local_pref = mre->local_pref; 1250 ctl.med = mre->med; 1251 /* weight is not part of the mrt dump so it can't be set */ 1252 if (mr->add_path) { 1253 ctl.flags |= F_PREF_PATH_ID; 1254 ctl.path_id = mre->path_id; 1255 } 1256 1257 if (mre->peer_idx < mp->npeers) { 1258 ctl.remote_addr = mp->peers[mre->peer_idx].addr; 1259 ctl.remote_id = mp->peers[mre->peer_idx].bgp_id; 1260 } 1261 1262 /* filter by neighbor */ 1263 if (req->neighbor.addr.aid != AID_UNSPEC && 1264 memcmp(&req->neighbor.addr, &ctl.remote_addr, 1265 sizeof(ctl.remote_addr)) != 0) 1266 continue; 1267 /* filter by AF */ 1268 if (req->aid && req->aid != ctl.prefix.aid) 1269 return; 1270 /* filter by prefix */ 1271 if (req->prefix.aid != AID_UNSPEC) { 1272 if (req->flags & F_LONGER) { 1273 if (req->prefixlen > ctl.prefixlen) 1274 return; 1275 if (prefix_compare(&req->prefix, &ctl.prefix, 1276 req->prefixlen)) 1277 return; 1278 } else if (req->flags & F_SHORTER) { 1279 if (req->prefixlen < ctl.prefixlen) 1280 return; 1281 if (prefix_compare(&req->prefix, &ctl.prefix, 1282 ctl.prefixlen)) 1283 return; 1284 } else { 1285 if (req->prefixlen != ctl.prefixlen) 1286 return; 1287 if (prefix_compare(&req->prefix, &ctl.prefix, 1288 req->prefixlen)) 1289 return; 1290 } 1291 } 1292 /* filter by AS */ 1293 if (req->as.type != AS_UNDEF && 1294 !match_aspath(mre->aspath, mre->aspath_len, &req->as)) 1295 continue; 1296 1297 ibuf_from_buffer(&ibuf, mre->aspath, mre->aspath_len); 1298 output->rib(&ctl, &ibuf, &res); 1299 if (req->flags & F_CTL_DETAIL) { 1300 for (j = 0; j < mre->nattrs; j++) { 1301 ibuf_from_buffer(&ibuf, mre->attrs[j].attr, 1302 mre->attrs[j].attr_len); 1303 output->attr(&ibuf, req->flags, 0); 1304 } 1305 } 1306 } 1307 } 1308 1309 void 1310 network_mrt_dump(struct mrt_rib *mr, struct mrt_peer *mp, void *arg) 1311 { 1312 struct ctl_show_rib ctl; 1313 struct network_config net; 1314 struct ctl_show_rib_request *req = arg; 1315 struct mrt_rib_entry *mre; 1316 struct ibuf *msg; 1317 time_t now; 1318 uint16_t i, j; 1319 1320 /* can't announce more than one path so ignore add-path */ 1321 if (mr->add_path) 1322 return; 1323 1324 now = time(NULL); 1325 for (i = 0; i < mr->nentries; i++) { 1326 mre = &mr->entries[i]; 1327 memset(&ctl, 0, sizeof(ctl)); 1328 ctl.prefix = mr->prefix; 1329 ctl.prefixlen = mr->prefixlen; 1330 if (mre->originated <= now) 1331 ctl.age = now - mre->originated; 1332 ctl.true_nexthop = mre->nexthop; 1333 ctl.exit_nexthop = mre->nexthop; 1334 ctl.origin = mre->origin; 1335 ctl.local_pref = mre->local_pref; 1336 ctl.med = mre->med; 1337 1338 if (mre->peer_idx < mp->npeers) { 1339 ctl.remote_addr = mp->peers[mre->peer_idx].addr; 1340 ctl.remote_id = mp->peers[mre->peer_idx].bgp_id; 1341 } 1342 1343 /* filter by neighbor */ 1344 if (req->neighbor.addr.aid != AID_UNSPEC && 1345 memcmp(&req->neighbor.addr, &ctl.remote_addr, 1346 sizeof(ctl.remote_addr)) != 0) 1347 continue; 1348 /* filter by AF */ 1349 if (req->aid && req->aid != ctl.prefix.aid) 1350 return; 1351 /* filter by prefix */ 1352 if (req->prefix.aid != AID_UNSPEC) { 1353 if (!prefix_compare(&req->prefix, &ctl.prefix, 1354 req->prefixlen)) { 1355 if (req->flags & F_LONGER) { 1356 if (req->prefixlen > ctl.prefixlen) 1357 return; 1358 } else if (req->prefixlen != ctl.prefixlen) 1359 return; 1360 } else 1361 return; 1362 } 1363 /* filter by AS */ 1364 if (req->as.type != AS_UNDEF && 1365 !match_aspath(mre->aspath, mre->aspath_len, &req->as)) 1366 continue; 1367 1368 memset(&net, 0, sizeof(net)); 1369 net.prefix = ctl.prefix; 1370 net.prefixlen = ctl.prefixlen; 1371 net.type = NETWORK_MRTCLONE; 1372 /* XXX rd can't be set and will be 0 */ 1373 1374 imsg_compose(imsgbuf, IMSG_NETWORK_ADD, 0, 0, -1, 1375 &net, sizeof(net)); 1376 if ((msg = imsg_create(imsgbuf, IMSG_NETWORK_ASPATH, 1377 0, 0, sizeof(ctl) + mre->aspath_len)) == NULL) 1378 errx(1, "imsg_create failure"); 1379 if (imsg_add(msg, &ctl, sizeof(ctl)) == -1 || 1380 imsg_add(msg, mre->aspath, mre->aspath_len) == -1) 1381 errx(1, "imsg_add failure"); 1382 imsg_close(imsgbuf, msg); 1383 for (j = 0; j < mre->nattrs; j++) 1384 imsg_compose(imsgbuf, IMSG_NETWORK_ATTR, 0, 0, -1, 1385 mre->attrs[j].attr, mre->attrs[j].attr_len); 1386 imsg_compose(imsgbuf, IMSG_NETWORK_DONE, 0, 0, -1, NULL, 0); 1387 1388 if (imsgbuf_flush(imsgbuf) == -1) 1389 err(1, "write error"); 1390 } 1391 } 1392 1393 static const char * 1394 fmt_time(struct timespec *t) 1395 { 1396 static char timebuf[32]; 1397 static struct timespec prevtime; 1398 struct timespec temp; 1399 1400 timespecsub(t, &prevtime, &temp); 1401 snprintf(timebuf, sizeof(timebuf), "%lld.%06ld", 1402 (long long)temp.tv_sec, temp.tv_nsec / 1000); 1403 prevtime = *t; 1404 return (timebuf); 1405 } 1406 1407 void 1408 show_mrt_state(struct mrt_bgp_state *ms, void *arg) 1409 { 1410 printf("%s %s[%u] -> ", fmt_time(&ms->time), 1411 log_addr(&ms->src), ms->src_as); 1412 printf("%s[%u]: %s -> %s\n", log_addr(&ms->dst), ms->dst_as, 1413 statenames[ms->old_state], statenames[ms->new_state]); 1414 } 1415 1416 static void 1417 print_afi(struct ibuf *b) 1418 { 1419 uint16_t afi; 1420 uint8_t safi, aid; 1421 1422 if (ibuf_get_n16(b, &afi) == -1 || /* afi, 2 byte */ 1423 ibuf_skip(b, 1) == -1 || /* reserved, 1 byte */ 1424 ibuf_get_n8(b, &safi) == -1 || /* safi, 1 byte */ 1425 ibuf_size(b) != 0) { 1426 printf("bad length"); 1427 return; 1428 } 1429 1430 if (afi2aid(afi, safi, &aid) == -1) 1431 printf("unknown afi %u safi %u", afi, safi); 1432 else 1433 printf("%s", aid2str(aid)); 1434 } 1435 1436 static void 1437 print_capability(uint8_t capa_code, struct ibuf *b) 1438 { 1439 uint32_t as; 1440 1441 switch (capa_code) { 1442 case CAPA_MP: 1443 printf("multiprotocol capability: "); 1444 print_afi(b); 1445 break; 1446 case CAPA_REFRESH: 1447 printf("route refresh capability"); 1448 break; 1449 case CAPA_RESTART: 1450 printf("graceful restart capability"); 1451 /* XXX there is more needed here */ 1452 break; 1453 case CAPA_AS4BYTE: 1454 printf("4-byte AS num capability: "); 1455 if (ibuf_get_n32(b, &as) == -1 || 1456 ibuf_size(b) != 0) 1457 printf("bad length"); 1458 else 1459 printf("AS %u", as); 1460 break; 1461 case CAPA_ADD_PATH: 1462 printf("add-path capability"); 1463 /* XXX there is more needed here */ 1464 break; 1465 case CAPA_ENHANCED_RR: 1466 printf("enhanced route refresh capability"); 1467 break; 1468 case CAPA_EXT_MSG: 1469 printf("extended message capability"); 1470 break; 1471 default: 1472 printf("unknown capability %u length %zu", 1473 capa_code, ibuf_size(b)); 1474 break; 1475 } 1476 } 1477 1478 static void 1479 print_notification(uint8_t errcode, uint8_t subcode) 1480 { 1481 const char *suberrname = NULL; 1482 int uk = 0; 1483 1484 switch (errcode) { 1485 case ERR_HEADER: 1486 if (subcode >= sizeof(suberr_header_names)/sizeof(char *)) 1487 uk = 1; 1488 else 1489 suberrname = suberr_header_names[subcode]; 1490 break; 1491 case ERR_OPEN: 1492 if (subcode >= sizeof(suberr_open_names)/sizeof(char *)) 1493 uk = 1; 1494 else 1495 suberrname = suberr_open_names[subcode]; 1496 break; 1497 case ERR_UPDATE: 1498 if (subcode >= sizeof(suberr_update_names)/sizeof(char *)) 1499 uk = 1; 1500 else 1501 suberrname = suberr_update_names[subcode]; 1502 break; 1503 case ERR_CEASE: 1504 if (subcode >= sizeof(suberr_cease_names)/sizeof(char *)) 1505 uk = 1; 1506 else 1507 suberrname = suberr_cease_names[subcode]; 1508 break; 1509 case ERR_HOLDTIMEREXPIRED: 1510 if (subcode != 0) 1511 uk = 1; 1512 break; 1513 case ERR_FSM: 1514 if (subcode >= sizeof(suberr_fsm_names)/sizeof(char *)) 1515 uk = 1; 1516 else 1517 suberrname = suberr_fsm_names[subcode]; 1518 break; 1519 default: 1520 printf("unknown errcode %u, subcode %u", 1521 errcode, subcode); 1522 return; 1523 } 1524 1525 if (uk) 1526 printf("%s, unknown subcode %u", errnames[errcode], subcode); 1527 else { 1528 if (suberrname == NULL) 1529 printf("%s", errnames[errcode]); 1530 else 1531 printf("%s, %s", errnames[errcode], suberrname); 1532 } 1533 } 1534 1535 static int 1536 show_mrt_capabilities(struct ibuf *b) 1537 { 1538 uint8_t capa_code, capa_len; 1539 struct ibuf cbuf; 1540 1541 while (ibuf_size(b) > 0) { 1542 if (ibuf_get_n8(b, &capa_code) == -1 || 1543 ibuf_get_n8(b, &capa_len) == -1 || 1544 ibuf_get_ibuf(b, capa_len, &cbuf) == -1) { 1545 printf("truncated capabilities"); 1546 return (-1); 1547 } 1548 printf("\n "); 1549 print_capability(capa_code, &cbuf); 1550 } 1551 return (0); 1552 } 1553 1554 static void 1555 show_mrt_open(struct ibuf *b) 1556 { 1557 struct in_addr ina; 1558 uint32_t bgpid; 1559 uint16_t short_as, holdtime; 1560 uint8_t version, optparamlen; 1561 1562 /* length check up to optparamlen already happened */ 1563 if (ibuf_get_n8(b, &version) == -1 || 1564 ibuf_get_n16(b, &short_as) == -1 || 1565 ibuf_get_n16(b, &holdtime) == -1 || 1566 ibuf_get_n32(b, &bgpid) == -1 || 1567 ibuf_get_n8(b, &optparamlen) == -1) { 1568 trunc: 1569 printf("truncated message"); 1570 return; 1571 } 1572 1573 printf("\n "); 1574 ina.s_addr = htonl(bgpid); 1575 printf("Version: %d AS: %u Holdtime: %u BGP Id: %s Paramlen: %u", 1576 version, short_as, holdtime, inet_ntoa(ina), optparamlen); 1577 if (optparamlen != ibuf_size(b)) { 1578 /* XXX missing support for RFC9072 */ 1579 printf("optional parameter length mismatch"); 1580 return; 1581 } 1582 while (ibuf_size(b) > 0) { 1583 uint8_t op_type, op_len; 1584 1585 if (ibuf_get_n8(b, &op_type) == -1 || 1586 ibuf_get_n8(b, &op_len) == -1) 1587 goto trunc; 1588 1589 printf("\n "); 1590 switch (op_type) { 1591 case OPT_PARAM_CAPABILITIES: 1592 printf("Capabilities: %u bytes", op_len); 1593 if (show_mrt_capabilities(b) == -1) 1594 return; 1595 break; 1596 case OPT_PARAM_AUTH: 1597 default: 1598 printf("unsupported optional parameter: type %u", 1599 op_type); 1600 return; 1601 } 1602 } 1603 } 1604 1605 static void 1606 show_mrt_notification(struct ibuf *b) 1607 { 1608 char reason[REASON_LEN]; 1609 uint8_t errcode, subcode, reason_len, c; 1610 size_t i, len; 1611 1612 if (ibuf_get_n8(b, &errcode) == -1 || 1613 ibuf_get_n8(b, &subcode) == -1) { 1614 trunc: 1615 printf("truncated message"); 1616 return; 1617 } 1618 1619 printf("\n "); 1620 print_notification(errcode, subcode); 1621 1622 if (errcode == ERR_CEASE && (subcode == ERR_CEASE_ADMIN_DOWN || 1623 subcode == ERR_CEASE_ADMIN_RESET)) { 1624 if (ibuf_size(b) > 1) { 1625 if (ibuf_get_n8(b, &reason_len) == -1) 1626 goto trunc; 1627 if (ibuf_get(b, reason, reason_len) == -1) 1628 goto trunc; 1629 reason[reason_len] = '\0'; 1630 printf("shutdown reason: \"%s\"", 1631 log_reason(reason)); 1632 } 1633 } 1634 if (errcode == ERR_OPEN && subcode == ERR_OPEN_CAPA) { 1635 if (show_mrt_capabilities(b) == -1) 1636 return; 1637 } 1638 1639 if (ibuf_size(b) > 0) { 1640 len = ibuf_size(b); 1641 printf("\n additional data, %zu bytes", len); 1642 for (i = 0; i < len; i++) { 1643 if (i % 16 == 0) 1644 printf("\n "); 1645 if (i % 8 == 0) 1646 printf(" "); 1647 if (ibuf_get_n8(b, &c) == -1) 1648 goto trunc; 1649 printf(" %02X", c); 1650 } 1651 } 1652 } 1653 1654 /* XXX this function does not handle JSON output */ 1655 static void 1656 show_mrt_update(struct ibuf *b, int reqflags, int addpath) 1657 { 1658 struct bgpd_addr prefix; 1659 struct ibuf wbuf, abuf; 1660 uint32_t pathid; 1661 uint16_t wlen, alen; 1662 uint8_t prefixlen; 1663 1664 if (ibuf_get_n16(b, &wlen) == -1 || 1665 ibuf_get_ibuf(b, wlen, &wbuf) == -1) 1666 goto trunc; 1667 1668 if (wlen > 0) { 1669 printf("\n Withdrawn prefixes:"); 1670 while (ibuf_size(&wbuf) > 0) { 1671 if (addpath) 1672 if (ibuf_get_n32(&wbuf, &pathid) == -1) 1673 goto trunc; 1674 if (nlri_get_prefix(&wbuf, &prefix, &prefixlen) == -1) 1675 goto trunc; 1676 1677 printf(" %s/%u", log_addr(&prefix), prefixlen); 1678 if (addpath) 1679 printf(" path-id %u", pathid); 1680 } 1681 } 1682 1683 if (ibuf_get_n16(b, &alen) == -1 || 1684 ibuf_get_ibuf(b, alen, &abuf) == -1) 1685 goto trunc; 1686 1687 printf("\n"); 1688 /* alen attributes here */ 1689 while (ibuf_size(&abuf) > 0) { 1690 struct ibuf attrbuf; 1691 uint16_t attrlen; 1692 uint8_t flags; 1693 1694 ibuf_from_ibuf(&abuf, &attrbuf); 1695 if (ibuf_get_n8(&attrbuf, &flags) == -1 || 1696 ibuf_skip(&attrbuf, 1) == -1) 1697 goto trunc; 1698 1699 /* get the attribute length */ 1700 if (flags & ATTR_EXTLEN) { 1701 if (ibuf_get_n16(&attrbuf, &attrlen) == -1) 1702 goto trunc; 1703 } else { 1704 uint8_t tmp; 1705 if (ibuf_get_n8(&attrbuf, &tmp) == -1) 1706 goto trunc; 1707 attrlen = tmp; 1708 } 1709 if (ibuf_truncate(&attrbuf, attrlen) == -1) 1710 goto trunc; 1711 ibuf_rewind(&attrbuf); 1712 if (ibuf_skip(&abuf, ibuf_size(&attrbuf)) == -1) 1713 goto trunc; 1714 1715 output->attr(&attrbuf, reqflags, addpath); 1716 } 1717 1718 if (ibuf_size(b) > 0) { 1719 printf(" NLRI prefixes:"); 1720 while (ibuf_size(b) > 0) { 1721 if (addpath) 1722 if (ibuf_get_n32(b, &pathid) == -1) 1723 goto trunc; 1724 if (nlri_get_prefix(b, &prefix, &prefixlen) == -1) 1725 goto trunc; 1726 1727 printf(" %s/%u", log_addr(&prefix), prefixlen); 1728 if (addpath) 1729 printf(" path-id %u", pathid); 1730 } 1731 } 1732 return; 1733 1734 trunc: 1735 printf("truncated message"); 1736 } 1737 1738 void 1739 show_mrt_msg(struct mrt_bgp_msg *mm, void *arg) 1740 { 1741 static const uint8_t marker[MSGSIZE_HEADER_MARKER] = { 1742 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1743 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 1744 uint8_t m[MSGSIZE_HEADER_MARKER]; 1745 struct ibuf *b; 1746 uint16_t len; 1747 uint8_t type; 1748 struct ctl_show_rib_request *req = arg; 1749 1750 printf("%s %s[%u] -> ", fmt_time(&mm->time), 1751 log_addr(&mm->src), mm->src_as); 1752 printf("%s[%u]: size %zu%s ", log_addr(&mm->dst), mm->dst_as, 1753 ibuf_size(&mm->msg), mm->add_path ? " addpath" : ""); 1754 b = &mm->msg; 1755 1756 if (ibuf_get(b, m, sizeof(m)) == -1) { 1757 printf("bad message: short header\n"); 1758 return; 1759 } 1760 1761 /* parse BGP message header */ 1762 if (memcmp(m, marker, sizeof(marker))) { 1763 printf("incorrect marker in BGP message\n"); 1764 return; 1765 } 1766 1767 if (ibuf_get_n16(b, &len) == -1 || 1768 ibuf_get_n8(b, &type) == -1) { 1769 printf("bad message: short header\n"); 1770 return; 1771 } 1772 1773 if (len < MSGSIZE_HEADER || len > MAX_PKTSIZE) { 1774 printf("illegal header length: %u byte\n", len); 1775 return; 1776 } 1777 1778 switch (type) { 1779 case MSG_OPEN: 1780 printf("%s ", msgtypenames[type]); 1781 if (len < MSGSIZE_OPEN_MIN) { 1782 printf("bad length: %u bytes\n", len); 1783 return; 1784 } 1785 show_mrt_open(b); 1786 break; 1787 case MSG_NOTIFICATION: 1788 printf("%s ", msgtypenames[type]); 1789 if (len < MSGSIZE_NOTIFICATION_MIN) { 1790 printf("bad length: %u bytes\n", len); 1791 return; 1792 } 1793 show_mrt_notification(b); 1794 break; 1795 case MSG_UPDATE: 1796 printf("%s ", msgtypenames[type]); 1797 if (len < MSGSIZE_UPDATE_MIN) { 1798 printf("bad length: %u bytes\n", len); 1799 return; 1800 } 1801 show_mrt_update(b, req->flags, mm->add_path); 1802 break; 1803 case MSG_KEEPALIVE: 1804 printf("%s ", msgtypenames[type]); 1805 if (len != MSGSIZE_KEEPALIVE) { 1806 printf("bad length: %u bytes\n", len); 1807 return; 1808 } 1809 /* nothing */ 1810 break; 1811 case MSG_RREFRESH: 1812 printf("%s ", msgtypenames[type]); 1813 if (len != MSGSIZE_RREFRESH) { 1814 printf("bad length: %u bytes\n", len); 1815 return; 1816 } 1817 print_afi(b); 1818 break; 1819 default: 1820 printf("unknown type %u\n", type); 1821 return; 1822 } 1823 printf("\n"); 1824 } 1825 1826 const char * 1827 msg_type(uint8_t type) 1828 { 1829 if (type >= sizeof(msgtypenames)/sizeof(msgtypenames[0])) 1830 return "BAD"; 1831 return (msgtypenames[type]); 1832 } 1833 1834 int 1835 match_aspath(void *data, uint16_t len, struct filter_as *f) 1836 { 1837 uint8_t *seg; 1838 int final; 1839 uint16_t seg_size; 1840 uint8_t i, seg_len; 1841 uint32_t as = 0; 1842 1843 if (f->type == AS_EMPTY) { 1844 if (len == 0) 1845 return (1); 1846 else 1847 return (0); 1848 } 1849 1850 seg = data; 1851 1852 /* just check the leftmost AS */ 1853 if (f->type == AS_PEER && len >= 6) { 1854 as = aspath_extract(seg, 0); 1855 if (f->as_min == as) 1856 return (1); 1857 else 1858 return (0); 1859 } 1860 1861 for (; len >= 6; len -= seg_size, seg += seg_size) { 1862 seg_len = seg[1]; 1863 seg_size = 2 + sizeof(uint32_t) * seg_len; 1864 1865 final = (len == seg_size); 1866 1867 if (f->type == AS_SOURCE) { 1868 /* 1869 * Just extract the rightmost AS 1870 * but if that segment is an AS_SET then the rightmost 1871 * AS of a previous AS_SEQUENCE segment should be used. 1872 * Because of that just look at AS_SEQUENCE segments. 1873 */ 1874 if (seg[0] == AS_SEQUENCE) 1875 as = aspath_extract(seg, seg_len - 1); 1876 /* not yet in the final segment */ 1877 if (!final) 1878 continue; 1879 if (f->as_min == as) 1880 return (1); 1881 else 1882 return (0); 1883 } 1884 /* AS_TRANSIT or AS_ALL */ 1885 for (i = 0; i < seg_len; i++) { 1886 /* 1887 * the source (rightmost) AS is excluded from 1888 * AS_TRANSIT matches. 1889 */ 1890 if (final && i == seg_len - 1 && f->type == AS_TRANSIT) 1891 return (0); 1892 as = aspath_extract(seg, i); 1893 if (f->as_min == as) 1894 return (1); 1895 } 1896 } 1897 return (0); 1898 } 1899 1900 static void 1901 component_finish(int type, uint8_t *data, int len) 1902 { 1903 uint8_t *last; 1904 int i; 1905 1906 switch (type) { 1907 case FLOWSPEC_TYPE_DEST: 1908 case FLOWSPEC_TYPE_SOURCE: 1909 /* nothing todo */ 1910 return; 1911 default: 1912 break; 1913 } 1914 1915 i = 0; 1916 do { 1917 last = data + i; 1918 i += FLOWSPEC_OP_LEN(*last) + 1; 1919 } while (i < len); 1920 *last |= FLOWSPEC_OP_EOL; 1921 } 1922 1923 static void 1924 push_prefix(struct parse_result *r, int type, struct bgpd_addr *addr, 1925 uint8_t len) 1926 { 1927 void *data; 1928 uint8_t *comp; 1929 int complen, l; 1930 1931 switch (addr->aid) { 1932 case AID_UNSPEC: 1933 return; 1934 case AID_INET: 1935 complen = PREFIX_SIZE(len); 1936 data = &addr->v4; 1937 break; 1938 case AID_INET6: 1939 /* IPv6 includes an offset byte */ 1940 complen = PREFIX_SIZE(len) + 1; 1941 data = &addr->v6; 1942 break; 1943 default: 1944 errx(1, "unsupported address family for flowspec address"); 1945 } 1946 comp = malloc(complen); 1947 if (comp == NULL) 1948 err(1, NULL); 1949 1950 l = 0; 1951 comp[l++] = len; 1952 if (addr->aid == AID_INET6) 1953 comp[l++] = 0; 1954 memcpy(comp + l, data, complen - l); 1955 1956 r->flow.complen[type] = complen; 1957 r->flow.components[type] = comp; 1958 } 1959 1960 1961 struct flowspec * 1962 res_to_flowspec(struct parse_result *r) 1963 { 1964 struct flowspec *f; 1965 int i, len = 0; 1966 uint8_t aid; 1967 1968 switch (r->aid) { 1969 case AID_INET: 1970 aid = AID_FLOWSPECv4; 1971 break; 1972 case AID_INET6: 1973 aid = AID_FLOWSPECv6; 1974 break; 1975 default: 1976 errx(1, "unsupported AFI %s for flowspec rule", 1977 aid2str(r->aid)); 1978 } 1979 1980 push_prefix(r, FLOWSPEC_TYPE_DEST, &r->flow.dst, r->flow.dstlen); 1981 push_prefix(r, FLOWSPEC_TYPE_SOURCE, &r->flow.src, r->flow.srclen); 1982 1983 for (i = FLOWSPEC_TYPE_MIN; i < FLOWSPEC_TYPE_MAX; i++) 1984 if (r->flow.components[i] != NULL) 1985 len += r->flow.complen[i] + 1; 1986 1987 if (len == 0) 1988 errx(1, "no flowspec rule defined"); 1989 1990 f = malloc(FLOWSPEC_SIZE + len); 1991 if (f == NULL) 1992 err(1, NULL); 1993 memset(f, 0, FLOWSPEC_SIZE); 1994 1995 f->aid = aid; 1996 f->len = len; 1997 1998 len = 0; 1999 for (i = FLOWSPEC_TYPE_MIN; i < FLOWSPEC_TYPE_MAX; i++) 2000 if (r->flow.components[i] != NULL) { 2001 f->data[len++] = i; 2002 component_finish(i, r->flow.components[i], 2003 r->flow.complen[i]); 2004 memcpy(f->data + len, r->flow.components[i], 2005 r->flow.complen[i]); 2006 len += r->flow.complen[i]; 2007 } 2008 2009 return f; 2010 } 2011