xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision b074b7bb)
13b3a8eb9SGleb Smirnoff /*-
23b3a8eb9SGleb Smirnoff  * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko.
33b3a8eb9SGleb Smirnoff  *
43b3a8eb9SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
53b3a8eb9SGleb Smirnoff  * modification, are permitted provided that the following conditions
63b3a8eb9SGleb Smirnoff  * are met:
73b3a8eb9SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
83b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
93b3a8eb9SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
103b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
113b3a8eb9SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
123b3a8eb9SGleb Smirnoff  *
133b3a8eb9SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
143b3a8eb9SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
153b3a8eb9SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
163b3a8eb9SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
173b3a8eb9SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
183b3a8eb9SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
193b3a8eb9SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
203b3a8eb9SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
213b3a8eb9SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
223b3a8eb9SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
233b3a8eb9SGleb Smirnoff  * SUCH DAMAGE.
243b3a8eb9SGleb Smirnoff  */
253b3a8eb9SGleb Smirnoff 
263b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
273b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$");
283b3a8eb9SGleb Smirnoff 
293b3a8eb9SGleb Smirnoff /*
303b3a8eb9SGleb Smirnoff  * Lookup table support for ipfw
313b3a8eb9SGleb Smirnoff  *
323b3a8eb9SGleb Smirnoff  * Lookup tables are implemented (at the moment) using the radix
333b3a8eb9SGleb Smirnoff  * tree used for routing tables. Tables store key-value entries, where
343b3a8eb9SGleb Smirnoff  * keys are network prefixes (addr/masklen), and values are integers.
353b3a8eb9SGleb Smirnoff  * As a degenerate case we can interpret keys as 32-bit integers
363b3a8eb9SGleb Smirnoff  * (with a /32 mask).
373b3a8eb9SGleb Smirnoff  *
383b3a8eb9SGleb Smirnoff  * The table is protected by the IPFW lock even for manipulation coming
393b3a8eb9SGleb Smirnoff  * from userland, because operations are typically fast.
403b3a8eb9SGleb Smirnoff  */
413b3a8eb9SGleb Smirnoff 
423b3a8eb9SGleb Smirnoff #include "opt_ipfw.h"
433b3a8eb9SGleb Smirnoff #include "opt_inet.h"
443b3a8eb9SGleb Smirnoff #ifndef INET
453b3a8eb9SGleb Smirnoff #error IPFIREWALL requires INET.
463b3a8eb9SGleb Smirnoff #endif /* INET */
473b3a8eb9SGleb Smirnoff #include "opt_inet6.h"
483b3a8eb9SGleb Smirnoff 
493b3a8eb9SGleb Smirnoff #include <sys/param.h>
503b3a8eb9SGleb Smirnoff #include <sys/systm.h>
513b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
523b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
533b3a8eb9SGleb Smirnoff #include <sys/lock.h>
543b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
553b3a8eb9SGleb Smirnoff #include <sys/socket.h>
563b3a8eb9SGleb Smirnoff #include <sys/queue.h>
573b3a8eb9SGleb Smirnoff #include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
583b3a8eb9SGleb Smirnoff #include <net/radix.h>
593b3a8eb9SGleb Smirnoff #include <net/route.h>
603b3a8eb9SGleb Smirnoff #include <net/vnet.h>
613b3a8eb9SGleb Smirnoff 
623b3a8eb9SGleb Smirnoff #include <netinet/in.h>
633b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
643b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
653b3a8eb9SGleb Smirnoff 
663b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
673b3a8eb9SGleb Smirnoff 
683b3a8eb9SGleb Smirnoff #ifdef MAC
693b3a8eb9SGleb Smirnoff #include <security/mac/mac_framework.h>
703b3a8eb9SGleb Smirnoff #endif
713b3a8eb9SGleb Smirnoff 
723b3a8eb9SGleb Smirnoff static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
733b3a8eb9SGleb Smirnoff 
743b3a8eb9SGleb Smirnoff struct table_entry {
753b3a8eb9SGleb Smirnoff 	struct radix_node	rn[2];
763b3a8eb9SGleb Smirnoff 	struct sockaddr_in	addr, mask;
773b3a8eb9SGleb Smirnoff 	u_int32_t		value;
783b3a8eb9SGleb Smirnoff };
793b3a8eb9SGleb Smirnoff 
803b3a8eb9SGleb Smirnoff struct xaddr_iface {
813b3a8eb9SGleb Smirnoff 	uint8_t		if_len;		/* length of this struct */
823b3a8eb9SGleb Smirnoff 	uint8_t		pad[7];		/* Align name */
833b3a8eb9SGleb Smirnoff 	char 		ifname[IF_NAMESIZE];	/* Interface name */
843b3a8eb9SGleb Smirnoff };
853b3a8eb9SGleb Smirnoff 
863b3a8eb9SGleb Smirnoff struct table_xentry {
873b3a8eb9SGleb Smirnoff 	struct radix_node	rn[2];
883b3a8eb9SGleb Smirnoff 	union {
893b3a8eb9SGleb Smirnoff #ifdef INET6
903b3a8eb9SGleb Smirnoff 		struct sockaddr_in6	addr6;
913b3a8eb9SGleb Smirnoff #endif
923b3a8eb9SGleb Smirnoff 		struct xaddr_iface	iface;
933b3a8eb9SGleb Smirnoff 	} a;
943b3a8eb9SGleb Smirnoff 	union {
953b3a8eb9SGleb Smirnoff #ifdef INET6
963b3a8eb9SGleb Smirnoff 		struct sockaddr_in6	mask6;
973b3a8eb9SGleb Smirnoff #endif
983b3a8eb9SGleb Smirnoff 		struct xaddr_iface	ifmask;
993b3a8eb9SGleb Smirnoff 	} m;
1003b3a8eb9SGleb Smirnoff 	u_int32_t		value;
1013b3a8eb9SGleb Smirnoff };
1023b3a8eb9SGleb Smirnoff 
1033b3a8eb9SGleb Smirnoff  /*
104b074b7bbSAlexander V. Chernikov  * Table has the following `type` concepts:
105b074b7bbSAlexander V. Chernikov  *
106b074b7bbSAlexander V. Chernikov  * `type` represents lookup key type (cidr, ifp, uid, etc..)
107b074b7bbSAlexander V. Chernikov  * `ftype` is pure userland field helping to properly format table data
108b074b7bbSAlexander V. Chernikov  * `atype` represents exact lookup algorithm for given tabletype.
109b074b7bbSAlexander V. Chernikov  *     For example, we can use more efficient search schemes if we plan
110b074b7bbSAlexander V. Chernikov  *     to use some specific table for storing host-routes only.
111b074b7bbSAlexander V. Chernikov  *
112b074b7bbSAlexander V. Chernikov  */
113b074b7bbSAlexander V. Chernikov struct table_config {
114b074b7bbSAlexander V. Chernikov 	struct named_object	no;
115b074b7bbSAlexander V. Chernikov 	uint8_t		ftype;		/* format table type */
116b074b7bbSAlexander V. Chernikov 	uint8_t		atype;		/* algorith type */
117b074b7bbSAlexander V. Chernikov 	uint8_t		linked;		/* 1 if already linked */
118b074b7bbSAlexander V. Chernikov 	uint8_t		spare0;
119b074b7bbSAlexander V. Chernikov 	uint32_t	count;		/* Number of records */
120b074b7bbSAlexander V. Chernikov 	char		tablename[64];	/* table name */
121b074b7bbSAlexander V. Chernikov 	void		*state;		/* Store some state if needed */
122b074b7bbSAlexander V. Chernikov 	void		*xstate;
123b074b7bbSAlexander V. Chernikov };
124b074b7bbSAlexander V. Chernikov #define	TABLE_SET(set)	((V_fw_tables_sets != 0) ? set : 0)
125b074b7bbSAlexander V. Chernikov 
126b074b7bbSAlexander V. Chernikov struct tables_config {
127b074b7bbSAlexander V. Chernikov 	struct namedobj_instance	*namehash;
128b074b7bbSAlexander V. Chernikov };
129b074b7bbSAlexander V. Chernikov 
130b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni,
131b074b7bbSAlexander V. Chernikov     struct tid_info *ti);
132b074b7bbSAlexander V. Chernikov static struct table_config *alloc_table_config(struct namedobj_instance *ni,
133b074b7bbSAlexander V. Chernikov     struct tid_info *ti);
134b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni,
135b074b7bbSAlexander V. Chernikov     struct table_config *tc);
136b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
137b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
138b074b7bbSAlexander V. Chernikov static int alloc_table_state(void **state, void **xstate, uint8_t type);
139b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type);
140b074b7bbSAlexander V. Chernikov 
141b074b7bbSAlexander V. Chernikov 
142b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
143b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
144b074b7bbSAlexander V. Chernikov 
145b074b7bbSAlexander V. Chernikov 
146b074b7bbSAlexander V. Chernikov /*
1473b3a8eb9SGleb Smirnoff  * The radix code expects addr and mask to be array of bytes,
1483b3a8eb9SGleb Smirnoff  * with the first byte being the length of the array. rn_inithead
1493b3a8eb9SGleb Smirnoff  * is called with the offset in bits of the lookup key within the
1503b3a8eb9SGleb Smirnoff  * array. If we use a sockaddr_in as the underlying type,
1513b3a8eb9SGleb Smirnoff  * sin_len is conveniently located at offset 0, sin_addr is at
1523b3a8eb9SGleb Smirnoff  * offset 4 and normally aligned.
1533b3a8eb9SGleb Smirnoff  * But for portability, let's avoid assumption and make the code explicit
1543b3a8eb9SGleb Smirnoff  */
1553b3a8eb9SGleb Smirnoff #define KEY_LEN(v)	*((uint8_t *)&(v))
1563b3a8eb9SGleb Smirnoff #define KEY_OFS		(8*offsetof(struct sockaddr_in, sin_addr))
1573b3a8eb9SGleb Smirnoff /*
1583b3a8eb9SGleb Smirnoff  * Do not require radix to compare more than actual IPv4/IPv6 address
1593b3a8eb9SGleb Smirnoff  */
1603b3a8eb9SGleb Smirnoff #define KEY_LEN_INET	(offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
1613b3a8eb9SGleb Smirnoff #define KEY_LEN_INET6	(offsetof(struct sockaddr_in6, sin6_addr) + sizeof(struct in6_addr))
1623b3a8eb9SGleb Smirnoff #define KEY_LEN_IFACE	(offsetof(struct xaddr_iface, ifname))
1633b3a8eb9SGleb Smirnoff 
1643b3a8eb9SGleb Smirnoff #define OFF_LEN_INET	(8 * offsetof(struct sockaddr_in, sin_addr))
1653b3a8eb9SGleb Smirnoff #define OFF_LEN_INET6	(8 * offsetof(struct sockaddr_in6, sin6_addr))
1663b3a8eb9SGleb Smirnoff #define OFF_LEN_IFACE	(8 * offsetof(struct xaddr_iface, ifname))
1673b3a8eb9SGleb Smirnoff 
1683b3a8eb9SGleb Smirnoff 
169a3043eeeSDimitry Andric #ifdef INET6
1703b3a8eb9SGleb Smirnoff static inline void
1713b3a8eb9SGleb Smirnoff ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
1723b3a8eb9SGleb Smirnoff {
1733b3a8eb9SGleb Smirnoff 	uint32_t *cp;
1743b3a8eb9SGleb Smirnoff 
1753b3a8eb9SGleb Smirnoff 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
1763b3a8eb9SGleb Smirnoff 		*cp++ = 0xFFFFFFFF;
1773b3a8eb9SGleb Smirnoff 	*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
1783b3a8eb9SGleb Smirnoff }
179a3043eeeSDimitry Andric #endif
1803b3a8eb9SGleb Smirnoff 
1813b3a8eb9SGleb Smirnoff int
182b074b7bbSAlexander V. Chernikov ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
183b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
1843b3a8eb9SGleb Smirnoff {
185b074b7bbSAlexander V. Chernikov 	struct radix_node_head *rnh;
1863b3a8eb9SGleb Smirnoff 	struct table_entry *ent;
1873b3a8eb9SGleb Smirnoff 	struct table_xentry *xent;
1883b3a8eb9SGleb Smirnoff 	struct radix_node *rn;
1893b3a8eb9SGleb Smirnoff 	in_addr_t addr;
1903b3a8eb9SGleb Smirnoff 	int offset;
1913b3a8eb9SGleb Smirnoff 	void *ent_ptr;
1923b3a8eb9SGleb Smirnoff 	struct sockaddr *addr_ptr, *mask_ptr;
193b074b7bbSAlexander V. Chernikov 	struct table_config *tc, *tc_new;
194b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1953b3a8eb9SGleb Smirnoff 	char c;
196b074b7bbSAlexander V. Chernikov 	uint8_t mlen;
197b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1983b3a8eb9SGleb Smirnoff 
199b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
2003b3a8eb9SGleb Smirnoff 		return (EINVAL);
2013b3a8eb9SGleb Smirnoff 
202b074b7bbSAlexander V. Chernikov 	mlen = tei->masklen;
203b074b7bbSAlexander V. Chernikov 
204b074b7bbSAlexander V. Chernikov 	switch (ti->type) {
2053b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
206b074b7bbSAlexander V. Chernikov 		if (tei->plen == sizeof(in_addr_t)) {
2073b3a8eb9SGleb Smirnoff #ifdef INET
2083b3a8eb9SGleb Smirnoff 			/* IPv4 case */
2093b3a8eb9SGleb Smirnoff 			if (mlen > 32)
2103b3a8eb9SGleb Smirnoff 				return (EINVAL);
2113b3a8eb9SGleb Smirnoff 			ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
212b074b7bbSAlexander V. Chernikov 			ent->value = tei->value;
2133b3a8eb9SGleb Smirnoff 			/* Set 'total' structure length */
2143b3a8eb9SGleb Smirnoff 			KEY_LEN(ent->addr) = KEY_LEN_INET;
2153b3a8eb9SGleb Smirnoff 			KEY_LEN(ent->mask) = KEY_LEN_INET;
2163b3a8eb9SGleb Smirnoff 			/* Set offset of IPv4 address in bits */
2173b3a8eb9SGleb Smirnoff 			offset = OFF_LEN_INET;
218b074b7bbSAlexander V. Chernikov 			ent->mask.sin_addr.s_addr =
219b074b7bbSAlexander V. Chernikov 			    htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
220b074b7bbSAlexander V. Chernikov 			addr = *((in_addr_t *)tei->paddr);
2213b3a8eb9SGleb Smirnoff 			ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
2223b3a8eb9SGleb Smirnoff 			/* Set pointers */
2233b3a8eb9SGleb Smirnoff 			ent_ptr = ent;
2243b3a8eb9SGleb Smirnoff 			addr_ptr = (struct sockaddr *)&ent->addr;
2253b3a8eb9SGleb Smirnoff 			mask_ptr = (struct sockaddr *)&ent->mask;
2263b3a8eb9SGleb Smirnoff #endif
2273b3a8eb9SGleb Smirnoff #ifdef INET6
228b074b7bbSAlexander V. Chernikov 		} else if (tei->plen == sizeof(struct in6_addr)) {
2293b3a8eb9SGleb Smirnoff 			/* IPv6 case */
2303b3a8eb9SGleb Smirnoff 			if (mlen > 128)
2313b3a8eb9SGleb Smirnoff 				return (EINVAL);
2323b3a8eb9SGleb Smirnoff 			xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
233b074b7bbSAlexander V. Chernikov 			xent->value = tei->value;
2343b3a8eb9SGleb Smirnoff 			/* Set 'total' structure length */
2353b3a8eb9SGleb Smirnoff 			KEY_LEN(xent->a.addr6) = KEY_LEN_INET6;
2363b3a8eb9SGleb Smirnoff 			KEY_LEN(xent->m.mask6) = KEY_LEN_INET6;
2373b3a8eb9SGleb Smirnoff 			/* Set offset of IPv6 address in bits */
2383b3a8eb9SGleb Smirnoff 			offset = OFF_LEN_INET6;
2393b3a8eb9SGleb Smirnoff 			ipv6_writemask(&xent->m.mask6.sin6_addr, mlen);
240b074b7bbSAlexander V. Chernikov 			memcpy(&xent->a.addr6.sin6_addr, tei->paddr,
241b074b7bbSAlexander V. Chernikov 			    sizeof(struct in6_addr));
2423b3a8eb9SGleb Smirnoff 			APPLY_MASK(&xent->a.addr6.sin6_addr, &xent->m.mask6.sin6_addr);
2433b3a8eb9SGleb Smirnoff 			/* Set pointers */
2443b3a8eb9SGleb Smirnoff 			ent_ptr = xent;
2453b3a8eb9SGleb Smirnoff 			addr_ptr = (struct sockaddr *)&xent->a.addr6;
2463b3a8eb9SGleb Smirnoff 			mask_ptr = (struct sockaddr *)&xent->m.mask6;
2473b3a8eb9SGleb Smirnoff #endif
2483b3a8eb9SGleb Smirnoff 		} else {
2493b3a8eb9SGleb Smirnoff 			/* Unknown CIDR type */
2503b3a8eb9SGleb Smirnoff 			return (EINVAL);
2513b3a8eb9SGleb Smirnoff 		}
2523b3a8eb9SGleb Smirnoff 		break;
2533b3a8eb9SGleb Smirnoff 
2543b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
2553b3a8eb9SGleb Smirnoff 		/* Check if string is terminated */
256b074b7bbSAlexander V. Chernikov 		c = ((char *)tei->paddr)[IF_NAMESIZE - 1];
257b074b7bbSAlexander V. Chernikov 		((char *)tei->paddr)[IF_NAMESIZE - 1] = '\0';
258b074b7bbSAlexander V. Chernikov 		mlen = strlen((char *)tei->paddr);
259b074b7bbSAlexander V. Chernikov 		if ((mlen == IF_NAMESIZE - 1) && (c != '\0'))
2603b3a8eb9SGleb Smirnoff 			return (EINVAL);
2613b3a8eb9SGleb Smirnoff 
2623b3a8eb9SGleb Smirnoff 		/* Include last \0 into comparison */
2633b3a8eb9SGleb Smirnoff 		mlen++;
2643b3a8eb9SGleb Smirnoff 
2653b3a8eb9SGleb Smirnoff 		xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
266b074b7bbSAlexander V. Chernikov 		xent->value = tei->value;
2673b3a8eb9SGleb Smirnoff 		/* Set 'total' structure length */
2683b3a8eb9SGleb Smirnoff 		KEY_LEN(xent->a.iface) = KEY_LEN_IFACE + mlen;
2693b3a8eb9SGleb Smirnoff 		KEY_LEN(xent->m.ifmask) = KEY_LEN_IFACE + mlen;
2703b3a8eb9SGleb Smirnoff 		/* Set offset of interface name in bits */
2713b3a8eb9SGleb Smirnoff 		offset = OFF_LEN_IFACE;
272b074b7bbSAlexander V. Chernikov 		memcpy(xent->a.iface.ifname, tei->paddr, mlen);
2733b3a8eb9SGleb Smirnoff 		/* Assume direct match */
2743b3a8eb9SGleb Smirnoff 		/* TODO: Add interface pattern matching */
2753b3a8eb9SGleb Smirnoff #if 0
2763b3a8eb9SGleb Smirnoff 		memset(xent->m.ifmask.ifname, 0xFF, IF_NAMESIZE);
2773b3a8eb9SGleb Smirnoff 		mask_ptr = (struct sockaddr *)&xent->m.ifmask;
2783b3a8eb9SGleb Smirnoff #endif
2793b3a8eb9SGleb Smirnoff 		/* Set pointers */
2803b3a8eb9SGleb Smirnoff 		ent_ptr = xent;
2813b3a8eb9SGleb Smirnoff 		addr_ptr = (struct sockaddr *)&xent->a.iface;
2823b3a8eb9SGleb Smirnoff 		mask_ptr = NULL;
2833b3a8eb9SGleb Smirnoff 		break;
2843b3a8eb9SGleb Smirnoff 
2853b3a8eb9SGleb Smirnoff 	default:
2863b3a8eb9SGleb Smirnoff 		return (EINVAL);
2873b3a8eb9SGleb Smirnoff 	}
2883b3a8eb9SGleb Smirnoff 
289b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
2903b3a8eb9SGleb Smirnoff 
291b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2923b3a8eb9SGleb Smirnoff 
293b074b7bbSAlexander V. Chernikov 	tc_new = NULL;
294b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
295b074b7bbSAlexander V. Chernikov 		/* Not found. We have to create new one */
296b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
297b074b7bbSAlexander V. Chernikov 
298b074b7bbSAlexander V. Chernikov 		tc_new = alloc_table_config(ni, ti);
299b074b7bbSAlexander V. Chernikov 		if (tc_new == NULL)
3003b3a8eb9SGleb Smirnoff 			return (ENOMEM);
3013b3a8eb9SGleb Smirnoff 
302b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
303b074b7bbSAlexander V. Chernikov 
304b074b7bbSAlexander V. Chernikov 		/* Check if table has already allocated by other thread */
305b074b7bbSAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
306b074b7bbSAlexander V. Chernikov 			if (tc->no.type != ti->type) {
307b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
308b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
3093b3a8eb9SGleb Smirnoff 				return (EINVAL);
3103b3a8eb9SGleb Smirnoff 			}
3113b3a8eb9SGleb Smirnoff 		} else {
3123b3a8eb9SGleb Smirnoff 			/*
313b074b7bbSAlexander V. Chernikov 			 * New table.
314b074b7bbSAlexander V. Chernikov 			 * Set tc_new to zero not to free it afterwards.
3153b3a8eb9SGleb Smirnoff 			 */
316b074b7bbSAlexander V. Chernikov 			tc = tc_new;
317b074b7bbSAlexander V. Chernikov 			tc_new = NULL;
318b074b7bbSAlexander V. Chernikov 
319b074b7bbSAlexander V. Chernikov 			/* Allocate table index. */
320b074b7bbSAlexander V. Chernikov 			if (ipfw_objhash_alloc_idx(ni, ti->set, &kidx) != 0) {
321b074b7bbSAlexander V. Chernikov 				/* Index full. */
322b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
323b074b7bbSAlexander V. Chernikov 				printf("Unable to allocate index for table %s."
324b074b7bbSAlexander V. Chernikov 				    " Consider increasing "
325b074b7bbSAlexander V. Chernikov 				    "net.inet.ip.fw.tables_max",
326b074b7bbSAlexander V. Chernikov 				    tc->no.name);
327b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
328b074b7bbSAlexander V. Chernikov 				return (EBUSY);
3293b3a8eb9SGleb Smirnoff 			}
330b074b7bbSAlexander V. Chernikov 			/* Save kidx */
331b074b7bbSAlexander V. Chernikov 			tc->no.kidx = kidx;
332b074b7bbSAlexander V. Chernikov 		}
333b074b7bbSAlexander V. Chernikov 	} else {
334b074b7bbSAlexander V. Chernikov 		/* We still have to check table type */
335b074b7bbSAlexander V. Chernikov 		if (tc->no.type != ti->type) {
336b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
337b074b7bbSAlexander V. Chernikov 			return (EINVAL);
338b074b7bbSAlexander V. Chernikov 		}
339b074b7bbSAlexander V. Chernikov 	}
340b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
341b074b7bbSAlexander V. Chernikov 
342b074b7bbSAlexander V. Chernikov 	/* We've got valid table in @tc. Let's add data */
343b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
344b074b7bbSAlexander V. Chernikov 
345b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0) {
346b074b7bbSAlexander V. Chernikov 		link_table(ch, tc);
347b074b7bbSAlexander V. Chernikov 	}
348b074b7bbSAlexander V. Chernikov 
349b074b7bbSAlexander V. Chernikov 	/* XXX: Temporary until splitting add/del to per-type functions */
350b074b7bbSAlexander V. Chernikov 	rnh = NULL;
351b074b7bbSAlexander V. Chernikov 	switch (ti->type) {
352b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_CIDR:
353b074b7bbSAlexander V. Chernikov 		if (tei->plen == sizeof(in_addr_t))
354b074b7bbSAlexander V. Chernikov 			rnh = ch->tables[kidx];
355b074b7bbSAlexander V. Chernikov 		else
356b074b7bbSAlexander V. Chernikov 			rnh = ch->xtables[kidx];
357b074b7bbSAlexander V. Chernikov 		break;
358b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_INTERFACE:
359b074b7bbSAlexander V. Chernikov 		rnh = ch->xtables[kidx];
360b074b7bbSAlexander V. Chernikov 		break;
3613b3a8eb9SGleb Smirnoff 	}
3623b3a8eb9SGleb Smirnoff 
3633b3a8eb9SGleb Smirnoff 	rn = rnh->rnh_addaddr(addr_ptr, mask_ptr, rnh, ent_ptr);
3643b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
365b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
366b074b7bbSAlexander V. Chernikov 
367b074b7bbSAlexander V. Chernikov 	if (tc_new != NULL)
368b074b7bbSAlexander V. Chernikov 		free_table_config(ni, tc);
3693b3a8eb9SGleb Smirnoff 
3703b3a8eb9SGleb Smirnoff 	if (rn == NULL) {
3713b3a8eb9SGleb Smirnoff 		free(ent_ptr, M_IPFW_TBL);
3723b3a8eb9SGleb Smirnoff 		return (EEXIST);
3733b3a8eb9SGleb Smirnoff 	}
374b074b7bbSAlexander V. Chernikov 
3753b3a8eb9SGleb Smirnoff 	return (0);
3763b3a8eb9SGleb Smirnoff }
3773b3a8eb9SGleb Smirnoff 
3783b3a8eb9SGleb Smirnoff int
379b074b7bbSAlexander V. Chernikov ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
380b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
3813b3a8eb9SGleb Smirnoff {
382b074b7bbSAlexander V. Chernikov 	struct radix_node_head *rnh;
3833b3a8eb9SGleb Smirnoff 	struct table_entry *ent;
3843b3a8eb9SGleb Smirnoff 	in_addr_t addr;
3853b3a8eb9SGleb Smirnoff 	struct sockaddr_in sa, mask;
3863b3a8eb9SGleb Smirnoff 	struct sockaddr *sa_ptr, *mask_ptr;
387b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
388b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
3893b3a8eb9SGleb Smirnoff 	char c;
390b074b7bbSAlexander V. Chernikov 	uint8_t mlen;
391b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
3923b3a8eb9SGleb Smirnoff 
393b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
3943b3a8eb9SGleb Smirnoff 		return (EINVAL);
3953b3a8eb9SGleb Smirnoff 
396b074b7bbSAlexander V. Chernikov 	mlen = tei->masklen;
397b074b7bbSAlexander V. Chernikov 
398b074b7bbSAlexander V. Chernikov 	switch (ti->type) {
3993b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
400b074b7bbSAlexander V. Chernikov 		if (tei->plen == sizeof(in_addr_t)) {
4013b3a8eb9SGleb Smirnoff 			/* Set 'total' structure length */
4023b3a8eb9SGleb Smirnoff 			KEY_LEN(sa) = KEY_LEN_INET;
4033b3a8eb9SGleb Smirnoff 			KEY_LEN(mask) = KEY_LEN_INET;
4043b3a8eb9SGleb Smirnoff 			mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
405b074b7bbSAlexander V. Chernikov 			addr = *((in_addr_t *)tei->paddr);
4063b3a8eb9SGleb Smirnoff 			sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
4073b3a8eb9SGleb Smirnoff 			sa_ptr = (struct sockaddr *)&sa;
4083b3a8eb9SGleb Smirnoff 			mask_ptr = (struct sockaddr *)&mask;
4093b3a8eb9SGleb Smirnoff #ifdef INET6
410b074b7bbSAlexander V. Chernikov 		} else if (tei->plen == sizeof(struct in6_addr)) {
4113b3a8eb9SGleb Smirnoff 			/* IPv6 case */
4123b3a8eb9SGleb Smirnoff 			if (mlen > 128)
4133b3a8eb9SGleb Smirnoff 				return (EINVAL);
4143b3a8eb9SGleb Smirnoff 			struct sockaddr_in6 sa6, mask6;
4153b3a8eb9SGleb Smirnoff 			memset(&sa6, 0, sizeof(struct sockaddr_in6));
4163b3a8eb9SGleb Smirnoff 			memset(&mask6, 0, sizeof(struct sockaddr_in6));
4173b3a8eb9SGleb Smirnoff 			/* Set 'total' structure length */
4183b3a8eb9SGleb Smirnoff 			KEY_LEN(sa6) = KEY_LEN_INET6;
4193b3a8eb9SGleb Smirnoff 			KEY_LEN(mask6) = KEY_LEN_INET6;
4203b3a8eb9SGleb Smirnoff 			ipv6_writemask(&mask6.sin6_addr, mlen);
421b074b7bbSAlexander V. Chernikov 			memcpy(&sa6.sin6_addr, tei->paddr,
422b074b7bbSAlexander V. Chernikov 			    sizeof(struct in6_addr));
4233b3a8eb9SGleb Smirnoff 			APPLY_MASK(&sa6.sin6_addr, &mask6.sin6_addr);
4243b3a8eb9SGleb Smirnoff 			sa_ptr = (struct sockaddr *)&sa6;
4253b3a8eb9SGleb Smirnoff 			mask_ptr = (struct sockaddr *)&mask6;
4263b3a8eb9SGleb Smirnoff #endif
4273b3a8eb9SGleb Smirnoff 		} else {
4283b3a8eb9SGleb Smirnoff 			/* Unknown CIDR type */
4293b3a8eb9SGleb Smirnoff 			return (EINVAL);
4303b3a8eb9SGleb Smirnoff 		}
4313b3a8eb9SGleb Smirnoff 		break;
4323b3a8eb9SGleb Smirnoff 
4333b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
4343b3a8eb9SGleb Smirnoff 		/* Check if string is terminated */
435b074b7bbSAlexander V. Chernikov 		c = ((char *)tei->paddr)[IF_NAMESIZE - 1];
436b074b7bbSAlexander V. Chernikov 		((char *)tei->paddr)[IF_NAMESIZE - 1] = '\0';
437b074b7bbSAlexander V. Chernikov 		mlen = strlen((char *)tei->paddr);
438b074b7bbSAlexander V. Chernikov 		if ((mlen == IF_NAMESIZE - 1) && (c != '\0'))
4393b3a8eb9SGleb Smirnoff 			return (EINVAL);
4403b3a8eb9SGleb Smirnoff 
4413b3a8eb9SGleb Smirnoff 		struct xaddr_iface ifname, ifmask;
4423b3a8eb9SGleb Smirnoff 		memset(&ifname, 0, sizeof(ifname));
4433b3a8eb9SGleb Smirnoff 
4443b3a8eb9SGleb Smirnoff 		/* Include last \0 into comparison */
4453b3a8eb9SGleb Smirnoff 		mlen++;
4463b3a8eb9SGleb Smirnoff 
4473b3a8eb9SGleb Smirnoff 		/* Set 'total' structure length */
4483b3a8eb9SGleb Smirnoff 		KEY_LEN(ifname) = KEY_LEN_IFACE + mlen;
4493b3a8eb9SGleb Smirnoff 		KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen;
4503b3a8eb9SGleb Smirnoff 		/* Assume direct match */
4513b3a8eb9SGleb Smirnoff 		/* FIXME: Add interface pattern matching */
4523b3a8eb9SGleb Smirnoff #if 0
4533b3a8eb9SGleb Smirnoff 		memset(ifmask.ifname, 0xFF, IF_NAMESIZE);
4543b3a8eb9SGleb Smirnoff 		mask_ptr = (struct sockaddr *)&ifmask;
4553b3a8eb9SGleb Smirnoff #endif
4563b3a8eb9SGleb Smirnoff 		mask_ptr = NULL;
457b074b7bbSAlexander V. Chernikov 		memcpy(ifname.ifname, tei->paddr, mlen);
4583b3a8eb9SGleb Smirnoff 		/* Set pointers */
4593b3a8eb9SGleb Smirnoff 		sa_ptr = (struct sockaddr *)&ifname;
4603b3a8eb9SGleb Smirnoff 
4613b3a8eb9SGleb Smirnoff 		break;
4623b3a8eb9SGleb Smirnoff 
4633b3a8eb9SGleb Smirnoff 	default:
4643b3a8eb9SGleb Smirnoff 		return (EINVAL);
4653b3a8eb9SGleb Smirnoff 	}
4663b3a8eb9SGleb Smirnoff 
467b074b7bbSAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
468b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
469b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
470b074b7bbSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
4713b3a8eb9SGleb Smirnoff 		return (ESRCH);
4723b3a8eb9SGleb Smirnoff 	}
4733b3a8eb9SGleb Smirnoff 
474b074b7bbSAlexander V. Chernikov 	if (tc->no.type != ti->type) {
475b074b7bbSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
4763b3a8eb9SGleb Smirnoff 		return (EINVAL);
4773b3a8eb9SGleb Smirnoff 	}
478b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
479b074b7bbSAlexander V. Chernikov 
480b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
481b074b7bbSAlexander V. Chernikov 
482b074b7bbSAlexander V. Chernikov 	rnh = NULL;
483b074b7bbSAlexander V. Chernikov 	switch (ti->type) {
484b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_CIDR:
485b074b7bbSAlexander V. Chernikov 		if (tei->plen == sizeof(in_addr_t))
486b074b7bbSAlexander V. Chernikov 			rnh = ch->tables[kidx];
487b074b7bbSAlexander V. Chernikov 		else
488b074b7bbSAlexander V. Chernikov 			rnh = ch->xtables[kidx];
489b074b7bbSAlexander V. Chernikov 		break;
490b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_INTERFACE:
491b074b7bbSAlexander V. Chernikov 		rnh = ch->xtables[kidx];
492b074b7bbSAlexander V. Chernikov 		break;
493b074b7bbSAlexander V. Chernikov 	}
4943b3a8eb9SGleb Smirnoff 
4953b3a8eb9SGleb Smirnoff 	ent = (struct table_entry *)rnh->rnh_deladdr(sa_ptr, mask_ptr, rnh);
4963b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
4973b3a8eb9SGleb Smirnoff 
498b074b7bbSAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
499b074b7bbSAlexander V. Chernikov 
5003b3a8eb9SGleb Smirnoff 	if (ent == NULL)
5013b3a8eb9SGleb Smirnoff 		return (ESRCH);
5023b3a8eb9SGleb Smirnoff 
5033b3a8eb9SGleb Smirnoff 	free(ent, M_IPFW_TBL);
5043b3a8eb9SGleb Smirnoff 	return (0);
5053b3a8eb9SGleb Smirnoff }
5063b3a8eb9SGleb Smirnoff 
5073b3a8eb9SGleb Smirnoff static int
5083b3a8eb9SGleb Smirnoff flush_table_entry(struct radix_node *rn, void *arg)
5093b3a8eb9SGleb Smirnoff {
5103b3a8eb9SGleb Smirnoff 	struct radix_node_head * const rnh = arg;
5113b3a8eb9SGleb Smirnoff 	struct table_entry *ent;
5123b3a8eb9SGleb Smirnoff 
5133b3a8eb9SGleb Smirnoff 	ent = (struct table_entry *)
5143b3a8eb9SGleb Smirnoff 	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
5153b3a8eb9SGleb Smirnoff 	if (ent != NULL)
5163b3a8eb9SGleb Smirnoff 		free(ent, M_IPFW_TBL);
5173b3a8eb9SGleb Smirnoff 	return (0);
5183b3a8eb9SGleb Smirnoff }
5193b3a8eb9SGleb Smirnoff 
520b074b7bbSAlexander V. Chernikov /*
521b074b7bbSAlexander V. Chernikov  * Flushes all entries in given table minimizing hoding chain WLOCKs.
522b074b7bbSAlexander V. Chernikov  *
523b074b7bbSAlexander V. Chernikov  */
5243b3a8eb9SGleb Smirnoff int
525b074b7bbSAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
5263b3a8eb9SGleb Smirnoff {
527b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
528b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
529b074b7bbSAlexander V. Chernikov 	void *ostate, *oxstate;
530b074b7bbSAlexander V. Chernikov 	void *state, *xstate;
531b074b7bbSAlexander V. Chernikov 	int error;
532b074b7bbSAlexander V. Chernikov 	uint8_t type;
533b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
5343b3a8eb9SGleb Smirnoff 
535b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
5363b3a8eb9SGleb Smirnoff 		return (EINVAL);
5373b3a8eb9SGleb Smirnoff 
5383b3a8eb9SGleb Smirnoff 	/*
539b074b7bbSAlexander V. Chernikov 	 * Stage 1: determine table type.
540b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
5413b3a8eb9SGleb Smirnoff 	 */
542b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
543b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
544b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
545b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
546b074b7bbSAlexander V. Chernikov 		return (ESRCH);
547b074b7bbSAlexander V. Chernikov 	}
548b074b7bbSAlexander V. Chernikov 	type = tc->no.type;
549b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
550b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5513b3a8eb9SGleb Smirnoff 
552b074b7bbSAlexander V. Chernikov 	/*
553b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate new state for given type.
554b074b7bbSAlexander V. Chernikov 	 */
555b074b7bbSAlexander V. Chernikov 	if ((error = alloc_table_state(&state, &xstate, type)) != 0) {
556b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
557b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
558b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
559b074b7bbSAlexander V. Chernikov 		return (error);
560b074b7bbSAlexander V. Chernikov 	}
561b074b7bbSAlexander V. Chernikov 
562b074b7bbSAlexander V. Chernikov 	/*
563b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
564b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
565b074b7bbSAlexander V. Chernikov 	 */
566b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
5673b3a8eb9SGleb Smirnoff 	IPFW_WLOCK(ch);
568b074b7bbSAlexander V. Chernikov 
569b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
570b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
571b074b7bbSAlexander V. Chernikov 
572b074b7bbSAlexander V. Chernikov 	ostate = ch->tables[kidx];
573b074b7bbSAlexander V. Chernikov 	ch->tables[kidx] = state;
574b074b7bbSAlexander V. Chernikov 	oxstate = ch->xtables[kidx];
575b074b7bbSAlexander V. Chernikov 	ch->xtables[kidx] = xstate;
576b074b7bbSAlexander V. Chernikov 
577b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
578b074b7bbSAlexander V. Chernikov 
5793b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
580b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5813b3a8eb9SGleb Smirnoff 
582b074b7bbSAlexander V. Chernikov 	/*
583b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
584b074b7bbSAlexander V. Chernikov 	 */
585b074b7bbSAlexander V. Chernikov 	free_table_state(&ostate, &xstate, tc->no.type);
5863b3a8eb9SGleb Smirnoff 
5873b3a8eb9SGleb Smirnoff 	return (0);
5883b3a8eb9SGleb Smirnoff }
5893b3a8eb9SGleb Smirnoff 
590b074b7bbSAlexander V. Chernikov /*
591b074b7bbSAlexander V. Chernikov  * Destroys given table @ti: flushes it,
592b074b7bbSAlexander V. Chernikov  */
593b074b7bbSAlexander V. Chernikov int
594b074b7bbSAlexander V. Chernikov ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti, int force)
595b074b7bbSAlexander V. Chernikov {
596b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
597b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
598b074b7bbSAlexander V. Chernikov 
599b074b7bbSAlexander V. Chernikov 	ti->set = TABLE_SET(ti->set);
600b074b7bbSAlexander V. Chernikov 
601b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
602b074b7bbSAlexander V. Chernikov 
603b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
604b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
605b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
606b074b7bbSAlexander V. Chernikov 		return (ESRCH);
607b074b7bbSAlexander V. Chernikov 	}
608b074b7bbSAlexander V. Chernikov 
609b074b7bbSAlexander V. Chernikov 	/* Do not permit destroying used tables */
610b074b7bbSAlexander V. Chernikov 	if (tc->no.refcnt > 0 && force == 0) {
611b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
612b074b7bbSAlexander V. Chernikov 		return (EBUSY);
613b074b7bbSAlexander V. Chernikov 	}
614b074b7bbSAlexander V. Chernikov 
615b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
616b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
617b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
618b074b7bbSAlexander V. Chernikov 
619b074b7bbSAlexander V. Chernikov 	/* Free obj index */
620b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.set, tc->no.kidx) != 0)
621b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
622b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
623b074b7bbSAlexander V. Chernikov 
624b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
625b074b7bbSAlexander V. Chernikov 
626b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
627b074b7bbSAlexander V. Chernikov 
628b074b7bbSAlexander V. Chernikov 	return (0);
629b074b7bbSAlexander V. Chernikov }
630b074b7bbSAlexander V. Chernikov 
631b074b7bbSAlexander V. Chernikov static void
632b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
633b074b7bbSAlexander V. Chernikov     void *arg)
634b074b7bbSAlexander V. Chernikov {
635b074b7bbSAlexander V. Chernikov 
636b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
637b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->set, no->kidx) != 0)
638b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
639b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
640b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
641b074b7bbSAlexander V. Chernikov }
642b074b7bbSAlexander V. Chernikov 
6433b3a8eb9SGleb Smirnoff void
6443b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
6453b3a8eb9SGleb Smirnoff {
6463b3a8eb9SGleb Smirnoff 
647b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
648b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
649b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
650b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
651b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
652b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
6533b3a8eb9SGleb Smirnoff 
6543b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
6553b3a8eb9SGleb Smirnoff 	free(ch->tables, M_IPFW);
6563b3a8eb9SGleb Smirnoff 	free(ch->xtables, M_IPFW);
657b074b7bbSAlexander V. Chernikov 
658b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
659b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
6603b3a8eb9SGleb Smirnoff }
6613b3a8eb9SGleb Smirnoff 
6623b3a8eb9SGleb Smirnoff int
6633b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
6643b3a8eb9SGleb Smirnoff {
665b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
666b074b7bbSAlexander V. Chernikov 
6673b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
6683b3a8eb9SGleb Smirnoff 	ch->tables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
6693b3a8eb9SGleb Smirnoff 	ch->xtables = malloc(V_fw_tables_max * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
670b074b7bbSAlexander V. Chernikov 
671b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
672b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
673b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
674b074b7bbSAlexander V. Chernikov 
6753b3a8eb9SGleb Smirnoff 	return (0);
6763b3a8eb9SGleb Smirnoff }
6773b3a8eb9SGleb Smirnoff 
6783b3a8eb9SGleb Smirnoff int
6793b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
6803b3a8eb9SGleb Smirnoff {
6813b3a8eb9SGleb Smirnoff 	struct radix_node_head **tables, **xtables, *rnh;
6823b3a8eb9SGleb Smirnoff 	struct radix_node_head **tables_old, **xtables_old;
6833b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
684b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
685b074b7bbSAlexander V. Chernikov 	void *new_idx;
686b074b7bbSAlexander V. Chernikov 	int new_blocks;
6873b3a8eb9SGleb Smirnoff 
6883b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
6893b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
6903b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
6913b3a8eb9SGleb Smirnoff 
6923b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
6933b3a8eb9SGleb Smirnoff 	tables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
6943b3a8eb9SGleb Smirnoff 	xtables = malloc(ntables * sizeof(void *), M_IPFW, M_WAITOK | M_ZERO);
695b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
6963b3a8eb9SGleb Smirnoff 
6973b3a8eb9SGleb Smirnoff 	IPFW_WLOCK(ch);
6983b3a8eb9SGleb Smirnoff 
6993b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
700b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
701b074b7bbSAlexander V. Chernikov 
702b074b7bbSAlexander V. Chernikov 	/* Temportary restrict decreasing max_tables  */
703b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks) != 0) {
704b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(ch);
705b074b7bbSAlexander V. Chernikov 		free(tables, M_IPFW);
706b074b7bbSAlexander V. Chernikov 		free(xtables, M_IPFW);
707b074b7bbSAlexander V. Chernikov 		ipfw_objhash_bitmap_free(new_idx, new_blocks);
708b074b7bbSAlexander V. Chernikov 		return (EINVAL);
709b074b7bbSAlexander V. Chernikov 	}
7103b3a8eb9SGleb Smirnoff 
7113b3a8eb9SGleb Smirnoff 	/* Copy old table pointers */
7123b3a8eb9SGleb Smirnoff 	memcpy(tables, ch->tables, sizeof(void *) * tbl);
7133b3a8eb9SGleb Smirnoff 	memcpy(xtables, ch->xtables, sizeof(void *) * tbl);
7143b3a8eb9SGleb Smirnoff 
7153b3a8eb9SGleb Smirnoff 	/* Change pointers and number of tables */
7163b3a8eb9SGleb Smirnoff 	tables_old = ch->tables;
7173b3a8eb9SGleb Smirnoff 	xtables_old = ch->xtables;
7183b3a8eb9SGleb Smirnoff 	ch->tables = tables;
7193b3a8eb9SGleb Smirnoff 	ch->xtables = xtables;
7203b3a8eb9SGleb Smirnoff 
7213b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
7223b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
7233b3a8eb9SGleb Smirnoff 
7243b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
7253b3a8eb9SGleb Smirnoff 
7263b3a8eb9SGleb Smirnoff 	/* Check if we need to destroy radix trees */
7273b3a8eb9SGleb Smirnoff 	if (ntables < ntables_old) {
7283b3a8eb9SGleb Smirnoff 		for (tbl = ntables; tbl < ntables_old; tbl++) {
7293b3a8eb9SGleb Smirnoff 			if ((rnh = tables_old[tbl]) != NULL) {
7303b3a8eb9SGleb Smirnoff 				rnh->rnh_walktree(rnh, flush_table_entry, rnh);
7313b3a8eb9SGleb Smirnoff 				rn_detachhead((void **)&rnh);
7323b3a8eb9SGleb Smirnoff 			}
7333b3a8eb9SGleb Smirnoff 
7343b3a8eb9SGleb Smirnoff 			if ((rnh = xtables_old[tbl]) != NULL) {
7353b3a8eb9SGleb Smirnoff 				rnh->rnh_walktree(rnh, flush_table_entry, rnh);
7363b3a8eb9SGleb Smirnoff 				rn_detachhead((void **)&rnh);
7373b3a8eb9SGleb Smirnoff 			}
7383b3a8eb9SGleb Smirnoff 		}
7393b3a8eb9SGleb Smirnoff 	}
7403b3a8eb9SGleb Smirnoff 
7413b3a8eb9SGleb Smirnoff 	/* Free old pointers */
7423b3a8eb9SGleb Smirnoff 	free(tables_old, M_IPFW);
7433b3a8eb9SGleb Smirnoff 	free(xtables_old, M_IPFW);
744b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
7453b3a8eb9SGleb Smirnoff 
7463b3a8eb9SGleb Smirnoff 	return (0);
7473b3a8eb9SGleb Smirnoff }
7483b3a8eb9SGleb Smirnoff 
7493b3a8eb9SGleb Smirnoff int
7503b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
7513b3a8eb9SGleb Smirnoff     uint32_t *val)
7523b3a8eb9SGleb Smirnoff {
7533b3a8eb9SGleb Smirnoff 	struct radix_node_head *rnh;
7543b3a8eb9SGleb Smirnoff 	struct table_entry *ent;
7553b3a8eb9SGleb Smirnoff 	struct sockaddr_in sa;
7563b3a8eb9SGleb Smirnoff 
7573b3a8eb9SGleb Smirnoff 	if (tbl >= V_fw_tables_max)
7583b3a8eb9SGleb Smirnoff 		return (0);
7593b3a8eb9SGleb Smirnoff 	if ((rnh = ch->tables[tbl]) == NULL)
7603b3a8eb9SGleb Smirnoff 		return (0);
7613b3a8eb9SGleb Smirnoff 	KEY_LEN(sa) = KEY_LEN_INET;
7623b3a8eb9SGleb Smirnoff 	sa.sin_addr.s_addr = addr;
763d28d2aa4SAlexander V. Chernikov 	ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh));
7643b3a8eb9SGleb Smirnoff 	if (ent != NULL) {
7653b3a8eb9SGleb Smirnoff 		*val = ent->value;
7663b3a8eb9SGleb Smirnoff 		return (1);
7673b3a8eb9SGleb Smirnoff 	}
7683b3a8eb9SGleb Smirnoff 	return (0);
7693b3a8eb9SGleb Smirnoff }
7703b3a8eb9SGleb Smirnoff 
7713b3a8eb9SGleb Smirnoff int
7723b3a8eb9SGleb Smirnoff ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
7733b3a8eb9SGleb Smirnoff     uint32_t *val, int type)
7743b3a8eb9SGleb Smirnoff {
7753b3a8eb9SGleb Smirnoff 	struct radix_node_head *rnh;
7763b3a8eb9SGleb Smirnoff 	struct table_xentry *xent;
7773b3a8eb9SGleb Smirnoff 	struct sockaddr_in6 sa6;
7783b3a8eb9SGleb Smirnoff 	struct xaddr_iface iface;
7793b3a8eb9SGleb Smirnoff 
7803b3a8eb9SGleb Smirnoff 	if (tbl >= V_fw_tables_max)
7813b3a8eb9SGleb Smirnoff 		return (0);
7823b3a8eb9SGleb Smirnoff 	if ((rnh = ch->xtables[tbl]) == NULL)
7833b3a8eb9SGleb Smirnoff 		return (0);
7843b3a8eb9SGleb Smirnoff 
7853b3a8eb9SGleb Smirnoff 	switch (type) {
7863b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
7873b3a8eb9SGleb Smirnoff 		KEY_LEN(sa6) = KEY_LEN_INET6;
7883b3a8eb9SGleb Smirnoff 		memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
789d28d2aa4SAlexander V. Chernikov 		xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
7903b3a8eb9SGleb Smirnoff 		break;
7913b3a8eb9SGleb Smirnoff 
7923b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
7933b3a8eb9SGleb Smirnoff 		KEY_LEN(iface) = KEY_LEN_IFACE +
7943b3a8eb9SGleb Smirnoff 		    strlcpy(iface.ifname, (char *)paddr, IF_NAMESIZE) + 1;
7953b3a8eb9SGleb Smirnoff 		/* Assume direct match */
7963b3a8eb9SGleb Smirnoff 		/* FIXME: Add interface pattern matching */
797d28d2aa4SAlexander V. Chernikov 		xent = (struct table_xentry *)(rnh->rnh_matchaddr(&iface, rnh));
7983b3a8eb9SGleb Smirnoff 		break;
7993b3a8eb9SGleb Smirnoff 
8003b3a8eb9SGleb Smirnoff 	default:
8013b3a8eb9SGleb Smirnoff 		return (0);
8023b3a8eb9SGleb Smirnoff 	}
8033b3a8eb9SGleb Smirnoff 
8043b3a8eb9SGleb Smirnoff 	if (xent != NULL) {
8053b3a8eb9SGleb Smirnoff 		*val = xent->value;
8063b3a8eb9SGleb Smirnoff 		return (1);
8073b3a8eb9SGleb Smirnoff 	}
8083b3a8eb9SGleb Smirnoff 	return (0);
8093b3a8eb9SGleb Smirnoff }
8103b3a8eb9SGleb Smirnoff 
8113b3a8eb9SGleb Smirnoff static int
8123b3a8eb9SGleb Smirnoff count_table_entry(struct radix_node *rn, void *arg)
8133b3a8eb9SGleb Smirnoff {
8143b3a8eb9SGleb Smirnoff 	u_int32_t * const cnt = arg;
8153b3a8eb9SGleb Smirnoff 
8163b3a8eb9SGleb Smirnoff 	(*cnt)++;
8173b3a8eb9SGleb Smirnoff 	return (0);
8183b3a8eb9SGleb Smirnoff }
8193b3a8eb9SGleb Smirnoff 
8203b3a8eb9SGleb Smirnoff int
821b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
8223b3a8eb9SGleb Smirnoff {
8233b3a8eb9SGleb Smirnoff 	struct radix_node_head *rnh;
824b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
8253b3a8eb9SGleb Smirnoff 
826b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
8273b3a8eb9SGleb Smirnoff 		return (EINVAL);
828b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
829b074b7bbSAlexander V. Chernikov 		return (ESRCH);
8303b3a8eb9SGleb Smirnoff 	*cnt = 0;
831b074b7bbSAlexander V. Chernikov 	if ((rnh = ch->tables[tc->no.kidx]) == NULL)
8323b3a8eb9SGleb Smirnoff 		return (0);
8333b3a8eb9SGleb Smirnoff 	rnh->rnh_walktree(rnh, count_table_entry, cnt);
8343b3a8eb9SGleb Smirnoff 	return (0);
8353b3a8eb9SGleb Smirnoff }
8363b3a8eb9SGleb Smirnoff 
8373b3a8eb9SGleb Smirnoff static int
8383b3a8eb9SGleb Smirnoff dump_table_entry(struct radix_node *rn, void *arg)
8393b3a8eb9SGleb Smirnoff {
8403b3a8eb9SGleb Smirnoff 	struct table_entry * const n = (struct table_entry *)rn;
8413b3a8eb9SGleb Smirnoff 	ipfw_table * const tbl = arg;
8423b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
8433b3a8eb9SGleb Smirnoff 
8443b3a8eb9SGleb Smirnoff 	if (tbl->cnt == tbl->size)
8453b3a8eb9SGleb Smirnoff 		return (1);
8463b3a8eb9SGleb Smirnoff 	ent = &tbl->ent[tbl->cnt];
8473b3a8eb9SGleb Smirnoff 	ent->tbl = tbl->tbl;
8483b3a8eb9SGleb Smirnoff 	if (in_nullhost(n->mask.sin_addr))
8493b3a8eb9SGleb Smirnoff 		ent->masklen = 0;
8503b3a8eb9SGleb Smirnoff 	else
8513b3a8eb9SGleb Smirnoff 		ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
8523b3a8eb9SGleb Smirnoff 	ent->addr = n->addr.sin_addr.s_addr;
8533b3a8eb9SGleb Smirnoff 	ent->value = n->value;
8543b3a8eb9SGleb Smirnoff 	tbl->cnt++;
8553b3a8eb9SGleb Smirnoff 	return (0);
8563b3a8eb9SGleb Smirnoff }
8573b3a8eb9SGleb Smirnoff 
8583b3a8eb9SGleb Smirnoff int
859b074b7bbSAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, struct tid_info *ti, ipfw_table *tbl)
8603b3a8eb9SGleb Smirnoff {
8613b3a8eb9SGleb Smirnoff 	struct radix_node_head *rnh;
862b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
8633b3a8eb9SGleb Smirnoff 
864b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
8653b3a8eb9SGleb Smirnoff 		return (EINVAL);
866b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
867b074b7bbSAlexander V. Chernikov 		return (ESRCH);
8683b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
869b074b7bbSAlexander V. Chernikov 	if ((rnh = ch->tables[tc->no.kidx]) == NULL)
8703b3a8eb9SGleb Smirnoff 		return (0);
8713b3a8eb9SGleb Smirnoff 	rnh->rnh_walktree(rnh, dump_table_entry, tbl);
8723b3a8eb9SGleb Smirnoff 	return (0);
8733b3a8eb9SGleb Smirnoff }
8743b3a8eb9SGleb Smirnoff 
8753b3a8eb9SGleb Smirnoff static int
8763b3a8eb9SGleb Smirnoff count_table_xentry(struct radix_node *rn, void *arg)
8773b3a8eb9SGleb Smirnoff {
8783b3a8eb9SGleb Smirnoff 	uint32_t * const cnt = arg;
8793b3a8eb9SGleb Smirnoff 
8803b3a8eb9SGleb Smirnoff 	(*cnt) += sizeof(ipfw_table_xentry);
8813b3a8eb9SGleb Smirnoff 	return (0);
8823b3a8eb9SGleb Smirnoff }
8833b3a8eb9SGleb Smirnoff 
8843b3a8eb9SGleb Smirnoff int
885b074b7bbSAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
8863b3a8eb9SGleb Smirnoff {
8873b3a8eb9SGleb Smirnoff 	struct radix_node_head *rnh;
888b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
8893b3a8eb9SGleb Smirnoff 
890b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
8913b3a8eb9SGleb Smirnoff 		return (EINVAL);
8923b3a8eb9SGleb Smirnoff 	*cnt = 0;
893b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
894b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
895b074b7bbSAlexander V. Chernikov 	if ((rnh = ch->tables[tc->no.kidx]) != NULL)
8963b3a8eb9SGleb Smirnoff 		rnh->rnh_walktree(rnh, count_table_xentry, cnt);
897b074b7bbSAlexander V. Chernikov 	if ((rnh = ch->xtables[tc->no.kidx]) != NULL)
8983b3a8eb9SGleb Smirnoff 		rnh->rnh_walktree(rnh, count_table_xentry, cnt);
8993b3a8eb9SGleb Smirnoff 	/* Return zero if table is empty */
9003b3a8eb9SGleb Smirnoff 	if (*cnt > 0)
9013b3a8eb9SGleb Smirnoff 		(*cnt) += sizeof(ipfw_xtable);
9023b3a8eb9SGleb Smirnoff 	return (0);
9033b3a8eb9SGleb Smirnoff }
9043b3a8eb9SGleb Smirnoff 
9053b3a8eb9SGleb Smirnoff 
9063b3a8eb9SGleb Smirnoff static int
9073b3a8eb9SGleb Smirnoff dump_table_xentry_base(struct radix_node *rn, void *arg)
9083b3a8eb9SGleb Smirnoff {
9093b3a8eb9SGleb Smirnoff 	struct table_entry * const n = (struct table_entry *)rn;
9103b3a8eb9SGleb Smirnoff 	ipfw_xtable * const tbl = arg;
9113b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
9123b3a8eb9SGleb Smirnoff 
9133b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
9143b3a8eb9SGleb Smirnoff 	if (tbl->cnt == tbl->size)
9153b3a8eb9SGleb Smirnoff 		return (1);
9163b3a8eb9SGleb Smirnoff 	xent = &tbl->xent[tbl->cnt];
9173b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
9183b3a8eb9SGleb Smirnoff 	xent->tbl = tbl->tbl;
9193b3a8eb9SGleb Smirnoff 	if (in_nullhost(n->mask.sin_addr))
9203b3a8eb9SGleb Smirnoff 		xent->masklen = 0;
9213b3a8eb9SGleb Smirnoff 	else
9223b3a8eb9SGleb Smirnoff 		xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
9233b3a8eb9SGleb Smirnoff 	/* Save IPv4 address as deprecated IPv6 compatible */
9243b3a8eb9SGleb Smirnoff 	xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr;
925c3015737SAlexander V. Chernikov 	xent->flags = IPFW_TCF_INET;
9263b3a8eb9SGleb Smirnoff 	xent->value = n->value;
9273b3a8eb9SGleb Smirnoff 	tbl->cnt++;
9283b3a8eb9SGleb Smirnoff 	return (0);
9293b3a8eb9SGleb Smirnoff }
9303b3a8eb9SGleb Smirnoff 
9313b3a8eb9SGleb Smirnoff static int
9323b3a8eb9SGleb Smirnoff dump_table_xentry_extended(struct radix_node *rn, void *arg)
9333b3a8eb9SGleb Smirnoff {
9343b3a8eb9SGleb Smirnoff 	struct table_xentry * const n = (struct table_xentry *)rn;
9353b3a8eb9SGleb Smirnoff 	ipfw_xtable * const tbl = arg;
9363b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
9373b3a8eb9SGleb Smirnoff #ifdef INET6
9383b3a8eb9SGleb Smirnoff 	int i;
9393b3a8eb9SGleb Smirnoff 	uint32_t *v;
9403b3a8eb9SGleb Smirnoff #endif
9413b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
9423b3a8eb9SGleb Smirnoff 	if (tbl->cnt == tbl->size)
9433b3a8eb9SGleb Smirnoff 		return (1);
9443b3a8eb9SGleb Smirnoff 	xent = &tbl->xent[tbl->cnt];
9453b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
9463b3a8eb9SGleb Smirnoff 	xent->tbl = tbl->tbl;
9473b3a8eb9SGleb Smirnoff 
9483b3a8eb9SGleb Smirnoff 	switch (tbl->type) {
9493b3a8eb9SGleb Smirnoff #ifdef INET6
9503b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
9513b3a8eb9SGleb Smirnoff 		/* Count IPv6 mask */
9523b3a8eb9SGleb Smirnoff 		v = (uint32_t *)&n->m.mask6.sin6_addr;
9533b3a8eb9SGleb Smirnoff 		for (i = 0; i < sizeof(struct in6_addr) / 4; i++, v++)
9543b3a8eb9SGleb Smirnoff 			xent->masklen += bitcount32(*v);
9553b3a8eb9SGleb Smirnoff 		memcpy(&xent->k, &n->a.addr6.sin6_addr, sizeof(struct in6_addr));
9563b3a8eb9SGleb Smirnoff 		break;
9573b3a8eb9SGleb Smirnoff #endif
9583b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
9593b3a8eb9SGleb Smirnoff 		/* Assume exact mask */
9603b3a8eb9SGleb Smirnoff 		xent->masklen = 8 * IF_NAMESIZE;
9613b3a8eb9SGleb Smirnoff 		memcpy(&xent->k, &n->a.iface.ifname, IF_NAMESIZE);
9623b3a8eb9SGleb Smirnoff 		break;
9633b3a8eb9SGleb Smirnoff 
9643b3a8eb9SGleb Smirnoff 	default:
9653b3a8eb9SGleb Smirnoff 		/* unknown, skip entry */
9663b3a8eb9SGleb Smirnoff 		return (0);
9673b3a8eb9SGleb Smirnoff 	}
9683b3a8eb9SGleb Smirnoff 
9693b3a8eb9SGleb Smirnoff 	xent->value = n->value;
9703b3a8eb9SGleb Smirnoff 	tbl->cnt++;
9713b3a8eb9SGleb Smirnoff 	return (0);
9723b3a8eb9SGleb Smirnoff }
9733b3a8eb9SGleb Smirnoff 
9743b3a8eb9SGleb Smirnoff int
975b074b7bbSAlexander V. Chernikov ipfw_dump_xtable(struct ip_fw_chain *ch, struct tid_info *ti, ipfw_xtable *tbl)
9763b3a8eb9SGleb Smirnoff {
9773b3a8eb9SGleb Smirnoff 	struct radix_node_head *rnh;
978b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
9793b3a8eb9SGleb Smirnoff 
9803b3a8eb9SGleb Smirnoff 	if (tbl->tbl >= V_fw_tables_max)
9813b3a8eb9SGleb Smirnoff 		return (EINVAL);
9823b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
983b074b7bbSAlexander V. Chernikov 
984b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
985b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
986b074b7bbSAlexander V. Chernikov 	tbl->type = tc->no.type;
987b074b7bbSAlexander V. Chernikov 	if ((rnh = ch->tables[tc->no.kidx]) != NULL)
9883b3a8eb9SGleb Smirnoff 		rnh->rnh_walktree(rnh, dump_table_xentry_base, tbl);
989b074b7bbSAlexander V. Chernikov 	if ((rnh = ch->xtables[tc->no.kidx]) != NULL)
9903b3a8eb9SGleb Smirnoff 		rnh->rnh_walktree(rnh, dump_table_xentry_extended, tbl);
9913b3a8eb9SGleb Smirnoff 	return (0);
9923b3a8eb9SGleb Smirnoff }
9933b3a8eb9SGleb Smirnoff 
994b074b7bbSAlexander V. Chernikov /*
995b074b7bbSAlexander V. Chernikov  * Tables rewriting code
996b074b7bbSAlexander V. Chernikov  *
997b074b7bbSAlexander V. Chernikov  */
998b074b7bbSAlexander V. Chernikov 
999b074b7bbSAlexander V. Chernikov /*
1000b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
1001b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
1002b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
1003b074b7bbSAlexander V. Chernikov  */
1004b074b7bbSAlexander V. Chernikov static int
1005b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
1006b074b7bbSAlexander V. Chernikov {
1007b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1008b074b7bbSAlexander V. Chernikov 	int skip;
1009b074b7bbSAlexander V. Chernikov 	uint16_t v;
1010b074b7bbSAlexander V. Chernikov 
1011b074b7bbSAlexander V. Chernikov 	skip = 1;
1012b074b7bbSAlexander V. Chernikov 
1013b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1014b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1015b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1016b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1017b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
1018b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
1019b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
1020b074b7bbSAlexander V. Chernikov 		skip = 0;
1021b074b7bbSAlexander V. Chernikov 
1022b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
1023b074b7bbSAlexander V. Chernikov 			/*
1024b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
1025b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
1026b074b7bbSAlexander V. Chernikov 			 */
1027b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
1028b074b7bbSAlexander V. Chernikov 			switch (v) {
1029b074b7bbSAlexander V. Chernikov 			case 0:
1030b074b7bbSAlexander V. Chernikov 			case 1:
1031b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
1032b074b7bbSAlexander V. Chernikov 				break;
1033b074b7bbSAlexander V. Chernikov 			case 2:
1034b074b7bbSAlexander V. Chernikov 			case 3:
1035b074b7bbSAlexander V. Chernikov 				/* src/dst port */
1036b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1037b074b7bbSAlexander V. Chernikov 				break;
1038b074b7bbSAlexander V. Chernikov 			case 4:
1039b074b7bbSAlexander V. Chernikov 				/* uid/gid */
1040b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1041b074b7bbSAlexander V. Chernikov 			case 5:
1042b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1043b074b7bbSAlexander V. Chernikov 				/* jid */
1044b074b7bbSAlexander V. Chernikov 			case 6:
1045b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1046b074b7bbSAlexander V. Chernikov 				/* dscp */
1047b074b7bbSAlexander V. Chernikov 				break;
1048b074b7bbSAlexander V. Chernikov 			}
1049b074b7bbSAlexander V. Chernikov 		}
1050b074b7bbSAlexander V. Chernikov 		break;
1051b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1052b074b7bbSAlexander V. Chernikov 	case O_RECV:
1053b074b7bbSAlexander V. Chernikov 	case O_VIA:
1054b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1055b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1056b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
1057b074b7bbSAlexander V. Chernikov 			break;
1058b074b7bbSAlexander V. Chernikov 
1059b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
1060b074b7bbSAlexander V. Chernikov 		*puidx = cmdif->p.glob;
1061b074b7bbSAlexander V. Chernikov 		skip = 0;
1062b074b7bbSAlexander V. Chernikov 		break;
1063b074b7bbSAlexander V. Chernikov 	}
1064b074b7bbSAlexander V. Chernikov 
1065b074b7bbSAlexander V. Chernikov 	return (skip);
1066b074b7bbSAlexander V. Chernikov }
1067b074b7bbSAlexander V. Chernikov 
1068b074b7bbSAlexander V. Chernikov /*
1069b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
1070b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
1071b074b7bbSAlexander V. Chernikov  */
1072b074b7bbSAlexander V. Chernikov static void
1073b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
1074b074b7bbSAlexander V. Chernikov {
1075b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1076b074b7bbSAlexander V. Chernikov 
1077b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1078b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1079b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1080b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1081b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
1082b074b7bbSAlexander V. Chernikov 		break;
1083b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1084b074b7bbSAlexander V. Chernikov 	case O_RECV:
1085b074b7bbSAlexander V. Chernikov 	case O_VIA:
1086b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1087b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1088b074b7bbSAlexander V. Chernikov 		cmdif->p.glob = idx;
1089b074b7bbSAlexander V. Chernikov 		break;
1090b074b7bbSAlexander V. Chernikov 	}
1091b074b7bbSAlexander V. Chernikov }
1092b074b7bbSAlexander V. Chernikov 
1093b074b7bbSAlexander V. Chernikov static char *
1094b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
1095b074b7bbSAlexander V. Chernikov {
1096b074b7bbSAlexander V. Chernikov 	ipfw_xtable_ntlv *ntlv;
1097b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
1098b074b7bbSAlexander V. Chernikov 	int l;
1099b074b7bbSAlexander V. Chernikov 
1100b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
1101b074b7bbSAlexander V. Chernikov 	pe = pa + len;
1102b074b7bbSAlexander V. Chernikov 	l = 0;
1103b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
1104b074b7bbSAlexander V. Chernikov 		ntlv = (ipfw_xtable_ntlv *)pa;
1105b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
1106b074b7bbSAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_NAME)
1107b074b7bbSAlexander V. Chernikov 			continue;
1108b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
1109b074b7bbSAlexander V. Chernikov 			continue;
1110b074b7bbSAlexander V. Chernikov 
1111b074b7bbSAlexander V. Chernikov 		return (ntlv->name);
1112b074b7bbSAlexander V. Chernikov 	}
1113b074b7bbSAlexander V. Chernikov 
1114b074b7bbSAlexander V. Chernikov 	return (NULL);
1115b074b7bbSAlexander V. Chernikov }
1116b074b7bbSAlexander V. Chernikov 
1117b074b7bbSAlexander V. Chernikov static struct table_config *
1118b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
1119b074b7bbSAlexander V. Chernikov {
1120b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1121b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1122b074b7bbSAlexander V. Chernikov 
1123b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1124b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1125b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1126b074b7bbSAlexander V. Chernikov 			return (NULL);
1127b074b7bbSAlexander V. Chernikov 	} else {
1128b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1129b074b7bbSAlexander V. Chernikov 		name = bname;
1130b074b7bbSAlexander V. Chernikov 	}
1131b074b7bbSAlexander V. Chernikov 
1132b074b7bbSAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, ti->set, name);
1133b074b7bbSAlexander V. Chernikov 
1134b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
1135b074b7bbSAlexander V. Chernikov }
1136b074b7bbSAlexander V. Chernikov 
1137b074b7bbSAlexander V. Chernikov static int
1138b074b7bbSAlexander V. Chernikov alloc_table_state(void **state, void **xstate, uint8_t type)
1139b074b7bbSAlexander V. Chernikov {
1140b074b7bbSAlexander V. Chernikov 
1141b074b7bbSAlexander V. Chernikov 	switch (type) {
1142b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_CIDR:
1143b074b7bbSAlexander V. Chernikov 		if (!rn_inithead(state, OFF_LEN_INET))
1144b074b7bbSAlexander V. Chernikov 			return (ENOMEM);
1145b074b7bbSAlexander V. Chernikov 		if (!rn_inithead(xstate, OFF_LEN_INET6)) {
1146b074b7bbSAlexander V. Chernikov 			rn_detachhead(state);
1147b074b7bbSAlexander V. Chernikov 			return (ENOMEM);
1148b074b7bbSAlexander V. Chernikov 		}
1149b074b7bbSAlexander V. Chernikov 		break;
1150b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_INTERFACE:
1151b074b7bbSAlexander V. Chernikov 		*state = NULL;
1152b074b7bbSAlexander V. Chernikov 		if (!rn_inithead(xstate, OFF_LEN_IFACE))
1153b074b7bbSAlexander V. Chernikov 			return (ENOMEM);
1154b074b7bbSAlexander V. Chernikov 		break;
1155b074b7bbSAlexander V. Chernikov 	}
1156b074b7bbSAlexander V. Chernikov 
1157b074b7bbSAlexander V. Chernikov 	return (0);
1158b074b7bbSAlexander V. Chernikov }
1159b074b7bbSAlexander V. Chernikov 
1160b074b7bbSAlexander V. Chernikov 
1161b074b7bbSAlexander V. Chernikov static struct table_config *
1162b074b7bbSAlexander V. Chernikov alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti)
1163b074b7bbSAlexander V. Chernikov {
1164b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1165b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1166b074b7bbSAlexander V. Chernikov 	int error;
1167b074b7bbSAlexander V. Chernikov 
1168b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1169b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1170b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1171b074b7bbSAlexander V. Chernikov 			return (NULL);
1172b074b7bbSAlexander V. Chernikov 	} else {
1173b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1174b074b7bbSAlexander V. Chernikov 		name = bname;
1175b074b7bbSAlexander V. Chernikov 	}
1176b074b7bbSAlexander V. Chernikov 
1177b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
1178b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
1179b074b7bbSAlexander V. Chernikov 	tc->no.type = ti->type;
1180b074b7bbSAlexander V. Chernikov 	tc->no.set = ti->set;
1181b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
1182b074b7bbSAlexander V. Chernikov 
1183b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
1184b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
1185b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
1186b074b7bbSAlexander V. Chernikov 	}
1187b074b7bbSAlexander V. Chernikov 
1188b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
1189b074b7bbSAlexander V. Chernikov 	error = alloc_table_state(&tc->state, &tc->xstate, ti->type);
1190b074b7bbSAlexander V. Chernikov 	if (error != 0) {
1191b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
1192b074b7bbSAlexander V. Chernikov 		return (NULL);
1193b074b7bbSAlexander V. Chernikov 	}
1194b074b7bbSAlexander V. Chernikov 
1195b074b7bbSAlexander V. Chernikov 	return (tc);
1196b074b7bbSAlexander V. Chernikov }
1197b074b7bbSAlexander V. Chernikov 
1198b074b7bbSAlexander V. Chernikov static void
1199b074b7bbSAlexander V. Chernikov free_table_state(void **state, void **xstate, uint8_t type)
1200b074b7bbSAlexander V. Chernikov {
1201b074b7bbSAlexander V. Chernikov 	struct radix_node_head *rnh;
1202b074b7bbSAlexander V. Chernikov 
1203b074b7bbSAlexander V. Chernikov 	switch (type) {
1204b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_CIDR:
1205b074b7bbSAlexander V. Chernikov 		rnh = (struct radix_node_head *)(*state);
1206b074b7bbSAlexander V. Chernikov 		rnh->rnh_walktree(rnh, flush_table_entry, rnh);
1207b074b7bbSAlexander V. Chernikov 		rn_detachhead(state);
1208b074b7bbSAlexander V. Chernikov 
1209b074b7bbSAlexander V. Chernikov 		rnh = (struct radix_node_head *)(*xstate);
1210b074b7bbSAlexander V. Chernikov 		rnh->rnh_walktree(rnh, flush_table_entry, rnh);
1211b074b7bbSAlexander V. Chernikov 		rn_detachhead(xstate);
1212b074b7bbSAlexander V. Chernikov 		break;
1213b074b7bbSAlexander V. Chernikov 	case IPFW_TABLE_INTERFACE:
1214b074b7bbSAlexander V. Chernikov 		rnh = (struct radix_node_head *)(*xstate);
1215b074b7bbSAlexander V. Chernikov 		rnh->rnh_walktree(rnh, flush_table_entry, rnh);
1216b074b7bbSAlexander V. Chernikov 		rn_detachhead(xstate);
1217b074b7bbSAlexander V. Chernikov 		break;
1218b074b7bbSAlexander V. Chernikov 	}
1219b074b7bbSAlexander V. Chernikov }
1220b074b7bbSAlexander V. Chernikov 
1221b074b7bbSAlexander V. Chernikov static void
1222b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
1223b074b7bbSAlexander V. Chernikov {
1224b074b7bbSAlexander V. Chernikov 
1225b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0)
1226b074b7bbSAlexander V. Chernikov 		free_table_state(&tc->state, &tc->xstate, tc->no.type);
1227b074b7bbSAlexander V. Chernikov 
1228b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
1229b074b7bbSAlexander V. Chernikov }
1230b074b7bbSAlexander V. Chernikov 
1231b074b7bbSAlexander V. Chernikov /*
1232b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
1233b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
1234b074b7bbSAlexander V. Chernikov  */
1235b074b7bbSAlexander V. Chernikov static void
1236b074b7bbSAlexander V. Chernikov link_table(struct ip_fw_chain *chain, struct table_config *tc)
1237b074b7bbSAlexander V. Chernikov {
1238b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1239b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1240b074b7bbSAlexander V. Chernikov 
1241b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(chain);
1242b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(chain);
1243b074b7bbSAlexander V. Chernikov 
1244b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1245b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1246b074b7bbSAlexander V. Chernikov 
1247b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
1248b074b7bbSAlexander V. Chernikov 	chain->tables[kidx] = tc->state;
1249b074b7bbSAlexander V. Chernikov 	chain->xtables[kidx] = tc->xstate;
1250b074b7bbSAlexander V. Chernikov 
1251b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
1252b074b7bbSAlexander V. Chernikov }
1253b074b7bbSAlexander V. Chernikov 
1254b074b7bbSAlexander V. Chernikov /*
1255b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
1256b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
1257b074b7bbSAlexander V. Chernikov  */
1258b074b7bbSAlexander V. Chernikov static void
1259b074b7bbSAlexander V. Chernikov unlink_table(struct ip_fw_chain *chain, struct table_config *tc)
1260b074b7bbSAlexander V. Chernikov {
1261b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1262b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1263b074b7bbSAlexander V. Chernikov 
1264b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(chain);
1265b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(chain);
1266b074b7bbSAlexander V. Chernikov 
1267b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1268b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1269b074b7bbSAlexander V. Chernikov 
1270b074b7bbSAlexander V. Chernikov 	/* Clear state and save pointers for flush */
1271b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
1272b074b7bbSAlexander V. Chernikov 	tc->state = chain->tables[kidx];
1273b074b7bbSAlexander V. Chernikov 	chain->tables[kidx] = NULL;
1274b074b7bbSAlexander V. Chernikov 	tc->xstate = chain->xtables[kidx];
1275b074b7bbSAlexander V. Chernikov 	chain->xtables[kidx] = NULL;
1276b074b7bbSAlexander V. Chernikov 
1277b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
1278b074b7bbSAlexander V. Chernikov }
1279b074b7bbSAlexander V. Chernikov 
1280b074b7bbSAlexander V. Chernikov /*
1281b074b7bbSAlexander V. Chernikov  * Finds named object by @uidx number.
1282b074b7bbSAlexander V. Chernikov  * Refs found object, allocate new index for non-existing object.
1283b074b7bbSAlexander V. Chernikov  * Fills in @pidx with userland/kernel indexes.
1284b074b7bbSAlexander V. Chernikov  *
1285b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1286b074b7bbSAlexander V. Chernikov  */
1287b074b7bbSAlexander V. Chernikov static int
1288b074b7bbSAlexander V. Chernikov bind_table(struct namedobj_instance *ni, struct rule_check_info *ci,
1289b074b7bbSAlexander V. Chernikov     struct obj_idx *pidx, struct tid_info *ti)
1290b074b7bbSAlexander V. Chernikov {
1291b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1292b074b7bbSAlexander V. Chernikov 
1293b074b7bbSAlexander V. Chernikov 	tc = find_table(ni, ti);
1294b074b7bbSAlexander V. Chernikov 
1295b074b7bbSAlexander V. Chernikov 	pidx->uidx = ti->uidx;
1296b074b7bbSAlexander V. Chernikov 	pidx->type = ti->type;
1297b074b7bbSAlexander V. Chernikov 
1298b074b7bbSAlexander V. Chernikov 	if (tc == NULL) {
1299b074b7bbSAlexander V. Chernikov 		/* Try to acquire refcount */
1300b074b7bbSAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, ti->set, &pidx->kidx) != 0) {
1301b074b7bbSAlexander V. Chernikov 			printf("Unable to allocate table index in set %u."
1302b074b7bbSAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max",
1303b074b7bbSAlexander V. Chernikov 				    ti->set);
1304b074b7bbSAlexander V. Chernikov 			return (EBUSY);
1305b074b7bbSAlexander V. Chernikov 		}
1306b074b7bbSAlexander V. Chernikov 
1307b074b7bbSAlexander V. Chernikov 		pidx->new = 1;
1308b074b7bbSAlexander V. Chernikov 		ci->new_tables++;
1309b074b7bbSAlexander V. Chernikov 
1310b074b7bbSAlexander V. Chernikov 		return (0);
1311b074b7bbSAlexander V. Chernikov 	}
1312b074b7bbSAlexander V. Chernikov 
1313b074b7bbSAlexander V. Chernikov 	/* Check if table type if valid first */
1314b074b7bbSAlexander V. Chernikov 	if (tc->no.type != ti->type)
1315b074b7bbSAlexander V. Chernikov 		return (EINVAL);
1316b074b7bbSAlexander V. Chernikov 
1317b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
1318b074b7bbSAlexander V. Chernikov 
1319b074b7bbSAlexander V. Chernikov 	pidx->kidx = tc->no.kidx;
1320b074b7bbSAlexander V. Chernikov 
1321b074b7bbSAlexander V. Chernikov 	return (0);
1322b074b7bbSAlexander V. Chernikov }
1323b074b7bbSAlexander V. Chernikov 
1324b074b7bbSAlexander V. Chernikov /*
1325b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
1326b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
1327b074b7bbSAlexander V. Chernikov  * Works for \d+ talbes only (e.g. for tables, converted
1328b074b7bbSAlexander V. Chernikov  * from old numbered system calls).
1329b074b7bbSAlexander V. Chernikov  *
1330b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1331b074b7bbSAlexander V. Chernikov  * Raises error on any other tables.
1332b074b7bbSAlexander V. Chernikov  */
1333b074b7bbSAlexander V. Chernikov int
1334b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
1335b074b7bbSAlexander V. Chernikov {
1336b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1337b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1338b074b7bbSAlexander V. Chernikov 	uint32_t set;
1339b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1340b074b7bbSAlexander V. Chernikov 	uint8_t type;
1341b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1342b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1343b074b7bbSAlexander V. Chernikov 
1344b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1345b074b7bbSAlexander V. Chernikov 
1346b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1347b074b7bbSAlexander V. Chernikov 
1348b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1349b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1350b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1351b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1352b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1353b074b7bbSAlexander V. Chernikov 
1354b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1355b074b7bbSAlexander V. Chernikov 			continue;
1356b074b7bbSAlexander V. Chernikov 
1357b074b7bbSAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_idx(ni, set, kidx)) == NULL)
1358b074b7bbSAlexander V. Chernikov 			return (1);
1359b074b7bbSAlexander V. Chernikov 
1360b074b7bbSAlexander V. Chernikov 		if (no->compat == 0)
1361b074b7bbSAlexander V. Chernikov 			return (2);
1362b074b7bbSAlexander V. Chernikov 
1363b074b7bbSAlexander V. Chernikov 		update_table_opcode(cmd, no->uidx);
1364b074b7bbSAlexander V. Chernikov 	}
1365b074b7bbSAlexander V. Chernikov 
1366b074b7bbSAlexander V. Chernikov 	return (0);
1367b074b7bbSAlexander V. Chernikov }
1368b074b7bbSAlexander V. Chernikov 
1369b074b7bbSAlexander V. Chernikov 
1370b074b7bbSAlexander V. Chernikov /*
1371b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
1372b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
1373b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
1374b074b7bbSAlexander V. Chernikov  *
1375b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
1376b074b7bbSAlexander V. Chernikov  */
1377b074b7bbSAlexander V. Chernikov int
1378b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
1379b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
1380b074b7bbSAlexander V. Chernikov {
1381b074b7bbSAlexander V. Chernikov 	int cmdlen, error, ftype, l;
1382b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1383b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
1384b074b7bbSAlexander V. Chernikov 	uint8_t type;
1385b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1386b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1387b074b7bbSAlexander V. Chernikov 	struct named_object *no, *no_n, *no_tmp;
1388b074b7bbSAlexander V. Chernikov 	struct obj_idx *pidx, *p, *oib;
1389b074b7bbSAlexander V. Chernikov 	struct namedobjects_head nh;
1390b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
1391b074b7bbSAlexander V. Chernikov 
1392b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1393b074b7bbSAlexander V. Chernikov 
1394b074b7bbSAlexander V. Chernikov 	/*
1395b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
1396b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
1397b074b7bbSAlexander V. Chernikov 	 */
1398b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
1399b074b7bbSAlexander V. Chernikov 		/* Stack */
1400b074b7bbSAlexander V. Chernikov 		pidx = ci->obuf;
1401b074b7bbSAlexander V. Chernikov 	} else
1402b074b7bbSAlexander V. Chernikov 		pidx = malloc(ci->table_opcodes * sizeof(struct obj_idx),
1403b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1404b074b7bbSAlexander V. Chernikov 
1405b074b7bbSAlexander V. Chernikov 	oib = pidx;
1406b074b7bbSAlexander V. Chernikov 	error = 0;
1407b074b7bbSAlexander V. Chernikov 
1408b074b7bbSAlexander V. Chernikov 	type = 0;
1409b074b7bbSAlexander V. Chernikov 	ftype = 0;
1410b074b7bbSAlexander V. Chernikov 
1411b074b7bbSAlexander V. Chernikov 	ci->tableset = TABLE_SET(ci->krule->set);
1412b074b7bbSAlexander V. Chernikov 
1413b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
1414b074b7bbSAlexander V. Chernikov 	ti.set = ci->tableset;
1415b074b7bbSAlexander V. Chernikov 	ti.tlvs = ci->tlvs;
1416b074b7bbSAlexander V. Chernikov 	ti.tlen = ci->tlen;
1417b074b7bbSAlexander V. Chernikov 
1418b074b7bbSAlexander V. Chernikov 	/*
1419b074b7bbSAlexander V. Chernikov 	 * Stage 1: reference existing tables and determine number
1420b074b7bbSAlexander V. Chernikov 	 * of tables we need to allocate
1421b074b7bbSAlexander V. Chernikov 	 */
1422b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
1423b074b7bbSAlexander V. Chernikov 
1424b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
1425b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
1426b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1427b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1428b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1429b074b7bbSAlexander V. Chernikov 
1430b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti.uidx, &ti.type) != 0)
1431b074b7bbSAlexander V. Chernikov 			continue;
1432b074b7bbSAlexander V. Chernikov 
1433b074b7bbSAlexander V. Chernikov 		/*
1434b074b7bbSAlexander V. Chernikov 		 * Got table opcode with necessary info.
1435b074b7bbSAlexander V. Chernikov 		 * Try to reference existing tables and allocate
1436b074b7bbSAlexander V. Chernikov 		 * indices for non-existing one while holding write lock.
1437b074b7bbSAlexander V. Chernikov 		 */
1438b074b7bbSAlexander V. Chernikov 		if ((error = bind_table(ni, ci, pidx, &ti)) != 0)
1439b074b7bbSAlexander V. Chernikov 			break;
1440b074b7bbSAlexander V. Chernikov 
1441b074b7bbSAlexander V. Chernikov 		/*
1442b074b7bbSAlexander V. Chernikov 		 * @pidx stores either existing ref'd table id or new one.
1443b074b7bbSAlexander V. Chernikov 		 * Move to next index
1444b074b7bbSAlexander V. Chernikov 		 */
1445b074b7bbSAlexander V. Chernikov 
1446b074b7bbSAlexander V. Chernikov 		pidx++;
1447b074b7bbSAlexander V. Chernikov 	}
1448b074b7bbSAlexander V. Chernikov 
1449b074b7bbSAlexander V. Chernikov 	if (error != 0) {
1450b074b7bbSAlexander V. Chernikov 		/* Unref everything we have already done */
1451b074b7bbSAlexander V. Chernikov 		for (p = oib; p < pidx; p++) {
1452b074b7bbSAlexander V. Chernikov 			if (p->new != 0) {
1453b074b7bbSAlexander V. Chernikov 				ipfw_objhash_free_idx(ni, ci->tableset,p->kidx);
1454b074b7bbSAlexander V. Chernikov 				continue;
1455b074b7bbSAlexander V. Chernikov 			}
1456b074b7bbSAlexander V. Chernikov 
1457b074b7bbSAlexander V. Chernikov 			/* Find & unref by existing idx */
1458b074b7bbSAlexander V. Chernikov 			no = ipfw_objhash_lookup_idx(ni, ci->tableset, p->kidx);
1459b074b7bbSAlexander V. Chernikov 			KASSERT(no!=NULL, ("Ref'd table %d disappeared",
1460b074b7bbSAlexander V. Chernikov 			    p->kidx));
1461b074b7bbSAlexander V. Chernikov 
1462b074b7bbSAlexander V. Chernikov 			no->refcnt--;
1463b074b7bbSAlexander V. Chernikov 		}
1464b074b7bbSAlexander V. Chernikov 
1465b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
1466b074b7bbSAlexander V. Chernikov 
1467b074b7bbSAlexander V. Chernikov 		if (oib != ci->obuf)
1468b074b7bbSAlexander V. Chernikov 			free(oib, M_IPFW);
1469b074b7bbSAlexander V. Chernikov 
1470b074b7bbSAlexander V. Chernikov 		return (error);
1471b074b7bbSAlexander V. Chernikov 	}
1472b074b7bbSAlexander V. Chernikov 
1473b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
1474b074b7bbSAlexander V. Chernikov 
1475b074b7bbSAlexander V. Chernikov 	/*
1476b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate table configs for every non-existent table
1477b074b7bbSAlexander V. Chernikov 	 */
1478b074b7bbSAlexander V. Chernikov 
1479b074b7bbSAlexander V. Chernikov 	if (ci->new_tables > 0) {
1480b074b7bbSAlexander V. Chernikov 		/* Prepare queue to store configs */
1481b074b7bbSAlexander V. Chernikov 		TAILQ_INIT(&nh);
1482b074b7bbSAlexander V. Chernikov 
1483b074b7bbSAlexander V. Chernikov 		for (p = oib; p < pidx; p++) {
1484b074b7bbSAlexander V. Chernikov 			if (p->new == 0)
1485b074b7bbSAlexander V. Chernikov 				continue;
1486b074b7bbSAlexander V. Chernikov 
1487b074b7bbSAlexander V. Chernikov 			/* TODO: get name from TLV */
1488b074b7bbSAlexander V. Chernikov 			ti.uidx = p->uidx;
1489b074b7bbSAlexander V. Chernikov 			ti.type = p->type;
1490b074b7bbSAlexander V. Chernikov 
1491b074b7bbSAlexander V. Chernikov 			tc = alloc_table_config(ni, &ti);
1492b074b7bbSAlexander V. Chernikov 
1493b074b7bbSAlexander V. Chernikov 			if (tc == NULL) {
1494b074b7bbSAlexander V. Chernikov 				error = ENOMEM;
1495b074b7bbSAlexander V. Chernikov 				goto free;
1496b074b7bbSAlexander V. Chernikov 			}
1497b074b7bbSAlexander V. Chernikov 
1498b074b7bbSAlexander V. Chernikov 			tc->no.kidx = p->kidx;
1499b074b7bbSAlexander V. Chernikov 			tc->no.refcnt = 1;
1500b074b7bbSAlexander V. Chernikov 
1501b074b7bbSAlexander V. Chernikov 			/* Add to list */
1502b074b7bbSAlexander V. Chernikov 			TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next);
1503b074b7bbSAlexander V. Chernikov 		}
1504b074b7bbSAlexander V. Chernikov 
1505b074b7bbSAlexander V. Chernikov 		/*
1506b074b7bbSAlexander V. Chernikov 		 * Stage 2.1: Check if we're going to create 2 tables
1507b074b7bbSAlexander V. Chernikov 		 * with the same name, but different table types.
1508b074b7bbSAlexander V. Chernikov 		 */
1509b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH(no, &nh, nn_next) {
1510b074b7bbSAlexander V. Chernikov 			TAILQ_FOREACH(no_tmp, &nh, nn_next) {
1511b074b7bbSAlexander V. Chernikov 				if (strcmp(no->name, no_tmp->name) != 0)
1512b074b7bbSAlexander V. Chernikov 					continue;
1513b074b7bbSAlexander V. Chernikov 				if (no->type != no_tmp->type) {
1514b074b7bbSAlexander V. Chernikov 					error = EINVAL;
1515b074b7bbSAlexander V. Chernikov 					goto free;
1516b074b7bbSAlexander V. Chernikov 				}
1517b074b7bbSAlexander V. Chernikov 			}
1518b074b7bbSAlexander V. Chernikov 		}
1519b074b7bbSAlexander V. Chernikov 
1520b074b7bbSAlexander V. Chernikov 		/*
1521b074b7bbSAlexander V. Chernikov 		 * Stage 3: link & reference new table configs
1522b074b7bbSAlexander V. Chernikov 		 */
1523b074b7bbSAlexander V. Chernikov 
1524b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(chain);
1525b074b7bbSAlexander V. Chernikov 
1526b074b7bbSAlexander V. Chernikov 		/*
1527b074b7bbSAlexander V. Chernikov 		 * Step 3.1: Check if some tables we need to create have been
1528b074b7bbSAlexander V. Chernikov 		 * already created with different table type.
1529b074b7bbSAlexander V. Chernikov 		 */
1530b074b7bbSAlexander V. Chernikov 
1531b074b7bbSAlexander V. Chernikov 		error = 0;
1532b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1533b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1534b074b7bbSAlexander V. Chernikov 			if (no_n == NULL)
1535b074b7bbSAlexander V. Chernikov 				continue;
1536b074b7bbSAlexander V. Chernikov 
1537b074b7bbSAlexander V. Chernikov 			if (no_n->type != no->type) {
1538b074b7bbSAlexander V. Chernikov 				error = EINVAL;
1539b074b7bbSAlexander V. Chernikov 				break;
1540b074b7bbSAlexander V. Chernikov 			}
1541b074b7bbSAlexander V. Chernikov 
1542b074b7bbSAlexander V. Chernikov 		}
1543b074b7bbSAlexander V. Chernikov 
1544b074b7bbSAlexander V. Chernikov 		if (error != 0) {
1545b074b7bbSAlexander V. Chernikov 			/*
1546b074b7bbSAlexander V. Chernikov 			 * Someone has allocated table with different table type.
1547b074b7bbSAlexander V. Chernikov 			 * We have to rollback everything.
1548b074b7bbSAlexander V. Chernikov 			 */
1549b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(chain);
1550b074b7bbSAlexander V. Chernikov 
1551b074b7bbSAlexander V. Chernikov 			goto free;
1552b074b7bbSAlexander V. Chernikov 		}
1553b074b7bbSAlexander V. Chernikov 
1554b074b7bbSAlexander V. Chernikov 
1555b074b7bbSAlexander V. Chernikov 		/*
1556b074b7bbSAlexander V. Chernikov 		 * Finally, attach tables and rewrite rule.
1557b074b7bbSAlexander V. Chernikov 		 * We need to set table type for each new table,
1558b074b7bbSAlexander V. Chernikov 		 * so we have to acquire main WLOCK.
1559b074b7bbSAlexander V. Chernikov 		 */
1560b074b7bbSAlexander V. Chernikov 		IPFW_WLOCK(chain);
1561b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1562b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1563b074b7bbSAlexander V. Chernikov 			if (no_n != NULL) {
1564b074b7bbSAlexander V. Chernikov 				/* Increase refcount for existing table */
1565b074b7bbSAlexander V. Chernikov 				no_n->refcnt++;
1566b074b7bbSAlexander V. Chernikov 				/* Keep oib array in sync: update kindx */
1567b074b7bbSAlexander V. Chernikov 				for (p = oib; p < pidx; p++) {
1568b074b7bbSAlexander V. Chernikov 					if (p->kidx == no->kidx) {
1569b074b7bbSAlexander V. Chernikov 						p->kidx = no_n->kidx;
1570b074b7bbSAlexander V. Chernikov 						break;
1571b074b7bbSAlexander V. Chernikov 					}
1572b074b7bbSAlexander V. Chernikov 				}
1573b074b7bbSAlexander V. Chernikov 
1574b074b7bbSAlexander V. Chernikov 				continue;
1575b074b7bbSAlexander V. Chernikov 			}
1576b074b7bbSAlexander V. Chernikov 
1577b074b7bbSAlexander V. Chernikov 			/* New table. Attach to runtime hash */
1578b074b7bbSAlexander V. Chernikov 			TAILQ_REMOVE(&nh, no, nn_next);
1579b074b7bbSAlexander V. Chernikov 
1580b074b7bbSAlexander V. Chernikov 			link_table(chain, (struct table_config *)no);
1581b074b7bbSAlexander V. Chernikov 		}
1582b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(chain);
1583b074b7bbSAlexander V. Chernikov 
1584b074b7bbSAlexander V. Chernikov 		/* Perform rule rewrite */
1585b074b7bbSAlexander V. Chernikov 		l = ci->krule->cmd_len;
1586b074b7bbSAlexander V. Chernikov 		cmd = ci->krule->cmd;
1587b074b7bbSAlexander V. Chernikov 		cmdlen = 0;
1588b074b7bbSAlexander V. Chernikov 		pidx = oib;
1589b074b7bbSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1590b074b7bbSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
1591b074b7bbSAlexander V. Chernikov 
1592b074b7bbSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &uidx, &type) != 0)
1593b074b7bbSAlexander V. Chernikov 				continue;
1594b074b7bbSAlexander V. Chernikov 			update_table_opcode(cmd, pidx->kidx);
1595b074b7bbSAlexander V. Chernikov 			pidx++;
1596b074b7bbSAlexander V. Chernikov 		}
1597b074b7bbSAlexander V. Chernikov 
1598b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
1599b074b7bbSAlexander V. Chernikov 	}
1600b074b7bbSAlexander V. Chernikov 
1601b074b7bbSAlexander V. Chernikov 	error = 0;
1602b074b7bbSAlexander V. Chernikov 
1603b074b7bbSAlexander V. Chernikov 	/*
1604b074b7bbSAlexander V. Chernikov 	 * Stage 4: free resources
1605b074b7bbSAlexander V. Chernikov 	 */
1606b074b7bbSAlexander V. Chernikov free:
1607b074b7bbSAlexander V. Chernikov 	TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp)
1608b074b7bbSAlexander V. Chernikov 		free_table_config(ni, tc);
1609b074b7bbSAlexander V. Chernikov 
1610b074b7bbSAlexander V. Chernikov 	if (oib != ci->obuf)
1611b074b7bbSAlexander V. Chernikov 		free(oib, M_IPFW);
1612b074b7bbSAlexander V. Chernikov 
1613b074b7bbSAlexander V. Chernikov 	return (error);
1614b074b7bbSAlexander V. Chernikov }
1615b074b7bbSAlexander V. Chernikov 
1616b074b7bbSAlexander V. Chernikov /*
1617b074b7bbSAlexander V. Chernikov  * Remove references from every table used in @rule.
1618b074b7bbSAlexander V. Chernikov  */
1619b074b7bbSAlexander V. Chernikov void
1620b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
1621b074b7bbSAlexander V. Chernikov {
1622b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1623b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1624b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1625b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1626b074b7bbSAlexander V. Chernikov 	uint32_t set;
1627b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1628b074b7bbSAlexander V. Chernikov 	uint8_t type;
1629b074b7bbSAlexander V. Chernikov 
1630b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1631b074b7bbSAlexander V. Chernikov 
1632b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1633b074b7bbSAlexander V. Chernikov 
1634b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1635b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1636b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1637b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1638b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1639b074b7bbSAlexander V. Chernikov 
1640b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1641b074b7bbSAlexander V. Chernikov 			continue;
1642b074b7bbSAlexander V. Chernikov 
1643b074b7bbSAlexander V. Chernikov 		no = ipfw_objhash_lookup_idx(ni, set, kidx);
1644b074b7bbSAlexander V. Chernikov 
1645b074b7bbSAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
1646b074b7bbSAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
1647b074b7bbSAlexander V. Chernikov 		    no->type, type, kidx));
1648b074b7bbSAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
1649b074b7bbSAlexander V. Chernikov 		    kidx, no->refcnt));
1650b074b7bbSAlexander V. Chernikov 
1651b074b7bbSAlexander V. Chernikov 		no->refcnt--;
1652b074b7bbSAlexander V. Chernikov 	}
1653b074b7bbSAlexander V. Chernikov }
1654b074b7bbSAlexander V. Chernikov 
1655b074b7bbSAlexander V. Chernikov 
1656b074b7bbSAlexander V. Chernikov /*
1657b074b7bbSAlexander V. Chernikov  * Removes table bindings for every rule in rule chain @head.
1658b074b7bbSAlexander V. Chernikov  */
1659b074b7bbSAlexander V. Chernikov void
1660b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head)
1661b074b7bbSAlexander V. Chernikov {
1662b074b7bbSAlexander V. Chernikov 	struct ip_fw *rule;
1663b074b7bbSAlexander V. Chernikov 
1664b074b7bbSAlexander V. Chernikov 	while ((rule = head) != NULL) {
1665b074b7bbSAlexander V. Chernikov 		head = head->x_next;
1666b074b7bbSAlexander V. Chernikov 		ipfw_unbind_table_rule(chain, rule);
1667b074b7bbSAlexander V. Chernikov 	}
1668b074b7bbSAlexander V. Chernikov }
1669b074b7bbSAlexander V. Chernikov 
1670b074b7bbSAlexander V. Chernikov 
16713b3a8eb9SGleb Smirnoff /* end of file */
1672