1 /* $OpenBSD: rde_trie.c,v 1.17 2022/12/28 21:30:16 jmc Exp $ */ 2 3 /* 4 * Copyright (c) 2018 Claudio Jeker <claudio@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 #include <sys/types.h> 19 #include <sys/queue.h> 20 21 #include <netinet/in.h> 22 23 #include <stdlib.h> 24 #include <stdio.h> 25 #include <string.h> 26 27 #include "bgpd.h" 28 #include "rde.h" 29 30 /* 31 * Bitwise compressed trie for prefix-sets. 32 * Since the trie is path compressed the traversing function needs to check 33 * that the lookup is still on path before taking a branch. 34 * There are two types of nodes in the trie. Internal branch nodes and real 35 * nodes. Internal nodes are added when needed because off path nodes are being 36 * inserted and are just used for branching. 37 * During lookup every node defines which bit is checked for branching. This 38 * offset is strictly increasing. For IPv4 the maximum is therefore 32 levels. 39 * Every node checks the bit at position plen to decide which branch to take. 40 * The real nodes also include a prefixlen mask which represents the prefixlen 41 * range that was defined. The prefixlen mask for IPv4 only has 32 bits but 42 * the prefixlen range is from 0 - 32 which needs 33bits. Now prefixlen 0 is 43 * special and only one element -- the default route -- can have prefixlen 0. 44 * So this default route is added as a flag to the trie_head and needs to be 45 * checked independent of the trie when doing a lookup. 46 * On insertion a prefix is added with its prefixlen plen and a min & max 47 * for the prefixlen range. The following precondition needs to hold 48 * plen <= min <= max 49 * To only match the prefix itself min and max are set to plen. 50 * If a same prefix (addr/plen) is added to the trie but with different 51 * min & max values then the masks of both nodes are merged with a binary OR. 52 * The match function returns true the moment the first node is found which 53 * covers the looked up prefix and where the prefixlen mask matches for the 54 * looked up prefixlen. The moment the plen of a node is bigger than the 55 * prefixlen of the looked up prefix the search can be stopped since 56 * there will be no match. 57 */ 58 struct tentry_v4 { 59 struct tentry_v4 *trie[2]; 60 struct set_table *set; /* for roa source-as set */ 61 struct in_addr addr; 62 struct in_addr plenmask; 63 uint8_t plen; 64 uint8_t node; 65 }; 66 67 struct tentry_v6 { 68 struct tentry_v6 *trie[2]; 69 struct set_table *set; /* for roa source-as set */ 70 struct in6_addr addr; 71 struct in6_addr plenmask; 72 uint8_t plen; 73 uint8_t node; 74 }; 75 76 /* 77 * Find first different bit between a & b starting from the MSB, 78 * a & b have to be different. 79 */ 80 static int 81 inet4findmsb(struct in_addr *a, struct in_addr *b) 82 { 83 int r = 0; 84 uint32_t v; 85 86 v = ntohl(a->s_addr ^ b->s_addr); 87 if (v > 0xffff) { r += 16; v >>= 16; } 88 if (v > 0x00ff) { r += 8; v >>= 8; } 89 if (v > 0x000f) { r += 4; v >>= 4; } 90 if (v > 0x0003) { r += 2; v >>= 2; } 91 if (v > 0x0001) { r += 1; } 92 93 return 31 - r; 94 } 95 96 /* 97 * Find first different bit between a & b starting from the MSB, 98 * a & b have to be different. 99 */ 100 static int 101 inet6findmsb(struct in6_addr *a, struct in6_addr *b) 102 { 103 int r = 0; 104 uint8_t i, x; 105 106 for (i = 0; i < sizeof(*a) && a->s6_addr[i] == b->s6_addr[i]; i++) 107 ; 108 /* first different octet */ 109 x = a->s6_addr[i] ^ b->s6_addr[i]; 110 111 /* first different bit */ 112 if (x > 0xf) { r += 4; x >>= 4; } 113 if (x > 0x3) { r += 2; x >>= 2; } 114 if (x > 0x1) { r += 1; } 115 116 return i * 8 + 7 - r; 117 } 118 119 static int 120 inet4isset(struct in_addr *addr, uint8_t bit) 121 { 122 return addr->s_addr & htonl(1U << (31 - bit)); 123 } 124 125 static int 126 inet6isset(struct in6_addr *addr, uint8_t bit) 127 { 128 return addr->s6_addr[bit / 8] & (0x80 >> (bit % 8)); 129 } 130 131 static void 132 inet4setbit(struct in_addr *addr, uint8_t bit) 133 { 134 /* bit 0 sets the MSB and 31 sets the LSB */ 135 addr->s_addr |= htonl(1U << (31 - bit)); 136 } 137 138 static void 139 inet6setbit(struct in6_addr *addr, uint8_t bit) 140 { 141 addr->s6_addr[bit / 8] |= (0x80 >> (bit % 8)); 142 } 143 144 static struct tentry_v4 * 145 trie_add_v4(struct trie_head *th, struct in_addr *prefix, uint8_t plen) 146 { 147 struct tentry_v4 *n, *new, *b, **prev; 148 struct in_addr p; 149 150 inet4applymask(&p, prefix, plen); 151 152 /* walk tree finding spot to insert */ 153 prev = &th->root_v4; 154 n = *prev; 155 while (n) { 156 struct in_addr mp; 157 uint8_t minlen; 158 159 minlen = n->plen > plen ? plen : n->plen; 160 inet4applymask(&mp, &p, minlen); 161 if (n->addr.s_addr != mp.s_addr) { 162 /* 163 * out of path, insert intermediary node between 164 * np and n, then insert n and new node there 165 */ 166 if ((b = calloc(1, sizeof(*b))) == NULL) 167 return NULL; 168 rdemem.pset_cnt++; 169 rdemem.pset_size += sizeof(*b); 170 b->plen = inet4findmsb(&n->addr, &mp); 171 inet4applymask(&b->addr, &n->addr, b->plen); 172 173 *prev = b; 174 if (inet4isset(&n->addr, b->plen)) { 175 b->trie[1] = n; 176 prev = &b->trie[0]; 177 } else { 178 b->trie[0] = n; 179 prev = &b->trie[1]; 180 } 181 n = NULL; 182 break; 183 } 184 185 if (n->plen > plen) { 186 /* n is more specific, just insert new in between */ 187 break; 188 } 189 190 if (n->plen == plen) { 191 /* matching node, adjust */ 192 if (n->node == 0) 193 th->v4_cnt++; 194 n->node = 1; 195 return n; 196 } 197 198 /* no need to check for n->plen == 32 because of above if */ 199 if (inet4isset(&p, n->plen)) 200 prev = &n->trie[1]; 201 else 202 prev = &n->trie[0]; 203 n = *prev; 204 } 205 206 /* create new node */ 207 if ((new = calloc(1, sizeof(*new))) == NULL) 208 return NULL; 209 th->v4_cnt++; 210 rdemem.pset_cnt++; 211 rdemem.pset_size += sizeof(*new); 212 new->addr = p; 213 new->plen = plen; 214 new->node = 1; 215 216 /* link node */ 217 *prev = new; 218 if (n) { 219 if (inet4isset(&n->addr, new->plen)) 220 new->trie[1] = n; 221 else 222 new->trie[0] = n; 223 } 224 return new; 225 } 226 227 static struct tentry_v6 * 228 trie_add_v6(struct trie_head *th, struct in6_addr *prefix, uint8_t plen) 229 { 230 struct tentry_v6 *n, *new, *b, **prev; 231 struct in6_addr p; 232 233 inet6applymask(&p, prefix, plen); 234 235 /* walk tree finding spot to insert */ 236 prev = &th->root_v6; 237 n = *prev; 238 while (n) { 239 struct in6_addr mp; 240 uint8_t minlen; 241 242 minlen = n->plen > plen ? plen : n->plen; 243 inet6applymask(&mp, &p, minlen); 244 if (memcmp(&n->addr, &mp, sizeof(mp)) != 0) { 245 /* 246 * out of path, insert intermediary node between 247 * np and n, then insert n and new node there 248 */ 249 if ((b = calloc(1, sizeof(*b))) == NULL) 250 return NULL; 251 rdemem.pset_cnt++; 252 rdemem.pset_size += sizeof(*b); 253 b->plen = inet6findmsb(&n->addr, &mp); 254 inet6applymask(&b->addr, &n->addr, b->plen); 255 256 *prev = b; 257 if (inet6isset(&n->addr, b->plen)) { 258 b->trie[1] = n; 259 prev = &b->trie[0]; 260 } else { 261 b->trie[0] = n; 262 prev = &b->trie[1]; 263 } 264 n = NULL; 265 break; 266 } 267 268 if (n->plen > plen) { 269 /* n is more specific, just insert new in between */ 270 break; 271 } 272 273 if (n->plen == plen) { 274 /* matching node, adjust */ 275 if (n->node == 0) 276 th->v6_cnt++; 277 n->node = 1; 278 return n; 279 } 280 281 /* no need to check for n->plen == 128 because of above if */ 282 if (inet6isset(&p, n->plen)) 283 prev = &n->trie[1]; 284 else 285 prev = &n->trie[0]; 286 n = *prev; 287 } 288 289 /* create new node */ 290 if ((new = calloc(1, sizeof(*new))) == NULL) 291 return NULL; 292 th->v6_cnt++; 293 rdemem.pset_cnt++; 294 rdemem.pset_size += sizeof(*new); 295 new->addr = p; 296 new->plen = plen; 297 new->node = 1; 298 299 /* link node */ 300 *prev = new; 301 if (n) { 302 if (inet6isset(&n->addr, new->plen)) 303 new->trie[1] = n; 304 else 305 new->trie[0] = n; 306 } 307 return new; 308 } 309 310 /* 311 * Insert prefix/plen into the trie with a prefixlen mask covering min - max. 312 * If plen == min == max then only the prefix/plen will match and no longer 313 * match is possible. Else all prefixes under prefix/plen with a prefixlen 314 * between min and max will match. 315 */ 316 int 317 trie_add(struct trie_head *th, struct bgpd_addr *prefix, uint8_t plen, 318 uint8_t min, uint8_t max) 319 { 320 struct tentry_v4 *n4; 321 struct tentry_v6 *n6; 322 uint8_t i; 323 324 /* precondition plen <= min <= max */ 325 if (plen > min || min > max) 326 return -1; 327 if (prefix->aid != AID_INET && prefix->aid != AID_INET6) 328 return -1; 329 330 /* 331 * Check for default route, this is special cased since prefixlen 0 332 * can't be checked in the prefixlen mask plenmask. Also there is 333 * only one default route so using a flag for this works. 334 */ 335 if (min == 0) { 336 if (prefix->aid == AID_INET) 337 th->match_default_v4 = 1; 338 else 339 th->match_default_v6 = 1; 340 341 if (max == 0) /* just the default route */ 342 return 0; 343 min = 1; 344 } 345 346 switch (prefix->aid) { 347 case AID_INET: 348 if (max > 32) 349 return -1; 350 351 n4 = trie_add_v4(th, &prefix->v4, plen); 352 if (n4 == NULL) 353 return -1; 354 /* 355 * The prefixlen min - max goes from 1 to 32 but the bitmask 356 * starts at 0 and so all bits are set with an offset of -1. 357 * The default /0 route is handled specially above. 358 */ 359 for (i = min; i <= max; i++) 360 inet4setbit(&n4->plenmask, i - 1); 361 break; 362 case AID_INET6: 363 if (max > 128) 364 return -1; 365 366 n6 = trie_add_v6(th, &prefix->v6, plen); 367 if (n6 == NULL) 368 return -1; 369 370 /* See above for the - 1 reason. */ 371 for (i = min; i <= max; i++) 372 inet6setbit(&n6->plenmask, i - 1); 373 break; 374 } 375 return 0; 376 } 377 378 /* 379 * Insert a ROA entry for prefix/plen. The prefix will insert a set with 380 * source_as and the maxlen as data. This makes it possible to validate if a 381 * prefix is matching this ROA record. It is possible to insert prefixes with 382 * source_as = 0. These entries will never return ROA_VALID on check and can 383 * be used to cover a large prefix as ROA_INVALID unless a more specific route 384 * is a match. 385 */ 386 int 387 trie_roa_add(struct trie_head *th, struct roa *roa) 388 { 389 struct tentry_v4 *n4; 390 struct tentry_v6 *n6; 391 struct set_table **stp; 392 struct roa_set rs, *rsp; 393 394 /* ignore possible default route since it does not make sense */ 395 396 switch (roa->aid) { 397 case AID_INET: 398 if (roa->prefixlen > 32) 399 return -1; 400 401 n4 = trie_add_v4(th, &roa->prefix.inet, roa->prefixlen); 402 if (n4 == NULL) 403 return -1; 404 stp = &n4->set; 405 break; 406 case AID_INET6: 407 if (roa->prefixlen > 128) 408 return -1; 409 410 n6 = trie_add_v6(th, &roa->prefix.inet6, roa->prefixlen); 411 if (n6 == NULL) 412 return -1; 413 stp = &n6->set; 414 break; 415 default: 416 /* anything else fails */ 417 return -1; 418 } 419 420 if (*stp == NULL) 421 if ((*stp = set_new(1, sizeof(rs))) == NULL) 422 return -1; 423 424 /* merge sets with same key, longer maxlen wins */ 425 if ((rsp = set_match(*stp, roa->asnum)) != NULL) { 426 if (rsp->maxlen < roa->maxlen) 427 rsp->maxlen = roa->maxlen; 428 } else { 429 rs.as = roa->asnum; 430 rs.maxlen = roa->maxlen; 431 if (set_add(*stp, &rs, 1) != 0) 432 return -1; 433 /* prep data so that set_match works */ 434 set_prep(*stp); 435 } 436 437 return 0; 438 } 439 440 static void 441 trie_free_v4(struct tentry_v4 *n) 442 { 443 if (n == NULL) 444 return; 445 trie_free_v4(n->trie[0]); 446 trie_free_v4(n->trie[1]); 447 set_free(n->set); 448 free(n); 449 rdemem.pset_cnt--; 450 rdemem.pset_size -= sizeof(*n); 451 } 452 453 static void 454 trie_free_v6(struct tentry_v6 *n) 455 { 456 if (n == NULL) 457 return; 458 trie_free_v6(n->trie[0]); 459 trie_free_v6(n->trie[1]); 460 set_free(n->set); 461 free(n); 462 rdemem.pset_cnt--; 463 rdemem.pset_size -= sizeof(*n); 464 } 465 466 void 467 trie_free(struct trie_head *th) 468 { 469 trie_free_v4(th->root_v4); 470 trie_free_v6(th->root_v6); 471 memset(th, 0, sizeof(*th)); 472 } 473 474 static int 475 trie_match_v4(struct trie_head *th, struct in_addr *prefix, uint8_t plen, 476 int orlonger) 477 { 478 struct tentry_v4 *n; 479 480 if (plen == 0) { 481 /* special handling for default route */ 482 return th->match_default_v4; 483 } 484 485 n = th->root_v4; 486 while (n) { 487 struct in_addr mp; 488 489 if (n->plen > plen) 490 break; /* too specific, no match possible */ 491 492 inet4applymask(&mp, prefix, n->plen); 493 if (n->addr.s_addr != mp.s_addr) 494 break; /* off path, no match possible */ 495 496 /* the match covers all larger prefixlens */ 497 if (n->node && orlonger) 498 return 1; 499 500 /* plen is from 1 - 32 but the bitmask starts with 0 */ 501 if (n->node && inet4isset(&n->plenmask, plen - 1)) 502 return 1; /* prefixlen allowed, match */ 503 504 if (n->plen == 32) 505 break; /* can't go deeper */ 506 if (inet4isset(prefix, n->plen)) 507 n = n->trie[1]; 508 else 509 n = n->trie[0]; 510 } 511 512 return 0; 513 } 514 515 static int 516 trie_match_v6(struct trie_head *th, struct in6_addr *prefix, uint8_t plen, 517 int orlonger) 518 { 519 struct tentry_v6 *n; 520 521 if (plen == 0) { 522 /* special handling for default route */ 523 return th->match_default_v6; 524 } 525 526 n = th->root_v6; 527 while (n) { 528 struct in6_addr mp; 529 530 if (n->plen > plen) 531 break; /* too specific, no match possible */ 532 533 inet6applymask(&mp, prefix, n->plen); 534 if (memcmp(&n->addr, &mp, sizeof(mp)) != 0) 535 break; /* off path, no match possible */ 536 537 /* the match covers all larger prefixlens */ 538 if (n->node && orlonger) 539 return 1; 540 541 /* plen is from 1 - 128 but the bitmask starts with 0 */ 542 if (n->node && inet6isset(&n->plenmask, plen - 1)) 543 return 1; /* prefixlen allowed, match */ 544 545 if (n->plen == 128) 546 break; /* can't go deeper */ 547 if (inet6isset(prefix, n->plen)) 548 n = n->trie[1]; 549 else 550 n = n->trie[0]; 551 } 552 return 0; 553 } 554 555 /* find first matching element in the trie for prefix "prefix/plen" */ 556 int 557 trie_match(struct trie_head *th, struct bgpd_addr *prefix, uint8_t plen, 558 int orlonger) 559 { 560 switch (prefix->aid) { 561 case AID_INET: 562 return trie_match_v4(th, &prefix->v4, plen, orlonger); 563 case AID_INET6: 564 return trie_match_v6(th, &prefix->v6, plen, orlonger); 565 default: 566 /* anything else is no match */ 567 return 0; 568 } 569 } 570 571 static int 572 trie_roa_check_v4(struct trie_head *th, struct in_addr *prefix, uint8_t plen, 573 uint32_t as) 574 { 575 struct tentry_v4 *n; 576 struct roa_set *rs; 577 int validity = ROA_NOTFOUND; 578 579 /* ignore possible default route since it does not make sense */ 580 581 n = th->root_v4; 582 while (n) { 583 struct in_addr mp; 584 585 if (n->plen > plen) 586 break; /* too specific, no match possible */ 587 588 inet4applymask(&mp, prefix, n->plen); 589 if (n->addr.s_addr != mp.s_addr) 590 break; /* off path, no other match possible */ 591 592 if (n->node) { 593 /* 594 * The prefix is covered by this roa node 595 * therefore invalid unless roa_set matches. 596 */ 597 validity = ROA_INVALID; 598 599 /* AS_NONE can never match, so don't try */ 600 if (as != AS_NONE) { 601 if ((rs = set_match(n->set, as)) != NULL) { 602 if (plen == n->plen || plen <= rs->maxlen) 603 return ROA_VALID; 604 } 605 } 606 } 607 608 if (n->plen == 32) 609 break; /* can't go deeper */ 610 if (inet4isset(prefix, n->plen)) 611 n = n->trie[1]; 612 else 613 n = n->trie[0]; 614 } 615 616 return validity; 617 } 618 619 static int 620 trie_roa_check_v6(struct trie_head *th, struct in6_addr *prefix, uint8_t plen, 621 uint32_t as) 622 { 623 struct tentry_v6 *n; 624 struct roa_set *rs; 625 int validity = ROA_NOTFOUND; 626 627 /* ignore possible default route since it does not make sense */ 628 629 n = th->root_v6; 630 while (n) { 631 struct in6_addr mp; 632 633 if (n->plen > plen) 634 break; /* too specific, no match possible */ 635 636 inet6applymask(&mp, prefix, n->plen); 637 if (memcmp(&n->addr, &mp, sizeof(mp)) != 0) 638 break; /* off path, no other match possible */ 639 640 if (n->node) { 641 /* 642 * This prefix is covered by this roa node. 643 * Therefore invalid unless proven otherwise. 644 */ 645 validity = ROA_INVALID; 646 647 /* AS_NONE can never match, so don't try */ 648 if (as != AS_NONE) { 649 if ((rs = set_match(n->set, as)) != NULL) 650 if (plen == n->plen || plen <= rs->maxlen) 651 return ROA_VALID; 652 } 653 } 654 655 if (n->plen == 128) 656 break; /* can't go deeper */ 657 if (inet6isset(prefix, n->plen)) 658 n = n->trie[1]; 659 else 660 n = n->trie[0]; 661 } 662 663 return validity; 664 } 665 666 /* 667 * Do a ROA (Route Origin Validation) check. Look for elements in the trie 668 * which cover prefix "prefix/plen" and match the source-as as. 669 * AS_NONE can be used when the source-as is unknown (e.g. AS_SET). 670 * The check will then only return ROA_NOTFOUND or ROA_INVALID depending if 671 * the prefix is covered by the ROA. 672 */ 673 int 674 trie_roa_check(struct trie_head *th, struct bgpd_addr *prefix, uint8_t plen, 675 uint32_t as) 676 { 677 /* valid, invalid, unknown */ 678 switch (prefix->aid) { 679 case AID_INET: 680 return trie_roa_check_v4(th, &prefix->v4, plen, as); 681 case AID_INET6: 682 return trie_roa_check_v6(th, &prefix->v6, plen, as); 683 default: 684 /* anything else is not-found */ 685 return ROA_NOTFOUND; 686 } 687 } 688 689 static int 690 trie_equal_v4(struct tentry_v4 *a, struct tentry_v4 *b) 691 { 692 if (a == NULL && b == NULL) 693 return 1; 694 if (a == NULL || b == NULL) 695 return 0; 696 697 if (a->addr.s_addr != b->addr.s_addr || 698 a->plen != b->plen || 699 a->node != b->node || 700 a->plenmask.s_addr != b->plenmask.s_addr) 701 return 0; 702 703 if (set_equal(a->set, b->set) == 0) 704 return 0; 705 706 if (trie_equal_v4(a->trie[0], b->trie[0]) == 0 || 707 trie_equal_v4(a->trie[1], b->trie[1]) == 0) 708 return 0; 709 710 return 1; 711 } 712 713 static int 714 trie_equal_v6(struct tentry_v6 *a, struct tentry_v6 *b) 715 { 716 if (a == NULL && b == NULL) 717 return 1; 718 if (a == NULL || b == NULL) 719 return 0; 720 721 if (memcmp(&a->addr, &b->addr, sizeof(a->addr)) != 0 || 722 a->plen != b->plen || 723 a->node != b->node || 724 memcmp(&a->plenmask, &b->plenmask, sizeof(a->plenmask)) != 0) 725 return 0; 726 727 if (set_equal(a->set, b->set) == 0) 728 return 0; 729 730 if (trie_equal_v6(a->trie[0], b->trie[0]) == 0 || 731 trie_equal_v6(a->trie[1], b->trie[1]) == 0) 732 return 0; 733 734 return 1; 735 } 736 737 /* Compare two tries and return 1 if they are the same else 0. */ 738 int 739 trie_equal(struct trie_head *a, struct trie_head *b) 740 { 741 if (a->match_default_v4 != b->match_default_v4 || 742 a->match_default_v6 != b->match_default_v6) 743 return 0; 744 if (trie_equal_v4(a->root_v4, b->root_v4) == 0) 745 return 0; 746 if (trie_equal_v6(a->root_v6, b->root_v6) == 0) 747 return 0; 748 return 1; 749 } 750 751 /* debugging functions for printing the trie */ 752 static void 753 trie_dump_v4(struct tentry_v4 *n) 754 { 755 if (n == NULL) 756 return; 757 if (n->node) 758 fprintf(stderr, "%s/%u plenmask %08x\n", inet_ntoa(n->addr), 759 n->plen, n->plenmask.s_addr); 760 else 761 fprintf(stderr, " %s/%u\n", inet_ntoa(n->addr), n->plen); 762 763 trie_dump_v4(n->trie[0]); 764 trie_dump_v4(n->trie[1]); 765 } 766 767 static void 768 trie_dump_v6(struct tentry_v6 *n) 769 { 770 char buf[48]; 771 unsigned int i; 772 773 if (n == NULL) 774 return; 775 if (n->node) { 776 fprintf(stderr, "%s/%u plenmask ", 777 inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen); 778 for (i = 0; i < sizeof(n->plenmask); i++) 779 fprintf(stderr, "%02x", n->plenmask.s6_addr[i]); 780 fprintf(stderr, "\n"); 781 } else 782 fprintf(stderr, " %s/%u\n", 783 inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen); 784 785 trie_dump_v6(n->trie[0]); 786 trie_dump_v6(n->trie[1]); 787 } 788 789 void 790 trie_dump(struct trie_head *th) 791 { 792 if (th->match_default_v4) 793 fprintf(stderr, "0.0.0.0/0 plenmask 0\n"); 794 trie_dump_v4(th->root_v4); 795 if (th->match_default_v6) 796 fprintf(stderr, "::/0 plenmask 0\n"); 797 trie_dump_v6(th->root_v6); 798 } 799