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