1 /* $OpenBSD: rde_trie.c,v 1.12 2020/12/30 07:29:56 claudio 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 therefor 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 u_int8_t plen; 64 u_int8_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 u_int8_t plen; 73 u_int8_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 u_int32_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 u_int8_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, u_int8_t bit) 121 { 122 return addr->s_addr & htonl(1 << (31 - bit)); 123 } 124 125 static int 126 inet6isset(struct in6_addr *addr, u_int8_t bit) 127 { 128 return addr->s6_addr[bit / 8] & (0x80 >> (bit % 8)); 129 } 130 131 static void 132 inet4setbit(struct in_addr *addr, u_int8_t bit) 133 { 134 /* bit 0 sets the MSB and 31 sets the LSB */ 135 addr->s_addr |= htonl(1 << (31 - bit)); 136 } 137 138 static void 139 inet6setbit(struct in6_addr *addr, u_int8_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, u_int8_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 u_int8_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, u_int8_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 u_int8_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, u_int8_t plen, 318 u_int8_t min, u_int8_t max) 319 { 320 struct tentry_v4 *n4; 321 struct tentry_v6 *n6; 322 u_int8_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 } 472 473 static int 474 trie_match_v4(struct trie_head *th, struct in_addr *prefix, u_int8_t plen, 475 int orlonger) 476 { 477 struct tentry_v4 *n; 478 479 if (plen == 0) { 480 /* special handling for default route */ 481 return th->match_default_v4; 482 } 483 484 n = th->root_v4; 485 while (n) { 486 struct in_addr mp; 487 488 if (n->plen > plen) 489 break; /* too specific, no match possible */ 490 491 inet4applymask(&mp, prefix, n->plen); 492 if (n->addr.s_addr != mp.s_addr) 493 break; /* off path, no match possible */ 494 495 /* the match covers all larger prefixlens */ 496 if (n->node && orlonger) 497 return 1; 498 499 /* plen is from 1 - 32 but the bitmask starts with 0 */ 500 if (n->node && inet4isset(&n->plenmask, plen - 1)) 501 return 1; /* prefixlen allowed, match */ 502 503 if (n->plen == 32) 504 break; /* can't go deeper */ 505 if (inet4isset(prefix, n->plen)) 506 n = n->trie[1]; 507 else 508 n = n->trie[0]; 509 } 510 511 return 0; 512 } 513 514 static int 515 trie_match_v6(struct trie_head *th, struct in6_addr *prefix, u_int8_t plen, 516 int orlonger) 517 { 518 struct tentry_v6 *n; 519 520 if (plen == 0) { 521 /* special handling for default route */ 522 return th->match_default_v6; 523 } 524 525 n = th->root_v6; 526 while (n) { 527 struct in6_addr mp; 528 529 if (n->plen > plen) 530 break; /* too specific, no match possible */ 531 532 inet6applymask(&mp, prefix, n->plen); 533 if (memcmp(&n->addr, &mp, sizeof(mp)) != 0) 534 break; /* off path, no match possible */ 535 536 /* the match covers all larger prefixlens */ 537 if (n->node && orlonger) 538 return 1; 539 540 /* plen is from 1 - 128 but the bitmask starts with 0 */ 541 if (n->node && inet6isset(&n->plenmask, plen - 1)) 542 return 1; /* prefixlen allowed, match */ 543 544 if (n->plen == 128) 545 break; /* can't go deeper */ 546 if (inet6isset(prefix, n->plen)) 547 n = n->trie[1]; 548 else 549 n = n->trie[0]; 550 } 551 return 0; 552 } 553 554 /* find first matching element in the trie for prefix "prefix/plen" */ 555 int 556 trie_match(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen, 557 int orlonger) 558 { 559 switch (prefix->aid) { 560 case AID_INET: 561 return trie_match_v4(th, &prefix->v4, plen, orlonger); 562 case AID_INET6: 563 return trie_match_v6(th, &prefix->v6, plen, orlonger); 564 default: 565 /* anything else is no match */ 566 return 0; 567 } 568 } 569 570 static int 571 trie_roa_check_v4(struct trie_head *th, struct in_addr *prefix, u_int8_t plen, 572 u_int32_t as) 573 { 574 struct tentry_v4 *n; 575 struct roa_set *rs; 576 int validity = ROA_NOTFOUND; 577 578 /* ignore possible default route since it does not make sense */ 579 580 n = th->root_v4; 581 while (n) { 582 struct in_addr mp; 583 584 if (n->plen > plen) 585 break; /* too specific, no match possible */ 586 587 inet4applymask(&mp, prefix, n->plen); 588 if (n->addr.s_addr != mp.s_addr) 589 break; /* off path, no other match possible */ 590 591 if (n->node) { 592 /* 593 * The prefix is covered by this roa node 594 * therefor invalid unless roa_set matches. 595 */ 596 validity = ROA_INVALID; 597 598 /* AS_NONE can never match, so don't try */ 599 if (as != AS_NONE) { 600 if ((rs = set_match(n->set, as)) != NULL) { 601 if (plen == n->plen || plen <= rs->maxlen) 602 return ROA_VALID; 603 } 604 } 605 } 606 607 if (n->plen == 32) 608 break; /* can't go deeper */ 609 if (inet4isset(prefix, n->plen)) 610 n = n->trie[1]; 611 else 612 n = n->trie[0]; 613 } 614 615 return validity; 616 } 617 618 static int 619 trie_roa_check_v6(struct trie_head *th, struct in6_addr *prefix, u_int8_t plen, 620 u_int32_t as) 621 { 622 struct tentry_v6 *n; 623 struct roa_set *rs; 624 int validity = ROA_NOTFOUND; 625 626 /* ignore possible default route since it does not make sense */ 627 628 n = th->root_v6; 629 while (n) { 630 struct in6_addr mp; 631 632 if (n->plen > plen) 633 break; /* too specific, no match possible */ 634 635 inet6applymask(&mp, prefix, n->plen); 636 if (memcmp(&n->addr, &mp, sizeof(mp)) != 0) 637 break; /* off path, no other match possible */ 638 639 if (n->node) { 640 /* 641 * This prefix is covered by this roa node. 642 * Therefor invalid unless proven otherwise. 643 */ 644 validity = ROA_INVALID; 645 646 /* AS_NONE can never match, so don't try */ 647 if (as != AS_NONE) { 648 if ((rs = set_match(n->set, as)) != NULL) 649 if (plen == n->plen || plen <= rs->maxlen) 650 return ROA_VALID; 651 } 652 } 653 654 if (n->plen == 128) 655 break; /* can't go deeper */ 656 if (inet6isset(prefix, n->plen)) 657 n = n->trie[1]; 658 else 659 n = n->trie[0]; 660 } 661 662 return validity; 663 } 664 665 /* 666 * Do a ROA (Route Origin Validation) check. Look for elements in the trie 667 * which cover prefix "prefix/plen" and match the source-as as. 668 * AS_NONE can be used when the source-as is unknown (e.g. AS_SET). 669 * The check will then only return ROA_NOTFOUND or ROA_INVALID depending if 670 * the prefix is covered by the ROA. 671 */ 672 int 673 trie_roa_check(struct trie_head *th, struct bgpd_addr *prefix, u_int8_t plen, 674 u_int32_t as) 675 { 676 /* valid, invalid, unknown */ 677 switch (prefix->aid) { 678 case AID_INET: 679 return trie_roa_check_v4(th, &prefix->v4, plen, as); 680 case AID_INET6: 681 return trie_roa_check_v6(th, &prefix->v6, plen, as); 682 default: 683 /* anything else is not-found */ 684 return ROA_NOTFOUND; 685 } 686 } 687 688 static int 689 trie_equal_v4(struct tentry_v4 *a, struct tentry_v4 *b) 690 { 691 if (a == NULL && b == NULL) 692 return 1; 693 if (a == NULL || b == NULL) 694 return 0; 695 696 if (a->addr.s_addr != b->addr.s_addr || 697 a->plen != b->plen || 698 a->node != b->node || 699 a->plenmask.s_addr != b->plenmask.s_addr) 700 return 0; 701 702 if (set_equal(a->set, b->set) == 0) 703 return 0; 704 705 if (trie_equal_v4(a->trie[0], b->trie[0]) == 0 || 706 trie_equal_v4(a->trie[1], b->trie[1]) == 0) 707 return 0; 708 709 return 1; 710 } 711 712 static int 713 trie_equal_v6(struct tentry_v6 *a, struct tentry_v6 *b) 714 { 715 if (a == NULL && b == NULL) 716 return 1; 717 if (a == NULL || b == NULL) 718 return 0; 719 720 if (memcmp(&a->addr, &b->addr, sizeof(a->addr)) != 0 || 721 a->plen != b->plen || 722 a->node != b->node || 723 memcmp(&a->plenmask, &b->plenmask, sizeof(a->plenmask)) != 0) 724 return 0; 725 726 if (set_equal(a->set, b->set) == 0) 727 return 0; 728 729 if (trie_equal_v6(a->trie[0], b->trie[0]) == 0 || 730 trie_equal_v6(a->trie[1], b->trie[1]) == 0) 731 return 0; 732 733 return 1; 734 } 735 736 /* Compare two tries and return 1 if they are the same else 0. */ 737 int 738 trie_equal(struct trie_head *a, struct trie_head *b) 739 { 740 if (a->match_default_v4 != b->match_default_v4 || 741 a->match_default_v6 != b->match_default_v6) 742 return 0; 743 if (trie_equal_v4(a->root_v4, b->root_v4) == 0) 744 return 0; 745 if (trie_equal_v6(a->root_v6, b->root_v6) == 0) 746 return 0; 747 return 1; 748 } 749 750 /* debugging functions for printing the trie */ 751 static void 752 trie_dump_v4(struct tentry_v4 *n) 753 { 754 if (n == NULL) 755 return; 756 if (n->node) 757 fprintf(stderr, "%s/%u plenmask %08x\n", inet_ntoa(n->addr), 758 n->plen, n->plenmask.s_addr); 759 else 760 fprintf(stderr, " %s/%u\n", inet_ntoa(n->addr), n->plen); 761 762 trie_dump_v4(n->trie[0]); 763 trie_dump_v4(n->trie[1]); 764 } 765 766 static void 767 trie_dump_v6(struct tentry_v6 *n) 768 { 769 char buf[48]; 770 unsigned int i; 771 772 if (n == NULL) 773 return; 774 if (n->node) { 775 fprintf(stderr, "%s/%u plenmask ", 776 inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen); 777 for (i = 0; i < sizeof(n->plenmask); i++) 778 fprintf(stderr, "%02x", n->plenmask.s6_addr[i]); 779 fprintf(stderr, "\n"); 780 } else 781 fprintf(stderr, " %s/%u\n", 782 inet_ntop(AF_INET6, &n->addr, buf, sizeof(buf)), n->plen); 783 784 trie_dump_v6(n->trie[0]); 785 trie_dump_v6(n->trie[1]); 786 } 787 788 void 789 trie_dump(struct trie_head *th) 790 { 791 if (th->match_default_v4) 792 fprintf(stderr, "0.0.0.0/0 plenmask 0\n"); 793 trie_dump_v4(th->root_v4); 794 if (th->match_default_v6) 795 fprintf(stderr, "::/0 plenmask 0\n"); 796 trie_dump_v6(th->root_v6); 797 } 798