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