ip_fw_table.c revision 238265
1200590Sluigi/*-
2200673Sru * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko.
3200590Sluigi *
4200590Sluigi * Redistribution and use in source and binary forms, with or without
5200590Sluigi * modification, are permitted provided that the following conditions
6200590Sluigi * are met:
7200590Sluigi * 1. Redistributions of source code must retain the above copyright
8200590Sluigi *    notice, this list of conditions and the following disclaimer.
9200590Sluigi * 2. Redistributions in binary form must reproduce the above copyright
10200590Sluigi *    notice, this list of conditions and the following disclaimer in the
11200590Sluigi *    documentation and/or other materials provided with the distribution.
12200590Sluigi *
13200590Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14200590Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15200590Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16200590Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17200590Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18200590Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19200590Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20200590Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21200590Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22200590Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23200590Sluigi * SUCH DAMAGE.
24200590Sluigi */
25200590Sluigi
26200590Sluigi#include <sys/cdefs.h>
27200590Sluigi__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_table.c 238265 2012-07-08 21:13:04Z melifaro $");
28200590Sluigi
29200590Sluigi/*
30200601Sluigi * Lookup table support for ipfw
31200601Sluigi *
32200601Sluigi * Lookup tables are implemented (at the moment) using the radix
33200601Sluigi * tree used for routing tables. Tables store key-value entries, where
34200601Sluigi * keys are network prefixes (addr/masklen), and values are integers.
35200601Sluigi * As a degenerate case we can interpret keys as 32-bit integers
36200601Sluigi * (with a /32 mask).
37200838Sluigi *
38200838Sluigi * The table is protected by the IPFW lock even for manipulation coming
39200838Sluigi * from userland, because operations are typically fast.
40200590Sluigi */
41200590Sluigi
42225518Sjhb#include "opt_ipfw.h"
43200590Sluigi#include "opt_inet.h"
44200590Sluigi#ifndef INET
45200590Sluigi#error IPFIREWALL requires INET.
46200590Sluigi#endif /* INET */
47200590Sluigi#include "opt_inet6.h"
48200590Sluigi
49200590Sluigi#include <sys/param.h>
50200590Sluigi#include <sys/systm.h>
51200590Sluigi#include <sys/malloc.h>
52200590Sluigi#include <sys/kernel.h>
53200590Sluigi#include <sys/lock.h>
54200590Sluigi#include <sys/rwlock.h>
55200590Sluigi#include <sys/socket.h>
56200590Sluigi#include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
57200590Sluigi#include <net/radix.h>
58200590Sluigi#include <net/route.h>
59200590Sluigi#include <net/vnet.h>
60200590Sluigi
61200590Sluigi#include <netinet/in.h>
62201732Sluigi#include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
63200590Sluigi#include <netinet/ip_fw.h>
64204591Sluigi#include <sys/queue.h> /* LIST_HEAD */
65200590Sluigi#include <netinet/ipfw/ip_fw_private.h>
66200590Sluigi
67200590Sluigi#ifdef MAC
68200590Sluigi#include <security/mac/mac_framework.h>
69200590Sluigi#endif
70200590Sluigi
71227293Sedstatic MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
72200590Sluigi
73200590Sluigistruct table_entry {
74200590Sluigi	struct radix_node	rn[2];
75200590Sluigi	struct sockaddr_in	addr, mask;
76200590Sluigi	u_int32_t		value;
77200590Sluigi};
78200590Sluigi
79232865Smelifarostruct xaddr_iface {
80232865Smelifaro	uint8_t		if_len;		/* length of this struct */
81232865Smelifaro	uint8_t		pad[7];		/* Align name */
82232865Smelifaro	char 		ifname[IF_NAMESIZE];	/* Interface name */
83232865Smelifaro};
84232865Smelifaro
85232865Smelifarostruct table_xentry {
86232865Smelifaro	struct radix_node	rn[2];
87232865Smelifaro	union {
88232865Smelifaro#ifdef INET6
89232865Smelifaro		struct sockaddr_in6	addr6;
90232865Smelifaro#endif
91232865Smelifaro		struct xaddr_iface	iface;
92232865Smelifaro	} a;
93232865Smelifaro	union {
94232865Smelifaro#ifdef INET6
95232865Smelifaro		struct sockaddr_in6	mask6;
96232865Smelifaro#endif
97232865Smelifaro		struct xaddr_iface	ifmask;
98232865Smelifaro	} m;
99232865Smelifaro	u_int32_t		value;
100232865Smelifaro};
101232865Smelifaro
102201120Sluigi/*
103201120Sluigi * The radix code expects addr and mask to be array of bytes,
104201120Sluigi * with the first byte being the length of the array. rn_inithead
105201120Sluigi * is called with the offset in bits of the lookup key within the
106201120Sluigi * array. If we use a sockaddr_in as the underlying type,
107201120Sluigi * sin_len is conveniently located at offset 0, sin_addr is at
108201120Sluigi * offset 4 and normally aligned.
109201120Sluigi * But for portability, let's avoid assumption and make the code explicit
110201120Sluigi */
111201120Sluigi#define KEY_LEN(v)	*((uint8_t *)&(v))
112201120Sluigi#define KEY_OFS		(8*offsetof(struct sockaddr_in, sin_addr))
113232865Smelifaro/*
114232865Smelifaro * Do not require radix to compare more than actual IPv4/IPv6 address
115232865Smelifaro */
116232865Smelifaro#define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
117232865Smelifaro#define KEY_LEN_INET6	(offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr))
118232865Smelifaro#define KEY_LEN_IFACE	(offsetof(struct xaddr_iface, ifname))
119201120Sluigi
120232865Smelifaro#define OFF_LEN_INET	(8 * offsetof(struct sockaddr_in, sin_addr))
121232865Smelifaro#define OFF_LEN_INET6	(8 * offsetof(struct sockaddr_in6, sin6_addr))
122232865Smelifaro#define OFF_LEN_IFACE	(8 * offsetof(struct xaddr_iface, ifname))
123232865Smelifaro
124232865Smelifaro
125232865Smelifarostatic inline void
126232865Smelifaroipv6_writemask(struct in6_addr *addr6, uint8_t mask)
127232865Smelifaro{
128232865Smelifaro	uint32_t *cp;
129232865Smelifaro
130232865Smelifaro	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
131232865Smelifaro		*cp++ = 0xFFFFFFFF;
132232865Smelifaro	*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
133232865Smelifaro}
134232865Smelifaro
135200590Sluigiint
136232865Smelifaroipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
137232865Smelifaro    uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value)
138200590Sluigi{
139232865Smelifaro	struct radix_node_head *rnh, **rnh_ptr;
140200590Sluigi	struct table_entry *ent;
141232865Smelifaro	struct table_xentry *xent;
142200590Sluigi	struct radix_node *rn;
143232865Smelifaro	in_addr_t addr;
144232865Smelifaro	int offset;
145232865Smelifaro	void *ent_ptr;
146232865Smelifaro	struct sockaddr *addr_ptr, *mask_ptr;
147232865Smelifaro	char c;
148200590Sluigi
149232865Smelifaro	if (tbl >= V_fw_tables_max)
150200590Sluigi		return (EINVAL);
151232865Smelifaro
152232865Smelifaro	switch (type) {
153232865Smelifaro	case IPFW_TABLE_CIDR:
154232865Smelifaro		if (plen == sizeof(in_addr_t)) {
155232865Smelifaro#ifdef INET
156236819Smelifaro			/* IPv4 case */
157236819Smelifaro			if (mlen > 32)
158236819Smelifaro				return (EINVAL);
159232865Smelifaro			ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
160232865Smelifaro			ent->value = value;
161232865Smelifaro			/* Set 'total' structure length */
162232865Smelifaro			KEY_LEN(ent->addr) = KEY_LEN_INET;
163232865Smelifaro			KEY_LEN(ent->mask) = KEY_LEN_INET;
164232865Smelifaro			/* Set offset of IPv4 address in bits */
165232865Smelifaro			offset = OFF_LEN_INET;
166232865Smelifaro			ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
167232865Smelifaro			addr = *((in_addr_t *)paddr);
168232865Smelifaro			ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
169232865Smelifaro			/* Set pointers */
170232865Smelifaro			rnh_ptr = &ch->tables[tbl];
171232865Smelifaro			ent_ptr = ent;
172232865Smelifaro			addr_ptr = (struct sockaddr *)&ent->addr;
173232865Smelifaro			mask_ptr = (struct sockaddr *)&ent->mask;
174232865Smelifaro#endif
175232865Smelifaro#ifdef INET6
176232865Smelifaro		} else if (plen == sizeof(struct in6_addr)) {
177232865Smelifaro			/* IPv6 case */
178232865Smelifaro			if (mlen > 128)
179232865Smelifaro				return (EINVAL);
180232865Smelifaro			xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
181232865Smelifaro			xent->value = value;
182232865Smelifaro			/* Set 'total' structure length */
183232865Smelifaro			KEY_LEN(xent->a.addr6) = KEY_LEN_INET6;
184232865Smelifaro			KEY_LEN(xent->m.mask6) = KEY_LEN_INET6;
185232865Smelifaro			/* Set offset of IPv6 address in bits */
186232865Smelifaro			offset = OFF_LEN_INET6;
187232865Smelifaro			ipv6_writemask(&xent->m.mask6.sin6_addr, mlen);
188232865Smelifaro			memcpy(&xent->a.addr6.sin6_addr, paddr, sizeof(struct in6_addr));
189232865Smelifaro			APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr);
190232865Smelifaro			/* Set pointers */
191232865Smelifaro			rnh_ptr = &ch->xtables[tbl];
192232865Smelifaro			ent_ptr = xent;
193232865Smelifaro			addr_ptr = (struct sockaddr *)&xent->a.addr6;
194232865Smelifaro			mask_ptr = (struct sockaddr *)&xent->m.mask6;
195232865Smelifaro#endif
196232865Smelifaro		} else {
197232865Smelifaro			/* Unknown CIDR type */
198232865Smelifaro			return (EINVAL);
199232865Smelifaro		}
200232865Smelifaro		break;
201232865Smelifaro
202232865Smelifaro	case IPFW_TABLE_INTERFACE:
203232865Smelifaro		/* Check if string is terminated */
204232865Smelifaro		c = ((char *)paddr)[IF_NAMESIZE - 1];
205232865Smelifaro		((char *)paddr)[IF_NAMESIZE - 1] = '\0';
206232865Smelifaro		if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0'))
207232865Smelifaro			return (EINVAL);
208232865Smelifaro
209232865Smelifaro		/* Include last \0 into comparison */
210232865Smelifaro		mlen++;
211232865Smelifaro
212232865Smelifaro		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
213232865Smelifaro		xent->value = value;
214232865Smelifaro		/* Set 'total' structure length */
215232865Smelifaro		KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen;
216232865Smelifaro		KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen;
217232865Smelifaro		/* Set offset of interface name in bits */
218232865Smelifaro		offset = OFF_LEN_IFACE;
219232865Smelifaro		memcpy(xent->a.iface.ifname, paddr, mlen);
220232865Smelifaro		/* Assume direct match */
221232865Smelifaro		/* TODO: Add interface pattern matching */
222232865Smelifaro#if 0
223232865Smelifaro		memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE);
224232865Smelifaro		mask_ptr = (struct sockaddr *)&xent->m.ifmask;
225232865Smelifaro#endif
226232865Smelifaro		/* Set pointers */
227232865Smelifaro		rnh_ptr = &ch->xtables[tbl];
228232865Smelifaro		ent_ptr = xent;
229232865Smelifaro		addr_ptr = (struct sockaddr *)&xent->a.iface;
230232865Smelifaro		mask_ptr = NULL;
231232865Smelifaro		break;
232232865Smelifaro
233232865Smelifaro	default:
234232865Smelifaro		return (EINVAL);
235232865Smelifaro	}
236232865Smelifaro
237200590Sluigi	IPFW_WLOCK(ch);
238232865Smelifaro
239232865Smelifaro	/* Check if tabletype is valid */
240232865Smelifaro	if ((ch->tabletype[tbl] != 0) && (ch->tabletype[tbl] != type)) {
241232865Smelifaro		IPFW_WUNLOCK(ch);
242232865Smelifaro		free(ent_ptr, M_IPFW_TBL);
243232865Smelifaro		return (EINVAL);
244232865Smelifaro	}
245232865Smelifaro
246232865Smelifaro	/* Check if radix tree exists */
247232865Smelifaro	if ((rnh = *rnh_ptr) == NULL) {
248232865Smelifaro		IPFW_WUNLOCK(ch);
249232865Smelifaro		/* Create radix for a new table */
250232865Smelifaro		if (!rn_inithead((void **)&rnh, offset)) {
251232865Smelifaro			free(ent_ptr, M_IPFW_TBL);
252232865Smelifaro			return (ENOMEM);
253232865Smelifaro		}
254232865Smelifaro
255232865Smelifaro		IPFW_WLOCK(ch);
256232865Smelifaro		if (*rnh_ptr != NULL) {
257232865Smelifaro			/* Tree is already attached by other thread */
258232865Smelifaro			rn_detachhead((void **)&rnh);
259232865Smelifaro			rnh = *rnh_ptr;
260232865Smelifaro			/* Check table type another time */
261232865Smelifaro			if (ch->tabletype[tbl] != type) {
262232865Smelifaro				IPFW_WUNLOCK(ch);
263232865Smelifaro				free(ent_ptr, M_IPFW_TBL);
264232865Smelifaro				return (EINVAL);
265232865Smelifaro			}
266232865Smelifaro		} else {
267232865Smelifaro			*rnh_ptr = rnh;
268232865Smelifaro			/*
269232865Smelifaro			 * Set table type. It can be set already
270232865Smelifaro			 * (if we have IPv6-only table) but setting
271232865Smelifaro			 * it another time does not hurt
272232865Smelifaro			 */
273232865Smelifaro			ch->tabletype[tbl] = type;
274232865Smelifaro		}
275232865Smelifaro	}
276232865Smelifaro
277232865Smelifaro	rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr);
278232865Smelifaro	IPFW_WUNLOCK(ch);
279232865Smelifaro
280200590Sluigi	if (rn == NULL) {
281232865Smelifaro		free(ent_ptr, M_IPFW_TBL);
282200590Sluigi		return (EEXIST);
283200590Sluigi	}
284200590Sluigi	return (0);
285200590Sluigi}
286200590Sluigi
287200590Sluigiint
288232865Smelifaroipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
289232865Smelifaro    uint8_t plen, uint8_t mlen, uint8_t type)
290200590Sluigi{
291232865Smelifaro	struct radix_node_head *rnh, **rnh_ptr;
292200590Sluigi	struct table_entry *ent;
293232865Smelifaro	in_addr_t addr;
294200590Sluigi	struct sockaddr_in sa, mask;
295232865Smelifaro	struct sockaddr *sa_ptr, *mask_ptr;
296232865Smelifaro	char c;
297200590Sluigi
298232865Smelifaro	if (tbl >= V_fw_tables_max)
299200590Sluigi		return (EINVAL);
300232865Smelifaro
301232865Smelifaro	switch (type) {
302232865Smelifaro	case IPFW_TABLE_CIDR:
303232865Smelifaro		if (plen == sizeof(in_addr_t)) {
304232865Smelifaro			/* Set 'total' structure length */
305232865Smelifaro			KEY_LEN(sa) = KEY_LEN_INET;
306232865Smelifaro			KEY_LEN(mask) = KEY_LEN_INET;
307232865Smelifaro			mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
308232865Smelifaro			addr = *((in_addr_t *)paddr);
309232865Smelifaro			sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
310232865Smelifaro			rnh_ptr = &ch->tables[tbl];
311232865Smelifaro			sa_ptr = (struct sockaddr *)&sa;
312232865Smelifaro			mask_ptr = (struct sockaddr *)&mask;
313232865Smelifaro#ifdef INET6
314232865Smelifaro		} else if (plen == sizeof(struct in6_addr)) {
315232865Smelifaro			/* IPv6 case */
316232865Smelifaro			if (mlen > 128)
317232865Smelifaro				return (EINVAL);
318232865Smelifaro			struct sockaddr_in6 sa6, mask6;
319232865Smelifaro			memset(&sa6, 0, sizeof(struct sockaddr_in6));
320232865Smelifaro			memset(&mask6, 0, sizeof(struct sockaddr_in6));
321232865Smelifaro			/* Set 'total' structure length */
322232865Smelifaro			KEY_LEN(sa6) = KEY_LEN_INET6;
323232865Smelifaro			KEY_LEN(mask6) = KEY_LEN_INET6;
324232865Smelifaro			ipv6_writemask(&mask6.sin6_addr, mlen);
325232865Smelifaro			memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
326232865Smelifaro			APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr);
327232865Smelifaro			rnh_ptr = &ch->xtables[tbl];
328232865Smelifaro			sa_ptr = (struct sockaddr *)&sa6;
329232865Smelifaro			mask_ptr = (struct sockaddr *)&mask6;
330232865Smelifaro#endif
331232865Smelifaro		} else {
332232865Smelifaro			/* Unknown CIDR type */
333232865Smelifaro			return (EINVAL);
334232865Smelifaro		}
335232865Smelifaro		break;
336232865Smelifaro
337232865Smelifaro	case IPFW_TABLE_INTERFACE:
338232865Smelifaro		/* Check if string is terminated */
339232865Smelifaro		c = ((char *)paddr)[IF_NAMESIZE - 1];
340232865Smelifaro		((char *)paddr)[IF_NAMESIZE - 1] = '\0';
341232865Smelifaro		if (((mlen = strlen((char *)paddr)) == IF_NAMESIZE - 1) && (c != '\0'))
342232865Smelifaro			return (EINVAL);
343232865Smelifaro
344232865Smelifaro		struct xaddr_iface ifname, ifmask;
345232865Smelifaro		memset(&ifname, 0, sizeof(ifname));
346232865Smelifaro
347238265Smelifaro		/* Include last \0 into comparison */
348238265Smelifaro		mlen++;
349238265Smelifaro
350232865Smelifaro		/* Set 'total' structure length */
351238265Smelifaro		KEY_LEN(ifname) = KEY_LEN_IFACE + mlen;
352238265Smelifaro		KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen;
353232865Smelifaro		/* Assume direct match */
354232865Smelifaro		/* FIXME: Add interface pattern matching */
355232865Smelifaro#if 0
356232865Smelifaro		memset(ifmask.ifname, 0xFF, IF_NAMESIZE);
357232865Smelifaro		mask_ptr = (struct sockaddr *)&ifmask;
358232865Smelifaro#endif
359232865Smelifaro		mask_ptr = NULL;
360232865Smelifaro		memcpy(ifname.ifname, paddr, mlen);
361232865Smelifaro		/* Set pointers */
362232865Smelifaro		rnh_ptr = &ch->xtables[tbl];
363232865Smelifaro		sa_ptr = (struct sockaddr *)&ifname;
364232865Smelifaro
365232865Smelifaro		break;
366232865Smelifaro
367232865Smelifaro	default:
368232865Smelifaro		return (EINVAL);
369232865Smelifaro	}
370232865Smelifaro
371200590Sluigi	IPFW_WLOCK(ch);
372232865Smelifaro	if ((rnh = *rnh_ptr) == NULL) {
373200590Sluigi		IPFW_WUNLOCK(ch);
374200590Sluigi		return (ESRCH);
375200590Sluigi	}
376232865Smelifaro
377232865Smelifaro	if (ch->tabletype[tbl] != type) {
378232865Smelifaro		IPFW_WUNLOCK(ch);
379232865Smelifaro		return (EINVAL);
380232865Smelifaro	}
381232865Smelifaro
382232865Smelifaro	ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh);
383200590Sluigi	IPFW_WUNLOCK(ch);
384232865Smelifaro
385232865Smelifaro	if (ent == NULL)
386232865Smelifaro		return (ESRCH);
387232865Smelifaro
388200590Sluigi	free(ent, M_IPFW_TBL);
389200590Sluigi	return (0);
390200590Sluigi}
391200590Sluigi
392200590Sluigistatic int
393200590Sluigiflush_table_entry(struct radix_node *rn, void *arg)
394200590Sluigi{
395200590Sluigi	struct radix_node_head * const rnh = arg;
396200590Sluigi	struct table_entry *ent;
397200590Sluigi
398200590Sluigi	ent = (struct table_entry *)
399200590Sluigi	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
400200590Sluigi	if (ent != NULL)
401200590Sluigi		free(ent, M_IPFW_TBL);
402200590Sluigi	return (0);
403200590Sluigi}
404200590Sluigi
405200590Sluigiint
406200590Sluigiipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl)
407200590Sluigi{
408232865Smelifaro	struct radix_node_head *rnh, *xrnh;
409200590Sluigi
410232865Smelifaro	if (tbl >= V_fw_tables_max)
411232865Smelifaro		return (EINVAL);
412200590Sluigi
413232865Smelifaro	/*
414232865Smelifaro	 * We free both (IPv4 and extended) radix trees and
415232865Smelifaro	 * clear table type here to permit table to be reused
416232865Smelifaro	 * for different type without module reload
417232865Smelifaro	 */
418232865Smelifaro
419232865Smelifaro	IPFW_WLOCK(ch);
420232865Smelifaro	/* Set IPv4 table pointer to zero */
421232865Smelifaro	if ((rnh = ch->tables[tbl]) != NULL)
422232865Smelifaro		ch->tables[tbl] = NULL;
423232865Smelifaro	/* Set extended table pointer to zero */
424232865Smelifaro	if ((xrnh = ch->xtables[tbl]) != NULL)
425232865Smelifaro		ch->xtables[tbl] = NULL;
426232865Smelifaro	/* Zero table type */
427232865Smelifaro	ch->tabletype[tbl] = 0;
428232865Smelifaro	IPFW_WUNLOCK(ch);
429232865Smelifaro
430232865Smelifaro	if (rnh != NULL) {
431232865Smelifaro		rnh->rnh_walktree(rnh, flush_table_entry, rnh);
432232865Smelifaro		rn_detachhead((void **)&rnh);
433232865Smelifaro	}
434232865Smelifaro
435232865Smelifaro	if (xrnh != NULL) {
436232865Smelifaro		xrnh->rnh_walktree(xrnh, flush_table_entry, xrnh);
437232865Smelifaro		rn_detachhead((void **)&xrnh);
438232865Smelifaro	}
439232865Smelifaro
440200590Sluigi	return (0);
441200590Sluigi}
442200590Sluigi
443200590Sluigivoid
444205415Sluigiipfw_destroy_tables(struct ip_fw_chain *ch)
445200590Sluigi{
446200590Sluigi	uint16_t tbl;
447200590Sluigi
448232865Smelifaro	/* Flush all tables */
449232865Smelifaro	for (tbl = 0; tbl < V_fw_tables_max; tbl++)
450232865Smelifaro		ipfw_flush_table(ch, tbl);
451200590Sluigi
452232865Smelifaro	/* Free pointers itself */
453232865Smelifaro	free(ch->tables, M_IPFW);
454232865Smelifaro	free(ch->xtables, M_IPFW);
455232865Smelifaro	free(ch->tabletype, M_IPFW);
456200590Sluigi}
457200590Sluigi
458200590Sluigiint
459200590Sluigiipfw_init_tables(struct ip_fw_chain *ch)
460232865Smelifaro{
461232865Smelifaro	/* Allocate pointers */
462232865Smelifaro	ch->tables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
463232865Smelifaro	ch->xtables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
464232865Smelifaro	ch->tabletype = malloc(V_fw_tables_max * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO);
465200590Sluigi	return (0);
466200590Sluigi}
467200590Sluigi
468200590Sluigiint
469233478Smelifaroipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
470233478Smelifaro{
471233478Smelifaro	struct radix_node_head **tables, **xtables, *rnh;
472233478Smelifaro	struct radix_node_head **tables_old, **xtables_old;
473233478Smelifaro	uint8_t *tabletype, *tabletype_old;
474233478Smelifaro	unsigned int ntables_old, tbl;
475233478Smelifaro
476233478Smelifaro	/* Check new value for validity */
477233478Smelifaro	if (ntables > IPFW_TABLES_MAX)
478233478Smelifaro		ntables = IPFW_TABLES_MAX;
479233478Smelifaro
480233478Smelifaro	/* Allocate new pointers */
481233478Smelifaro	tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
482233478Smelifaro	xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
483233478Smelifaro	tabletype = malloc(ntables * sizeof(uint8_t), M_IPFW, M_WAITOK | M_ZERO);
484233478Smelifaro
485233478Smelifaro	IPFW_WLOCK(ch);
486233478Smelifaro
487233478Smelifaro	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
488233478Smelifaro
489233478Smelifaro	/* Copy old table pointers */
490233478Smelifaro	memcpy(tables, ch->tables, sizeof(void *) * tbl);
491233478Smelifaro	memcpy(xtables, ch->xtables, sizeof(void *) * tbl);
492233478Smelifaro	memcpy(tabletype, ch->tabletype, sizeof(uint8_t) * tbl);
493233478Smelifaro
494233478Smelifaro	/* Change pointers and number of tables */
495233478Smelifaro	tables_old = ch->tables;
496233478Smelifaro	xtables_old = ch->xtables;
497233478Smelifaro	tabletype_old = ch->tabletype;
498233478Smelifaro	ch->tables = tables;
499233478Smelifaro	ch->xtables = xtables;
500233478Smelifaro	ch->tabletype = tabletype;
501233478Smelifaro
502233478Smelifaro	ntables_old = V_fw_tables_max;
503233478Smelifaro	V_fw_tables_max = ntables;
504233478Smelifaro
505233478Smelifaro	IPFW_WUNLOCK(ch);
506233478Smelifaro
507233478Smelifaro	/* Check if we need to destroy radix trees */
508233478Smelifaro	if (ntables < ntables_old) {
509233478Smelifaro		for (tbl = ntables; tbl < ntables_old; tbl++) {
510233478Smelifaro			if ((rnh = tables_old[tbl]) != NULL) {
511233478Smelifaro				rnh->rnh_walktree(rnh, flush_table_entry, rnh);
512233478Smelifaro				rn_detachhead((void **)&rnh);
513233478Smelifaro			}
514233478Smelifaro
515233478Smelifaro			if ((rnh = xtables_old[tbl]) != NULL) {
516233478Smelifaro				rnh->rnh_walktree(rnh, flush_table_entry, rnh);
517233478Smelifaro				rn_detachhead((void **)&rnh);
518233478Smelifaro			}
519233478Smelifaro		}
520233478Smelifaro	}
521233478Smelifaro
522233478Smelifaro	/* Free old pointers */
523233478Smelifaro	free(tables_old, M_IPFW);
524233478Smelifaro	free(xtables_old, M_IPFW);
525233478Smelifaro	free(tabletype_old, M_IPFW);
526233478Smelifaro
527233478Smelifaro	return (0);
528233478Smelifaro}
529233478Smelifaro
530233478Smelifaroint
531200590Sluigiipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
532200590Sluigi    uint32_t *val)
533200590Sluigi{
534200590Sluigi	struct radix_node_head *rnh;
535200590Sluigi	struct table_entry *ent;
536200590Sluigi	struct sockaddr_in sa;
537200590Sluigi
538232865Smelifaro	if (tbl >= V_fw_tables_max)
539200590Sluigi		return (0);
540232865Smelifaro	if ((rnh = ch->tables[tbl]) == NULL)
541232865Smelifaro		return (0);
542232865Smelifaro	KEY_LEN(sa) = KEY_LEN_INET;
543200590Sluigi	sa.sin_addr.s_addr = addr;
544200590Sluigi	ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
545200590Sluigi	if (ent != NULL) {
546200590Sluigi		*val = ent->value;
547200590Sluigi		return (1);
548200590Sluigi	}
549200590Sluigi	return (0);
550200590Sluigi}
551200590Sluigi
552232865Smelifaroint
553232865Smelifaroipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
554232865Smelifaro    uint32_t *val, int type)
555232865Smelifaro{
556232865Smelifaro	struct radix_node_head *rnh;
557232865Smelifaro	struct table_xentry *xent;
558232865Smelifaro	struct sockaddr_in6 sa6;
559232865Smelifaro	struct xaddr_iface iface;
560232865Smelifaro
561232865Smelifaro	if (tbl >= V_fw_tables_max)
562232865Smelifaro		return (0);
563232865Smelifaro	if ((rnh = ch->xtables[tbl]) == NULL)
564232865Smelifaro		return (0);
565232865Smelifaro
566232865Smelifaro	switch (type) {
567232865Smelifaro	case IPFW_TABLE_CIDR:
568232865Smelifaro		KEY_LEN(sa6) = KEY_LEN_INET6;
569232865Smelifaro		memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
570232865Smelifaro		xent = (struct table_xentry *)(rnh->rnh_lookup(&sa6, NULL, rnh));
571232865Smelifaro		break;
572232865Smelifaro
573232865Smelifaro	case IPFW_TABLE_INTERFACE:
574237479Smelifaro		KEY_LEN(iface) = KEY_LEN_IFACE +
575238265Smelifaro		    strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1;
576232865Smelifaro		/* Assume direct match */
577232865Smelifaro		/* FIXME: Add interface pattern matching */
578232865Smelifaro		xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh));
579232865Smelifaro		break;
580232865Smelifaro
581232865Smelifaro	default:
582232865Smelifaro		return (0);
583232865Smelifaro	}
584232865Smelifaro
585232865Smelifaro	if (xent != NULL) {
586232865Smelifaro		*val = xent->value;
587232865Smelifaro		return (1);
588232865Smelifaro	}
589232865Smelifaro	return (0);
590232865Smelifaro}
591232865Smelifaro
592200590Sluigistatic int
593200590Sluigicount_table_entry(struct radix_node *rn, void *arg)
594200590Sluigi{
595200590Sluigi	u_int32_t * const cnt = arg;
596200590Sluigi
597200590Sluigi	(*cnt)++;
598200590Sluigi	return (0);
599200590Sluigi}
600200590Sluigi
601200590Sluigiint
602200590Sluigiipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt)
603200590Sluigi{
604200590Sluigi	struct radix_node_head *rnh;
605200590Sluigi
606232865Smelifaro	if (tbl >= V_fw_tables_max)
607200590Sluigi		return (EINVAL);
608200590Sluigi	*cnt = 0;
609232865Smelifaro	if ((rnh = ch->tables[tbl]) == NULL)
610232865Smelifaro		return (0);
611200590Sluigi	rnh->rnh_walktree(rnh, count_table_entry, cnt);
612200590Sluigi	return (0);
613200590Sluigi}
614200590Sluigi
615200590Sluigistatic int
616200590Sluigidump_table_entry(struct radix_node *rn, void *arg)
617200590Sluigi{
618200590Sluigi	struct table_entry * const n = (struct table_entry *)rn;
619200590Sluigi	ipfw_table * const tbl = arg;
620200590Sluigi	ipfw_table_entry *ent;
621200590Sluigi
622200590Sluigi	if (tbl->cnt == tbl->size)
623200590Sluigi		return (1);
624200590Sluigi	ent = &tbl->ent[tbl->cnt];
625200590Sluigi	ent->tbl = tbl->tbl;
626200590Sluigi	if (in_nullhost(n->mask.sin_addr))
627200590Sluigi		ent->masklen = 0;
628200590Sluigi	else
629200590Sluigi		ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
630200590Sluigi	ent->addr = n->addr.sin_addr.s_addr;
631200590Sluigi	ent->value = n->value;
632200590Sluigi	tbl->cnt++;
633200590Sluigi	return (0);
634200590Sluigi}
635200590Sluigi
636200590Sluigiint
637200590Sluigiipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl)
638200590Sluigi{
639200590Sluigi	struct radix_node_head *rnh;
640200590Sluigi
641232865Smelifaro	if (tbl->tbl >= V_fw_tables_max)
642200590Sluigi		return (EINVAL);
643200590Sluigi	tbl->cnt = 0;
644232865Smelifaro	if ((rnh = ch->tables[tbl->tbl]) == NULL)
645232865Smelifaro		return (0);
646200590Sluigi	rnh->rnh_walktree(rnh, dump_table_entry, tbl);
647200590Sluigi	return (0);
648200590Sluigi}
649232865Smelifaro
650232865Smelifarostatic int
651232865Smelifarocount_table_xentry(struct radix_node *rn, void *arg)
652232865Smelifaro{
653232865Smelifaro	uint32_t * const cnt = arg;
654232865Smelifaro
655232865Smelifaro	(*cnt) += sizeof(ipfw_table_xentry);
656232865Smelifaro	return (0);
657232865Smelifaro}
658232865Smelifaro
659232865Smelifaroint
660232865Smelifaroipfw_count_xtable(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt)
661232865Smelifaro{
662232865Smelifaro	struct radix_node_head *rnh;
663232865Smelifaro
664232865Smelifaro	if (tbl >= V_fw_tables_max)
665232865Smelifaro		return (EINVAL);
666232865Smelifaro	*cnt = 0;
667232865Smelifaro	if ((rnh = ch->tables[tbl]) != NULL)
668232865Smelifaro		rnh->rnh_walktree(rnh, count_table_xentry, cnt);
669232865Smelifaro	if ((rnh = ch->xtables[tbl]) != NULL)
670232865Smelifaro		rnh->rnh_walktree(rnh, count_table_xentry, cnt);
671232865Smelifaro	/* Return zero if table is empty */
672232865Smelifaro	if (*cnt > 0)
673232865Smelifaro		(*cnt) += sizeof(ipfw_xtable);
674232865Smelifaro	return (0);
675232865Smelifaro}
676232865Smelifaro
677232865Smelifaro
678232865Smelifarostatic int
679232865Smelifarodump_table_xentry_base(struct radix_node *rn, void *arg)
680232865Smelifaro{
681232865Smelifaro	struct table_entry * const n = (struct table_entry *)rn;
682232865Smelifaro	ipfw_xtable * const tbl = arg;
683232865Smelifaro	ipfw_table_xentry *xent;
684232865Smelifaro
685232865Smelifaro	/* Out of memory, returning */
686232865Smelifaro	if (tbl->cnt == tbl->size)
687232865Smelifaro		return (1);
688232865Smelifaro	xent = &tbl->xent[tbl->cnt];
689232865Smelifaro	xent->len = sizeof(ipfw_table_xentry);
690232865Smelifaro	xent->tbl = tbl->tbl;
691232865Smelifaro	if (in_nullhost(n->mask.sin_addr))
692232865Smelifaro		xent->masklen = 0;
693232865Smelifaro	else
694232865Smelifaro		xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
695232865Smelifaro	/* Save IPv4 address as deprecated IPv6 compatible */
696232865Smelifaro	xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr;
697232865Smelifaro	xent->value = n->value;
698232865Smelifaro	tbl->cnt++;
699232865Smelifaro	return (0);
700232865Smelifaro}
701232865Smelifaro
702232865Smelifarostatic int
703232865Smelifarodump_table_xentry_extended(struct radix_node *rn, void *arg)
704232865Smelifaro{
705232865Smelifaro	struct table_xentry * const n = (struct table_xentry *)rn;
706232865Smelifaro	ipfw_xtable * const tbl = arg;
707232865Smelifaro	ipfw_table_xentry *xent;
708232865Smelifaro#ifdef INET6
709232865Smelifaro	int i;
710232865Smelifaro	uint32_t *v;
711232865Smelifaro#endif
712232865Smelifaro	/* Out of memory, returning */
713232865Smelifaro	if (tbl->cnt == tbl->size)
714232865Smelifaro		return (1);
715232865Smelifaro	xent = &tbl->xent[tbl->cnt];
716232865Smelifaro	xent->len = sizeof(ipfw_table_xentry);
717232865Smelifaro	xent->tbl = tbl->tbl;
718232865Smelifaro
719232865Smelifaro	switch (tbl->type) {
720232865Smelifaro#ifdef INET6
721232865Smelifaro	case IPFW_TABLE_CIDR:
722232865Smelifaro		/* Count IPv6 mask */
723232865Smelifaro		v = (uint32_t *)&n->m.mask6.sin6_addr;
724232865Smelifaro		for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++)
725232865Smelifaro			xent->masklen += bitcount32(*v);
726232865Smelifaro		memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr));
727232865Smelifaro		break;
728232865Smelifaro#endif
729232865Smelifaro	case IPFW_TABLE_INTERFACE:
730232865Smelifaro		/* Assume exact mask */
731232865Smelifaro		xent->masklen = 8 * IF_NAMESIZE;
732232865Smelifaro		memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE);
733232865Smelifaro		break;
734232865Smelifaro
735232865Smelifaro	default:
736232865Smelifaro		/* unknown, skip entry */
737232865Smelifaro		return (0);
738232865Smelifaro	}
739232865Smelifaro
740232865Smelifaro	xent->value = n->value;
741232865Smelifaro	tbl->cnt++;
742232865Smelifaro	return (0);
743232865Smelifaro}
744232865Smelifaro
745232865Smelifaroint
746232865Smelifaroipfw_dump_xtable(struct ip_fw_chain *ch, ipfw_xtable *tbl)
747232865Smelifaro{
748232865Smelifaro	struct radix_node_head *rnh;
749232865Smelifaro
750232865Smelifaro	if (tbl->tbl >= V_fw_tables_max)
751232865Smelifaro		return (EINVAL);
752232865Smelifaro	tbl->cnt = 0;
753232865Smelifaro	tbl->type = ch->tabletype[tbl->tbl];
754232865Smelifaro	if ((rnh = ch->tables[tbl->tbl]) != NULL)
755232865Smelifaro		rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl);
756232865Smelifaro	if ((rnh = ch->xtables[tbl->tbl]) != NULL)
757232865Smelifaro		rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl);
758232865Smelifaro	return (0);
759232865Smelifaro}
760232865Smelifaro
761200601Sluigi/* end of file */
762