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>
|
48 49#include <sys/param.h> 50#include <sys/systm.h> 51#include <sys/malloc.h> 52#include <sys/kernel.h> 53#include <sys/lock.h> 54#include <sys/rwlock.h> 55#include <sys/socket.h> 56#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */ 57#include <net/radix.h> 58#include <net/route.h> 59#include <net/vnet.h> 60 61#include <netinet/in.h> 62#include <netinet/ip_var.h> /* struct ipfw_rule_ref */ 63#include <netinet/ip_fw.h> 64#include <sys/queue.h> /* LIST_HEAD */ 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 79/* 80 * The radix code expects addr and mask to be array of bytes, 81 * with the first byte being the length of the array. rn_inithead 82 * is called with the offset in bits of the lookup key within the 83 * array. If we use a sockaddr_in as the underlying type, 84 * sin_len is conveniently located at offset 0, sin_addr is at 85 * offset 4 and normally aligned. 86 * But for portability, let's avoid assumption and make the code explicit 87 */ 88#define KEY_LEN(v) *((uint8_t *)&(v)) 89#define KEY_OFS (8*offsetof(struct sockaddr_in, sin_addr)) 90 91int 92ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 93 uint8_t mlen, uint32_t value) 94{ 95 struct radix_node_head *rnh; 96 struct table_entry *ent; 97 struct radix_node *rn; 98 99 if (tbl >= IPFW_TABLES_MAX) 100 return (EINVAL); 101 rnh = ch->tables[tbl]; 102 ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO); 103 if (ent == NULL) 104 return (ENOMEM); 105 ent->value = value; 106 KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8; 107 ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 108 ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr; 109 IPFW_WLOCK(ch); 110 rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent); 111 if (rn == NULL) { 112 IPFW_WUNLOCK(ch); 113 free(ent, M_IPFW_TBL); 114 return (EEXIST); 115 } 116 IPFW_WUNLOCK(ch); 117 return (0); 118} 119 120int 121ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 122 uint8_t mlen) 123{ 124 struct radix_node_head *rnh; 125 struct table_entry *ent; 126 struct sockaddr_in sa, mask; 127 128 if (tbl >= IPFW_TABLES_MAX) 129 return (EINVAL); 130 rnh = ch->tables[tbl]; 131 KEY_LEN(sa) = KEY_LEN(mask) = 8; 132 mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0); 133 sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr; 134 IPFW_WLOCK(ch); 135 ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh); 136 if (ent == NULL) { 137 IPFW_WUNLOCK(ch); 138 return (ESRCH); 139 } 140 IPFW_WUNLOCK(ch); 141 free(ent, M_IPFW_TBL); 142 return (0); 143} 144 145static int 146flush_table_entry(struct radix_node *rn, void *arg) 147{ 148 struct radix_node_head * const rnh = arg; 149 struct table_entry *ent; 150 151 ent = (struct table_entry *) 152 rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh); 153 if (ent != NULL) 154 free(ent, M_IPFW_TBL); 155 return (0); 156} 157 158int 159ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl) 160{ 161 struct radix_node_head *rnh; 162 163 IPFW_WLOCK_ASSERT(ch); 164 165 if (tbl >= IPFW_TABLES_MAX) 166 return (EINVAL); 167 rnh = ch->tables[tbl]; 168 KASSERT(rnh != NULL, ("NULL IPFW table")); 169 rnh->rnh_walktree(rnh, flush_table_entry, rnh); 170 return (0); 171} 172 173void 174ipfw_destroy_tables(struct ip_fw_chain *ch) 175{ 176 uint16_t tbl; 177 struct radix_node_head *rnh; 178 179 IPFW_WLOCK_ASSERT(ch); 180 181 for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) { 182 ipfw_flush_table(ch, tbl); 183 rnh = ch->tables[tbl]; 184 rn_detachhead((void **)&rnh); 185 } 186} 187 188int 189ipfw_init_tables(struct ip_fw_chain *ch) 190{ 191 int i; 192 uint16_t j; 193 194 for (i = 0; i < IPFW_TABLES_MAX; i++) { 195 if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) { 196 for (j = 0; j < i; j++) { 197 (void) ipfw_flush_table(ch, j); 198 } 199 return (ENOMEM); 200 } 201 } 202 return (0); 203} 204 205int 206ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr, 207 uint32_t *val) 208{ 209 struct radix_node_head *rnh; 210 struct table_entry *ent; 211 struct sockaddr_in sa; 212 213 if (tbl >= IPFW_TABLES_MAX) 214 return (0); 215 rnh = ch->tables[tbl]; 216 KEY_LEN(sa) = 8; 217 sa.sin_addr.s_addr = addr; 218 ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh)); 219 if (ent != NULL) { 220 *val = ent->value; 221 return (1); 222 } 223 return (0); 224} 225 226static int 227count_table_entry(struct radix_node *rn, void *arg) 228{ 229 u_int32_t * const cnt = arg; 230 231 (*cnt)++; 232 return (0); 233} 234 235int 236ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt) 237{ 238 struct radix_node_head *rnh; 239 240 if (tbl >= IPFW_TABLES_MAX) 241 return (EINVAL); 242 rnh = ch->tables[tbl]; 243 *cnt = 0; 244 rnh->rnh_walktree(rnh, count_table_entry, cnt); 245 return (0); 246} 247 248static int 249dump_table_entry(struct radix_node *rn, void *arg) 250{ 251 struct table_entry * const n = (struct table_entry *)rn; 252 ipfw_table * const tbl = arg; 253 ipfw_table_entry *ent; 254 255 if (tbl->cnt == tbl->size) 256 return (1); 257 ent = &tbl->ent[tbl->cnt]; 258 ent->tbl = tbl->tbl; 259 if (in_nullhost(n->mask.sin_addr)) 260 ent->masklen = 0; 261 else 262 ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr)); 263 ent->addr = n->addr.sin_addr.s_addr; 264 ent->value = n->value; 265 tbl->cnt++; 266 return (0); 267} 268 269int 270ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl) 271{ 272 struct radix_node_head *rnh; 273 274 if (tbl->tbl >= IPFW_TABLES_MAX) 275 return (EINVAL); 276 rnh = ch->tables[tbl->tbl]; 277 tbl->cnt = 0; 278 rnh->rnh_walktree(rnh, dump_table_entry, tbl); 279 return (0); 280} 281/* end of file */
|