1 /* $OpenBSD: config.c,v 1.108 2023/08/16 08:26:35 claudio Exp $ */ 2 3 /* 4 * Copyright (c) 2003, 2004, 2005 Henning Brauer <henning@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 22 #include <errno.h> 23 #include <ifaddrs.h> 24 #include <netdb.h> 25 #include <stddef.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "bgpd.h" 32 #include "session.h" 33 #include "log.h" 34 35 int host_ip(const char *, struct bgpd_addr *, uint8_t *); 36 void free_networks(struct network_head *); 37 void free_flowspecs(struct flowspec_tree *); 38 39 struct bgpd_config * 40 new_config(void) 41 { 42 struct bgpd_config *conf; 43 44 if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL) 45 fatal(NULL); 46 47 if ((conf->filters = calloc(1, sizeof(struct filter_head))) == NULL) 48 fatal(NULL); 49 if ((conf->listen_addrs = calloc(1, sizeof(struct listen_addrs))) == 50 NULL) 51 fatal(NULL); 52 if ((conf->mrt = calloc(1, sizeof(struct mrt_head))) == NULL) 53 fatal(NULL); 54 55 /* init the various list for later */ 56 RB_INIT(&conf->peers); 57 TAILQ_INIT(&conf->networks); 58 RB_INIT(&conf->flowspecs); 59 SIMPLEQ_INIT(&conf->l3vpns); 60 SIMPLEQ_INIT(&conf->prefixsets); 61 SIMPLEQ_INIT(&conf->originsets); 62 SIMPLEQ_INIT(&conf->rde_prefixsets); 63 SIMPLEQ_INIT(&conf->rde_originsets); 64 RB_INIT(&conf->roa); 65 RB_INIT(&conf->aspa); 66 SIMPLEQ_INIT(&conf->as_sets); 67 SIMPLEQ_INIT(&conf->rtrs); 68 69 TAILQ_INIT(conf->filters); 70 TAILQ_INIT(conf->listen_addrs); 71 LIST_INIT(conf->mrt); 72 73 return (conf); 74 } 75 76 void 77 copy_config(struct bgpd_config *to, struct bgpd_config *from) 78 { 79 to->flags = from->flags; 80 to->log = from->log; 81 to->default_tableid = from->default_tableid; 82 to->bgpid = from->bgpid; 83 to->clusterid = from->clusterid; 84 to->as = from->as; 85 to->short_as = from->short_as; 86 to->holdtime = from->holdtime; 87 to->min_holdtime = from->min_holdtime; 88 to->connectretry = from->connectretry; 89 to->fib_priority = from->fib_priority; 90 } 91 92 void 93 network_free(struct network *n) 94 { 95 rtlabel_unref(n->net.rtlabel); 96 filterset_free(&n->net.attrset); 97 free(n); 98 } 99 100 void 101 free_networks(struct network_head *networks) 102 { 103 struct network *n; 104 105 while ((n = TAILQ_FIRST(networks)) != NULL) { 106 TAILQ_REMOVE(networks, n, entry); 107 network_free(n); 108 } 109 } 110 111 struct flowspec_config * 112 flowspec_alloc(uint8_t aid, int len) 113 { 114 struct flowspec_config *conf; 115 struct flowspec *flow; 116 117 flow = malloc(FLOWSPEC_SIZE + len); 118 if (flow == NULL) 119 return NULL; 120 memset(flow, 0, FLOWSPEC_SIZE); 121 122 conf = calloc(1, sizeof(*conf)); 123 if (conf == NULL) { 124 free(flow); 125 return NULL; 126 } 127 128 conf->flow = flow; 129 TAILQ_INIT(&conf->attrset); 130 flow->len = len; 131 flow->aid = aid; 132 133 return conf; 134 } 135 136 void 137 flowspec_free(struct flowspec_config *f) 138 { 139 filterset_free(&f->attrset); 140 free(f->flow); 141 free(f); 142 } 143 144 void 145 free_flowspecs(struct flowspec_tree *flowspecs) 146 { 147 struct flowspec_config *f, *nf; 148 149 RB_FOREACH_SAFE(f, flowspec_tree, flowspecs, nf) { 150 RB_REMOVE(flowspec_tree, flowspecs, f); 151 flowspec_free(f); 152 } 153 } 154 155 void 156 free_l3vpns(struct l3vpn_head *l3vpns) 157 { 158 struct l3vpn *vpn; 159 160 while ((vpn = SIMPLEQ_FIRST(l3vpns)) != NULL) { 161 SIMPLEQ_REMOVE_HEAD(l3vpns, entry); 162 filterset_free(&vpn->export); 163 filterset_free(&vpn->import); 164 free_networks(&vpn->net_l); 165 free(vpn); 166 } 167 } 168 169 void 170 free_prefixsets(struct prefixset_head *psh) 171 { 172 struct prefixset *ps; 173 174 while (!SIMPLEQ_EMPTY(psh)) { 175 ps = SIMPLEQ_FIRST(psh); 176 free_roatree(&ps->roaitems); 177 free_prefixtree(&ps->psitems); 178 SIMPLEQ_REMOVE_HEAD(psh, entry); 179 free(ps); 180 } 181 } 182 183 void 184 free_rde_prefixsets(struct rde_prefixset_head *psh) 185 { 186 struct rde_prefixset *ps; 187 188 if (psh == NULL) 189 return; 190 191 while (!SIMPLEQ_EMPTY(psh)) { 192 ps = SIMPLEQ_FIRST(psh); 193 trie_free(&ps->th); 194 SIMPLEQ_REMOVE_HEAD(psh, entry); 195 free(ps); 196 } 197 } 198 199 void 200 free_prefixtree(struct prefixset_tree *p) 201 { 202 struct prefixset_item *psi, *npsi; 203 204 RB_FOREACH_SAFE(psi, prefixset_tree, p, npsi) { 205 RB_REMOVE(prefixset_tree, p, psi); 206 free(psi); 207 } 208 } 209 210 void 211 free_roatree(struct roa_tree *r) 212 { 213 struct roa *roa, *nroa; 214 215 RB_FOREACH_SAFE(roa, roa_tree, r, nroa) { 216 RB_REMOVE(roa_tree, r, roa); 217 free(roa); 218 } 219 } 220 221 void 222 free_aspa(struct aspa_set *aspa) 223 { 224 if (aspa == NULL) 225 return; 226 free(aspa->tas); 227 free(aspa); 228 } 229 230 void 231 free_aspatree(struct aspa_tree *a) 232 { 233 struct aspa_set *aspa, *naspa; 234 235 RB_FOREACH_SAFE(aspa, aspa_tree, a, naspa) { 236 RB_REMOVE(aspa_tree, a, aspa); 237 free_aspa(aspa); 238 } 239 } 240 241 void 242 free_rtrs(struct rtr_config_head *rh) 243 { 244 struct rtr_config *r; 245 246 while (!SIMPLEQ_EMPTY(rh)) { 247 r = SIMPLEQ_FIRST(rh); 248 SIMPLEQ_REMOVE_HEAD(rh, entry); 249 free(r); 250 } 251 } 252 253 void 254 free_config(struct bgpd_config *conf) 255 { 256 struct peer *p, *next; 257 struct listen_addr *la; 258 struct mrt *m; 259 260 free_l3vpns(&conf->l3vpns); 261 free_networks(&conf->networks); 262 free_flowspecs(&conf->flowspecs); 263 filterlist_free(conf->filters); 264 free_prefixsets(&conf->prefixsets); 265 free_prefixsets(&conf->originsets); 266 free_rde_prefixsets(&conf->rde_prefixsets); 267 free_rde_prefixsets(&conf->rde_originsets); 268 as_sets_free(&conf->as_sets); 269 free_roatree(&conf->roa); 270 free_aspatree(&conf->aspa); 271 free_rtrs(&conf->rtrs); 272 273 while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) { 274 TAILQ_REMOVE(conf->listen_addrs, la, entry); 275 free(la); 276 } 277 free(conf->listen_addrs); 278 279 while ((m = LIST_FIRST(conf->mrt)) != NULL) { 280 LIST_REMOVE(m, entry); 281 free(m); 282 } 283 free(conf->mrt); 284 285 RB_FOREACH_SAFE(p, peer_head, &conf->peers, next) { 286 RB_REMOVE(peer_head, &conf->peers, p); 287 free(p); 288 } 289 290 free(conf->csock); 291 free(conf->rcsock); 292 293 free(conf); 294 } 295 296 void 297 merge_config(struct bgpd_config *xconf, struct bgpd_config *conf) 298 { 299 struct listen_addr *nla, *ola, *next; 300 struct peer *p, *np, *nextp; 301 struct flowspec_config *f, *nextf, *xf; 302 303 /* 304 * merge the freshly parsed conf into the running xconf 305 */ 306 307 /* adjust FIB priority if changed */ 308 /* if xconf is uninitialized we get RTP_NONE */ 309 if (xconf->fib_priority != conf->fib_priority) { 310 kr_fib_decouple_all(); 311 kr_fib_prio_set(conf->fib_priority); 312 kr_fib_couple_all(); 313 } 314 315 /* take over the easy config changes */ 316 copy_config(xconf, conf); 317 318 /* clear old control sockets and use new */ 319 free(xconf->csock); 320 free(xconf->rcsock); 321 xconf->csock = conf->csock; 322 xconf->rcsock = conf->rcsock; 323 /* set old one to NULL so we don't double free */ 324 conf->csock = NULL; 325 conf->rcsock = NULL; 326 327 /* clear all current filters and take over the new ones */ 328 filterlist_free(xconf->filters); 329 xconf->filters = conf->filters; 330 conf->filters = NULL; 331 332 /* merge mrt config */ 333 mrt_mergeconfig(xconf->mrt, conf->mrt); 334 335 /* switch the roa, first remove the old one */ 336 free_roatree(&xconf->roa); 337 /* then move the RB tree root */ 338 RB_ROOT(&xconf->roa) = RB_ROOT(&conf->roa); 339 RB_ROOT(&conf->roa) = NULL; 340 341 /* switch the aspa, first remove the old one */ 342 free_aspatree(&xconf->aspa); 343 /* then move the RB tree root */ 344 RB_ROOT(&xconf->aspa) = RB_ROOT(&conf->aspa); 345 RB_ROOT(&conf->aspa) = NULL; 346 347 /* switch the rtr_configs, first remove the old ones */ 348 free_rtrs(&xconf->rtrs); 349 SIMPLEQ_CONCAT(&xconf->rtrs, &conf->rtrs); 350 351 /* switch the prefixsets, first remove the old ones */ 352 free_prefixsets(&xconf->prefixsets); 353 SIMPLEQ_CONCAT(&xconf->prefixsets, &conf->prefixsets); 354 355 /* switch the originsets, first remove the old ones */ 356 free_prefixsets(&xconf->originsets); 357 SIMPLEQ_CONCAT(&xconf->originsets, &conf->originsets); 358 359 /* switch the as_sets, first remove the old ones */ 360 as_sets_free(&xconf->as_sets); 361 SIMPLEQ_CONCAT(&xconf->as_sets, &conf->as_sets); 362 363 /* switch the network statements, but first remove the old ones */ 364 free_networks(&xconf->networks); 365 TAILQ_CONCAT(&xconf->networks, &conf->networks, entry); 366 367 /* 368 * Merge the flowspec statements. Mark the old ones for deletion 369 * which happens when the flowspec is sent to the RDE. 370 */ 371 RB_FOREACH(f, flowspec_tree, &xconf->flowspecs) 372 f->reconf_action = RECONF_DELETE; 373 374 RB_FOREACH_SAFE(f, flowspec_tree, &conf->flowspecs, nextf) { 375 RB_REMOVE(flowspec_tree, &conf->flowspecs, f); 376 377 xf = RB_INSERT(flowspec_tree, &xconf->flowspecs, f); 378 if (xf != NULL) { 379 filterset_free(&xf->attrset); 380 filterset_move(&f->attrset, &xf->attrset); 381 flowspec_free(f); 382 xf->reconf_action = RECONF_KEEP; 383 } else 384 f->reconf_action = RECONF_KEEP; 385 } 386 387 /* switch the l3vpn configs, first remove the old ones */ 388 free_l3vpns(&xconf->l3vpns); 389 SIMPLEQ_CONCAT(&xconf->l3vpns, &conf->l3vpns); 390 391 /* 392 * merge new listeners: 393 * -flag all existing ones as to be deleted 394 * -those that are in both new and old: flag to keep 395 * -new ones get inserted and flagged as to reinit 396 * -remove all that are still flagged for deletion 397 */ 398 399 TAILQ_FOREACH(nla, xconf->listen_addrs, entry) 400 nla->reconf = RECONF_DELETE; 401 402 /* no new listeners? preserve default ones */ 403 if (TAILQ_EMPTY(conf->listen_addrs)) 404 TAILQ_FOREACH(ola, xconf->listen_addrs, entry) 405 if (ola->flags & DEFAULT_LISTENER) 406 ola->reconf = RECONF_KEEP; 407 /* else loop over listeners and merge configs */ 408 for (nla = TAILQ_FIRST(conf->listen_addrs); nla != NULL; nla = next) { 409 next = TAILQ_NEXT(nla, entry); 410 411 TAILQ_FOREACH(ola, xconf->listen_addrs, entry) 412 if (!memcmp(&nla->sa, &ola->sa, sizeof(nla->sa))) 413 break; 414 415 if (ola == NULL) { 416 /* new listener, copy over */ 417 TAILQ_REMOVE(conf->listen_addrs, nla, entry); 418 TAILQ_INSERT_TAIL(xconf->listen_addrs, nla, entry); 419 nla->reconf = RECONF_REINIT; 420 } else /* exists, just flag */ 421 ola->reconf = RECONF_KEEP; 422 } 423 /* finally clean up the original list and remove all stale entries */ 424 for (nla = TAILQ_FIRST(xconf->listen_addrs); nla != NULL; nla = next) { 425 next = TAILQ_NEXT(nla, entry); 426 if (nla->reconf == RECONF_DELETE) { 427 TAILQ_REMOVE(xconf->listen_addrs, nla, entry); 428 free(nla); 429 } 430 } 431 432 /* 433 * merge peers: 434 * - need to know which peers are new, replaced and removed 435 * - walk over old peers and check if there is a corresponding new 436 * peer if so mark it RECONF_KEEP. Remove all old peers. 437 * - swap lists (old peer list is actually empty). 438 */ 439 RB_FOREACH_SAFE(p, peer_head, &xconf->peers, nextp) { 440 np = getpeerbyid(conf, p->conf.id); 441 if (np != NULL) { 442 np->reconf_action = RECONF_KEEP; 443 /* copy the auth state since parent uses it */ 444 np->auth = p->auth; 445 } else { 446 /* peer no longer exists, clear pfkey state */ 447 pfkey_remove(p); 448 } 449 450 RB_REMOVE(peer_head, &xconf->peers, p); 451 free(p); 452 } 453 RB_FOREACH_SAFE(np, peer_head, &conf->peers, nextp) { 454 RB_REMOVE(peer_head, &conf->peers, np); 455 if (RB_INSERT(peer_head, &xconf->peers, np) != NULL) 456 fatalx("%s: peer tree is corrupt", __func__); 457 } 458 459 /* conf is merged so free it */ 460 free_config(conf); 461 } 462 463 uint32_t 464 get_bgpid(void) 465 { 466 struct ifaddrs *ifap, *ifa; 467 uint32_t ip = 0, cur, localnet; 468 469 localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET); 470 471 if (getifaddrs(&ifap) == -1) 472 fatal("getifaddrs"); 473 474 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 475 if (ifa->ifa_addr == NULL || 476 ifa->ifa_addr->sa_family != AF_INET) 477 continue; 478 cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr; 479 if ((cur & localnet) == localnet) /* skip 127/8 */ 480 continue; 481 if (ntohl(cur) > ntohl(ip)) 482 ip = cur; 483 } 484 freeifaddrs(ifap); 485 486 return (ip); 487 } 488 489 int 490 host(const char *s, struct bgpd_addr *h, uint8_t *len) 491 { 492 int mask = 128; 493 char *p, *ps; 494 const char *errstr; 495 496 if ((ps = strdup(s)) == NULL) 497 fatal("%s: strdup", __func__); 498 499 if ((p = strrchr(ps, '/')) != NULL) { 500 mask = strtonum(p+1, 0, 128, &errstr); 501 if (errstr) { 502 log_warnx("prefixlen is %s: %s", errstr, p); 503 free(ps); 504 return (0); 505 } 506 p[0] = '\0'; 507 } 508 509 memset(h, 0, sizeof(*h)); 510 511 if (host_ip(ps, h, len) == 0) { 512 free(ps); 513 return (0); 514 } 515 516 if (p != NULL) 517 *len = mask; 518 519 free(ps); 520 return (1); 521 } 522 523 int 524 host_ip(const char *s, struct bgpd_addr *h, uint8_t *len) 525 { 526 struct addrinfo hints, *res; 527 int bits; 528 529 memset(&hints, 0, sizeof(hints)); 530 hints.ai_family = AF_UNSPEC; 531 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 532 hints.ai_flags = AI_NUMERICHOST; 533 if (getaddrinfo(s, NULL, &hints, &res) == 0) { 534 *len = res->ai_family == AF_INET6 ? 128 : 32; 535 sa2addr(res->ai_addr, h, NULL); 536 freeaddrinfo(res); 537 } else { /* ie. for 10/8 parsing */ 538 if ((bits = inet_net_pton(AF_INET, s, &h->v4, 539 sizeof(h->v4))) == -1) 540 return (0); 541 *len = bits; 542 h->aid = AID_INET; 543 } 544 545 return (1); 546 } 547 548 int 549 prepare_listeners(struct bgpd_config *conf) 550 { 551 struct listen_addr *la, *next; 552 int opt = 1; 553 int r = 0; 554 555 for (la = TAILQ_FIRST(conf->listen_addrs); la != NULL; la = next) { 556 next = TAILQ_NEXT(la, entry); 557 if (la->reconf != RECONF_REINIT) 558 continue; 559 560 if ((la->fd = socket(la->sa.ss_family, 561 SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 562 IPPROTO_TCP)) == -1) { 563 if (la->flags & DEFAULT_LISTENER && (errno == 564 EAFNOSUPPORT || errno == EPROTONOSUPPORT)) { 565 TAILQ_REMOVE(conf->listen_addrs, la, entry); 566 free(la); 567 continue; 568 } else 569 fatal("socket"); 570 } 571 572 opt = 1; 573 if (setsockopt(la->fd, SOL_SOCKET, SO_REUSEADDR, 574 &opt, sizeof(opt)) == -1) 575 fatal("setsockopt SO_REUSEADDR"); 576 577 if (bind(la->fd, (struct sockaddr *)&la->sa, la->sa_len) == 578 -1) { 579 switch (la->sa.ss_family) { 580 case AF_INET: 581 log_warn("cannot bind to %s:%u", 582 log_sockaddr((struct sockaddr *)&la->sa, 583 la->sa_len), ntohs(((struct sockaddr_in *) 584 &la->sa)->sin_port)); 585 break; 586 case AF_INET6: 587 log_warn("cannot bind to [%s]:%u", 588 log_sockaddr((struct sockaddr *)&la->sa, 589 la->sa_len), ntohs(((struct sockaddr_in6 *) 590 &la->sa)->sin6_port)); 591 break; 592 default: 593 log_warn("cannot bind to %s", 594 log_sockaddr((struct sockaddr *)&la->sa, 595 la->sa_len)); 596 break; 597 } 598 close(la->fd); 599 TAILQ_REMOVE(conf->listen_addrs, la, entry); 600 free(la); 601 r = -1; 602 continue; 603 } 604 } 605 606 return (r); 607 } 608 609 void 610 expand_networks(struct bgpd_config *c, struct network_head *nw) 611 { 612 struct network *n, *m, *tmp; 613 struct prefixset *ps; 614 struct prefixset_item *psi; 615 616 TAILQ_FOREACH_SAFE(n, nw, entry, tmp) { 617 if (n->net.type == NETWORK_PREFIXSET) { 618 TAILQ_REMOVE(nw, n, entry); 619 if ((ps = find_prefixset(n->net.psname, &c->prefixsets)) 620 == NULL) 621 fatal("%s: prefixset %s not found", __func__, 622 n->net.psname); 623 RB_FOREACH(psi, prefixset_tree, &ps->psitems) { 624 if ((m = calloc(1, sizeof(struct network))) 625 == NULL) 626 fatal(NULL); 627 memcpy(&m->net.prefix, &psi->p.addr, 628 sizeof(m->net.prefix)); 629 m->net.prefixlen = psi->p.len; 630 filterset_copy(&n->net.attrset, 631 &m->net.attrset); 632 TAILQ_INSERT_TAIL(nw, m, entry); 633 } 634 network_free(n); 635 } 636 } 637 } 638 639 static inline int 640 prefixset_cmp(struct prefixset_item *a, struct prefixset_item *b) 641 { 642 int i; 643 644 if (a->p.addr.aid < b->p.addr.aid) 645 return (-1); 646 if (a->p.addr.aid > b->p.addr.aid) 647 return (1); 648 649 switch (a->p.addr.aid) { 650 case AID_INET: 651 i = memcmp(&a->p.addr.v4, &b->p.addr.v4, 652 sizeof(struct in_addr)); 653 break; 654 case AID_INET6: 655 i = memcmp(&a->p.addr.v6, &b->p.addr.v6, 656 sizeof(struct in6_addr)); 657 break; 658 default: 659 fatalx("%s: unknown af", __func__); 660 } 661 if (i > 0) 662 return (1); 663 if (i < 0) 664 return (-1); 665 if (a->p.len < b->p.len) 666 return (-1); 667 if (a->p.len > b->p.len) 668 return (1); 669 if (a->p.len_min < b->p.len_min) 670 return (-1); 671 if (a->p.len_min > b->p.len_min) 672 return (1); 673 if (a->p.len_max < b->p.len_max) 674 return (-1); 675 if (a->p.len_max > b->p.len_max) 676 return (1); 677 return (0); 678 } 679 680 RB_GENERATE(prefixset_tree, prefixset_item, entry, prefixset_cmp); 681 682 static inline int 683 roa_cmp(struct roa *a, struct roa *b) 684 { 685 int i; 686 687 if (a->aid < b->aid) 688 return (-1); 689 if (a->aid > b->aid) 690 return (1); 691 692 switch (a->aid) { 693 case AID_INET: 694 i = memcmp(&a->prefix.inet, &b->prefix.inet, 695 sizeof(struct in_addr)); 696 break; 697 case AID_INET6: 698 i = memcmp(&a->prefix.inet6, &b->prefix.inet6, 699 sizeof(struct in6_addr)); 700 break; 701 default: 702 fatalx("%s: unknown af", __func__); 703 } 704 if (i > 0) 705 return (1); 706 if (i < 0) 707 return (-1); 708 if (a->prefixlen < b->prefixlen) 709 return (-1); 710 if (a->prefixlen > b->prefixlen) 711 return (1); 712 713 if (a->asnum < b->asnum) 714 return (-1); 715 if (a->asnum > b->asnum) 716 return (1); 717 718 if (a->maxlen < b->maxlen) 719 return (-1); 720 if (a->maxlen > b->maxlen) 721 return (1); 722 723 return (0); 724 } 725 726 RB_GENERATE(roa_tree, roa, entry, roa_cmp); 727 728 static inline int 729 aspa_cmp(struct aspa_set *a, struct aspa_set *b) 730 { 731 if (a->as < b->as) 732 return (-1); 733 if (a->as > b->as) 734 return (1); 735 return (0); 736 } 737 738 RB_GENERATE(aspa_tree, aspa_set, entry, aspa_cmp); 739 740 static inline int 741 flowspec_config_cmp(struct flowspec_config *a, struct flowspec_config *b) 742 { 743 if (a->flow->aid < b->flow->aid) 744 return -1; 745 if (a->flow->aid > b->flow->aid) 746 return 1; 747 748 return flowspec_cmp(a->flow->data, a->flow->len, 749 b->flow->data, b->flow->len, a->flow->aid == AID_FLOWSPECv6); 750 } 751 752 RB_GENERATE(flowspec_tree, flowspec_config, entry, flowspec_config_cmp); 753