ip_fw_table.c revision 238265
1200590Sluigi/*- 2200673Sru * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 3200590Sluigi * 4200590Sluigi * Redistribution and use in source and binary forms, with or without 5200590Sluigi * modification, are permitted provided that the following conditions 6200590Sluigi * are met: 7200590Sluigi * 1. Redistributions of source code must retain the above copyright 8200590Sluigi * notice, this list of conditions and the following disclaimer. 9200590Sluigi * 2. Redistributions in binary form must reproduce the above copyright 10200590Sluigi * notice, this list of conditions and the following disclaimer in the 11200590Sluigi * documentation and/or other materials provided with the distribution. 12200590Sluigi * 13200590Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14200590Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15200590Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16200590Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17200590Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18200590Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19200590Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20200590Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21200590Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22200590Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23200590Sluigi * SUCH DAMAGE. 24200590Sluigi */ 25200590Sluigi 26200590Sluigi#include <sys/cdefs.h> 27200590Sluigi__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_table.c 238265 2012-07-08 21:13:04Z melifaro $"); 28200590Sluigi 29200590Sluigi/* 30200601Sluigi * Lookup table support for ipfw 31200601Sluigi * 32200601Sluigi * Lookup tables are implemented (at the moment) using the radix 33200601Sluigi * tree used for routing tables. Tables store key-value entries, where 34200601Sluigi * keys are network prefixes (addr/masklen), and values are integers. 35200601Sluigi * As a degenerate case we can interpret keys as 32-bit integers 36200601Sluigi * (with a /32 mask). 37200838Sluigi * 38200838Sluigi * The table is protected by the IPFW lock even for manipulation coming 39200838Sluigi * from userland, because operations are typically fast. 40200590Sluigi */ 41200590Sluigi 42225518Sjhb#include "opt_ipfw.h" 43200590Sluigi#include "opt_inet.h" 44200590Sluigi#ifndef INET 45200590Sluigi#error IPFIREWALL requires INET. 46200590Sluigi#endif /* INET */ 47200590Sluigi#include "opt_inet6.h" 48200590Sluigi 49200590Sluigi#include <sys/param.h> 50200590Sluigi#include <sys/systm.h> 51200590Sluigi#include <sys/malloc.h> 52200590Sluigi#include <sys/kernel.h> 53200590Sluigi#include <sys/lock.h> 54200590Sluigi#include <sys/rwlock.h> 55200590Sluigi#include <sys/socket.h> 56200590Sluigi#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 57200590Sluigi#include <net/radix.h> 58200590Sluigi#include <net/route.h> 59200590Sluigi#include <net/vnet.h> 60200590Sluigi 61200590Sluigi#include <netinet/in.h> 62201732Sluigi#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 63200590Sluigi#include <netinet/ip_fw.h> 64204591Sluigi#include <sys/queue.h> /* LIST_HEAD */ 65200590Sluigi#include <netinet/ipfw/ip_fw_private.h> 66200590Sluigi 67200590Sluigi#ifdef MAC 68200590Sluigi#include <security/mac/mac_framework.h> 69200590Sluigi#endif 70200590Sluigi 71227293Sedstatic MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); 72200590Sluigi 73200590Sluigistruct table_entry { 74200590Sluigi struct radix_node rn[2]; 75200590Sluigi struct sockaddr_in addr, mask; 76200590Sluigi u_int32_t value; 77200590Sluigi}; 78200590Sluigi 79232865Smelifarostruct xaddr_iface { 80232865Smelifaro uint8_t if_len; /* length of this struct */ 81232865Smelifaro uint8_t pad[7]; /* Align name */ 82232865Smelifaro char ifname[IF_NAMESIZE]; /* Interface name */ 83232865Smelifaro}; 84232865Smelifaro 85232865Smelifarostruct table_xentry { 86232865Smelifaro struct radix_node rn[2]; 87232865Smelifaro union { 88232865Smelifaro#ifdef INET6 89232865Smelifaro struct sockaddr_in6 addr6; 90232865Smelifaro#endif 91232865Smelifaro struct xaddr_iface iface; 92232865Smelifaro } a; 93232865Smelifaro union { 94232865Smelifaro#ifdef INET6 95232865Smelifaro struct sockaddr_in6 mask6; 96232865Smelifaro#endif 97232865Smelifaro struct xaddr_iface ifmask; 98232865Smelifaro } m; 99232865Smelifaro u_int32_t value; 100232865Smelifaro}; 101232865Smelifaro 102201120Sluigi/* 103201120Sluigi * The radix code expects addr and mask to be array of bytes, 104201120Sluigi * with the first byte being the length of the array. rn_inithead 105201120Sluigi * is called with the offset in bits of the lookup key within the 106201120Sluigi * array. If we use a sockaddr_in as the underlying type, 107201120Sluigi * sin_len is conveniently located at offset 0, sin_addr is at 108201120Sluigi * offset 4 and normally aligned. 109201120Sluigi * But for portability, let's avoid assumption and make the code explicit 110201120Sluigi */ 111201120Sluigi#define KEY_LEN(v) *((uint8_t *)&(v)) 112201120Sluigi#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 113232865Smelifaro/* 114232865Smelifaro * Do not require radix to compare more than actual IPv4/IPv6 address 115232865Smelifaro */ 116232865Smelifaro#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t)) 117232865Smelifaro#define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr)) 118232865Smelifaro#define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname)) 119201120Sluigi 120232865Smelifaro#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr)) 121232865Smelifaro#define OFF_LEN_INET6 (8 * offsetof(struct sockaddr_in6, sin6_addr)) 122232865Smelifaro#define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname)) 123232865Smelifaro 124232865Smelifaro 125232865Smelifarostatic inline void 126232865Smelifaroipv6_writemask(struct in6_addr *addr6, uint8_t mask) 127232865Smelifaro{ 128232865Smelifaro uint32_t *cp; 129232865Smelifaro 130232865Smelifaro for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) 131232865Smelifaro *cp++ = 0xFFFFFFFF; 132232865Smelifaro *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); 133232865Smelifaro} 134232865Smelifaro 135200590Sluigiint 136232865Smelifaroipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 137232865Smelifaro uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) 138200590Sluigi{ 139232865Smelifaro struct radix_node_head *rnh, **rnh_ptr; 140200590Sluigi struct table_entry *ent; 141232865Smelifaro struct table_xentry *xent; 142200590Sluigi struct radix_node *rn; 143232865Smelifaro in_addr_t addr; 144232865Smelifaro int offset; 145232865Smelifaro void *ent_ptr; 146232865Smelifaro struct sockaddr *addr_ptr, *mask_ptr; 147232865Smelifaro char c; 148200590Sluigi 149232865Smelifaro if (tbl >= V_fw_tables_max) 150200590Sluigi return (EINVAL); 151232865Smelifaro 152232865Smelifaro switch (type) { 153232865Smelifaro case IPFW_TABLE_CIDR: 154232865Smelifaro if (plen == sizeof(in_addr_t)) { 155232865Smelifaro#ifdef INET 156236819Smelifaro /* IPv4 case */ 157236819Smelifaro if (mlen > 32) 158236819Smelifaro return (EINVAL); 159232865Smelifaro ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); 160232865Smelifaro ent->value = value; 161232865Smelifaro /* Set 'total' structure length */ 162232865Smelifaro KEY_LEN(ent->addr) = KEY_LEN_INET; 163232865Smelifaro KEY_LEN(ent->mask) = KEY_LEN_INET; 164232865Smelifaro /* Set offset of IPv4 address in bits */ 165232865Smelifaro offset = OFF_LEN_INET; 166232865Smelifaro ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 167232865Smelifaro addr = *((in_addr_t *)paddr); 168232865Smelifaro ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 169232865Smelifaro /* Set pointers */ 170232865Smelifaro rnh_ptr = &ch->tables[tbl]; 171232865Smelifaro ent_ptr = ent; 172232865Smelifaro addr_ptr = (struct sockaddr *)&ent->addr; 173232865Smelifaro mask_ptr = (struct sockaddr *)&ent->mask; 174232865Smelifaro#endif 175232865Smelifaro#ifdef INET6 176232865Smelifaro } else if (plen == sizeof(struct in6_addr)) { 177232865Smelifaro /* IPv6 case */ 178232865Smelifaro if (mlen > 128) 179232865Smelifaro return (EINVAL); 180232865Smelifaro xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); 181232865Smelifaro xent->value = value; 182232865Smelifaro /* Set 'total' structure length */ 183232865Smelifaro KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; 184232865Smelifaro KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; 185232865Smelifaro /* Set offset of IPv6 address in bits */ 186232865Smelifaro offset = OFF_LEN_INET6; 187232865Smelifaro ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); 188232865Smelifaro memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); 189232865Smelifaro APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); 190232865Smelifaro /* Set pointers */ 191232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 192232865Smelifaro ent_ptr = xent; 193232865Smelifaro addr_ptr = (struct sockaddr *)&xent->a.addr6; 194232865Smelifaro mask_ptr = (struct sockaddr *)&xent->m.mask6; 195232865Smelifaro#endif 196232865Smelifaro } else { 197232865Smelifaro /* Unknown CIDR type */ 198232865Smelifaro return (EINVAL); 199232865Smelifaro } 200232865Smelifaro break; 201232865Smelifaro 202232865Smelifaro case IPFW_TABLE_INTERFACE: 203232865Smelifaro /* Check if string is terminated */ 204232865Smelifaro c = ((char *)paddr)[IF_NAMESIZE - 1]; 205232865Smelifaro ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; 206232865Smelifaro if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) 207232865Smelifaro return (EINVAL); 208232865Smelifaro 209232865Smelifaro /* Include last \0 into comparison */ 210232865Smelifaro mlen++; 211232865Smelifaro 212232865Smelifaro xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); 213232865Smelifaro xent->value = value; 214232865Smelifaro /* Set 'total' structure length */ 215232865Smelifaro KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen; 216232865Smelifaro KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen; 217232865Smelifaro /* Set offset of interface name in bits */ 218232865Smelifaro offset = OFF_LEN_IFACE; 219232865Smelifaro memcpy(xent->a.iface.ifname, paddr, mlen); 220232865Smelifaro /* Assume direct match */ 221232865Smelifaro /* TODO: Add interface pattern matching */ 222232865Smelifaro#if 0 223232865Smelifaro memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); 224232865Smelifaro mask_ptr = (struct sockaddr *)&xent->m.ifmask; 225232865Smelifaro#endif 226232865Smelifaro /* Set pointers */ 227232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 228232865Smelifaro ent_ptr = xent; 229232865Smelifaro addr_ptr = (struct sockaddr *)&xent->a.iface; 230232865Smelifaro mask_ptr = NULL; 231232865Smelifaro break; 232232865Smelifaro 233232865Smelifaro default: 234232865Smelifaro return (EINVAL); 235232865Smelifaro } 236232865Smelifaro 237200590Sluigi IPFW_WLOCK(ch); 238232865Smelifaro 239232865Smelifaro /* Check if tabletype is valid */ 240232865Smelifaro if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) { 241232865Smelifaro IPFW_WUNLOCK(ch); 242232865Smelifaro free(ent_ptr, M_IPFW_TBL); 243232865Smelifaro return (EINVAL); 244232865Smelifaro } 245232865Smelifaro 246232865Smelifaro /* Check if radix tree exists */ 247232865Smelifaro if ((rnh = *rnh_ptr) == NULL) { 248232865Smelifaro IPFW_WUNLOCK(ch); 249232865Smelifaro /* Create radix for a new table */ 250232865Smelifaro if (!rn_inithead((void **)&rnh, offset)) { 251232865Smelifaro free(ent_ptr, M_IPFW_TBL); 252232865Smelifaro return (ENOMEM); 253232865Smelifaro } 254232865Smelifaro 255232865Smelifaro IPFW_WLOCK(ch); 256232865Smelifaro if (*rnh_ptr != NULL) { 257232865Smelifaro /* Tree is already attached by other thread */ 258232865Smelifaro rn_detachhead((void **)&rnh); 259232865Smelifaro rnh = *rnh_ptr; 260232865Smelifaro /* Check table type another time */ 261232865Smelifaro if (ch->tabletype[tbl] != type) { 262232865Smelifaro IPFW_WUNLOCK(ch); 263232865Smelifaro free(ent_ptr, M_IPFW_TBL); 264232865Smelifaro return (EINVAL); 265232865Smelifaro } 266232865Smelifaro } else { 267232865Smelifaro *rnh_ptr = rnh; 268232865Smelifaro /* 269232865Smelifaro * Set table type. It can be set already 270232865Smelifaro * (if we have IPv6-only table) but setting 271232865Smelifaro * it another time does not hurt 272232865Smelifaro */ 273232865Smelifaro ch->tabletype[tbl] = type; 274232865Smelifaro } 275232865Smelifaro } 276232865Smelifaro 277232865Smelifaro rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); 278232865Smelifaro IPFW_WUNLOCK(ch); 279232865Smelifaro 280200590Sluigi if (rn == NULL) { 281232865Smelifaro free(ent_ptr, M_IPFW_TBL); 282200590Sluigi return (EEXIST); 283200590Sluigi } 284200590Sluigi return (0); 285200590Sluigi} 286200590Sluigi 287200590Sluigiint 288232865Smelifaroipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 289232865Smelifaro uint8_t plen, uint8_t mlen, uint8_t type) 290200590Sluigi{ 291232865Smelifaro struct radix_node_head *rnh, **rnh_ptr; 292200590Sluigi struct table_entry *ent; 293232865Smelifaro in_addr_t addr; 294200590Sluigi struct sockaddr_in sa, mask; 295232865Smelifaro struct sockaddr *sa_ptr, *mask_ptr; 296232865Smelifaro char c; 297200590Sluigi 298232865Smelifaro if (tbl >= V_fw_tables_max) 299200590Sluigi return (EINVAL); 300232865Smelifaro 301232865Smelifaro switch (type) { 302232865Smelifaro case IPFW_TABLE_CIDR: 303232865Smelifaro if (plen == sizeof(in_addr_t)) { 304232865Smelifaro /* Set 'total' structure length */ 305232865Smelifaro KEY_LEN(sa) = KEY_LEN_INET; 306232865Smelifaro KEY_LEN(mask) = KEY_LEN_INET; 307232865Smelifaro mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 308232865Smelifaro addr = *((in_addr_t *)paddr); 309232865Smelifaro sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 310232865Smelifaro rnh_ptr = &ch->tables[tbl]; 311232865Smelifaro sa_ptr = (struct sockaddr *)&sa; 312232865Smelifaro mask_ptr = (struct sockaddr *)&mask; 313232865Smelifaro#ifdef INET6 314232865Smelifaro } else if (plen == sizeof(struct in6_addr)) { 315232865Smelifaro /* IPv6 case */ 316232865Smelifaro if (mlen > 128) 317232865Smelifaro return (EINVAL); 318232865Smelifaro struct sockaddr_in6 sa6, mask6; 319232865Smelifaro memset(&sa6, 0, sizeof(struct sockaddr_in6)); 320232865Smelifaro memset(&mask6, 0, sizeof(struct sockaddr_in6)); 321232865Smelifaro /* Set 'total' structure length */ 322232865Smelifaro KEY_LEN(sa6) = KEY_LEN_INET6; 323232865Smelifaro KEY_LEN(mask6) = KEY_LEN_INET6; 324232865Smelifaro ipv6_writemask(&mask6.sin6_addr, mlen); 325232865Smelifaro memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); 326232865Smelifaro APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); 327232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 328232865Smelifaro sa_ptr = (struct sockaddr *)&sa6; 329232865Smelifaro mask_ptr = (struct sockaddr *)&mask6; 330232865Smelifaro#endif 331232865Smelifaro } else { 332232865Smelifaro /* Unknown CIDR type */ 333232865Smelifaro return (EINVAL); 334232865Smelifaro } 335232865Smelifaro break; 336232865Smelifaro 337232865Smelifaro case IPFW_TABLE_INTERFACE: 338232865Smelifaro /* Check if string is terminated */ 339232865Smelifaro c = ((char *)paddr)[IF_NAMESIZE - 1]; 340232865Smelifaro ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; 341232865Smelifaro if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) 342232865Smelifaro return (EINVAL); 343232865Smelifaro 344232865Smelifaro struct xaddr_iface ifname, ifmask; 345232865Smelifaro memset(&ifname, 0, sizeof(ifname)); 346232865Smelifaro 347238265Smelifaro /* Include last \0 into comparison */ 348238265Smelifaro mlen++; 349238265Smelifaro 350232865Smelifaro /* Set 'total' structure length */ 351238265Smelifaro KEY_LEN(ifname) = KEY_LEN_IFACE + mlen; 352238265Smelifaro KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen; 353232865Smelifaro /* Assume direct match */ 354232865Smelifaro /* FIXME: Add interface pattern matching */ 355232865Smelifaro#if 0 356232865Smelifaro memset(ifmask.ifname, 0xFF, IF_NAMESIZE); 357232865Smelifaro mask_ptr = (struct sockaddr *)&ifmask; 358232865Smelifaro#endif 359232865Smelifaro mask_ptr = NULL; 360232865Smelifaro memcpy(ifname.ifname, paddr, mlen); 361232865Smelifaro /* Set pointers */ 362232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 363232865Smelifaro sa_ptr = (struct sockaddr *)&ifname; 364232865Smelifaro 365232865Smelifaro break; 366232865Smelifaro 367232865Smelifaro default: 368232865Smelifaro return (EINVAL); 369232865Smelifaro } 370232865Smelifaro 371200590Sluigi IPFW_WLOCK(ch); 372232865Smelifaro if ((rnh = *rnh_ptr) == NULL) { 373200590Sluigi IPFW_WUNLOCK(ch); 374200590Sluigi return (ESRCH); 375200590Sluigi } 376232865Smelifaro 377232865Smelifaro if (ch->tabletype[tbl] != type) { 378232865Smelifaro IPFW_WUNLOCK(ch); 379232865Smelifaro return (EINVAL); 380232865Smelifaro } 381232865Smelifaro 382232865Smelifaro ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); 383200590Sluigi IPFW_WUNLOCK(ch); 384232865Smelifaro 385232865Smelifaro if (ent == NULL) 386232865Smelifaro return (ESRCH); 387232865Smelifaro 388200590Sluigi free(ent, M_IPFW_TBL); 389200590Sluigi return (0); 390200590Sluigi} 391200590Sluigi 392200590Sluigistatic int 393200590Sluigiflush_table_entry(struct radix_node *rn, void *arg) 394200590Sluigi{ 395200590Sluigi struct radix_node_head * const rnh = arg; 396200590Sluigi struct table_entry *ent; 397200590Sluigi 398200590Sluigi ent = (struct table_entry *) 399200590Sluigi rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 400200590Sluigi if (ent != NULL) 401200590Sluigi free(ent, M_IPFW_TBL); 402200590Sluigi return (0); 403200590Sluigi} 404200590Sluigi 405200590Sluigiint 406200590Sluigiipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 407200590Sluigi{ 408232865Smelifaro struct radix_node_head *rnh, *xrnh; 409200590Sluigi 410232865Smelifaro if (tbl >= V_fw_tables_max) 411232865Smelifaro return (EINVAL); 412200590Sluigi 413232865Smelifaro /* 414232865Smelifaro * We free both (IPv4 and extended) radix trees and 415232865Smelifaro * clear table type here to permit table to be reused 416232865Smelifaro * for different type without module reload 417232865Smelifaro */ 418232865Smelifaro 419232865Smelifaro IPFW_WLOCK(ch); 420232865Smelifaro /* Set IPv4 table pointer to zero */ 421232865Smelifaro if ((rnh = ch->tables[tbl]) != NULL) 422232865Smelifaro ch->tables[tbl] = NULL; 423232865Smelifaro /* Set extended table pointer to zero */ 424232865Smelifaro if ((xrnh = ch->xtables[tbl]) != NULL) 425232865Smelifaro ch->xtables[tbl] = NULL; 426232865Smelifaro /* Zero table type */ 427232865Smelifaro ch->tabletype[tbl] = 0; 428232865Smelifaro IPFW_WUNLOCK(ch); 429232865Smelifaro 430232865Smelifaro if (rnh != NULL) { 431232865Smelifaro rnh->rnh_walktree(rnh, flush_table_entry, rnh); 432232865Smelifaro rn_detachhead((void **)&rnh); 433232865Smelifaro } 434232865Smelifaro 435232865Smelifaro if (xrnh != NULL) { 436232865Smelifaro xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh); 437232865Smelifaro rn_detachhead((void **)&xrnh); 438232865Smelifaro } 439232865Smelifaro 440200590Sluigi return (0); 441200590Sluigi} 442200590Sluigi 443200590Sluigivoid 444205415Sluigiipfw_destroy_tables(struct ip_fw_chain *ch) 445200590Sluigi{ 446200590Sluigi uint16_t tbl; 447200590Sluigi 448232865Smelifaro /* Flush all tables */ 449232865Smelifaro for (tbl = 0; tbl < V_fw_tables_max; tbl++) 450232865Smelifaro ipfw_flush_table(ch, tbl); 451200590Sluigi 452232865Smelifaro /* Free pointers itself */ 453232865Smelifaro free(ch->tables, M_IPFW); 454232865Smelifaro free(ch->xtables, M_IPFW); 455232865Smelifaro free(ch->tabletype, M_IPFW); 456200590Sluigi} 457200590Sluigi 458200590Sluigiint 459200590Sluigiipfw_init_tables(struct ip_fw_chain *ch) 460232865Smelifaro{ 461232865Smelifaro /* Allocate pointers */ 462232865Smelifaro ch->tables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 463232865Smelifaro ch->xtables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 464232865Smelifaro ch->tabletype = malloc(V_fw_tables_max * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); 465200590Sluigi return (0); 466200590Sluigi} 467200590Sluigi 468200590Sluigiint 469233478Smelifaroipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 470233478Smelifaro{ 471233478Smelifaro struct radix_node_head **tables, **xtables, *rnh; 472233478Smelifaro struct radix_node_head **tables_old, **xtables_old; 473233478Smelifaro uint8_t *tabletype, *tabletype_old; 474233478Smelifaro unsigned int ntables_old, tbl; 475233478Smelifaro 476233478Smelifaro /* Check new value for validity */ 477233478Smelifaro if (ntables > IPFW_TABLES_MAX) 478233478Smelifaro ntables = IPFW_TABLES_MAX; 479233478Smelifaro 480233478Smelifaro /* Allocate new pointers */ 481233478Smelifaro tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 482233478Smelifaro xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 483233478Smelifaro tabletype = malloc(ntables * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); 484233478Smelifaro 485233478Smelifaro IPFW_WLOCK(ch); 486233478Smelifaro 487233478Smelifaro tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 488233478Smelifaro 489233478Smelifaro /* Copy old table pointers */ 490233478Smelifaro memcpy(tables, ch->tables, sizeof(void *) * tbl); 491233478Smelifaro memcpy(xtables, ch->xtables, sizeof(void *) * tbl); 492233478Smelifaro memcpy(tabletype, ch->tabletype, sizeof(uint8_t) * tbl); 493233478Smelifaro 494233478Smelifaro /* Change pointers and number of tables */ 495233478Smelifaro tables_old = ch->tables; 496233478Smelifaro xtables_old = ch->xtables; 497233478Smelifaro tabletype_old = ch->tabletype; 498233478Smelifaro ch->tables = tables; 499233478Smelifaro ch->xtables = xtables; 500233478Smelifaro ch->tabletype = tabletype; 501233478Smelifaro 502233478Smelifaro ntables_old = V_fw_tables_max; 503233478Smelifaro V_fw_tables_max = ntables; 504233478Smelifaro 505233478Smelifaro IPFW_WUNLOCK(ch); 506233478Smelifaro 507233478Smelifaro /* Check if we need to destroy radix trees */ 508233478Smelifaro if (ntables < ntables_old) { 509233478Smelifaro for (tbl = ntables; tbl < ntables_old; tbl++) { 510233478Smelifaro if ((rnh = tables_old[tbl]) != NULL) { 511233478Smelifaro rnh->rnh_walktree(rnh, flush_table_entry, rnh); 512233478Smelifaro rn_detachhead((void **)&rnh); 513233478Smelifaro } 514233478Smelifaro 515233478Smelifaro if ((rnh = xtables_old[tbl]) != NULL) { 516233478Smelifaro rnh->rnh_walktree(rnh, flush_table_entry, rnh); 517233478Smelifaro rn_detachhead((void **)&rnh); 518233478Smelifaro } 519233478Smelifaro } 520233478Smelifaro } 521233478Smelifaro 522233478Smelifaro /* Free old pointers */ 523233478Smelifaro free(tables_old, M_IPFW); 524233478Smelifaro free(xtables_old, M_IPFW); 525233478Smelifaro free(tabletype_old, M_IPFW); 526233478Smelifaro 527233478Smelifaro return (0); 528233478Smelifaro} 529233478Smelifaro 530233478Smelifaroint 531200590Sluigiipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 532200590Sluigi uint32_t *val) 533200590Sluigi{ 534200590Sluigi struct radix_node_head *rnh; 535200590Sluigi struct table_entry *ent; 536200590Sluigi struct sockaddr_in sa; 537200590Sluigi 538232865Smelifaro if (tbl >= V_fw_tables_max) 539200590Sluigi return (0); 540232865Smelifaro if ((rnh = ch->tables[tbl]) == NULL) 541232865Smelifaro return (0); 542232865Smelifaro KEY_LEN(sa) = KEY_LEN_INET; 543200590Sluigi sa.sin_addr.s_addr = addr; 544200590Sluigi ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 545200590Sluigi if (ent != NULL) { 546200590Sluigi *val = ent->value; 547200590Sluigi return (1); 548200590Sluigi } 549200590Sluigi return (0); 550200590Sluigi} 551200590Sluigi 552232865Smelifaroint 553232865Smelifaroipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 554232865Smelifaro uint32_t *val, int type) 555232865Smelifaro{ 556232865Smelifaro struct radix_node_head *rnh; 557232865Smelifaro struct table_xentry *xent; 558232865Smelifaro struct sockaddr_in6 sa6; 559232865Smelifaro struct xaddr_iface iface; 560232865Smelifaro 561232865Smelifaro if (tbl >= V_fw_tables_max) 562232865Smelifaro return (0); 563232865Smelifaro if ((rnh = ch->xtables[tbl]) == NULL) 564232865Smelifaro return (0); 565232865Smelifaro 566232865Smelifaro switch (type) { 567232865Smelifaro case IPFW_TABLE_CIDR: 568232865Smelifaro KEY_LEN(sa6) = KEY_LEN_INET6; 569232865Smelifaro memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); 570232865Smelifaro xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); 571232865Smelifaro break; 572232865Smelifaro 573232865Smelifaro case IPFW_TABLE_INTERFACE: 574237479Smelifaro KEY_LEN(iface) = KEY_LEN_IFACE + 575238265Smelifaro strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; 576232865Smelifaro /* Assume direct match */ 577232865Smelifaro /* FIXME: Add interface pattern matching */ 578232865Smelifaro xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); 579232865Smelifaro break; 580232865Smelifaro 581232865Smelifaro default: 582232865Smelifaro return (0); 583232865Smelifaro } 584232865Smelifaro 585232865Smelifaro if (xent != NULL) { 586232865Smelifaro *val = xent->value; 587232865Smelifaro return (1); 588232865Smelifaro } 589232865Smelifaro return (0); 590232865Smelifaro} 591232865Smelifaro 592200590Sluigistatic int 593200590Sluigicount_table_entry(struct radix_node *rn, void *arg) 594200590Sluigi{ 595200590Sluigi u_int32_t * const cnt = arg; 596200590Sluigi 597200590Sluigi (*cnt)++; 598200590Sluigi return (0); 599200590Sluigi} 600200590Sluigi 601200590Sluigiint 602200590Sluigiipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 603200590Sluigi{ 604200590Sluigi struct radix_node_head *rnh; 605200590Sluigi 606232865Smelifaro if (tbl >= V_fw_tables_max) 607200590Sluigi return (EINVAL); 608200590Sluigi *cnt = 0; 609232865Smelifaro if ((rnh = ch->tables[tbl]) == NULL) 610232865Smelifaro return (0); 611200590Sluigi rnh->rnh_walktree(rnh, count_table_entry, cnt); 612200590Sluigi return (0); 613200590Sluigi} 614200590Sluigi 615200590Sluigistatic int 616200590Sluigidump_table_entry(struct radix_node *rn, void *arg) 617200590Sluigi{ 618200590Sluigi struct table_entry * const n = (struct table_entry *)rn; 619200590Sluigi ipfw_table * const tbl = arg; 620200590Sluigi ipfw_table_entry *ent; 621200590Sluigi 622200590Sluigi if (tbl->cnt == tbl->size) 623200590Sluigi return (1); 624200590Sluigi ent = &tbl->ent[tbl->cnt]; 625200590Sluigi ent->tbl = tbl->tbl; 626200590Sluigi if (in_nullhost(n->mask.sin_addr)) 627200590Sluigi ent->masklen = 0; 628200590Sluigi else 629200590Sluigi ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 630200590Sluigi ent->addr = n->addr.sin_addr.s_addr; 631200590Sluigi ent->value = n->value; 632200590Sluigi tbl->cnt++; 633200590Sluigi return (0); 634200590Sluigi} 635200590Sluigi 636200590Sluigiint 637200590Sluigiipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 638200590Sluigi{ 639200590Sluigi struct radix_node_head *rnh; 640200590Sluigi 641232865Smelifaro if (tbl->tbl >= V_fw_tables_max) 642200590Sluigi return (EINVAL); 643200590Sluigi tbl->cnt = 0; 644232865Smelifaro if ((rnh = ch->tables[tbl->tbl]) == NULL) 645232865Smelifaro return (0); 646200590Sluigi rnh->rnh_walktree(rnh, dump_table_entry, tbl); 647200590Sluigi return (0); 648200590Sluigi} 649232865Smelifaro 650232865Smelifarostatic int 651232865Smelifarocount_table_xentry(struct radix_node *rn, void *arg) 652232865Smelifaro{ 653232865Smelifaro uint32_t * const cnt = arg; 654232865Smelifaro 655232865Smelifaro (*cnt) += sizeof(ipfw_table_xentry); 656232865Smelifaro return (0); 657232865Smelifaro} 658232865Smelifaro 659232865Smelifaroint 660232865Smelifaroipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 661232865Smelifaro{ 662232865Smelifaro struct radix_node_head *rnh; 663232865Smelifaro 664232865Smelifaro if (tbl >= V_fw_tables_max) 665232865Smelifaro return (EINVAL); 666232865Smelifaro *cnt = 0; 667232865Smelifaro if ((rnh = ch->tables[tbl]) != NULL) 668232865Smelifaro rnh->rnh_walktree(rnh, count_table_xentry, cnt); 669232865Smelifaro if ((rnh = ch->xtables[tbl]) != NULL) 670232865Smelifaro rnh->rnh_walktree(rnh, count_table_xentry, cnt); 671232865Smelifaro /* Return zero if table is empty */ 672232865Smelifaro if (*cnt > 0) 673232865Smelifaro (*cnt) += sizeof(ipfw_xtable); 674232865Smelifaro return (0); 675232865Smelifaro} 676232865Smelifaro 677232865Smelifaro 678232865Smelifarostatic int 679232865Smelifarodump_table_xentry_base(struct radix_node *rn, void *arg) 680232865Smelifaro{ 681232865Smelifaro struct table_entry * const n = (struct table_entry *)rn; 682232865Smelifaro ipfw_xtable * const tbl = arg; 683232865Smelifaro ipfw_table_xentry *xent; 684232865Smelifaro 685232865Smelifaro /* Out of memory, returning */ 686232865Smelifaro if (tbl->cnt == tbl->size) 687232865Smelifaro return (1); 688232865Smelifaro xent = &tbl->xent[tbl->cnt]; 689232865Smelifaro xent->len = sizeof(ipfw_table_xentry); 690232865Smelifaro xent->tbl = tbl->tbl; 691232865Smelifaro if (in_nullhost(n->mask.sin_addr)) 692232865Smelifaro xent->masklen = 0; 693232865Smelifaro else 694232865Smelifaro xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 695232865Smelifaro /* Save IPv4 address as deprecated IPv6 compatible */ 696232865Smelifaro xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr; 697232865Smelifaro xent->value = n->value; 698232865Smelifaro tbl->cnt++; 699232865Smelifaro return (0); 700232865Smelifaro} 701232865Smelifaro 702232865Smelifarostatic int 703232865Smelifarodump_table_xentry_extended(struct radix_node *rn, void *arg) 704232865Smelifaro{ 705232865Smelifaro struct table_xentry * const n = (struct table_xentry *)rn; 706232865Smelifaro ipfw_xtable * const tbl = arg; 707232865Smelifaro ipfw_table_xentry *xent; 708232865Smelifaro#ifdef INET6 709232865Smelifaro int i; 710232865Smelifaro uint32_t *v; 711232865Smelifaro#endif 712232865Smelifaro /* Out of memory, returning */ 713232865Smelifaro if (tbl->cnt == tbl->size) 714232865Smelifaro return (1); 715232865Smelifaro xent = &tbl->xent[tbl->cnt]; 716232865Smelifaro xent->len = sizeof(ipfw_table_xentry); 717232865Smelifaro xent->tbl = tbl->tbl; 718232865Smelifaro 719232865Smelifaro switch (tbl->type) { 720232865Smelifaro#ifdef INET6 721232865Smelifaro case IPFW_TABLE_CIDR: 722232865Smelifaro /* Count IPv6 mask */ 723232865Smelifaro v = (uint32_t *)&n->m.mask6.sin6_addr; 724232865Smelifaro for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++) 725232865Smelifaro xent->masklen += bitcount32(*v); 726232865Smelifaro memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr)); 727232865Smelifaro break; 728232865Smelifaro#endif 729232865Smelifaro case IPFW_TABLE_INTERFACE: 730232865Smelifaro /* Assume exact mask */ 731232865Smelifaro xent->masklen = 8 * IF_NAMESIZE; 732232865Smelifaro memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE); 733232865Smelifaro break; 734232865Smelifaro 735232865Smelifaro default: 736232865Smelifaro /* unknown, skip entry */ 737232865Smelifaro return (0); 738232865Smelifaro } 739232865Smelifaro 740232865Smelifaro xent->value = n->value; 741232865Smelifaro tbl->cnt++; 742232865Smelifaro return (0); 743232865Smelifaro} 744232865Smelifaro 745232865Smelifaroint 746232865Smelifaroipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl) 747232865Smelifaro{ 748232865Smelifaro struct radix_node_head *rnh; 749232865Smelifaro 750232865Smelifaro if (tbl->tbl >= V_fw_tables_max) 751232865Smelifaro return (EINVAL); 752232865Smelifaro tbl->cnt = 0; 753232865Smelifaro tbl->type = ch->tabletype[tbl->tbl]; 754232865Smelifaro if ((rnh = ch->tables[tbl->tbl]) != NULL) 755232865Smelifaro rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl); 756232865Smelifaro if ((rnh = ch->xtables[tbl->tbl]) != NULL) 757232865Smelifaro rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl); 758232865Smelifaro return (0); 759232865Smelifaro} 760232865Smelifaro 761200601Sluigi/* end of file */ 762