ip_fw_table.c revision 205415
1249293Sed/*- 2244541Sbrooks * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 3244541Sbrooks * 4244541Sbrooks * Redistribution and use in source and binary forms, with or without 5244541Sbrooks * modification, are permitted provided that the following conditions 6244541Sbrooks * are met: 7244541Sbrooks * 1. Redistributions of source code must retain the above copyright 8244541Sbrooks * notice, this list of conditions and the following disclaimer. 9244541Sbrooks * 2. Redistributions in binary form must reproduce the above copyright 10244541Sbrooks * notice, this list of conditions and the following disclaimer in the 11244541Sbrooks * documentation and/or other materials provided with the distribution. 12244541Sbrooks * 13244541Sbrooks * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14244541Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15244541Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16244541Sbrooks * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17244541Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18244541Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19244541Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20244541Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21244541Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22244541Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23244541Sbrooks * SUCH DAMAGE. 24244541Sbrooks */ 25244541Sbrooks 26244541Sbrooks#include <sys/cdefs.h> 27244541Sbrooks__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_table.c 205415 2010-03-21 15:54:07Z luigi $"); 28244541Sbrooks 29244541Sbrooks/* 30244541Sbrooks * Lookup table support for ipfw 31244541Sbrooks * 32244541Sbrooks * Lookup tables are implemented (at the moment) using the radix 33244541Sbrooks * tree used for routing tables. Tables store key-value entries, where 34244541Sbrooks * keys are network prefixes (addr/masklen), and values are integers. 35244541Sbrooks * As a degenerate case we can interpret keys as 32-bit integers 36244541Sbrooks * (with a /32 mask). 37244541Sbrooks * 38244541Sbrooks * The table is protected by the IPFW lock even for manipulation coming 39244541Sbrooks * from userland, because operations are typically fast. 40244541Sbrooks */ 41244541Sbrooks 42244541Sbrooks#if !defined(KLD_MODULE) 43244541Sbrooks#include "opt_ipfw.h" 44244541Sbrooks#include "opt_ipdivert.h" 45244541Sbrooks#include "opt_ipdn.h" 46249293Sed#include "opt_inet.h" 47244541Sbrooks#ifndef INET 48244541Sbrooks#error IPFIREWALL requires INET. 49244541Sbrooks#endif /* INET */ 50244541Sbrooks#endif 51244541Sbrooks#include "opt_inet6.h" 52244541Sbrooks#include "opt_ipsec.h" 53244541Sbrooks 54244541Sbrooks#include <sys/param.h> 55244541Sbrooks#include <sys/systm.h> 56244541Sbrooks#include <sys/malloc.h> 57244541Sbrooks#include <sys/kernel.h> 58244541Sbrooks#include <sys/lock.h> 59244541Sbrooks#include <sys/rwlock.h> 60244541Sbrooks#include <sys/socket.h> 61244541Sbrooks#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 62249293Sed#include <net/radix.h> 63249293Sed#include <net/route.h> 64244541Sbrooks#include <net/vnet.h> 65244541Sbrooks 66244541Sbrooks#include <netinet/in.h> 67244541Sbrooks#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 68244541Sbrooks#include <netinet/ip_fw.h> 69244541Sbrooks#include <sys/queue.h> /* LIST_HEAD */ 70244541Sbrooks#include <netinet/ipfw/ip_fw_private.h> 71244541Sbrooks 72244541Sbrooks#ifdef MAC 73244541Sbrooks#include <security/mac/mac_framework.h> 74244541Sbrooks#endif 75244541Sbrooks 76244541SbrooksMALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); 77244541Sbrooks 78244541Sbrooksstruct table_entry { 79244541Sbrooks struct radix_node rn[2]; 80244541Sbrooks struct sockaddr_in addr, mask; 81244541Sbrooks u_int32_t value; 82249293Sed}; 83244541Sbrooks 84244541Sbrooks/* 85244541Sbrooks * The radix code expects addr and mask to be array of bytes, 86244541Sbrooks * with the first byte being the length of the array. rn_inithead 87244541Sbrooks * is called with the offset in bits of the lookup key within the 88249293Sed * array. If we use a sockaddr_in as the underlying type, 89244541Sbrooks * sin_len is conveniently located at offset 0, sin_addr is at 90244541Sbrooks * offset 4 and normally aligned. 91244541Sbrooks * But for portability, let's avoid assumption and make the code explicit 92244541Sbrooks */ 93244541Sbrooks#define KEY_LEN(v) *((uint8_t *)&(v)) 94244541Sbrooks#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 95249293Sed 96244541Sbrooksint 97244541Sbrooksipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 98244541Sbrooks uint8_t mlen, uint32_t value) 99244541Sbrooks{ 100244541Sbrooks struct radix_node_head *rnh; 101244541Sbrooks struct table_entry *ent; 102244541Sbrooks struct radix_node *rn; 103244541Sbrooks 104244541Sbrooks if (tbl >= IPFW_TABLES_MAX) 105244541Sbrooks return (EINVAL); 106244541Sbrooks rnh = ch->tables[tbl]; 107244541Sbrooks ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); 108244541Sbrooks if (ent == NULL) 109244541Sbrooks return (ENOMEM); 110244541Sbrooks ent->value = value; 111244541Sbrooks KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8; 112244541Sbrooks ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 113244541Sbrooks ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 114244541Sbrooks IPFW_WLOCK(ch); 115244541Sbrooks rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); 116244541Sbrooks if (rn == NULL) { 117244541Sbrooks IPFW_WUNLOCK(ch); 118244541Sbrooks free(ent, M_IPFW_TBL); 119244541Sbrooks return (EEXIST); 120244541Sbrooks } 121244541Sbrooks IPFW_WUNLOCK(ch); 122244541Sbrooks return (0); 123244541Sbrooks} 124244541Sbrooks 125244541Sbrooksint 126244541Sbrooksipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 127244541Sbrooks uint8_t mlen) 128244541Sbrooks{ 129244541Sbrooks struct radix_node_head *rnh; 130244541Sbrooks struct table_entry *ent; 131244541Sbrooks struct sockaddr_in sa, mask; 132244541Sbrooks 133244541Sbrooks if (tbl >= IPFW_TABLES_MAX) 134244541Sbrooks return (EINVAL); 135244541Sbrooks rnh = ch->tables[tbl]; 136244541Sbrooks KEY_LEN(sa) = KEY_LEN(mask) = 8; 137244541Sbrooks mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 138244541Sbrooks sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 139244541Sbrooks IPFW_WLOCK(ch); 140244541Sbrooks ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); 141244541Sbrooks if (ent == NULL) { 142244541Sbrooks IPFW_WUNLOCK(ch); 143244541Sbrooks return (ESRCH); 144244541Sbrooks } 145244541Sbrooks IPFW_WUNLOCK(ch); 146244541Sbrooks free(ent, M_IPFW_TBL); 147244541Sbrooks return (0); 148244541Sbrooks} 149244541Sbrooks 150244541Sbrooksstatic int 151244541Sbrooksflush_table_entry(struct radix_node *rn, void *arg) 152244541Sbrooks{ 153244541Sbrooks struct radix_node_head * const rnh = arg; 154244541Sbrooks struct table_entry *ent; 155244541Sbrooks 156244541Sbrooks ent = (struct table_entry *) 157244541Sbrooks rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 158244541Sbrooks if (ent != NULL) 159244541Sbrooks free(ent, M_IPFW_TBL); 160244541Sbrooks return (0); 161244541Sbrooks} 162244541Sbrooks 163244541Sbrooksint 164244541Sbrooksipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 165244541Sbrooks{ 166244541Sbrooks struct radix_node_head *rnh; 167244541Sbrooks 168244541Sbrooks IPFW_WLOCK_ASSERT(ch); 169244541Sbrooks 170244541Sbrooks if (tbl >= IPFW_TABLES_MAX) 171244541Sbrooks return (EINVAL); 172244541Sbrooks rnh = ch->tables[tbl]; 173244541Sbrooks KASSERT(rnh != NULL, ("NULL IPFW table")); 174244541Sbrooks rnh->rnh_walktree(rnh, flush_table_entry, rnh); 175244541Sbrooks return (0); 176244541Sbrooks} 177244541Sbrooks 178244541Sbrooksvoid 179244541Sbrooksipfw_destroy_tables(struct ip_fw_chain *ch) 180244541Sbrooks{ 181244541Sbrooks uint16_t tbl; 182244541Sbrooks struct radix_node_head *rnh; 183244541Sbrooks 184249293Sed IPFW_WLOCK_ASSERT(ch); 185249293Sed 186249293Sed for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) { 187244541Sbrooks ipfw_flush_table(ch, tbl); 188244541Sbrooks rnh = ch->tables[tbl]; 189244541Sbrooks rn_detachhead((void **)&rnh); 190244541Sbrooks } 191244541Sbrooks} 192244541Sbrooks 193244541Sbrooksint 194244541Sbrooksipfw_init_tables(struct ip_fw_chain *ch) 195244541Sbrooks{ 196244541Sbrooks int i; 197244541Sbrooks uint16_t j; 198244541Sbrooks 199244541Sbrooks for (i = 0; i < IPFW_TABLES_MAX; i++) { 200244541Sbrooks if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) { 201244541Sbrooks for (j = 0; j < i; j++) { 202244541Sbrooks (void) ipfw_flush_table(ch, j); 203244541Sbrooks } 204244541Sbrooks return (ENOMEM); 205244541Sbrooks } 206244541Sbrooks } 207244541Sbrooks return (0); 208244541Sbrooks} 209244541Sbrooks 210244541Sbrooksint 211244541Sbrooksipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 212244541Sbrooks uint32_t *val) 213244541Sbrooks{ 214244541Sbrooks struct radix_node_head *rnh; 215244541Sbrooks struct table_entry *ent; 216244541Sbrooks struct sockaddr_in sa; 217244541Sbrooks 218244541Sbrooks if (tbl >= IPFW_TABLES_MAX) 219244541Sbrooks return (0); 220244541Sbrooks rnh = ch->tables[tbl]; 221244541Sbrooks KEY_LEN(sa) = 8; 222244541Sbrooks sa.sin_addr.s_addr = addr; 223244541Sbrooks ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 224244541Sbrooks if (ent != NULL) { 225244541Sbrooks *val = ent->value; 226244541Sbrooks return (1); 227244541Sbrooks } 228244541Sbrooks return (0); 229244541Sbrooks} 230244541Sbrooks 231244541Sbrooksstatic int 232244541Sbrookscount_table_entry(struct radix_node *rn, void *arg) 233244541Sbrooks{ 234244541Sbrooks u_int32_t * const cnt = arg; 235244541Sbrooks 236244541Sbrooks (*cnt)++; 237244541Sbrooks return (0); 238244541Sbrooks} 239244541Sbrooks 240244541Sbrooksint 241244541Sbrooksipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 242244541Sbrooks{ 243244541Sbrooks struct radix_node_head *rnh; 244244541Sbrooks 245244541Sbrooks if (tbl >= IPFW_TABLES_MAX) 246244541Sbrooks return (EINVAL); 247244541Sbrooks rnh = ch->tables[tbl]; 248244541Sbrooks *cnt = 0; 249244541Sbrooks rnh->rnh_walktree(rnh, count_table_entry, cnt); 250244541Sbrooks return (0); 251244541Sbrooks} 252244541Sbrooks 253244541Sbrooksstatic int 254244541Sbrooksdump_table_entry(struct radix_node *rn, void *arg) 255244541Sbrooks{ 256244541Sbrooks struct table_entry * const n = (struct table_entry *)rn; 257244541Sbrooks ipfw_table * const tbl = arg; 258244541Sbrooks ipfw_table_entry *ent; 259244541Sbrooks 260244541Sbrooks if (tbl->cnt == tbl->size) 261244541Sbrooks return (1); 262244541Sbrooks ent = &tbl->ent[tbl->cnt]; 263244541Sbrooks ent->tbl = tbl->tbl; 264244541Sbrooks if (in_nullhost(n->mask.sin_addr)) 265244541Sbrooks ent->masklen = 0; 266244541Sbrooks else 267244541Sbrooks ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 268244541Sbrooks ent->addr = n->addr.sin_addr.s_addr; 269244541Sbrooks ent->value = n->value; 270244541Sbrooks tbl->cnt++; 271244541Sbrooks return (0); 272244541Sbrooks} 273244541Sbrooks 274244541Sbrooksint 275244541Sbrooksipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 276244541Sbrooks{ 277244541Sbrooks struct radix_node_head *rnh; 278244541Sbrooks 279244541Sbrooks if (tbl->tbl >= IPFW_TABLES_MAX) 280244541Sbrooks return (EINVAL); 281244541Sbrooks rnh = ch->tables[tbl->tbl]; 282244541Sbrooks tbl->cnt = 0; 283244541Sbrooks rnh->rnh_walktree(rnh, dump_table_entry, tbl); 284244541Sbrooks return (0); 285244541Sbrooks} 286244541Sbrooks/* end of file */ 287244541Sbrooks