ip_fw_table.c revision 227293
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 227293 2011-11-07 06:44:47Z ed $"); 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 79201120Sluigi/* 80201120Sluigi * The radix code expects addr and mask to be array of bytes, 81201120Sluigi * with the first byte being the length of the array. rn_inithead 82201120Sluigi * is called with the offset in bits of the lookup key within the 83201120Sluigi * array. If we use a sockaddr_in as the underlying type, 84201120Sluigi * sin_len is conveniently located at offset 0, sin_addr is at 85201120Sluigi * offset 4 and normally aligned. 86201120Sluigi * But for portability, let's avoid assumption and make the code explicit 87201120Sluigi */ 88201120Sluigi#define KEY_LEN(v) *((uint8_t *)&(v)) 89201120Sluigi#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 90201120Sluigi 91200590Sluigiint 92200590Sluigiipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 93200590Sluigi uint8_t mlen, uint32_t value) 94200590Sluigi{ 95200590Sluigi struct radix_node_head *rnh; 96200590Sluigi struct table_entry *ent; 97200590Sluigi struct radix_node *rn; 98200590Sluigi 99200590Sluigi if (tbl >= IPFW_TABLES_MAX) 100200590Sluigi return (EINVAL); 101200590Sluigi rnh = ch->tables[tbl]; 102200590Sluigi ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); 103200590Sluigi if (ent == NULL) 104200590Sluigi return (ENOMEM); 105200590Sluigi ent->value = value; 106201120Sluigi KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8; 107200590Sluigi ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 108200590Sluigi ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 109200590Sluigi IPFW_WLOCK(ch); 110200590Sluigi rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); 111200590Sluigi if (rn == NULL) { 112200590Sluigi IPFW_WUNLOCK(ch); 113200590Sluigi free(ent, M_IPFW_TBL); 114200590Sluigi return (EEXIST); 115200590Sluigi } 116200590Sluigi IPFW_WUNLOCK(ch); 117200590Sluigi return (0); 118200590Sluigi} 119200590Sluigi 120200590Sluigiint 121200590Sluigiipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 122200590Sluigi uint8_t mlen) 123200590Sluigi{ 124200590Sluigi struct radix_node_head *rnh; 125200590Sluigi struct table_entry *ent; 126200590Sluigi struct sockaddr_in sa, mask; 127200590Sluigi 128200590Sluigi if (tbl >= IPFW_TABLES_MAX) 129200590Sluigi return (EINVAL); 130200590Sluigi rnh = ch->tables[tbl]; 131201120Sluigi KEY_LEN(sa) = KEY_LEN(mask) = 8; 132200590Sluigi mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 133200590Sluigi sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 134200590Sluigi IPFW_WLOCK(ch); 135200590Sluigi ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); 136200590Sluigi if (ent == NULL) { 137200590Sluigi IPFW_WUNLOCK(ch); 138200590Sluigi return (ESRCH); 139200590Sluigi } 140200590Sluigi IPFW_WUNLOCK(ch); 141200590Sluigi free(ent, M_IPFW_TBL); 142200590Sluigi return (0); 143200590Sluigi} 144200590Sluigi 145200590Sluigistatic int 146200590Sluigiflush_table_entry(struct radix_node *rn, void *arg) 147200590Sluigi{ 148200590Sluigi struct radix_node_head * const rnh = arg; 149200590Sluigi struct table_entry *ent; 150200590Sluigi 151200590Sluigi ent = (struct table_entry *) 152200590Sluigi rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 153200590Sluigi if (ent != NULL) 154200590Sluigi free(ent, M_IPFW_TBL); 155200590Sluigi return (0); 156200590Sluigi} 157200590Sluigi 158200590Sluigiint 159200590Sluigiipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 160200590Sluigi{ 161200590Sluigi struct radix_node_head *rnh; 162200590Sluigi 163200590Sluigi IPFW_WLOCK_ASSERT(ch); 164200590Sluigi 165200590Sluigi if (tbl >= IPFW_TABLES_MAX) 166200590Sluigi return (EINVAL); 167200590Sluigi rnh = ch->tables[tbl]; 168200590Sluigi KASSERT(rnh != NULL, ("NULL IPFW table")); 169200590Sluigi rnh->rnh_walktree(rnh, flush_table_entry, rnh); 170200590Sluigi return (0); 171200590Sluigi} 172200590Sluigi 173200590Sluigivoid 174205415Sluigiipfw_destroy_tables(struct ip_fw_chain *ch) 175200590Sluigi{ 176200590Sluigi uint16_t tbl; 177205415Sluigi struct radix_node_head *rnh; 178200590Sluigi 179200590Sluigi IPFW_WLOCK_ASSERT(ch); 180200590Sluigi 181205415Sluigi for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) { 182200590Sluigi ipfw_flush_table(ch, tbl); 183205415Sluigi rnh = ch->tables[tbl]; 184205415Sluigi rn_detachhead((void **)&rnh); 185205415Sluigi } 186200590Sluigi} 187200590Sluigi 188200590Sluigiint 189200590Sluigiipfw_init_tables(struct ip_fw_chain *ch) 190200590Sluigi{ 191200590Sluigi int i; 192200590Sluigi uint16_t j; 193200590Sluigi 194200590Sluigi for (i = 0; i < IPFW_TABLES_MAX; i++) { 195201120Sluigi if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) { 196200590Sluigi for (j = 0; j < i; j++) { 197200590Sluigi (void) ipfw_flush_table(ch, j); 198200590Sluigi } 199200590Sluigi return (ENOMEM); 200200590Sluigi } 201200590Sluigi } 202200590Sluigi return (0); 203200590Sluigi} 204200590Sluigi 205200590Sluigiint 206200590Sluigiipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 207200590Sluigi uint32_t *val) 208200590Sluigi{ 209200590Sluigi struct radix_node_head *rnh; 210200590Sluigi struct table_entry *ent; 211200590Sluigi struct sockaddr_in sa; 212200590Sluigi 213200590Sluigi if (tbl >= IPFW_TABLES_MAX) 214200590Sluigi return (0); 215200590Sluigi rnh = ch->tables[tbl]; 216201120Sluigi KEY_LEN(sa) = 8; 217200590Sluigi sa.sin_addr.s_addr = addr; 218200590Sluigi ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 219200590Sluigi if (ent != NULL) { 220200590Sluigi *val = ent->value; 221200590Sluigi return (1); 222200590Sluigi } 223200590Sluigi return (0); 224200590Sluigi} 225200590Sluigi 226200590Sluigistatic int 227200590Sluigicount_table_entry(struct radix_node *rn, void *arg) 228200590Sluigi{ 229200590Sluigi u_int32_t * const cnt = arg; 230200590Sluigi 231200590Sluigi (*cnt)++; 232200590Sluigi return (0); 233200590Sluigi} 234200590Sluigi 235200590Sluigiint 236200590Sluigiipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 237200590Sluigi{ 238200590Sluigi struct radix_node_head *rnh; 239200590Sluigi 240200590Sluigi if (tbl >= IPFW_TABLES_MAX) 241200590Sluigi return (EINVAL); 242200590Sluigi rnh = ch->tables[tbl]; 243200590Sluigi *cnt = 0; 244200590Sluigi rnh->rnh_walktree(rnh, count_table_entry, cnt); 245200590Sluigi return (0); 246200590Sluigi} 247200590Sluigi 248200590Sluigistatic int 249200590Sluigidump_table_entry(struct radix_node *rn, void *arg) 250200590Sluigi{ 251200590Sluigi struct table_entry * const n = (struct table_entry *)rn; 252200590Sluigi ipfw_table * const tbl = arg; 253200590Sluigi ipfw_table_entry *ent; 254200590Sluigi 255200590Sluigi if (tbl->cnt == tbl->size) 256200590Sluigi return (1); 257200590Sluigi ent = &tbl->ent[tbl->cnt]; 258200590Sluigi ent->tbl = tbl->tbl; 259200590Sluigi if (in_nullhost(n->mask.sin_addr)) 260200590Sluigi ent->masklen = 0; 261200590Sluigi else 262200590Sluigi ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 263200590Sluigi ent->addr = n->addr.sin_addr.s_addr; 264200590Sluigi ent->value = n->value; 265200590Sluigi tbl->cnt++; 266200590Sluigi return (0); 267200590Sluigi} 268200590Sluigi 269200590Sluigiint 270200590Sluigiipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 271200590Sluigi{ 272200590Sluigi struct radix_node_head *rnh; 273200590Sluigi 274200590Sluigi if (tbl->tbl >= IPFW_TABLES_MAX) 275200590Sluigi return (EINVAL); 276200590Sluigi rnh = ch->tables[tbl->tbl]; 277200590Sluigi tbl->cnt = 0; 278200590Sluigi rnh->rnh_walktree(rnh, dump_table_entry, tbl); 279200590Sluigi return (0); 280200590Sluigi} 281200601Sluigi/* end of file */ 282