ip_fw_table.c revision 240494
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/netpfil/ipfw/ip_fw_table.c 240494 2012-09-14 11:51:49Z glebius $"); 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> 56240494Sglebius#include <sys/queue.h> 57200590Sluigi#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 58200590Sluigi#include <net/radix.h> 59200590Sluigi#include <net/route.h> 60200590Sluigi#include <net/vnet.h> 61200590Sluigi 62200590Sluigi#include <netinet/in.h> 63201732Sluigi#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 64200590Sluigi#include <netinet/ip_fw.h> 65200590Sluigi 66240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 67240494Sglebius 68200590Sluigi#ifdef MAC 69200590Sluigi#include <security/mac/mac_framework.h> 70200590Sluigi#endif 71200590Sluigi 72227293Sedstatic MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); 73200590Sluigi 74200590Sluigistruct table_entry { 75200590Sluigi struct radix_node rn[2]; 76200590Sluigi struct sockaddr_in addr, mask; 77200590Sluigi u_int32_t value; 78200590Sluigi}; 79200590Sluigi 80232865Smelifarostruct xaddr_iface { 81232865Smelifaro uint8_t if_len; /* length of this struct */ 82232865Smelifaro uint8_t pad[7]; /* Align name */ 83232865Smelifaro char ifname[IF_NAMESIZE]; /* Interface name */ 84232865Smelifaro}; 85232865Smelifaro 86232865Smelifarostruct table_xentry { 87232865Smelifaro struct radix_node rn[2]; 88232865Smelifaro union { 89232865Smelifaro#ifdef INET6 90232865Smelifaro struct sockaddr_in6 addr6; 91232865Smelifaro#endif 92232865Smelifaro struct xaddr_iface iface; 93232865Smelifaro } a; 94232865Smelifaro union { 95232865Smelifaro#ifdef INET6 96232865Smelifaro struct sockaddr_in6 mask6; 97232865Smelifaro#endif 98232865Smelifaro struct xaddr_iface ifmask; 99232865Smelifaro } m; 100232865Smelifaro u_int32_t value; 101232865Smelifaro}; 102232865Smelifaro 103201120Sluigi/* 104201120Sluigi * The radix code expects addr and mask to be array of bytes, 105201120Sluigi * with the first byte being the length of the array. rn_inithead 106201120Sluigi * is called with the offset in bits of the lookup key within the 107201120Sluigi * array. If we use a sockaddr_in as the underlying type, 108201120Sluigi * sin_len is conveniently located at offset 0, sin_addr is at 109201120Sluigi * offset 4 and normally aligned. 110201120Sluigi * But for portability, let's avoid assumption and make the code explicit 111201120Sluigi */ 112201120Sluigi#define KEY_LEN(v) *((uint8_t *)&(v)) 113201120Sluigi#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 114232865Smelifaro/* 115232865Smelifaro * Do not require radix to compare more than actual IPv4/IPv6 address 116232865Smelifaro */ 117232865Smelifaro#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t)) 118232865Smelifaro#define KEY_LEN_INET6 (offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr)) 119232865Smelifaro#define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname)) 120201120Sluigi 121232865Smelifaro#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr)) 122232865Smelifaro#define OFF_LEN_INET6 (8 * offsetof(struct sockaddr_in6, sin6_addr)) 123232865Smelifaro#define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname)) 124232865Smelifaro 125232865Smelifaro 126232865Smelifarostatic inline void 127232865Smelifaroipv6_writemask(struct in6_addr *addr6, uint8_t mask) 128232865Smelifaro{ 129232865Smelifaro uint32_t *cp; 130232865Smelifaro 131232865Smelifaro for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) 132232865Smelifaro *cp++ = 0xFFFFFFFF; 133232865Smelifaro *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); 134232865Smelifaro} 135232865Smelifaro 136200590Sluigiint 137232865Smelifaroipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 138232865Smelifaro uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value) 139200590Sluigi{ 140232865Smelifaro struct radix_node_head *rnh, **rnh_ptr; 141200590Sluigi struct table_entry *ent; 142232865Smelifaro struct table_xentry *xent; 143200590Sluigi struct radix_node *rn; 144232865Smelifaro in_addr_t addr; 145232865Smelifaro int offset; 146232865Smelifaro void *ent_ptr; 147232865Smelifaro struct sockaddr *addr_ptr, *mask_ptr; 148232865Smelifaro char c; 149200590Sluigi 150232865Smelifaro if (tbl >= V_fw_tables_max) 151200590Sluigi return (EINVAL); 152232865Smelifaro 153232865Smelifaro switch (type) { 154232865Smelifaro case IPFW_TABLE_CIDR: 155232865Smelifaro if (plen == sizeof(in_addr_t)) { 156232865Smelifaro#ifdef INET 157236819Smelifaro /* IPv4 case */ 158236819Smelifaro if (mlen > 32) 159236819Smelifaro return (EINVAL); 160232865Smelifaro ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO); 161232865Smelifaro ent->value = value; 162232865Smelifaro /* Set 'total' structure length */ 163232865Smelifaro KEY_LEN(ent->addr) = KEY_LEN_INET; 164232865Smelifaro KEY_LEN(ent->mask) = KEY_LEN_INET; 165232865Smelifaro /* Set offset of IPv4 address in bits */ 166232865Smelifaro offset = OFF_LEN_INET; 167232865Smelifaro ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 168232865Smelifaro addr = *((in_addr_t *)paddr); 169232865Smelifaro ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 170232865Smelifaro /* Set pointers */ 171232865Smelifaro rnh_ptr = &ch->tables[tbl]; 172232865Smelifaro ent_ptr = ent; 173232865Smelifaro addr_ptr = (struct sockaddr *)&ent->addr; 174232865Smelifaro mask_ptr = (struct sockaddr *)&ent->mask; 175232865Smelifaro#endif 176232865Smelifaro#ifdef INET6 177232865Smelifaro } else if (plen == sizeof(struct in6_addr)) { 178232865Smelifaro /* IPv6 case */ 179232865Smelifaro if (mlen > 128) 180232865Smelifaro return (EINVAL); 181232865Smelifaro xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); 182232865Smelifaro xent->value = value; 183232865Smelifaro /* Set 'total' structure length */ 184232865Smelifaro KEY_LEN(xent->a.addr6) = KEY_LEN_INET6; 185232865Smelifaro KEY_LEN(xent->m.mask6) = KEY_LEN_INET6; 186232865Smelifaro /* Set offset of IPv6 address in bits */ 187232865Smelifaro offset = OFF_LEN_INET6; 188232865Smelifaro ipv6_writemask(&xent->m.mask6.sin6_addr, mlen); 189232865Smelifaro memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr)); 190232865Smelifaro APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr); 191232865Smelifaro /* Set pointers */ 192232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 193232865Smelifaro ent_ptr = xent; 194232865Smelifaro addr_ptr = (struct sockaddr *)&xent->a.addr6; 195232865Smelifaro mask_ptr = (struct sockaddr *)&xent->m.mask6; 196232865Smelifaro#endif 197232865Smelifaro } else { 198232865Smelifaro /* Unknown CIDR type */ 199232865Smelifaro return (EINVAL); 200232865Smelifaro } 201232865Smelifaro break; 202232865Smelifaro 203232865Smelifaro case IPFW_TABLE_INTERFACE: 204232865Smelifaro /* Check if string is terminated */ 205232865Smelifaro c = ((char *)paddr)[IF_NAMESIZE - 1]; 206232865Smelifaro ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; 207232865Smelifaro if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) 208232865Smelifaro return (EINVAL); 209232865Smelifaro 210232865Smelifaro /* Include last \0 into comparison */ 211232865Smelifaro mlen++; 212232865Smelifaro 213232865Smelifaro xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO); 214232865Smelifaro xent->value = value; 215232865Smelifaro /* Set 'total' structure length */ 216232865Smelifaro KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen; 217232865Smelifaro KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen; 218232865Smelifaro /* Set offset of interface name in bits */ 219232865Smelifaro offset = OFF_LEN_IFACE; 220232865Smelifaro memcpy(xent->a.iface.ifname, paddr, mlen); 221232865Smelifaro /* Assume direct match */ 222232865Smelifaro /* TODO: Add interface pattern matching */ 223232865Smelifaro#if 0 224232865Smelifaro memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE); 225232865Smelifaro mask_ptr = (struct sockaddr *)&xent->m.ifmask; 226232865Smelifaro#endif 227232865Smelifaro /* Set pointers */ 228232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 229232865Smelifaro ent_ptr = xent; 230232865Smelifaro addr_ptr = (struct sockaddr *)&xent->a.iface; 231232865Smelifaro mask_ptr = NULL; 232232865Smelifaro break; 233232865Smelifaro 234232865Smelifaro default: 235232865Smelifaro return (EINVAL); 236232865Smelifaro } 237232865Smelifaro 238200590Sluigi IPFW_WLOCK(ch); 239232865Smelifaro 240232865Smelifaro /* Check if tabletype is valid */ 241232865Smelifaro if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) { 242232865Smelifaro IPFW_WUNLOCK(ch); 243232865Smelifaro free(ent_ptr, M_IPFW_TBL); 244232865Smelifaro return (EINVAL); 245232865Smelifaro } 246232865Smelifaro 247232865Smelifaro /* Check if radix tree exists */ 248232865Smelifaro if ((rnh = *rnh_ptr) == NULL) { 249232865Smelifaro IPFW_WUNLOCK(ch); 250232865Smelifaro /* Create radix for a new table */ 251232865Smelifaro if (!rn_inithead((void **)&rnh, offset)) { 252232865Smelifaro free(ent_ptr, M_IPFW_TBL); 253232865Smelifaro return (ENOMEM); 254232865Smelifaro } 255232865Smelifaro 256232865Smelifaro IPFW_WLOCK(ch); 257232865Smelifaro if (*rnh_ptr != NULL) { 258232865Smelifaro /* Tree is already attached by other thread */ 259232865Smelifaro rn_detachhead((void **)&rnh); 260232865Smelifaro rnh = *rnh_ptr; 261232865Smelifaro /* Check table type another time */ 262232865Smelifaro if (ch->tabletype[tbl] != type) { 263232865Smelifaro IPFW_WUNLOCK(ch); 264232865Smelifaro free(ent_ptr, M_IPFW_TBL); 265232865Smelifaro return (EINVAL); 266232865Smelifaro } 267232865Smelifaro } else { 268232865Smelifaro *rnh_ptr = rnh; 269232865Smelifaro /* 270232865Smelifaro * Set table type. It can be set already 271232865Smelifaro * (if we have IPv6-only table) but setting 272232865Smelifaro * it another time does not hurt 273232865Smelifaro */ 274232865Smelifaro ch->tabletype[tbl] = type; 275232865Smelifaro } 276232865Smelifaro } 277232865Smelifaro 278232865Smelifaro rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr); 279232865Smelifaro IPFW_WUNLOCK(ch); 280232865Smelifaro 281200590Sluigi if (rn == NULL) { 282232865Smelifaro free(ent_ptr, M_IPFW_TBL); 283200590Sluigi return (EEXIST); 284200590Sluigi } 285200590Sluigi return (0); 286200590Sluigi} 287200590Sluigi 288200590Sluigiint 289232865Smelifaroipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 290232865Smelifaro uint8_t plen, uint8_t mlen, uint8_t type) 291200590Sluigi{ 292232865Smelifaro struct radix_node_head *rnh, **rnh_ptr; 293200590Sluigi struct table_entry *ent; 294232865Smelifaro in_addr_t addr; 295200590Sluigi struct sockaddr_in sa, mask; 296232865Smelifaro struct sockaddr *sa_ptr, *mask_ptr; 297232865Smelifaro char c; 298200590Sluigi 299232865Smelifaro if (tbl >= V_fw_tables_max) 300200590Sluigi return (EINVAL); 301232865Smelifaro 302232865Smelifaro switch (type) { 303232865Smelifaro case IPFW_TABLE_CIDR: 304232865Smelifaro if (plen == sizeof(in_addr_t)) { 305232865Smelifaro /* Set 'total' structure length */ 306232865Smelifaro KEY_LEN(sa) = KEY_LEN_INET; 307232865Smelifaro KEY_LEN(mask) = KEY_LEN_INET; 308232865Smelifaro mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 309232865Smelifaro addr = *((in_addr_t *)paddr); 310232865Smelifaro sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 311232865Smelifaro rnh_ptr = &ch->tables[tbl]; 312232865Smelifaro sa_ptr = (struct sockaddr *)&sa; 313232865Smelifaro mask_ptr = (struct sockaddr *)&mask; 314232865Smelifaro#ifdef INET6 315232865Smelifaro } else if (plen == sizeof(struct in6_addr)) { 316232865Smelifaro /* IPv6 case */ 317232865Smelifaro if (mlen > 128) 318232865Smelifaro return (EINVAL); 319232865Smelifaro struct sockaddr_in6 sa6, mask6; 320232865Smelifaro memset(&sa6, 0, sizeof(struct sockaddr_in6)); 321232865Smelifaro memset(&mask6, 0, sizeof(struct sockaddr_in6)); 322232865Smelifaro /* Set 'total' structure length */ 323232865Smelifaro KEY_LEN(sa6) = KEY_LEN_INET6; 324232865Smelifaro KEY_LEN(mask6) = KEY_LEN_INET6; 325232865Smelifaro ipv6_writemask(&mask6.sin6_addr, mlen); 326232865Smelifaro memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); 327232865Smelifaro APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr); 328232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 329232865Smelifaro sa_ptr = (struct sockaddr *)&sa6; 330232865Smelifaro mask_ptr = (struct sockaddr *)&mask6; 331232865Smelifaro#endif 332232865Smelifaro } else { 333232865Smelifaro /* Unknown CIDR type */ 334232865Smelifaro return (EINVAL); 335232865Smelifaro } 336232865Smelifaro break; 337232865Smelifaro 338232865Smelifaro case IPFW_TABLE_INTERFACE: 339232865Smelifaro /* Check if string is terminated */ 340232865Smelifaro c = ((char *)paddr)[IF_NAMESIZE - 1]; 341232865Smelifaro ((char *)paddr)[IF_NAMESIZE - 1] = '\0'; 342232865Smelifaro if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0')) 343232865Smelifaro return (EINVAL); 344232865Smelifaro 345232865Smelifaro struct xaddr_iface ifname, ifmask; 346232865Smelifaro memset(&ifname, 0, sizeof(ifname)); 347232865Smelifaro 348238265Smelifaro /* Include last \0 into comparison */ 349238265Smelifaro mlen++; 350238265Smelifaro 351232865Smelifaro /* Set 'total' structure length */ 352238265Smelifaro KEY_LEN(ifname) = KEY_LEN_IFACE + mlen; 353238265Smelifaro KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen; 354232865Smelifaro /* Assume direct match */ 355232865Smelifaro /* FIXME: Add interface pattern matching */ 356232865Smelifaro#if 0 357232865Smelifaro memset(ifmask.ifname, 0xFF, IF_NAMESIZE); 358232865Smelifaro mask_ptr = (struct sockaddr *)&ifmask; 359232865Smelifaro#endif 360232865Smelifaro mask_ptr = NULL; 361232865Smelifaro memcpy(ifname.ifname, paddr, mlen); 362232865Smelifaro /* Set pointers */ 363232865Smelifaro rnh_ptr = &ch->xtables[tbl]; 364232865Smelifaro sa_ptr = (struct sockaddr *)&ifname; 365232865Smelifaro 366232865Smelifaro break; 367232865Smelifaro 368232865Smelifaro default: 369232865Smelifaro return (EINVAL); 370232865Smelifaro } 371232865Smelifaro 372200590Sluigi IPFW_WLOCK(ch); 373232865Smelifaro if ((rnh = *rnh_ptr) == NULL) { 374200590Sluigi IPFW_WUNLOCK(ch); 375200590Sluigi return (ESRCH); 376200590Sluigi } 377232865Smelifaro 378232865Smelifaro if (ch->tabletype[tbl] != type) { 379232865Smelifaro IPFW_WUNLOCK(ch); 380232865Smelifaro return (EINVAL); 381232865Smelifaro } 382232865Smelifaro 383232865Smelifaro ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh); 384200590Sluigi IPFW_WUNLOCK(ch); 385232865Smelifaro 386232865Smelifaro if (ent == NULL) 387232865Smelifaro return (ESRCH); 388232865Smelifaro 389200590Sluigi free(ent, M_IPFW_TBL); 390200590Sluigi return (0); 391200590Sluigi} 392200590Sluigi 393200590Sluigistatic int 394200590Sluigiflush_table_entry(struct radix_node *rn, void *arg) 395200590Sluigi{ 396200590Sluigi struct radix_node_head * const rnh = arg; 397200590Sluigi struct table_entry *ent; 398200590Sluigi 399200590Sluigi ent = (struct table_entry *) 400200590Sluigi rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 401200590Sluigi if (ent != NULL) 402200590Sluigi free(ent, M_IPFW_TBL); 403200590Sluigi return (0); 404200590Sluigi} 405200590Sluigi 406200590Sluigiint 407200590Sluigiipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 408200590Sluigi{ 409232865Smelifaro struct radix_node_head *rnh, *xrnh; 410200590Sluigi 411232865Smelifaro if (tbl >= V_fw_tables_max) 412232865Smelifaro return (EINVAL); 413200590Sluigi 414232865Smelifaro /* 415232865Smelifaro * We free both (IPv4 and extended) radix trees and 416232865Smelifaro * clear table type here to permit table to be reused 417232865Smelifaro * for different type without module reload 418232865Smelifaro */ 419232865Smelifaro 420232865Smelifaro IPFW_WLOCK(ch); 421232865Smelifaro /* Set IPv4 table pointer to zero */ 422232865Smelifaro if ((rnh = ch->tables[tbl]) != NULL) 423232865Smelifaro ch->tables[tbl] = NULL; 424232865Smelifaro /* Set extended table pointer to zero */ 425232865Smelifaro if ((xrnh = ch->xtables[tbl]) != NULL) 426232865Smelifaro ch->xtables[tbl] = NULL; 427232865Smelifaro /* Zero table type */ 428232865Smelifaro ch->tabletype[tbl] = 0; 429232865Smelifaro IPFW_WUNLOCK(ch); 430232865Smelifaro 431232865Smelifaro if (rnh != NULL) { 432232865Smelifaro rnh->rnh_walktree(rnh, flush_table_entry, rnh); 433232865Smelifaro rn_detachhead((void **)&rnh); 434232865Smelifaro } 435232865Smelifaro 436232865Smelifaro if (xrnh != NULL) { 437232865Smelifaro xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh); 438232865Smelifaro rn_detachhead((void **)&xrnh); 439232865Smelifaro } 440232865Smelifaro 441200590Sluigi return (0); 442200590Sluigi} 443200590Sluigi 444200590Sluigivoid 445205415Sluigiipfw_destroy_tables(struct ip_fw_chain *ch) 446200590Sluigi{ 447200590Sluigi uint16_t tbl; 448200590Sluigi 449232865Smelifaro /* Flush all tables */ 450232865Smelifaro for (tbl = 0; tbl < V_fw_tables_max; tbl++) 451232865Smelifaro ipfw_flush_table(ch, tbl); 452200590Sluigi 453232865Smelifaro /* Free pointers itself */ 454232865Smelifaro free(ch->tables, M_IPFW); 455232865Smelifaro free(ch->xtables, M_IPFW); 456232865Smelifaro free(ch->tabletype, M_IPFW); 457200590Sluigi} 458200590Sluigi 459200590Sluigiint 460200590Sluigiipfw_init_tables(struct ip_fw_chain *ch) 461232865Smelifaro{ 462232865Smelifaro /* Allocate pointers */ 463232865Smelifaro ch->tables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 464232865Smelifaro ch->xtables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 465232865Smelifaro ch->tabletype = malloc(V_fw_tables_max * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); 466200590Sluigi return (0); 467200590Sluigi} 468200590Sluigi 469200590Sluigiint 470233478Smelifaroipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables) 471233478Smelifaro{ 472233478Smelifaro struct radix_node_head **tables, **xtables, *rnh; 473233478Smelifaro struct radix_node_head **tables_old, **xtables_old; 474233478Smelifaro uint8_t *tabletype, *tabletype_old; 475233478Smelifaro unsigned int ntables_old, tbl; 476233478Smelifaro 477233478Smelifaro /* Check new value for validity */ 478233478Smelifaro if (ntables > IPFW_TABLES_MAX) 479233478Smelifaro ntables = IPFW_TABLES_MAX; 480233478Smelifaro 481233478Smelifaro /* Allocate new pointers */ 482233478Smelifaro tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 483233478Smelifaro xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO); 484233478Smelifaro tabletype = malloc(ntables * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO); 485233478Smelifaro 486233478Smelifaro IPFW_WLOCK(ch); 487233478Smelifaro 488233478Smelifaro tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables; 489233478Smelifaro 490233478Smelifaro /* Copy old table pointers */ 491233478Smelifaro memcpy(tables, ch->tables, sizeof(void *) * tbl); 492233478Smelifaro memcpy(xtables, ch->xtables, sizeof(void *) * tbl); 493233478Smelifaro memcpy(tabletype, ch->tabletype, sizeof(uint8_t) * tbl); 494233478Smelifaro 495233478Smelifaro /* Change pointers and number of tables */ 496233478Smelifaro tables_old = ch->tables; 497233478Smelifaro xtables_old = ch->xtables; 498233478Smelifaro tabletype_old = ch->tabletype; 499233478Smelifaro ch->tables = tables; 500233478Smelifaro ch->xtables = xtables; 501233478Smelifaro ch->tabletype = tabletype; 502233478Smelifaro 503233478Smelifaro ntables_old = V_fw_tables_max; 504233478Smelifaro V_fw_tables_max = ntables; 505233478Smelifaro 506233478Smelifaro IPFW_WUNLOCK(ch); 507233478Smelifaro 508233478Smelifaro /* Check if we need to destroy radix trees */ 509233478Smelifaro if (ntables < ntables_old) { 510233478Smelifaro for (tbl = ntables; tbl < ntables_old; tbl++) { 511233478Smelifaro if ((rnh = tables_old[tbl]) != NULL) { 512233478Smelifaro rnh->rnh_walktree(rnh, flush_table_entry, rnh); 513233478Smelifaro rn_detachhead((void **)&rnh); 514233478Smelifaro } 515233478Smelifaro 516233478Smelifaro if ((rnh = xtables_old[tbl]) != NULL) { 517233478Smelifaro rnh->rnh_walktree(rnh, flush_table_entry, rnh); 518233478Smelifaro rn_detachhead((void **)&rnh); 519233478Smelifaro } 520233478Smelifaro } 521233478Smelifaro } 522233478Smelifaro 523233478Smelifaro /* Free old pointers */ 524233478Smelifaro free(tables_old, M_IPFW); 525233478Smelifaro free(xtables_old, M_IPFW); 526233478Smelifaro free(tabletype_old, M_IPFW); 527233478Smelifaro 528233478Smelifaro return (0); 529233478Smelifaro} 530233478Smelifaro 531233478Smelifaroint 532200590Sluigiipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 533200590Sluigi uint32_t *val) 534200590Sluigi{ 535200590Sluigi struct radix_node_head *rnh; 536200590Sluigi struct table_entry *ent; 537200590Sluigi struct sockaddr_in sa; 538200590Sluigi 539232865Smelifaro if (tbl >= V_fw_tables_max) 540200590Sluigi return (0); 541232865Smelifaro if ((rnh = ch->tables[tbl]) == NULL) 542232865Smelifaro return (0); 543232865Smelifaro KEY_LEN(sa) = KEY_LEN_INET; 544200590Sluigi sa.sin_addr.s_addr = addr; 545200590Sluigi ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 546200590Sluigi if (ent != NULL) { 547200590Sluigi *val = ent->value; 548200590Sluigi return (1); 549200590Sluigi } 550200590Sluigi return (0); 551200590Sluigi} 552200590Sluigi 553232865Smelifaroint 554232865Smelifaroipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr, 555232865Smelifaro uint32_t *val, int type) 556232865Smelifaro{ 557232865Smelifaro struct radix_node_head *rnh; 558232865Smelifaro struct table_xentry *xent; 559232865Smelifaro struct sockaddr_in6 sa6; 560232865Smelifaro struct xaddr_iface iface; 561232865Smelifaro 562232865Smelifaro if (tbl >= V_fw_tables_max) 563232865Smelifaro return (0); 564232865Smelifaro if ((rnh = ch->xtables[tbl]) == NULL) 565232865Smelifaro return (0); 566232865Smelifaro 567232865Smelifaro switch (type) { 568232865Smelifaro case IPFW_TABLE_CIDR: 569232865Smelifaro KEY_LEN(sa6) = KEY_LEN_INET6; 570232865Smelifaro memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr)); 571232865Smelifaro xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh)); 572232865Smelifaro break; 573232865Smelifaro 574232865Smelifaro case IPFW_TABLE_INTERFACE: 575237479Smelifaro KEY_LEN(iface) = KEY_LEN_IFACE + 576238265Smelifaro strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1; 577232865Smelifaro /* Assume direct match */ 578232865Smelifaro /* FIXME: Add interface pattern matching */ 579232865Smelifaro xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh)); 580232865Smelifaro break; 581232865Smelifaro 582232865Smelifaro default: 583232865Smelifaro return (0); 584232865Smelifaro } 585232865Smelifaro 586232865Smelifaro if (xent != NULL) { 587232865Smelifaro *val = xent->value; 588232865Smelifaro return (1); 589232865Smelifaro } 590232865Smelifaro return (0); 591232865Smelifaro} 592232865Smelifaro 593200590Sluigistatic int 594200590Sluigicount_table_entry(struct radix_node *rn, void *arg) 595200590Sluigi{ 596200590Sluigi u_int32_t * const cnt = arg; 597200590Sluigi 598200590Sluigi (*cnt)++; 599200590Sluigi return (0); 600200590Sluigi} 601200590Sluigi 602200590Sluigiint 603200590Sluigiipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 604200590Sluigi{ 605200590Sluigi struct radix_node_head *rnh; 606200590Sluigi 607232865Smelifaro if (tbl >= V_fw_tables_max) 608200590Sluigi return (EINVAL); 609200590Sluigi *cnt = 0; 610232865Smelifaro if ((rnh = ch->tables[tbl]) == NULL) 611232865Smelifaro return (0); 612200590Sluigi rnh->rnh_walktree(rnh, count_table_entry, cnt); 613200590Sluigi return (0); 614200590Sluigi} 615200590Sluigi 616200590Sluigistatic int 617200590Sluigidump_table_entry(struct radix_node *rn, void *arg) 618200590Sluigi{ 619200590Sluigi struct table_entry * const n = (struct table_entry *)rn; 620200590Sluigi ipfw_table * const tbl = arg; 621200590Sluigi ipfw_table_entry *ent; 622200590Sluigi 623200590Sluigi if (tbl->cnt == tbl->size) 624200590Sluigi return (1); 625200590Sluigi ent = &tbl->ent[tbl->cnt]; 626200590Sluigi ent->tbl = tbl->tbl; 627200590Sluigi if (in_nullhost(n->mask.sin_addr)) 628200590Sluigi ent->masklen = 0; 629200590Sluigi else 630200590Sluigi ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 631200590Sluigi ent->addr = n->addr.sin_addr.s_addr; 632200590Sluigi ent->value = n->value; 633200590Sluigi tbl->cnt++; 634200590Sluigi return (0); 635200590Sluigi} 636200590Sluigi 637200590Sluigiint 638200590Sluigiipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 639200590Sluigi{ 640200590Sluigi struct radix_node_head *rnh; 641200590Sluigi 642232865Smelifaro if (tbl->tbl >= V_fw_tables_max) 643200590Sluigi return (EINVAL); 644200590Sluigi tbl->cnt = 0; 645232865Smelifaro if ((rnh = ch->tables[tbl->tbl]) == NULL) 646232865Smelifaro return (0); 647200590Sluigi rnh->rnh_walktree(rnh, dump_table_entry, tbl); 648200590Sluigi return (0); 649200590Sluigi} 650232865Smelifaro 651232865Smelifarostatic int 652232865Smelifarocount_table_xentry(struct radix_node *rn, void *arg) 653232865Smelifaro{ 654232865Smelifaro uint32_t * const cnt = arg; 655232865Smelifaro 656232865Smelifaro (*cnt) += sizeof(ipfw_table_xentry); 657232865Smelifaro return (0); 658232865Smelifaro} 659232865Smelifaro 660232865Smelifaroint 661232865Smelifaroipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 662232865Smelifaro{ 663232865Smelifaro struct radix_node_head *rnh; 664232865Smelifaro 665232865Smelifaro if (tbl >= V_fw_tables_max) 666232865Smelifaro return (EINVAL); 667232865Smelifaro *cnt = 0; 668232865Smelifaro if ((rnh = ch->tables[tbl]) != NULL) 669232865Smelifaro rnh->rnh_walktree(rnh, count_table_xentry, cnt); 670232865Smelifaro if ((rnh = ch->xtables[tbl]) != NULL) 671232865Smelifaro rnh->rnh_walktree(rnh, count_table_xentry, cnt); 672232865Smelifaro /* Return zero if table is empty */ 673232865Smelifaro if (*cnt > 0) 674232865Smelifaro (*cnt) += sizeof(ipfw_xtable); 675232865Smelifaro return (0); 676232865Smelifaro} 677232865Smelifaro 678232865Smelifaro 679232865Smelifarostatic int 680232865Smelifarodump_table_xentry_base(struct radix_node *rn, void *arg) 681232865Smelifaro{ 682232865Smelifaro struct table_entry * const n = (struct table_entry *)rn; 683232865Smelifaro ipfw_xtable * const tbl = arg; 684232865Smelifaro ipfw_table_xentry *xent; 685232865Smelifaro 686232865Smelifaro /* Out of memory, returning */ 687232865Smelifaro if (tbl->cnt == tbl->size) 688232865Smelifaro return (1); 689232865Smelifaro xent = &tbl->xent[tbl->cnt]; 690232865Smelifaro xent->len = sizeof(ipfw_table_xentry); 691232865Smelifaro xent->tbl = tbl->tbl; 692232865Smelifaro if (in_nullhost(n->mask.sin_addr)) 693232865Smelifaro xent->masklen = 0; 694232865Smelifaro else 695232865Smelifaro xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 696232865Smelifaro /* Save IPv4 address as deprecated IPv6 compatible */ 697232865Smelifaro xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr; 698232865Smelifaro xent->value = n->value; 699232865Smelifaro tbl->cnt++; 700232865Smelifaro return (0); 701232865Smelifaro} 702232865Smelifaro 703232865Smelifarostatic int 704232865Smelifarodump_table_xentry_extended(struct radix_node *rn, void *arg) 705232865Smelifaro{ 706232865Smelifaro struct table_xentry * const n = (struct table_xentry *)rn; 707232865Smelifaro ipfw_xtable * const tbl = arg; 708232865Smelifaro ipfw_table_xentry *xent; 709232865Smelifaro#ifdef INET6 710232865Smelifaro int i; 711232865Smelifaro uint32_t *v; 712232865Smelifaro#endif 713232865Smelifaro /* Out of memory, returning */ 714232865Smelifaro if (tbl->cnt == tbl->size) 715232865Smelifaro return (1); 716232865Smelifaro xent = &tbl->xent[tbl->cnt]; 717232865Smelifaro xent->len = sizeof(ipfw_table_xentry); 718232865Smelifaro xent->tbl = tbl->tbl; 719232865Smelifaro 720232865Smelifaro switch (tbl->type) { 721232865Smelifaro#ifdef INET6 722232865Smelifaro case IPFW_TABLE_CIDR: 723232865Smelifaro /* Count IPv6 mask */ 724232865Smelifaro v = (uint32_t *)&n->m.mask6.sin6_addr; 725232865Smelifaro for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++) 726232865Smelifaro xent->masklen += bitcount32(*v); 727232865Smelifaro memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr)); 728232865Smelifaro break; 729232865Smelifaro#endif 730232865Smelifaro case IPFW_TABLE_INTERFACE: 731232865Smelifaro /* Assume exact mask */ 732232865Smelifaro xent->masklen = 8 * IF_NAMESIZE; 733232865Smelifaro memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE); 734232865Smelifaro break; 735232865Smelifaro 736232865Smelifaro default: 737232865Smelifaro /* unknown, skip entry */ 738232865Smelifaro return (0); 739232865Smelifaro } 740232865Smelifaro 741232865Smelifaro xent->value = n->value; 742232865Smelifaro tbl->cnt++; 743232865Smelifaro return (0); 744232865Smelifaro} 745232865Smelifaro 746232865Smelifaroint 747232865Smelifaroipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl) 748232865Smelifaro{ 749232865Smelifaro struct radix_node_head *rnh; 750232865Smelifaro 751232865Smelifaro if (tbl->tbl >= V_fw_tables_max) 752232865Smelifaro return (EINVAL); 753232865Smelifaro tbl->cnt = 0; 754232865Smelifaro tbl->type = ch->tabletype[tbl->tbl]; 755232865Smelifaro if ((rnh = ch->tables[tbl->tbl]) != NULL) 756232865Smelifaro rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl); 757232865Smelifaro if ((rnh = ch->xtables[tbl->tbl]) != NULL) 758232865Smelifaro rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl); 759232865Smelifaro return (0); 760232865Smelifaro} 761232865Smelifaro 762200601Sluigi/* end of file */ 763