ip_fw_table.c revision 200601
1/*- 2 * Copyright (c) 2002 ......... 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 200601 2009-12-16 10:48:40Z 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 39#if !defined(KLD_MODULE) 40#include "opt_ipfw.h" 41#include "opt_ipdivert.h" 42#include "opt_ipdn.h" 43#include "opt_inet.h" 44#ifndef INET 45#error IPFIREWALL requires INET. 46#endif /* INET */ 47#endif 48#include "opt_inet6.h" 49#include "opt_ipsec.h" 50 51#include <sys/param.h> 52#include <sys/systm.h> 53#include <sys/malloc.h> 54#include <sys/kernel.h> 55#include <sys/lock.h> 56#include <sys/rwlock.h> 57#include <sys/socket.h> 58#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 59#include <net/radix.h> 60#include <net/route.h> 61#include <net/vnet.h> 62 63#include <netinet/in.h> 64#include <netinet/ip_fw.h> 65#include <netinet/ipfw/ip_fw_private.h> 66 67#ifdef MAC 68#include <security/mac/mac_framework.h> 69#endif 70 71MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables"); 72 73struct table_entry { 74 struct radix_node rn[2]; 75 struct sockaddr_in addr, mask; 76 u_int32_t value; 77}; 78 79int 80ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 81 uint8_t mlen, uint32_t value) 82{ 83 struct radix_node_head *rnh; 84 struct table_entry *ent; 85 struct radix_node *rn; 86 87 if (tbl >= IPFW_TABLES_MAX) 88 return (EINVAL); 89 rnh = ch->tables[tbl]; 90 ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); 91 if (ent == NULL) 92 return (ENOMEM); 93 ent->value = value; 94 ent->addr.sin_len = ent->mask.sin_len = 8; 95 ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 96 ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 97 IPFW_WLOCK(ch); 98 rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); 99 if (rn == NULL) { 100 IPFW_WUNLOCK(ch); 101 free(ent, M_IPFW_TBL); 102 return (EEXIST); 103 } 104 IPFW_WUNLOCK(ch); 105 return (0); 106} 107 108int 109ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 110 uint8_t mlen) 111{ 112 struct radix_node_head *rnh; 113 struct table_entry *ent; 114 struct sockaddr_in sa, mask; 115 116 if (tbl >= IPFW_TABLES_MAX) 117 return (EINVAL); 118 rnh = ch->tables[tbl]; 119 sa.sin_len = mask.sin_len = 8; 120 mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 121 sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 122 IPFW_WLOCK(ch); 123 ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); 124 if (ent == NULL) { 125 IPFW_WUNLOCK(ch); 126 return (ESRCH); 127 } 128 IPFW_WUNLOCK(ch); 129 free(ent, M_IPFW_TBL); 130 return (0); 131} 132 133static int 134flush_table_entry(struct radix_node *rn, void *arg) 135{ 136 struct radix_node_head * const rnh = arg; 137 struct table_entry *ent; 138 139 ent = (struct table_entry *) 140 rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 141 if (ent != NULL) 142 free(ent, M_IPFW_TBL); 143 return (0); 144} 145 146int 147ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 148{ 149 struct radix_node_head *rnh; 150 151 IPFW_WLOCK_ASSERT(ch); 152 153 if (tbl >= IPFW_TABLES_MAX) 154 return (EINVAL); 155 rnh = ch->tables[tbl]; 156 KASSERT(rnh != NULL, ("NULL IPFW table")); 157 rnh->rnh_walktree(rnh, flush_table_entry, rnh); 158 return (0); 159} 160 161void 162ipfw_flush_tables(struct ip_fw_chain *ch) 163{ 164 uint16_t tbl; 165 166 IPFW_WLOCK_ASSERT(ch); 167 168 for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) 169 ipfw_flush_table(ch, tbl); 170} 171 172int 173ipfw_init_tables(struct ip_fw_chain *ch) 174{ 175 int i; 176 uint16_t j; 177 178 for (i = 0; i < IPFW_TABLES_MAX; i++) { 179 if (!rn_inithead((void **)&ch->tables[i], 32)) { 180 for (j = 0; j < i; j++) { 181 (void) ipfw_flush_table(ch, j); 182 } 183 return (ENOMEM); 184 } 185 } 186 return (0); 187} 188 189int 190ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 191 uint32_t *val) 192{ 193 struct radix_node_head *rnh; 194 struct table_entry *ent; 195 struct sockaddr_in sa; 196 197 if (tbl >= IPFW_TABLES_MAX) 198 return (0); 199 rnh = ch->tables[tbl]; 200 sa.sin_len = 8; 201 sa.sin_addr.s_addr = addr; 202 ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 203 if (ent != NULL) { 204 *val = ent->value; 205 return (1); 206 } 207 return (0); 208} 209 210static int 211count_table_entry(struct radix_node *rn, void *arg) 212{ 213 u_int32_t * const cnt = arg; 214 215 (*cnt)++; 216 return (0); 217} 218 219int 220ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 221{ 222 struct radix_node_head *rnh; 223 224 if (tbl >= IPFW_TABLES_MAX) 225 return (EINVAL); 226 rnh = ch->tables[tbl]; 227 *cnt = 0; 228 rnh->rnh_walktree(rnh, count_table_entry, cnt); 229 return (0); 230} 231 232static int 233dump_table_entry(struct radix_node *rn, void *arg) 234{ 235 struct table_entry * const n = (struct table_entry *)rn; 236 ipfw_table * const tbl = arg; 237 ipfw_table_entry *ent; 238 239 if (tbl->cnt == tbl->size) 240 return (1); 241 ent = &tbl->ent[tbl->cnt]; 242 ent->tbl = tbl->tbl; 243 if (in_nullhost(n->mask.sin_addr)) 244 ent->masklen = 0; 245 else 246 ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 247 ent->addr = n->addr.sin_addr.s_addr; 248 ent->value = n->value; 249 tbl->cnt++; 250 return (0); 251} 252 253int 254ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 255{ 256 struct radix_node_head *rnh; 257 258 if (tbl->tbl >= IPFW_TABLES_MAX) 259 return (EINVAL); 260 rnh = ch->tables[tbl->tbl]; 261 tbl->cnt = 0; 262 rnh->rnh_walktree(rnh, dump_table_entry, tbl); 263 return (0); 264} 265/* end of file */ 266