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