ip_fw_table.c revision 225518
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 225518 2011-09-12 21:09:56Z jhb $"); 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#if !defined(KLD_MODULE) 44200590Sluigi#include "opt_ipdivert.h" 45200590Sluigi#include "opt_ipdn.h" 46200590Sluigi#include "opt_inet.h" 47200590Sluigi#ifndef INET 48200590Sluigi#error IPFIREWALL requires INET. 49200590Sluigi#endif /* INET */ 50200590Sluigi#endif 51200590Sluigi#include "opt_inet6.h" 52200590Sluigi#include "opt_ipsec.h" 53200590Sluigi 54200590Sluigi#include <sys/param.h> 55200590Sluigi#include <sys/systm.h> 56200590Sluigi#include <sys/malloc.h> 57200590Sluigi#include <sys/kernel.h> 58200590Sluigi#include <sys/lock.h> 59200590Sluigi#include <sys/rwlock.h> 60200590Sluigi#include <sys/socket.h> 61200590Sluigi#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 62200590Sluigi#include <net/radix.h> 63200590Sluigi#include <net/route.h> 64200590Sluigi#include <net/vnet.h> 65200590Sluigi 66200590Sluigi#include <netinet/in.h> 67201732Sluigi#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 68200590Sluigi#include <netinet/ip_fw.h> 69204591Sluigi#include <sys/queue.h> /* LIST_HEAD */ 70200590Sluigi#include <netinet/ipfw/ip_fw_private.h> 71200590Sluigi 72200590Sluigi#ifdef MAC 73200590Sluigi#include <security/mac/mac_framework.h> 74200590Sluigi#endif 75200590Sluigi 76200590SluigiMALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); 77200590Sluigi 78200590Sluigistruct table_entry { 79200590Sluigi struct radix_node rn[2]; 80200590Sluigi struct sockaddr_in addr, mask; 81200590Sluigi u_int32_t value; 82200590Sluigi}; 83200590Sluigi 84201120Sluigi/* 85201120Sluigi * The radix code expects addr and mask to be array of bytes, 86201120Sluigi * with the first byte being the length of the array. rn_inithead 87201120Sluigi * is called with the offset in bits of the lookup key within the 88201120Sluigi * array. If we use a sockaddr_in as the underlying type, 89201120Sluigi * sin_len is conveniently located at offset 0, sin_addr is at 90201120Sluigi * offset 4 and normally aligned. 91201120Sluigi * But for portability, let's avoid assumption and make the code explicit 92201120Sluigi */ 93201120Sluigi#define KEY_LEN(v) *((uint8_t *)&(v)) 94201120Sluigi#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 95201120Sluigi 96200590Sluigiint 97200590Sluigiipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 98200590Sluigi uint8_t mlen, uint32_t value) 99200590Sluigi{ 100200590Sluigi struct radix_node_head *rnh; 101200590Sluigi struct table_entry *ent; 102200590Sluigi struct radix_node *rn; 103200590Sluigi 104200590Sluigi if (tbl >= IPFW_TABLES_MAX) 105200590Sluigi return (EINVAL); 106200590Sluigi rnh = ch->tables[tbl]; 107200590Sluigi ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); 108200590Sluigi if (ent == NULL) 109200590Sluigi return (ENOMEM); 110200590Sluigi ent->value = value; 111201120Sluigi KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8; 112200590Sluigi ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 113200590Sluigi ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 114200590Sluigi IPFW_WLOCK(ch); 115200590Sluigi rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); 116200590Sluigi if (rn == NULL) { 117200590Sluigi IPFW_WUNLOCK(ch); 118200590Sluigi free(ent, M_IPFW_TBL); 119200590Sluigi return (EEXIST); 120200590Sluigi } 121200590Sluigi IPFW_WUNLOCK(ch); 122200590Sluigi return (0); 123200590Sluigi} 124200590Sluigi 125200590Sluigiint 126200590Sluigiipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 127200590Sluigi uint8_t mlen) 128200590Sluigi{ 129200590Sluigi struct radix_node_head *rnh; 130200590Sluigi struct table_entry *ent; 131200590Sluigi struct sockaddr_in sa, mask; 132200590Sluigi 133200590Sluigi if (tbl >= IPFW_TABLES_MAX) 134200590Sluigi return (EINVAL); 135200590Sluigi rnh = ch->tables[tbl]; 136201120Sluigi KEY_LEN(sa) = KEY_LEN(mask) = 8; 137200590Sluigi mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 138200590Sluigi sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 139200590Sluigi IPFW_WLOCK(ch); 140200590Sluigi ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); 141200590Sluigi if (ent == NULL) { 142200590Sluigi IPFW_WUNLOCK(ch); 143200590Sluigi return (ESRCH); 144200590Sluigi } 145200590Sluigi IPFW_WUNLOCK(ch); 146200590Sluigi free(ent, M_IPFW_TBL); 147200590Sluigi return (0); 148200590Sluigi} 149200590Sluigi 150200590Sluigistatic int 151200590Sluigiflush_table_entry(struct radix_node *rn, void *arg) 152200590Sluigi{ 153200590Sluigi struct radix_node_head * const rnh = arg; 154200590Sluigi struct table_entry *ent; 155200590Sluigi 156200590Sluigi ent = (struct table_entry *) 157200590Sluigi rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 158200590Sluigi if (ent != NULL) 159200590Sluigi free(ent, M_IPFW_TBL); 160200590Sluigi return (0); 161200590Sluigi} 162200590Sluigi 163200590Sluigiint 164200590Sluigiipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 165200590Sluigi{ 166200590Sluigi struct radix_node_head *rnh; 167200590Sluigi 168200590Sluigi IPFW_WLOCK_ASSERT(ch); 169200590Sluigi 170200590Sluigi if (tbl >= IPFW_TABLES_MAX) 171200590Sluigi return (EINVAL); 172200590Sluigi rnh = ch->tables[tbl]; 173200590Sluigi KASSERT(rnh != NULL, ("NULL IPFW table")); 174200590Sluigi rnh->rnh_walktree(rnh, flush_table_entry, rnh); 175200590Sluigi return (0); 176200590Sluigi} 177200590Sluigi 178200590Sluigivoid 179205415Sluigiipfw_destroy_tables(struct ip_fw_chain *ch) 180200590Sluigi{ 181200590Sluigi uint16_t tbl; 182205415Sluigi struct radix_node_head *rnh; 183200590Sluigi 184200590Sluigi IPFW_WLOCK_ASSERT(ch); 185200590Sluigi 186205415Sluigi for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) { 187200590Sluigi ipfw_flush_table(ch, tbl); 188205415Sluigi rnh = ch->tables[tbl]; 189205415Sluigi rn_detachhead((void **)&rnh); 190205415Sluigi } 191200590Sluigi} 192200590Sluigi 193200590Sluigiint 194200590Sluigiipfw_init_tables(struct ip_fw_chain *ch) 195200590Sluigi{ 196200590Sluigi int i; 197200590Sluigi uint16_t j; 198200590Sluigi 199200590Sluigi for (i = 0; i < IPFW_TABLES_MAX; i++) { 200201120Sluigi if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) { 201200590Sluigi for (j = 0; j < i; j++) { 202200590Sluigi (void) ipfw_flush_table(ch, j); 203200590Sluigi } 204200590Sluigi return (ENOMEM); 205200590Sluigi } 206200590Sluigi } 207200590Sluigi return (0); 208200590Sluigi} 209200590Sluigi 210200590Sluigiint 211200590Sluigiipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 212200590Sluigi uint32_t *val) 213200590Sluigi{ 214200590Sluigi struct radix_node_head *rnh; 215200590Sluigi struct table_entry *ent; 216200590Sluigi struct sockaddr_in sa; 217200590Sluigi 218200590Sluigi if (tbl >= IPFW_TABLES_MAX) 219200590Sluigi return (0); 220200590Sluigi rnh = ch->tables[tbl]; 221201120Sluigi KEY_LEN(sa) = 8; 222200590Sluigi sa.sin_addr.s_addr = addr; 223200590Sluigi ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 224200590Sluigi if (ent != NULL) { 225200590Sluigi *val = ent->value; 226200590Sluigi return (1); 227200590Sluigi } 228200590Sluigi return (0); 229200590Sluigi} 230200590Sluigi 231200590Sluigistatic int 232200590Sluigicount_table_entry(struct radix_node *rn, void *arg) 233200590Sluigi{ 234200590Sluigi u_int32_t * const cnt = arg; 235200590Sluigi 236200590Sluigi (*cnt)++; 237200590Sluigi return (0); 238200590Sluigi} 239200590Sluigi 240200590Sluigiint 241200590Sluigiipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 242200590Sluigi{ 243200590Sluigi struct radix_node_head *rnh; 244200590Sluigi 245200590Sluigi if (tbl >= IPFW_TABLES_MAX) 246200590Sluigi return (EINVAL); 247200590Sluigi rnh = ch->tables[tbl]; 248200590Sluigi *cnt = 0; 249200590Sluigi rnh->rnh_walktree(rnh, count_table_entry, cnt); 250200590Sluigi return (0); 251200590Sluigi} 252200590Sluigi 253200590Sluigistatic int 254200590Sluigidump_table_entry(struct radix_node *rn, void *arg) 255200590Sluigi{ 256200590Sluigi struct table_entry * const n = (struct table_entry *)rn; 257200590Sluigi ipfw_table * const tbl = arg; 258200590Sluigi ipfw_table_entry *ent; 259200590Sluigi 260200590Sluigi if (tbl->cnt == tbl->size) 261200590Sluigi return (1); 262200590Sluigi ent = &tbl->ent[tbl->cnt]; 263200590Sluigi ent->tbl = tbl->tbl; 264200590Sluigi if (in_nullhost(n->mask.sin_addr)) 265200590Sluigi ent->masklen = 0; 266200590Sluigi else 267200590Sluigi ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 268200590Sluigi ent->addr = n->addr.sin_addr.s_addr; 269200590Sluigi ent->value = n->value; 270200590Sluigi tbl->cnt++; 271200590Sluigi return (0); 272200590Sluigi} 273200590Sluigi 274200590Sluigiint 275200590Sluigiipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 276200590Sluigi{ 277200590Sluigi struct radix_node_head *rnh; 278200590Sluigi 279200590Sluigi if (tbl->tbl >= IPFW_TABLES_MAX) 280200590Sluigi return (EINVAL); 281200590Sluigi rnh = ch->tables[tbl->tbl]; 282200590Sluigi tbl->cnt = 0; 283200590Sluigi rnh->rnh_walktree(rnh, dump_table_entry, tbl); 284200590Sluigi return (0); 285200590Sluigi} 286200601Sluigi/* end of file */ 287