1 /*- 2 * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 /* 30 * Lookup table support for ipfw 31 * 32 * Lookup tables are implemented (at the moment) using the radix 33 * tree used for routing tables. Tables store key-value entries, where 34 * keys are network prefixes (addr/masklen), and values are integers. 35 * As a degenerate case we can interpret keys as 32-bit integers 36 * (with a /32 mask). 37 * 38 * The table is protected by the IPFW lock even for manipulation coming 39 * from userland, because operations are typically fast. 40 */ 41 42 #include "opt_ipfw.h" 43 #include "opt_inet.h" 44 #ifndef INET 45 #error IPFIREWALL requires INET. 46 #endif /* INET */ 47 #include "opt_inet6.h" 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/malloc.h> 52 #include <sys/kernel.h> 53 #include <sys/lock.h> 54 #include <sys/rwlock.h> 55 #include <sys/socket.h> 56 #include <sys/queue.h> 57 #include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 58 #include <net/radix.h> 59 #include <net/route.h> 60 #include <net/vnet.h> 61 62 #include <netinet/in.h> 63 #include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 64 #include <netinet/ip_fw.h> 65 66 #include <netpfil/ipfw/ip_fw_private.h> 67 68 #ifdef MAC 69 #include <security/mac/mac_framework.h> 70 #endif 71 72 static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); 73 74 struct table_entry { 75 struct radix_node rn[2]; 76 struct sockaddr_in addr, mask; 77 u_int32_t value; 78 }; 79 80 struct xaddr_iface { 81 uint8_t if_len; /* length of this struct */ 82 uint8_t pad[7]; /* Align name */ 83 char ifname[IF_NAMESIZE]; /* Interface name */ 84 }; 85 86 struct table_xentry { 87 struct radix_node rn[2]; 88 union { 89 #ifdef INET6 90 struct sockaddr_in6 addr6; 91 #endif 92 struct xaddr_iface iface; 93 } a; 94 union { 95 #ifdef INET6 96 struct sockaddr_in6 mask6; 97 #endif 98 struct xaddr_iface ifmask; 99 } m; 100 u_int32_t value; 101 }; 102 103 /* 104 * The radix code expects addr and mask to be array of bytes, 105 * with the first byte being the length of the array. rn_inithead 106 * is called with the offset in bits of the lookup key within the 107 * array. If we use a sockaddr_in as the underlying type, 108 * sin_len is conveniently located at offset 0, sin_addr is at 109 * offset 4 and normally aligned. 110 * But for portability, let's avoid assumption and make the code explicit 111 */ 112 #define KEY_LEN(v) *((uint8_t *)&(v)) 113 #define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 114 /* 115 * Do not require radix to compare more than actual IPv4/IPv6 address 116 */ 117 #define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t)) 118 #define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr)) 119 #define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname)) 120 121 #define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr)) 122 #define OFF_LEN_INET6 (8 * offsetof(struct sockaddr_in6, sin6_addr)) 123 #define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname)) 124 125 126 #ifdef INET6 127 static inline void 128 ipv6_writemask(struct in6_addr *addr6, uint8_t mask) 129 { 130 uint32_t *cp; 131 132 for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) 133 *cp++ = 0xFFFFFFFF; 134 *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); 135 } 136 #endif 137 138 int 139 ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 140 uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) 141 { 142 struct radix_node_head *rnh, **rnh_ptr; 143 struct table_entry *ent; 144 struct table_xentry *xent; 145 struct radix_node *rn; 146 in_addr_t addr; 147 int offset; 148 void *ent_ptr; 149 struct sockaddr *addr_ptr, *mask_ptr; 150 char c; 151 152 if (tbl >= V_fw_tables_max) 153 return (EINVAL); 154 155 switch (type) { 156 case IPFW_TABLE_CIDR: 157 if (plen == sizeof(in_addr_t)) { 158 #ifdef INET 159 /* IPv4 case */ 160 if (mlen > 32) 161 return (EINVAL); 162 ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); 163 ent->value = value; 164 /* Set 'total' structure length */ 165 KEY_LEN(ent->addr) = KEY_LEN_INET; 166 KEY_LEN(ent->mask) = KEY_LEN_INET; 167 /* Set offset of IPv4 address in bits */ 168 offset = OFF_LEN_INET; 169 ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 170 addr = *((in_addr_t *)paddr); 171 ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 172 /* Set pointers */ 173 rnh_ptr = &ch->tables[tbl]; 174 ent_ptr = ent; 175 addr_ptr = (struct sockaddr *)&ent->addr; 176 mask_ptr = (struct sockaddr *)&ent->mask; 177 #endif 178 #ifdef INET6 179 } else if (plen == sizeof(struct in6_addr)) { 180 /* IPv6 case */ 181 if (mlen > 128) 182 return (EINVAL); 183 xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); 184 xent->value = value; 185 /* Set 'total' structure length */ 186 KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; 187 KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; 188 /* Set offset of IPv6 address in bits */ 189 offset = OFF_LEN_INET6; 190 ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); 191 memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); 192 APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); 193 /* Set pointers */ 194 rnh_ptr = &ch->xtables[tbl]; 195 ent_ptr = xent; 196 addr_ptr = (struct sockaddr *)&xent->a.addr6; 197 mask_ptr = (struct sockaddr *)&xent->m.mask6; 198 #endif 199 } else { 200 /* Unknown CIDR type */ 201 return (EINVAL); 202 } 203 break; 204 205 case IPFW_TABLE_INTERFACE: 206 /* Check if string is terminated */ 207 c = ((char *)paddr)[IF_NAMESIZE - 1]; 208 ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; 209 if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) 210 return (EINVAL); 211 212 /* Include last \0 into comparison */ 213 mlen++; 214 215 xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); 216 xent->value = value; 217 /* Set 'total' structure length */ 218 KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen; 219 KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen; 220 /* Set offset of interface name in bits */ 221 offset = OFF_LEN_IFACE; 222 memcpy(xent->a.iface.ifname, paddr, mlen); 223 /* Assume direct match */ 224 /* TODO: Add interface pattern matching */ 225 #if 0 226 memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); 227 mask_ptr = (struct sockaddr *)&xent->m.ifmask; 228 #endif 229 /* Set pointers */ 230 rnh_ptr = &ch->xtables[tbl]; 231 ent_ptr = xent; 232 addr_ptr = (struct sockaddr *)&xent->a.iface; 233 mask_ptr = NULL; 234 break; 235 236 default: 237 return (EINVAL); 238 } 239 240 IPFW_WLOCK(ch); 241 242 /* Check if tabletype is valid */ 243 if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) { 244 IPFW_WUNLOCK(ch); 245 free(ent_ptr, M_IPFW_TBL); 246 return (EINVAL); 247 } 248 249 /* Check if radix tree exists */ 250 if ((rnh = *rnh_ptr) == NULL) { 251 IPFW_WUNLOCK(ch); 252 /* Create radix for a new table */ 253 if (!rn_inithead((void **)&rnh, offset)) { 254 free(ent_ptr, M_IPFW_TBL); 255 return (ENOMEM); 256 } 257 258 IPFW_WLOCK(ch); 259 if (*rnh_ptr != NULL) { 260 /* Tree is already attached by other thread */ 261 rn_detachhead((void **)&rnh); 262 rnh = *rnh_ptr; 263 /* Check table type another time */ 264 if (ch->tabletype[tbl] != type) { 265 IPFW_WUNLOCK(ch); 266 free(ent_ptr, M_IPFW_TBL); 267 return (EINVAL); 268 } 269 } else { 270 *rnh_ptr = rnh; 271 /* 272 * Set table type. It can be set already 273 * (if we have IPv6-only table) but setting 274 * it another time does not hurt 275 */ 276 ch->tabletype[tbl] = type; 277 } 278 } 279 280 rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); 281 IPFW_WUNLOCK(ch); 282 283 if (rn == NULL) { 284 free(ent_ptr, M_IPFW_TBL); 285 return (EEXIST); 286 } 287 return (0); 288 } 289 290 int 291 ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 292 uint8_t plen, uint8_t mlen, uint8_t type) 293 { 294 struct radix_node_head *rnh, **rnh_ptr; 295 struct table_entry *ent; 296 in_addr_t addr; 297 struct sockaddr_in sa, mask; 298 struct sockaddr *sa_ptr, *mask_ptr; 299 char c; 300 301 if (tbl >= V_fw_tables_max) 302 return (EINVAL); 303 304 switch (type) { 305 case IPFW_TABLE_CIDR: 306 if (plen == sizeof(in_addr_t)) { 307 /* Set 'total' structure length */ 308 KEY_LEN(sa) = KEY_LEN_INET; 309 KEY_LEN(mask) = KEY_LEN_INET; 310 mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 311 addr = *((in_addr_t *)paddr); 312 sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 313 rnh_ptr = &ch->tables[tbl]; 314 sa_ptr = (struct sockaddr *)&sa; 315 mask_ptr = (struct sockaddr *)&mask; 316 #ifdef INET6 317 } else if (plen == sizeof(struct in6_addr)) { 318 /* IPv6 case */ 319 if (mlen > 128) 320 return (EINVAL); 321 struct sockaddr_in6 sa6, mask6; 322 memset(&sa6, 0, sizeof(struct sockaddr_in6)); 323 memset(&mask6, 0, sizeof(struct sockaddr_in6)); 324 /* Set 'total' structure length */ 325 KEY_LEN(sa6) = KEY_LEN_INET6; 326 KEY_LEN(mask6) = KEY_LEN_INET6; 327 ipv6_writemask(&mask6.sin6_addr, mlen); 328 memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); 329 APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); 330 rnh_ptr = &ch->xtables[tbl]; 331 sa_ptr = (struct sockaddr *)&sa6; 332 mask_ptr = (struct sockaddr *)&mask6; 333 #endif 334 } else { 335 /* Unknown CIDR type */ 336 return (EINVAL); 337 } 338 break; 339 340 case IPFW_TABLE_INTERFACE: 341 /* Check if string is terminated */ 342 c = ((char *)paddr)[IF_NAMESIZE - 1]; 343 ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; 344 if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) 345 return (EINVAL); 346 347 struct xaddr_iface ifname, ifmask; 348 memset(&ifname, 0, sizeof(ifname)); 349 350 /* Include last \0 into comparison */ 351 mlen++; 352 353 /* Set 'total' structure length */ 354 KEY_LEN(ifname) = KEY_LEN_IFACE + mlen; 355 KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen; 356 /* Assume direct match */ 357 /* FIXME: Add interface pattern matching */ 358 #if 0 359 memset(ifmask.ifname, 0xFF, IF_NAMESIZE); 360 mask_ptr = (struct sockaddr *)&ifmask; 361 #endif 362 mask_ptr = NULL; 363 memcpy(ifname.ifname, paddr, mlen); 364 /* Set pointers */ 365 rnh_ptr = &ch->xtables[tbl]; 366 sa_ptr = (struct sockaddr *)&ifname; 367 368 break; 369 370 default: 371 return (EINVAL); 372 } 373 374 IPFW_WLOCK(ch); 375 if ((rnh = *rnh_ptr) == NULL) { 376 IPFW_WUNLOCK(ch); 377 return (ESRCH); 378 } 379 380 if (ch->tabletype[tbl] != type) { 381 IPFW_WUNLOCK(ch); 382 return (EINVAL); 383 } 384 385 ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); 386 IPFW_WUNLOCK(ch); 387 388 if (ent == NULL) 389 return (ESRCH); 390 391 free(ent, M_IPFW_TBL); 392 return (0); 393 } 394 395 static int 396 flush_table_entry(struct radix_node *rn, void *arg) 397 { 398 struct radix_node_head * const rnh = arg; 399 struct table_entry *ent; 400 401 ent = (struct table_entry *) 402 rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 403 if (ent != NULL) 404 free(ent, M_IPFW_TBL); 405 return (0); 406 } 407 408 int 409 ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 410 { 411 struct radix_node_head *rnh, *xrnh; 412 413 if (tbl >= V_fw_tables_max) 414 return (EINVAL); 415 416 /* 417 * We free both (IPv4 and extended) radix trees and 418 * clear table type here to permit table to be reused 419 * for different type without module reload 420 */ 421 422 IPFW_WLOCK(ch); 423 /* Set IPv4 table pointer to zero */ 424 if ((rnh = ch->tables[tbl]) != NULL) 425 ch->tables[tbl] = NULL; 426 /* Set extended table pointer to zero */ 427 if ((xrnh = ch->xtables[tbl]) != NULL) 428 ch->xtables[tbl] = NULL; 429 /* Zero table type */ 430 ch->tabletype[tbl] = 0; 431 IPFW_WUNLOCK(ch); 432 433 if (rnh != NULL) { 434 rnh->rnh_walktree(rnh, flush_table_entry, rnh); 435 rn_detachhead((void **)&rnh); 436 } 437 438 if (xrnh != NULL) { 439 xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh); 440 rn_detachhead((void **)&xrnh); 441 } 442 443 return (0); 444 } 445 446 void 447 ipfw_destroy_tables(struct ip_fw_chain *ch) 448 { 449 uint16_t tbl; 450 451 /* Flush all tables */ 452 for (tbl = 0; tbl < V_fw_tables_max; tbl++) 453 ipfw_flush_table(ch, tbl); 454 455 /* Free pointers itself */ 456 free(ch->tables, M_IPFW); 457 free(ch->xtables, M_IPFW); 458 free(ch->tabletype, M_IPFW); 459 } 460 461 int 462 ipfw_init_tables(struct ip_fw_chain *ch) 463 { 464 /* Allocate pointers */ 465 ch->tables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 466 ch->xtables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 467 ch->tabletype = malloc(V_fw_tables_max * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); 468 return (0); 469 } 470 471 int 472 ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 473 { 474 struct radix_node_head **tables, **xtables, *rnh; 475 struct radix_node_head **tables_old, **xtables_old; 476 uint8_t *tabletype, *tabletype_old; 477 unsigned int ntables_old, tbl; 478 479 /* Check new value for validity */ 480 if (ntables > IPFW_TABLES_MAX) 481 ntables = IPFW_TABLES_MAX; 482 483 /* Allocate new pointers */ 484 tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 485 xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 486 tabletype = malloc(ntables * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); 487 488 IPFW_WLOCK(ch); 489 490 tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 491 492 /* Copy old table pointers */ 493 memcpy(tables, ch->tables, sizeof(void *) * tbl); 494 memcpy(xtables, ch->xtables, sizeof(void *) * tbl); 495 memcpy(tabletype, ch->tabletype, sizeof(uint8_t) * tbl); 496 497 /* Change pointers and number of tables */ 498 tables_old = ch->tables; 499 xtables_old = ch->xtables; 500 tabletype_old = ch->tabletype; 501 ch->tables = tables; 502 ch->xtables = xtables; 503 ch->tabletype = tabletype; 504 505 ntables_old = V_fw_tables_max; 506 V_fw_tables_max = ntables; 507 508 IPFW_WUNLOCK(ch); 509 510 /* Check if we need to destroy radix trees */ 511 if (ntables < ntables_old) { 512 for (tbl = ntables; tbl < ntables_old; tbl++) { 513 if ((rnh = tables_old[tbl]) != NULL) { 514 rnh->rnh_walktree(rnh, flush_table_entry, rnh); 515 rn_detachhead((void **)&rnh); 516 } 517 518 if ((rnh = xtables_old[tbl]) != NULL) { 519 rnh->rnh_walktree(rnh, flush_table_entry, rnh); 520 rn_detachhead((void **)&rnh); 521 } 522 } 523 } 524 525 /* Free old pointers */ 526 free(tables_old, M_IPFW); 527 free(xtables_old, M_IPFW); 528 free(tabletype_old, M_IPFW); 529 530 return (0); 531 } 532 533 int 534 ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 535 uint32_t *val) 536 { 537 struct radix_node_head *rnh; 538 struct table_entry *ent; 539 struct sockaddr_in sa; 540 541 if (tbl >= V_fw_tables_max) 542 return (0); 543 if ((rnh = ch->tables[tbl]) == NULL) 544 return (0); 545 KEY_LEN(sa) = KEY_LEN_INET; 546 sa.sin_addr.s_addr = addr; 547 ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh)); 548 if (ent != NULL) { 549 *val = ent->value; 550 return (1); 551 } 552 return (0); 553 } 554 555 int 556 ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 557 uint32_t *val, int type) 558 { 559 struct radix_node_head *rnh; 560 struct table_xentry *xent; 561 struct sockaddr_in6 sa6; 562 struct xaddr_iface iface; 563 564 if (tbl >= V_fw_tables_max) 565 return (0); 566 if ((rnh = ch->xtables[tbl]) == NULL) 567 return (0); 568 569 switch (type) { 570 case IPFW_TABLE_CIDR: 571 KEY_LEN(sa6) = KEY_LEN_INET6; 572 memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); 573 xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh)); 574 break; 575 576 case IPFW_TABLE_INTERFACE: 577 KEY_LEN(iface) = KEY_LEN_IFACE + 578 strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; 579 /* Assume direct match */ 580 /* FIXME: Add interface pattern matching */ 581 xent = (struct table_xentry *)(rnh->rnh_matchaddr(&iface, rnh)); 582 break; 583 584 default: 585 return (0); 586 } 587 588 if (xent != NULL) { 589 *val = xent->value; 590 return (1); 591 } 592 return (0); 593 } 594 595 static int 596 count_table_entry(struct radix_node *rn, void *arg) 597 { 598 u_int32_t * const cnt = arg; 599 600 (*cnt)++; 601 return (0); 602 } 603 604 int 605 ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 606 { 607 struct radix_node_head *rnh; 608 609 if (tbl >= V_fw_tables_max) 610 return (EINVAL); 611 *cnt = 0; 612 if ((rnh = ch->tables[tbl]) == NULL) 613 return (0); 614 rnh->rnh_walktree(rnh, count_table_entry, cnt); 615 return (0); 616 } 617 618 static int 619 dump_table_entry(struct radix_node *rn, void *arg) 620 { 621 struct table_entry * const n = (struct table_entry *)rn; 622 ipfw_table * const tbl = arg; 623 ipfw_table_entry *ent; 624 625 if (tbl->cnt == tbl->size) 626 return (1); 627 ent = &tbl->ent[tbl->cnt]; 628 ent->tbl = tbl->tbl; 629 if (in_nullhost(n->mask.sin_addr)) 630 ent->masklen = 0; 631 else 632 ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 633 ent->addr = n->addr.sin_addr.s_addr; 634 ent->value = n->value; 635 tbl->cnt++; 636 return (0); 637 } 638 639 int 640 ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 641 { 642 struct radix_node_head *rnh; 643 644 if (tbl->tbl >= V_fw_tables_max) 645 return (EINVAL); 646 tbl->cnt = 0; 647 if ((rnh = ch->tables[tbl->tbl]) == NULL) 648 return (0); 649 rnh->rnh_walktree(rnh, dump_table_entry, tbl); 650 return (0); 651 } 652 653 static int 654 count_table_xentry(struct radix_node *rn, void *arg) 655 { 656 uint32_t * const cnt = arg; 657 658 (*cnt) += sizeof(ipfw_table_xentry); 659 return (0); 660 } 661 662 int 663 ipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 664 { 665 struct radix_node_head *rnh; 666 667 if (tbl >= V_fw_tables_max) 668 return (EINVAL); 669 *cnt = 0; 670 if ((rnh = ch->tables[tbl]) != NULL) 671 rnh->rnh_walktree(rnh, count_table_xentry, cnt); 672 if ((rnh = ch->xtables[tbl]) != NULL) 673 rnh->rnh_walktree(rnh, count_table_xentry, cnt); 674 /* Return zero if table is empty */ 675 if (*cnt > 0) 676 (*cnt) += sizeof(ipfw_xtable); 677 return (0); 678 } 679 680 681 static int 682 dump_table_xentry_base(struct radix_node *rn, void *arg) 683 { 684 struct table_entry * const n = (struct table_entry *)rn; 685 ipfw_xtable * const tbl = arg; 686 ipfw_table_xentry *xent; 687 688 /* Out of memory, returning */ 689 if (tbl->cnt == tbl->size) 690 return (1); 691 xent = &tbl->xent[tbl->cnt]; 692 xent->len = sizeof(ipfw_table_xentry); 693 xent->tbl = tbl->tbl; 694 if (in_nullhost(n->mask.sin_addr)) 695 xent->masklen = 0; 696 else 697 xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 698 /* Save IPv4 address as deprecated IPv6 compatible */ 699 xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr; 700 xent->flags = IPFW_TCF_INET; 701 xent->value = n->value; 702 tbl->cnt++; 703 return (0); 704 } 705 706 static int 707 dump_table_xentry_extended(struct radix_node *rn, void *arg) 708 { 709 struct table_xentry * const n = (struct table_xentry *)rn; 710 ipfw_xtable * const tbl = arg; 711 ipfw_table_xentry *xent; 712 #ifdef INET6 713 int i; 714 uint32_t *v; 715 #endif 716 /* Out of memory, returning */ 717 if (tbl->cnt == tbl->size) 718 return (1); 719 xent = &tbl->xent[tbl->cnt]; 720 xent->len = sizeof(ipfw_table_xentry); 721 xent->tbl = tbl->tbl; 722 723 switch (tbl->type) { 724 #ifdef INET6 725 case IPFW_TABLE_CIDR: 726 /* Count IPv6 mask */ 727 v = (uint32_t *)&n->m.mask6.sin6_addr; 728 for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++) 729 xent->masklen += bitcount32(*v); 730 memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr)); 731 break; 732 #endif 733 case IPFW_TABLE_INTERFACE: 734 /* Assume exact mask */ 735 xent->masklen = 8 * IF_NAMESIZE; 736 memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE); 737 break; 738 739 default: 740 /* unknown, skip entry */ 741 return (0); 742 } 743 744 xent->value = n->value; 745 tbl->cnt++; 746 return (0); 747 } 748 749 int 750 ipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl) 751 { 752 struct radix_node_head *rnh; 753 754 if (tbl->tbl >= V_fw_tables_max) 755 return (EINVAL); 756 tbl->cnt = 0; 757 tbl->type = ch->tabletype[tbl->tbl]; 758 if ((rnh = ch->tables[tbl->tbl]) != NULL) 759 rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl); 760 if ((rnh = ch->xtables[tbl->tbl]) != NULL) 761 rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl); 762 return (0); 763 } 764 765 /* end of file */ 766