ip_fw_table.c revision 201120
1/*- 2 * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_table.c 201120 2009-12-28 10:12:35Z luigi $"); 28 29/* 30 * Lookup table support for ipfw 31 * 32 * Lookup tables are implemented (at the moment) using the radix 33 * tree used for routing tables. Tables store key-value entries, where 34 * keys are network prefixes (addr/masklen), and values are integers. 35 * As a degenerate case we can interpret keys as 32-bit integers 36 * (with a /32 mask). 37 * 38 * The table is protected by the IPFW lock even for manipulation coming 39 * from userland, because operations are typically fast. 40 */ 41 42#if !defined(KLD_MODULE) 43#include "opt_ipfw.h" 44#include "opt_ipdivert.h" 45#include "opt_ipdn.h" 46#include "opt_inet.h" 47#ifndef INET 48#error IPFIREWALL requires INET. 49#endif /* INET */ 50#endif 51#include "opt_inet6.h" 52#include "opt_ipsec.h" 53 54#include <sys/param.h> 55#include <sys/systm.h> 56#include <sys/malloc.h> 57#include <sys/kernel.h> 58#include <sys/lock.h> 59#include <sys/rwlock.h> 60#include <sys/socket.h> 61#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 62#include <net/radix.h> 63#include <net/route.h> 64#include <net/vnet.h> 65 66#include <netinet/in.h> 67#include <netinet/ip_fw.h> 68#include <netinet/ipfw/ip_fw_private.h> 69 70#ifdef MAC 71#include <security/mac/mac_framework.h> 72#endif 73 74MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); 75 76struct table_entry { 77 struct radix_node rn[2]; 78 struct sockaddr_in addr, mask; 79 u_int32_t value; 80}; 81 82/* 83 * The radix code expects addr and mask to be array of bytes, 84 * with the first byte being the length of the array. rn_inithead 85 * is called with the offset in bits of the lookup key within the 86 * array. If we use a sockaddr_in as the underlying type, 87 * sin_len is conveniently located at offset 0, sin_addr is at 88 * offset 4 and normally aligned. 89 * But for portability, let's avoid assumption and make the code explicit 90 */ 91#define KEY_LEN(v) *((uint8_t *)&(v)) 92#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 93 94int 95ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 96 uint8_t mlen, uint32_t value) 97{ 98 struct radix_node_head *rnh; 99 struct table_entry *ent; 100 struct radix_node *rn; 101 102 if (tbl >= IPFW_TABLES_MAX) 103 return (EINVAL); 104 rnh = ch->tables[tbl]; 105 ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); 106 if (ent == NULL) 107 return (ENOMEM); 108 ent->value = value; 109 KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8; 110 ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 111 ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 112 IPFW_WLOCK(ch); 113 rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); 114 if (rn == NULL) { 115 IPFW_WUNLOCK(ch); 116 free(ent, M_IPFW_TBL); 117 return (EEXIST); 118 } 119 IPFW_WUNLOCK(ch); 120 return (0); 121} 122 123int 124ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 125 uint8_t mlen) 126{ 127 struct radix_node_head *rnh; 128 struct table_entry *ent; 129 struct sockaddr_in sa, mask; 130 131 if (tbl >= IPFW_TABLES_MAX) 132 return (EINVAL); 133 rnh = ch->tables[tbl]; 134 KEY_LEN(sa) = KEY_LEN(mask) = 8; 135 mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 136 sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 137 IPFW_WLOCK(ch); 138 ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); 139 if (ent == NULL) { 140 IPFW_WUNLOCK(ch); 141 return (ESRCH); 142 } 143 IPFW_WUNLOCK(ch); 144 free(ent, M_IPFW_TBL); 145 return (0); 146} 147 148static int 149flush_table_entry(struct radix_node *rn, void *arg) 150{ 151 struct radix_node_head * const rnh = arg; 152 struct table_entry *ent; 153 154 ent = (struct table_entry *) 155 rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 156 if (ent != NULL) 157 free(ent, M_IPFW_TBL); 158 return (0); 159} 160 161int 162ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 163{ 164 struct radix_node_head *rnh; 165 166 IPFW_WLOCK_ASSERT(ch); 167 168 if (tbl >= IPFW_TABLES_MAX) 169 return (EINVAL); 170 rnh = ch->tables[tbl]; 171 KASSERT(rnh != NULL, ("NULL IPFW table")); 172 rnh->rnh_walktree(rnh, flush_table_entry, rnh); 173 return (0); 174} 175 176void 177ipfw_flush_tables(struct ip_fw_chain *ch) 178{ 179 uint16_t tbl; 180 181 IPFW_WLOCK_ASSERT(ch); 182 183 for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) 184 ipfw_flush_table(ch, tbl); 185} 186 187int 188ipfw_init_tables(struct ip_fw_chain *ch) 189{ 190 int i; 191 uint16_t j; 192 193 for (i = 0; i < IPFW_TABLES_MAX; i++) { 194 if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) { 195 for (j = 0; j < i; j++) { 196 (void) ipfw_flush_table(ch, j); 197 } 198 return (ENOMEM); 199 } 200 } 201 return (0); 202} 203 204int 205ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 206 uint32_t *val) 207{ 208 struct radix_node_head *rnh; 209 struct table_entry *ent; 210 struct sockaddr_in sa; 211 212 if (tbl >= IPFW_TABLES_MAX) 213 return (0); 214 rnh = ch->tables[tbl]; 215 KEY_LEN(sa) = 8; 216 sa.sin_addr.s_addr = addr; 217 ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 218 if (ent != NULL) { 219 *val = ent->value; 220 return (1); 221 } 222 return (0); 223} 224 225static int 226count_table_entry(struct radix_node *rn, void *arg) 227{ 228 u_int32_t * const cnt = arg; 229 230 (*cnt)++; 231 return (0); 232} 233 234int 235ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 236{ 237 struct radix_node_head *rnh; 238 239 if (tbl >= IPFW_TABLES_MAX) 240 return (EINVAL); 241 rnh = ch->tables[tbl]; 242 *cnt = 0; 243 rnh->rnh_walktree(rnh, count_table_entry, cnt); 244 return (0); 245} 246 247static int 248dump_table_entry(struct radix_node *rn, void *arg) 249{ 250 struct table_entry * const n = (struct table_entry *)rn; 251 ipfw_table * const tbl = arg; 252 ipfw_table_entry *ent; 253 254 if (tbl->cnt == tbl->size) 255 return (1); 256 ent = &tbl->ent[tbl->cnt]; 257 ent->tbl = tbl->tbl; 258 if (in_nullhost(n->mask.sin_addr)) 259 ent->masklen = 0; 260 else 261 ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 262 ent->addr = n->addr.sin_addr.s_addr; 263 ent->value = n->value; 264 tbl->cnt++; 265 return (0); 266} 267 268int 269ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 270{ 271 struct radix_node_head *rnh; 272 273 if (tbl->tbl >= IPFW_TABLES_MAX) 274 return (EINVAL); 275 rnh = ch->tables[tbl->tbl]; 276 tbl->cnt = 0; 277 rnh->rnh_walktree(rnh, dump_table_entry, tbl); 278 return (0); 279} 280/* end of file */ 281