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