xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision 9490a627)
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 
443b3a8eb9SGleb Smirnoff #include <sys/param.h>
453b3a8eb9SGleb Smirnoff #include <sys/systm.h>
463b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
473b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
483b3a8eb9SGleb Smirnoff #include <sys/lock.h>
493b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
503b3a8eb9SGleb Smirnoff #include <sys/socket.h>
51f1220db8SAlexander V. Chernikov #include <sys/socketvar.h>
523b3a8eb9SGleb Smirnoff #include <sys/queue.h>
533b3a8eb9SGleb Smirnoff #include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
543b3a8eb9SGleb Smirnoff #include <net/route.h>
553b3a8eb9SGleb Smirnoff #include <net/vnet.h>
563b3a8eb9SGleb Smirnoff 
573b3a8eb9SGleb Smirnoff #include <netinet/in.h>
583b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
593b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
603b3a8eb9SGleb Smirnoff 
613b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
62ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
633b3a8eb9SGleb Smirnoff 
643b3a8eb9SGleb Smirnoff 
653b3a8eb9SGleb Smirnoff  /*
66b074b7bbSAlexander V. Chernikov  * Table has the following `type` concepts:
67b074b7bbSAlexander V. Chernikov  *
689f7d47b0SAlexander V. Chernikov  * `no.type` represents lookup key type (cidr, ifp, uid, etc..)
699f7d47b0SAlexander V. Chernikov  * `ta->atype` represents exact lookup algorithm.
70b074b7bbSAlexander V. Chernikov  *     For example, we can use more efficient search schemes if we plan
71b074b7bbSAlexander V. Chernikov  *     to use some specific table for storing host-routes only.
729490a627SAlexander V. Chernikov  * `ftype` (at the moment )is pure userland field helping to properly
739490a627SAlexander V. Chernikov  *     format value data e.g. "value is IPv4 nexthop" or "value is DSCP"
749490a627SAlexander V. Chernikov  *     or "value is port".
75b074b7bbSAlexander V. Chernikov  *
76b074b7bbSAlexander V. Chernikov  */
77b074b7bbSAlexander V. Chernikov struct table_config {
78b074b7bbSAlexander V. Chernikov 	struct named_object	no;
79b074b7bbSAlexander V. Chernikov 	uint8_t		ftype;		/* format table type */
80b074b7bbSAlexander V. Chernikov 	uint8_t		linked;		/* 1 if already linked */
819f7d47b0SAlexander V. Chernikov 	uint16_t	spare0;
82b074b7bbSAlexander V. Chernikov 	uint32_t	count;		/* Number of records */
83b074b7bbSAlexander V. Chernikov 	char		tablename[64];	/* table name */
849f7d47b0SAlexander V. Chernikov 	struct table_algo	*ta;	/* Callbacks for given algo */
859f7d47b0SAlexander V. Chernikov 	void		*astate;	/* algorithm state */
869f7d47b0SAlexander V. Chernikov 	struct table_info	ti;	/* data to put to table_info */
87b074b7bbSAlexander V. Chernikov };
88b074b7bbSAlexander V. Chernikov #define	TABLE_SET(set)	((V_fw_tables_sets != 0) ? set : 0)
89b074b7bbSAlexander V. Chernikov 
90b074b7bbSAlexander V. Chernikov struct tables_config {
91b074b7bbSAlexander V. Chernikov 	struct namedobj_instance	*namehash;
929f7d47b0SAlexander V. Chernikov 	int				algo_count;
939f7d47b0SAlexander V. Chernikov 	struct table_algo 		*algo[256];
94b074b7bbSAlexander V. Chernikov };
95b074b7bbSAlexander V. Chernikov 
96b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni,
97b074b7bbSAlexander V. Chernikov     struct tid_info *ti);
98b074b7bbSAlexander V. Chernikov static struct table_config *alloc_table_config(struct namedobj_instance *ni,
999490a627SAlexander V. Chernikov     struct tid_info *ti, struct table_algo *ta, char *adata);
100b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni,
101b074b7bbSAlexander V. Chernikov     struct table_config *tc);
102b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
103b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
104b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type);
105f1220db8SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh);
106f1220db8SAlexander V. Chernikov static void export_table_info(struct table_config *tc, ipfw_xtable_info *i);
107f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg);
108b074b7bbSAlexander V. Chernikov 
109d3a4f924SAlexander V. Chernikov static int check_buffer(size_t items, size_t item_size, size_t header,
110d3a4f924SAlexander V. Chernikov     size_t bufsize);
111d3a4f924SAlexander V. Chernikov 
112d3a4f924SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt,
113d3a4f924SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize);
114d3a4f924SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
115d3a4f924SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize);
116d3a4f924SAlexander V. Chernikov 
1179f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1189490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
119b074b7bbSAlexander V. Chernikov 
120b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
121b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1229f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
123b074b7bbSAlexander V. Chernikov 
124b074b7bbSAlexander V. Chernikov 
1253b3a8eb9SGleb Smirnoff 
1263b3a8eb9SGleb Smirnoff int
127b074b7bbSAlexander V. Chernikov ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
128b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
1293b3a8eb9SGleb Smirnoff {
130b074b7bbSAlexander V. Chernikov 	struct table_config *tc, *tc_new;
1319f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
132b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
133b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1349f7d47b0SAlexander V. Chernikov 	int error;
1359f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
1363b3a8eb9SGleb Smirnoff 
1379f7d47b0SAlexander V. Chernikov #if 0
138b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
1393b3a8eb9SGleb Smirnoff 		return (EINVAL);
1403b3a8eb9SGleb Smirnoff #endif
1419f7d47b0SAlexander V. Chernikov 
1429f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1439f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1449f7d47b0SAlexander V. Chernikov 
1459f7d47b0SAlexander V. Chernikov 	/*
1469f7d47b0SAlexander V. Chernikov 	 * Find and reference existing table.
1479f7d47b0SAlexander V. Chernikov 	 */
1489f7d47b0SAlexander V. Chernikov 	ta = NULL;
1499f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
1509f7d47b0SAlexander V. Chernikov 		/* check table type */
1519f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
1529f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1533b3a8eb9SGleb Smirnoff 			return (EINVAL);
1543b3a8eb9SGleb Smirnoff 		}
1553b3a8eb9SGleb Smirnoff 
1569f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
1579f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
1589f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
1599f7d47b0SAlexander V. Chernikov 	}
1609f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1613b3a8eb9SGleb Smirnoff 
1629f7d47b0SAlexander V. Chernikov 	tc_new = NULL;
1639f7d47b0SAlexander V. Chernikov 	if (ta == NULL) {
1649f7d47b0SAlexander V. Chernikov 		/* Table not found. We have to create new one */
1659490a627SAlexander V. Chernikov 		if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL)
1669f7d47b0SAlexander V. Chernikov 			return (ENOTSUP);
1673b3a8eb9SGleb Smirnoff 
1689490a627SAlexander V. Chernikov 		tc_new = alloc_table_config(ni, ti, ta, NULL);
1699f7d47b0SAlexander V. Chernikov 		if (tc_new == NULL)
1709f7d47b0SAlexander V. Chernikov 			return (ENOMEM);
1719f7d47b0SAlexander V. Chernikov 	}
1723b3a8eb9SGleb Smirnoff 
1739f7d47b0SAlexander V. Chernikov 	/* Prepare record (allocate memory) */
1749f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
1759f7d47b0SAlexander V. Chernikov 	error = ta->prepare_add(tei, &ta_buf);
1769f7d47b0SAlexander V. Chernikov 	if (error != 0) {
1779f7d47b0SAlexander V. Chernikov 		if (tc_new != NULL)
1789f7d47b0SAlexander V. Chernikov 			free_table_config(ni, tc_new);
1799f7d47b0SAlexander V. Chernikov 		return (error);
1803b3a8eb9SGleb Smirnoff 	}
1813b3a8eb9SGleb Smirnoff 
182b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1833b3a8eb9SGleb Smirnoff 
184b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1853b3a8eb9SGleb Smirnoff 
1869f7d47b0SAlexander V. Chernikov 	if (tc == NULL) {
1879f7d47b0SAlexander V. Chernikov 		/* Check if another table was allocated by other thread */
188b074b7bbSAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
1899f7d47b0SAlexander V. Chernikov 
1909f7d47b0SAlexander V. Chernikov 			/*
1919f7d47b0SAlexander V. Chernikov 			 * Check if algoritm is the same since we've
1929f7d47b0SAlexander V. Chernikov 			 * already allocated state using @ta algoritm
1939f7d47b0SAlexander V. Chernikov 			 * callbacks.
1949f7d47b0SAlexander V. Chernikov 			 */
1959f7d47b0SAlexander V. Chernikov 			if (tc->ta != ta) {
196b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
197b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
1983b3a8eb9SGleb Smirnoff 				return (EINVAL);
1993b3a8eb9SGleb Smirnoff 			}
2003b3a8eb9SGleb Smirnoff 		} else {
2013b3a8eb9SGleb Smirnoff 			/*
2029f7d47b0SAlexander V. Chernikov 			 * We're first to create this table.
203b074b7bbSAlexander V. Chernikov 			 * Set tc_new to zero not to free it afterwards.
2043b3a8eb9SGleb Smirnoff 			 */
205b074b7bbSAlexander V. Chernikov 			tc = tc_new;
206b074b7bbSAlexander V. Chernikov 			tc_new = NULL;
207b074b7bbSAlexander V. Chernikov 
208b074b7bbSAlexander V. Chernikov 			/* Allocate table index. */
209b074b7bbSAlexander V. Chernikov 			if (ipfw_objhash_alloc_idx(ni, ti->set, &kidx) != 0) {
210b074b7bbSAlexander V. Chernikov 				/* Index full. */
211b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
212b074b7bbSAlexander V. Chernikov 				printf("Unable to allocate index for table %s."
213b074b7bbSAlexander V. Chernikov 				    " Consider increasing "
214b074b7bbSAlexander V. Chernikov 				    "net.inet.ip.fw.tables_max",
215b074b7bbSAlexander V. Chernikov 				    tc->no.name);
216b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
217b074b7bbSAlexander V. Chernikov 				return (EBUSY);
2183b3a8eb9SGleb Smirnoff 			}
219b074b7bbSAlexander V. Chernikov 			/* Save kidx */
220b074b7bbSAlexander V. Chernikov 			tc->no.kidx = kidx;
221b074b7bbSAlexander V. Chernikov 		}
222b074b7bbSAlexander V. Chernikov 	} else {
2239f7d47b0SAlexander V. Chernikov 		/* Drop reference we've used in first search */
2249f7d47b0SAlexander V. Chernikov 		tc->no.refcnt--;
225b074b7bbSAlexander V. Chernikov 	}
226b074b7bbSAlexander V. Chernikov 
227b074b7bbSAlexander V. Chernikov 	/* We've got valid table in @tc. Let's add data */
2289f7d47b0SAlexander V. Chernikov 	kidx = tc->no.kidx;
2299f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2309f7d47b0SAlexander V. Chernikov 
231b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
232b074b7bbSAlexander V. Chernikov 
233b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0) {
234b074b7bbSAlexander V. Chernikov 		link_table(ch, tc);
235b074b7bbSAlexander V. Chernikov 	}
236b074b7bbSAlexander V. Chernikov 
2379f7d47b0SAlexander V. Chernikov 	error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2383b3a8eb9SGleb Smirnoff 
2393b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2409f7d47b0SAlexander V. Chernikov 
2419f7d47b0SAlexander V. Chernikov 	if (error == 0)
2429f7d47b0SAlexander V. Chernikov 		tc->count++;
2439f7d47b0SAlexander V. Chernikov 
244b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
245b074b7bbSAlexander V. Chernikov 
246b074b7bbSAlexander V. Chernikov 	if (tc_new != NULL)
247b074b7bbSAlexander V. Chernikov 		free_table_config(ni, tc);
2483b3a8eb9SGleb Smirnoff 
2499f7d47b0SAlexander V. Chernikov 	if (error != 0)
2509f7d47b0SAlexander V. Chernikov 		ta->flush_entry(tei, &ta_buf);
251b074b7bbSAlexander V. Chernikov 
2529f7d47b0SAlexander V. Chernikov 	return (error);
2533b3a8eb9SGleb Smirnoff }
2543b3a8eb9SGleb Smirnoff 
2553b3a8eb9SGleb Smirnoff int
256b074b7bbSAlexander V. Chernikov ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
257b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
2583b3a8eb9SGleb Smirnoff {
259b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2609f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
261b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
262b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2639f7d47b0SAlexander V. Chernikov 	int error;
2649f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
2653b3a8eb9SGleb Smirnoff 
2669f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
267b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
268b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
2699f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2703b3a8eb9SGleb Smirnoff 		return (ESRCH);
2713b3a8eb9SGleb Smirnoff 	}
2723b3a8eb9SGleb Smirnoff 
273b074b7bbSAlexander V. Chernikov 	if (tc->no.type != ti->type) {
2749f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2753b3a8eb9SGleb Smirnoff 		return (EINVAL);
2763b3a8eb9SGleb Smirnoff 	}
2779f7d47b0SAlexander V. Chernikov 
2789f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2799f7d47b0SAlexander V. Chernikov 
2809f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
2819f7d47b0SAlexander V. Chernikov 	if ((error = ta->prepare_del(tei, &ta_buf)) != 0) {
2829f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2839f7d47b0SAlexander V. Chernikov 		return (error);
2849f7d47b0SAlexander V. Chernikov 	}
2859f7d47b0SAlexander V. Chernikov 
286b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
287b074b7bbSAlexander V. Chernikov 
288b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
2899f7d47b0SAlexander V. Chernikov 	error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2903b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2913b3a8eb9SGleb Smirnoff 
2929f7d47b0SAlexander V. Chernikov 	if (error == 0)
2939f7d47b0SAlexander V. Chernikov 		tc->count--;
294b074b7bbSAlexander V. Chernikov 
2959f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
2963b3a8eb9SGleb Smirnoff 
2979f7d47b0SAlexander V. Chernikov 	if (error != 0)
2989f7d47b0SAlexander V. Chernikov 		return (error);
2993b3a8eb9SGleb Smirnoff 
3009f7d47b0SAlexander V. Chernikov 	ta->flush_entry(tei, &ta_buf);
3013b3a8eb9SGleb Smirnoff 	return (0);
3023b3a8eb9SGleb Smirnoff }
3033b3a8eb9SGleb Smirnoff 
304b074b7bbSAlexander V. Chernikov /*
3059f7d47b0SAlexander V. Chernikov  * Flushes all entries in given table.
306b074b7bbSAlexander V. Chernikov  */
3073b3a8eb9SGleb Smirnoff int
308b074b7bbSAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
3093b3a8eb9SGleb Smirnoff {
310b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
311b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
3129f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
3139f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
3149f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
315b074b7bbSAlexander V. Chernikov 	int error;
316b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
3173b3a8eb9SGleb Smirnoff 
3183b3a8eb9SGleb Smirnoff 	/*
3199f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
320b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
3213b3a8eb9SGleb Smirnoff 	 */
322b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
323b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
324b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
325b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
326b074b7bbSAlexander V. Chernikov 		return (ESRCH);
327b074b7bbSAlexander V. Chernikov 	}
3289f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
329b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
330b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3313b3a8eb9SGleb Smirnoff 
332b074b7bbSAlexander V. Chernikov 	/*
3339f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
3349490a627SAlexander V. Chernikov 	 * TODO: pass startup parametes somehow.
335b074b7bbSAlexander V. Chernikov 	 */
3369f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
3379490a627SAlexander V. Chernikov 	if ((error = ta->init(&astate_new, &ti_new, NULL)) != 0) {
338b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
339b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
340b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
341b074b7bbSAlexander V. Chernikov 		return (error);
342b074b7bbSAlexander V. Chernikov 	}
343b074b7bbSAlexander V. Chernikov 
344b074b7bbSAlexander V. Chernikov 	/*
345b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
346b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
347b074b7bbSAlexander V. Chernikov 	 */
348b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
349b074b7bbSAlexander V. Chernikov 
350b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
351b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
3529f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
353b074b7bbSAlexander V. Chernikov 
3549f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
3559f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
3569f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
3579f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
358b074b7bbSAlexander V. Chernikov 
3599f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
3609f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
3619f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
3629f7d47b0SAlexander V. Chernikov 	tc->count = 0;
363b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
364b074b7bbSAlexander V. Chernikov 
365b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3663b3a8eb9SGleb Smirnoff 
367b074b7bbSAlexander V. Chernikov 	/*
368b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
369b074b7bbSAlexander V. Chernikov 	 */
3709f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
3713b3a8eb9SGleb Smirnoff 
3723b3a8eb9SGleb Smirnoff 	return (0);
3733b3a8eb9SGleb Smirnoff }
3743b3a8eb9SGleb Smirnoff 
375b074b7bbSAlexander V. Chernikov /*
3769f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
377b074b7bbSAlexander V. Chernikov  */
378b074b7bbSAlexander V. Chernikov int
3799f7d47b0SAlexander V. Chernikov ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
380b074b7bbSAlexander V. Chernikov {
381b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
382b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
383b074b7bbSAlexander V. Chernikov 
384b074b7bbSAlexander V. Chernikov 	ti->set = TABLE_SET(ti->set);
385b074b7bbSAlexander V. Chernikov 
386b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
387b074b7bbSAlexander V. Chernikov 
388b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
389b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
390b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
391b074b7bbSAlexander V. Chernikov 		return (ESRCH);
392b074b7bbSAlexander V. Chernikov 	}
393b074b7bbSAlexander V. Chernikov 
3949f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
3959f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
396b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
397b074b7bbSAlexander V. Chernikov 		return (EBUSY);
398b074b7bbSAlexander V. Chernikov 	}
399b074b7bbSAlexander V. Chernikov 
400b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
401b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
402b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
403b074b7bbSAlexander V. Chernikov 
404b074b7bbSAlexander V. Chernikov 	/* Free obj index */
405b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.set, tc->no.kidx) != 0)
406b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
407b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
408b074b7bbSAlexander V. Chernikov 
409b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
410b074b7bbSAlexander V. Chernikov 
411b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
412b074b7bbSAlexander V. Chernikov 
413b074b7bbSAlexander V. Chernikov 	return (0);
414b074b7bbSAlexander V. Chernikov }
415b074b7bbSAlexander V. Chernikov 
416b074b7bbSAlexander V. Chernikov static void
417b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
418b074b7bbSAlexander V. Chernikov     void *arg)
419b074b7bbSAlexander V. Chernikov {
420b074b7bbSAlexander V. Chernikov 
421b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
422b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->set, no->kidx) != 0)
423b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
424b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
425b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
426b074b7bbSAlexander V. Chernikov }
427b074b7bbSAlexander V. Chernikov 
4283b3a8eb9SGleb Smirnoff void
4293b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
4303b3a8eb9SGleb Smirnoff {
4313b3a8eb9SGleb Smirnoff 
432b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
433b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
434b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
435b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
436b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
437b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
4383b3a8eb9SGleb Smirnoff 
4393b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
4409f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
4419f7d47b0SAlexander V. Chernikov 
4429f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
443b074b7bbSAlexander V. Chernikov 
444b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
445b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
4463b3a8eb9SGleb Smirnoff }
4473b3a8eb9SGleb Smirnoff 
4483b3a8eb9SGleb Smirnoff int
4493b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
4503b3a8eb9SGleb Smirnoff {
451b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
452b074b7bbSAlexander V. Chernikov 
4533b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
4549f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
4559f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
456b074b7bbSAlexander V. Chernikov 
457b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
458b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
459b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
460b074b7bbSAlexander V. Chernikov 
4619f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
4629f7d47b0SAlexander V. Chernikov 
4633b3a8eb9SGleb Smirnoff 	return (0);
4643b3a8eb9SGleb Smirnoff }
4653b3a8eb9SGleb Smirnoff 
4663b3a8eb9SGleb Smirnoff int
4673b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
4683b3a8eb9SGleb Smirnoff {
4693b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
470b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
4719f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
472b074b7bbSAlexander V. Chernikov 	int new_blocks;
4733b3a8eb9SGleb Smirnoff 
4743b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
4753b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
4763b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
4773b3a8eb9SGleb Smirnoff 
4783b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
4799f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
4809f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
4819f7d47b0SAlexander V. Chernikov 
482b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
4833b3a8eb9SGleb Smirnoff 
4849f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
4853b3a8eb9SGleb Smirnoff 
4863b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
487b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
488b074b7bbSAlexander V. Chernikov 
4899f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
4909f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
4919f7d47b0SAlexander V. Chernikov 
4929f7d47b0SAlexander V. Chernikov 		/*
4939f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
4949f7d47b0SAlexander V. Chernikov 		 */
4959f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
496b074b7bbSAlexander V. Chernikov 		return (EINVAL);
497b074b7bbSAlexander V. Chernikov 	}
4983b3a8eb9SGleb Smirnoff 
4999f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
5009f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
5019f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
5023b3a8eb9SGleb Smirnoff 
5039f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
5049f7d47b0SAlexander V. Chernikov 
5059f7d47b0SAlexander V. Chernikov 	/* Change pointers */
5069f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
5079f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
5089f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
5093b3a8eb9SGleb Smirnoff 
5103b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
5113b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
5123b3a8eb9SGleb Smirnoff 
5133b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
5149f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5153b3a8eb9SGleb Smirnoff 
5163b3a8eb9SGleb Smirnoff 	/* Free old pointers */
5179f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
518b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
5193b3a8eb9SGleb Smirnoff 
5203b3a8eb9SGleb Smirnoff 	return (0);
5213b3a8eb9SGleb Smirnoff }
5223b3a8eb9SGleb Smirnoff 
5233b3a8eb9SGleb Smirnoff int
5243b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
5253b3a8eb9SGleb Smirnoff     uint32_t *val)
5263b3a8eb9SGleb Smirnoff {
5279f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
5283b3a8eb9SGleb Smirnoff 
5299f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
5309f7d47b0SAlexander V. Chernikov 
5319f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
5323b3a8eb9SGleb Smirnoff }
5339f7d47b0SAlexander V. Chernikov 
5349f7d47b0SAlexander V. Chernikov int
5359f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
5369f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
5379f7d47b0SAlexander V. Chernikov {
5389f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
5399f7d47b0SAlexander V. Chernikov 
5409f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
5419f7d47b0SAlexander V. Chernikov 
5429f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
5439f7d47b0SAlexander V. Chernikov }
5449f7d47b0SAlexander V. Chernikov 
5459f7d47b0SAlexander V. Chernikov /*
5469f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
5479f7d47b0SAlexander V. Chernikov  *
5489f7d47b0SAlexander V. Chernikov  */
5499f7d47b0SAlexander V. Chernikov 
550f1220db8SAlexander V. Chernikov /*
551d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
552d3a4f924SAlexander V. Chernikov  */
553d3a4f924SAlexander V. Chernikov 
554d3a4f924SAlexander V. Chernikov /*
555d3a4f924SAlexander V. Chernikov  * Get buffer size needed to list info for all tables.
556d3a4f924SAlexander V. Chernikov  * Data layout:
557d3a4f924SAlexander V. Chernikov  * Request: [ empty ], size = sizeof(ipfw_obj_lheader)
558d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ]
559d3a4f924SAlexander V. Chernikov  *
560d3a4f924SAlexander V. Chernikov  * Returns 0 on success
561f1220db8SAlexander V. Chernikov  */
562f1220db8SAlexander V. Chernikov int
563f1220db8SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
564f1220db8SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize)
565f1220db8SAlexander V. Chernikov {
566f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
567f1220db8SAlexander V. Chernikov 
568d3a4f924SAlexander V. Chernikov 	if (sopt->sopt_valsize < sizeof(*olh))
569d3a4f924SAlexander V. Chernikov 		return (EINVAL);
570d3a4f924SAlexander V. Chernikov 
571f1220db8SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)op3;
572f1220db8SAlexander V. Chernikov 
573f1220db8SAlexander V. Chernikov 	olh->size = sizeof(*olh); /* Make export_table store needed size */
574f1220db8SAlexander V. Chernikov 
575f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
576f1220db8SAlexander V. Chernikov 	export_tables(ch, olh);
577f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
578f1220db8SAlexander V. Chernikov 
579f1220db8SAlexander V. Chernikov 	sopt->sopt_valsize = sizeof(*olh);
580f1220db8SAlexander V. Chernikov 	return (sooptcopyout(sopt, olh, sopt->sopt_valsize));
581f1220db8SAlexander V. Chernikov }
582f1220db8SAlexander V. Chernikov 
583d3a4f924SAlexander V. Chernikov /*
584d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
585d3a4f924SAlexander V. Chernikov  * Data layout:
586d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
587d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
588d3a4f924SAlexander V. Chernikov  *
589d3a4f924SAlexander V. Chernikov  * Returns 0 on success
590d3a4f924SAlexander V. Chernikov  */
591f1220db8SAlexander V. Chernikov int
592f1220db8SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
593f1220db8SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize)
594f1220db8SAlexander V. Chernikov {
595f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
596d3a4f924SAlexander V. Chernikov 	uint32_t sz;
597f1220db8SAlexander V. Chernikov 	int error;
598f1220db8SAlexander V. Chernikov 
599d3a4f924SAlexander V. Chernikov 	if (sopt->sopt_valsize < sizeof(*olh))
600d3a4f924SAlexander V. Chernikov 		return (EINVAL);
601d3a4f924SAlexander V. Chernikov 
602f1220db8SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)op3;
603f1220db8SAlexander V. Chernikov 
604f1220db8SAlexander V. Chernikov 	if (valsize != olh->size)
605f1220db8SAlexander V. Chernikov 		return (EINVAL);
606f1220db8SAlexander V. Chernikov 
607f1220db8SAlexander V. Chernikov 	/*
608f1220db8SAlexander V. Chernikov 	 * Check if array size is "reasonable":
609f1220db8SAlexander V. Chernikov 	 * Permit valsize between current size and
610f1220db8SAlexander V. Chernikov 	 * 2x current size + 1
611f1220db8SAlexander V. Chernikov 	 */
612f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
613f1220db8SAlexander V. Chernikov 	sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
614f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
615f1220db8SAlexander V. Chernikov 
616d3a4f924SAlexander V. Chernikov 	if (check_buffer(sz, sizeof(ipfw_xtable_info),
617d3a4f924SAlexander V. Chernikov 	    sizeof(*olh), valsize) != 0)
618f1220db8SAlexander V. Chernikov 		return (EINVAL);
619f1220db8SAlexander V. Chernikov 
620f1220db8SAlexander V. Chernikov 	olh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
621f1220db8SAlexander V. Chernikov 	/* Copy header to new storage */
622f1220db8SAlexander V. Chernikov 	memcpy(olh, op3, sizeof(*olh));
623f1220db8SAlexander V. Chernikov 
624f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
625f1220db8SAlexander V. Chernikov 	error = export_tables(ch, olh);
626f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
627f1220db8SAlexander V. Chernikov 
628f1220db8SAlexander V. Chernikov 	if (error != 0) {
629f1220db8SAlexander V. Chernikov 		free(olh, M_TEMP);
630f1220db8SAlexander V. Chernikov 		return (error);
631f1220db8SAlexander V. Chernikov 	}
632f1220db8SAlexander V. Chernikov 
633f1220db8SAlexander V. Chernikov 	/*
634f1220db8SAlexander V. Chernikov 	 * Since we call sooptcopyin() with small buffer,
635f1220db8SAlexander V. Chernikov 	 * sopt_valsize is decreased to reflect supplied
636f1220db8SAlexander V. Chernikov 	 * buffer size. Set it back to original value.
637f1220db8SAlexander V. Chernikov 	 */
638f1220db8SAlexander V. Chernikov 	sopt->sopt_valsize = valsize;
639f1220db8SAlexander V. Chernikov 	error = sooptcopyout(sopt, olh, olh->size);
640f1220db8SAlexander V. Chernikov 	free(olh, M_TEMP);
641f1220db8SAlexander V. Chernikov 
642f1220db8SAlexander V. Chernikov 	return (0);
643f1220db8SAlexander V. Chernikov }
644f1220db8SAlexander V. Chernikov 
645d3a4f924SAlexander V. Chernikov /*
646d3a4f924SAlexander V. Chernikov  * Store table info to buffer provided by @op3.
647d3a4f924SAlexander V. Chernikov  * Data layout:
648d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
649d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
650d3a4f924SAlexander V. Chernikov  *
651d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
652d3a4f924SAlexander V. Chernikov  */
653d3a4f924SAlexander V. Chernikov int
654d3a4f924SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt *sopt,
655d3a4f924SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize)
656d3a4f924SAlexander V. Chernikov {
657d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
658d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
659d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
660d3a4f924SAlexander V. Chernikov 	size_t sz;
661d3a4f924SAlexander V. Chernikov 	int error;
662d3a4f924SAlexander V. Chernikov 
663d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
664d3a4f924SAlexander V. Chernikov 	if (sopt->sopt_valsize < sz)
665d3a4f924SAlexander V. Chernikov 		return (EINVAL);
666d3a4f924SAlexander V. Chernikov 
667d3a4f924SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
668d3a4f924SAlexander V. Chernikov 
669d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
670d3a4f924SAlexander V. Chernikov 
671d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
672d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
673d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
674d3a4f924SAlexander V. Chernikov 		return (ESRCH);
675d3a4f924SAlexander V. Chernikov 	}
676d3a4f924SAlexander V. Chernikov 
677d3a4f924SAlexander V. Chernikov 	export_table_info(tc, (ipfw_xtable_info *)(oh + 1));
678d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
679d3a4f924SAlexander V. Chernikov 
680d3a4f924SAlexander V. Chernikov 	error = sooptcopyout(sopt, oh, sz);
681d3a4f924SAlexander V. Chernikov 
682d3a4f924SAlexander V. Chernikov 	return (error);
683d3a4f924SAlexander V. Chernikov }
684d3a4f924SAlexander V. Chernikov 
685f1220db8SAlexander V. Chernikov struct dump_args {
686f1220db8SAlexander V. Chernikov 	struct table_info *ti;
687f1220db8SAlexander V. Chernikov 	struct table_config *tc;
688f1220db8SAlexander V. Chernikov 	ipfw_table_entry *ent;
689f1220db8SAlexander V. Chernikov 	ipfw_table_xentry *xent;
690f1220db8SAlexander V. Chernikov 	uint32_t cnt;
691f1220db8SAlexander V. Chernikov 	uint32_t size;
692f1220db8SAlexander V. Chernikov 	uint16_t uidx;
693f1220db8SAlexander V. Chernikov };
694f1220db8SAlexander V. Chernikov 
695f1220db8SAlexander V. Chernikov int
696f1220db8SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
697f1220db8SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize)
698f1220db8SAlexander V. Chernikov {
699d3a4f924SAlexander V. Chernikov 	int error;
700d3a4f924SAlexander V. Chernikov 
701d3a4f924SAlexander V. Chernikov 	switch (op3->version) {
702d3a4f924SAlexander V. Chernikov 	case 0:
703d3a4f924SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sopt, op3, valsize);
704d3a4f924SAlexander V. Chernikov 		break;
705d3a4f924SAlexander V. Chernikov 	case 1:
706d3a4f924SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sopt, op3, valsize);
707d3a4f924SAlexander V. Chernikov 		break;
708d3a4f924SAlexander V. Chernikov 	default:
709d3a4f924SAlexander V. Chernikov 		error = ENOTSUP;
710d3a4f924SAlexander V. Chernikov 	}
711d3a4f924SAlexander V. Chernikov 
712d3a4f924SAlexander V. Chernikov 	return (error);
713d3a4f924SAlexander V. Chernikov }
714d3a4f924SAlexander V. Chernikov 
715d3a4f924SAlexander V. Chernikov /*
716d3a4f924SAlexander V. Chernikov  * Dumps all table data
717d3a4f924SAlexander V. Chernikov  * Data layout (version 1):
718d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_xtable_info.size
719d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info ipfw_table_xentry x N ]
720d3a4f924SAlexander V. Chernikov  *
721d3a4f924SAlexander V. Chernikov  * Returns 0 on success
722d3a4f924SAlexander V. Chernikov  */
723d3a4f924SAlexander V. Chernikov static int
724d3a4f924SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt *sopt,
725d3a4f924SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize)
726d3a4f924SAlexander V. Chernikov {
727f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
728f1220db8SAlexander V. Chernikov 	ipfw_xtable_info *i;
729f1220db8SAlexander V. Chernikov 	struct tid_info ti;
730f1220db8SAlexander V. Chernikov 	struct table_config *tc;
731f1220db8SAlexander V. Chernikov 	struct table_algo *ta;
732f1220db8SAlexander V. Chernikov 	struct dump_args da;
733d3a4f924SAlexander V. Chernikov 	uint32_t sz;
734f1220db8SAlexander V. Chernikov 	int error;
735f1220db8SAlexander V. Chernikov 
736d3a4f924SAlexander V. Chernikov 	if (sopt->sopt_valsize < sizeof(*oh))
737d3a4f924SAlexander V. Chernikov 		return (EINVAL);
738d3a4f924SAlexander V. Chernikov 
739f1220db8SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
740f1220db8SAlexander V. Chernikov 
741d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
742f1220db8SAlexander V. Chernikov 
743f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
744f1220db8SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
745f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
746f1220db8SAlexander V. Chernikov 		return (ESRCH);
747f1220db8SAlexander V. Chernikov 	}
748f1220db8SAlexander V. Chernikov 	sz = tc->count;
749f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
750f1220db8SAlexander V. Chernikov 
751d3a4f924SAlexander V. Chernikov 	if (check_buffer(sz, sizeof(ipfw_table_xentry),
752d3a4f924SAlexander V. Chernikov 	    sizeof(ipfw_xtable_info) + sizeof(*oh), valsize) != 0)
753f1220db8SAlexander V. Chernikov 		return (EINVAL);
754f1220db8SAlexander V. Chernikov 
755f1220db8SAlexander V. Chernikov 	oh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
756f1220db8SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
757f1220db8SAlexander V. Chernikov 	/* Copy header to new storage */
758f1220db8SAlexander V. Chernikov 	memcpy(oh, op3, sizeof(*oh));
759f1220db8SAlexander V. Chernikov 
760f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
761f1220db8SAlexander V. Chernikov 	/* Find table and export info */
762f1220db8SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
763f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
764f1220db8SAlexander V. Chernikov 		free(oh, M_TEMP);
765f1220db8SAlexander V. Chernikov 		return (ESRCH);
766f1220db8SAlexander V. Chernikov 	}
767f1220db8SAlexander V. Chernikov 
768f1220db8SAlexander V. Chernikov 	export_table_info(tc, i);
769f1220db8SAlexander V. Chernikov 	if (i->size > valsize) {
770f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
771f1220db8SAlexander V. Chernikov 		/* XXX: Should we pass size structure back ? */
772f1220db8SAlexander V. Chernikov 		free(oh, M_TEMP);
773f1220db8SAlexander V. Chernikov 		return (EINVAL);
774f1220db8SAlexander V. Chernikov 	}
775f1220db8SAlexander V. Chernikov 
776f1220db8SAlexander V. Chernikov 	/*
777f1220db8SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
778f1220db8SAlexander V. Chernikov 	 */
779d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
780f1220db8SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
781f1220db8SAlexander V. Chernikov 	da.tc = tc;
782f1220db8SAlexander V. Chernikov 	da.xent = (ipfw_table_xentry *)(i + 1);
783f1220db8SAlexander V. Chernikov 	da.size = (valsize - sizeof(*oh) - sizeof(ipfw_xtable_info)) /
784f1220db8SAlexander V. Chernikov 	    sizeof(ipfw_table_xentry);
785f1220db8SAlexander V. Chernikov 
786f1220db8SAlexander V. Chernikov 	ta = tc->ta;
787f1220db8SAlexander V. Chernikov 
788f1220db8SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
789f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
790f1220db8SAlexander V. Chernikov 
791f1220db8SAlexander V. Chernikov 	/*
792f1220db8SAlexander V. Chernikov 	 * Since we call sooptcopyin() with small buffer,
793f1220db8SAlexander V. Chernikov 	 * sopt_valsize is decreased to reflect supplied
794f1220db8SAlexander V. Chernikov 	 * buffer size. Set it back to original value.
795f1220db8SAlexander V. Chernikov 	 */
796f1220db8SAlexander V. Chernikov 	sopt->sopt_valsize = valsize;
797f1220db8SAlexander V. Chernikov 	error = sooptcopyout(sopt, oh, i->size);
798f1220db8SAlexander V. Chernikov 	free(oh, M_TEMP);
799f1220db8SAlexander V. Chernikov 
800f1220db8SAlexander V. Chernikov 	return (0);
801f1220db8SAlexander V. Chernikov }
802f1220db8SAlexander V. Chernikov 
803d3a4f924SAlexander V. Chernikov /*
804d3a4f924SAlexander V. Chernikov  * Dumps all table data
805d3a4f924SAlexander V. Chernikov  * Data layout (version 0):
806d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
807d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
808d3a4f924SAlexander V. Chernikov  *
809d3a4f924SAlexander V. Chernikov  * Returns 0 on success
810d3a4f924SAlexander V. Chernikov  */
811d3a4f924SAlexander V. Chernikov static int
812d3a4f924SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt *sopt,
813d3a4f924SAlexander V. Chernikov     ip_fw3_opheader *op3, size_t valsize)
814d3a4f924SAlexander V. Chernikov {
815d3a4f924SAlexander V. Chernikov 	ipfw_xtable *xtbl;
816d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
817d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
818d3a4f924SAlexander V. Chernikov 	struct table_algo *ta;
819d3a4f924SAlexander V. Chernikov 	struct dump_args da;
820d3a4f924SAlexander V. Chernikov 	int error;
821d3a4f924SAlexander V. Chernikov 	size_t sz;
822d3a4f924SAlexander V. Chernikov 
823d3a4f924SAlexander V. Chernikov 	if (valsize < sizeof(ipfw_xtable))
824d3a4f924SAlexander V. Chernikov 		return (EINVAL);
825d3a4f924SAlexander V. Chernikov 
826d3a4f924SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)op3;
827d3a4f924SAlexander V. Chernikov 
828d3a4f924SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
829d3a4f924SAlexander V. Chernikov 	ti.set = 0; /* XXX: No way to specify set */
830d3a4f924SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
831d3a4f924SAlexander V. Chernikov 
832d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
833d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
834d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
835d3a4f924SAlexander V. Chernikov 		return (0);
836d3a4f924SAlexander V. Chernikov 	}
837d3a4f924SAlexander V. Chernikov 	sz = tc->count;
838d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
839d3a4f924SAlexander V. Chernikov 
840d3a4f924SAlexander V. Chernikov 	if (check_buffer(sz, sizeof(ipfw_table_xentry),
841d3a4f924SAlexander V. Chernikov 	    sizeof(ipfw_xtable) - sizeof(ipfw_table_xentry), valsize) != 0)
842d3a4f924SAlexander V. Chernikov 		return (EINVAL);
843d3a4f924SAlexander V. Chernikov 
844d3a4f924SAlexander V. Chernikov 	xtbl = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
845d3a4f924SAlexander V. Chernikov 	memcpy(xtbl, op3, sizeof(ipfw_xtable));
846d3a4f924SAlexander V. Chernikov 
847d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
848d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
849d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
850d3a4f924SAlexander V. Chernikov 		free(xtbl, M_TEMP);
851d3a4f924SAlexander V. Chernikov 		return (0);
852d3a4f924SAlexander V. Chernikov 	}
853d3a4f924SAlexander V. Chernikov 
854d3a4f924SAlexander V. Chernikov 	/* Check size another time */
855d3a4f924SAlexander V. Chernikov 	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
856d3a4f924SAlexander V. Chernikov 	if (sz > valsize) {
857d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
858d3a4f924SAlexander V. Chernikov 		free(xtbl, M_TEMP);
859d3a4f924SAlexander V. Chernikov 		return (EINVAL);
860d3a4f924SAlexander V. Chernikov 	}
861d3a4f924SAlexander V. Chernikov 
862d3a4f924SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
863d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
864d3a4f924SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
865d3a4f924SAlexander V. Chernikov 	da.tc = tc;
866d3a4f924SAlexander V. Chernikov 	da.xent = &xtbl->xent[0];
867d3a4f924SAlexander V. Chernikov 	da.size = tc->count;
868d3a4f924SAlexander V. Chernikov 	xtbl->type = tc->no.type;
869d3a4f924SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
870d3a4f924SAlexander V. Chernikov 	ta = tc->ta;
871d3a4f924SAlexander V. Chernikov 
872d3a4f924SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
873d3a4f924SAlexander V. Chernikov 	xtbl->cnt = da.cnt;
874d3a4f924SAlexander V. Chernikov 	xtbl->size = sz;
875d3a4f924SAlexander V. Chernikov 
876d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
877d3a4f924SAlexander V. Chernikov 
878d3a4f924SAlexander V. Chernikov 	/*
879d3a4f924SAlexander V. Chernikov 	 * Since we call sooptcopyin() with small buffer, sopt_valsize is
880d3a4f924SAlexander V. Chernikov 	 * decreased to reflect supplied buffer size. Set it back to original value
881d3a4f924SAlexander V. Chernikov 	 */
882d3a4f924SAlexander V. Chernikov 	sopt->sopt_valsize = valsize;
883d3a4f924SAlexander V. Chernikov 	error = sooptcopyout(sopt, xtbl, sz);
884d3a4f924SAlexander V. Chernikov 	free(xtbl, M_TEMP);
885d3a4f924SAlexander V. Chernikov 
886d3a4f924SAlexander V. Chernikov 	return (error);
887d3a4f924SAlexander V. Chernikov }
888d3a4f924SAlexander V. Chernikov 
889d3a4f924SAlexander V. Chernikov /*
8909490a627SAlexander V. Chernikov  * High-level setsockopt cmds
8919490a627SAlexander V. Chernikov  */
8929490a627SAlexander V. Chernikov int
8939490a627SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt,
8949490a627SAlexander V. Chernikov     ip_fw3_opheader *op3)
8959490a627SAlexander V. Chernikov {
8969490a627SAlexander V. Chernikov 
8979490a627SAlexander V. Chernikov 	return (ENOTSUP);
8989490a627SAlexander V. Chernikov }
8999490a627SAlexander V. Chernikov 
9009490a627SAlexander V. Chernikov /*
9019490a627SAlexander V. Chernikov  * Creates new table.
9029490a627SAlexander V. Chernikov  * Data layout:
9039490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
9049490a627SAlexander V. Chernikov  *
9059490a627SAlexander V. Chernikov  * Returns 0 on success
9069490a627SAlexander V. Chernikov  */
9079490a627SAlexander V. Chernikov int
9089490a627SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
9099490a627SAlexander V. Chernikov     ip_fw3_opheader *op3)
9109490a627SAlexander V. Chernikov {
9119490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
9129490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
9139490a627SAlexander V. Chernikov 	char *tname, *aname;
9149490a627SAlexander V. Chernikov 	struct tid_info ti;
9159490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
9169490a627SAlexander V. Chernikov 	struct table_config *tc;
9179490a627SAlexander V. Chernikov 	struct table_algo *ta;
9189490a627SAlexander V. Chernikov 	uint16_t kidx;
9199490a627SAlexander V. Chernikov 
9209490a627SAlexander V. Chernikov 	if (sopt->sopt_valsize < sizeof(*oh) + sizeof(ipfw_xtable_info))
9219490a627SAlexander V. Chernikov 		return (EINVAL);
9229490a627SAlexander V. Chernikov 
9239490a627SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
9249490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
9259490a627SAlexander V. Chernikov 
9269490a627SAlexander V. Chernikov 	/*
9279490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
9289490a627SAlexander V. Chernikov 	 * Check for null-terminated/zero-lenght strings/
9299490a627SAlexander V. Chernikov 	 */
9309490a627SAlexander V. Chernikov 	tname = i->tablename;
9319490a627SAlexander V. Chernikov 	aname = i->algoname;
9329490a627SAlexander V. Chernikov 	if (strnlen(tname, sizeof(i->tablename)) == sizeof(i->tablename) ||
9339490a627SAlexander V. Chernikov 	    tname[0] == '\0' ||
9349490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
9359490a627SAlexander V. Chernikov 		return (EINVAL);
9369490a627SAlexander V. Chernikov 
9379490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
9389490a627SAlexander V. Chernikov 		/* Use default algorithm */
9399490a627SAlexander V. Chernikov 		aname = NULL;
9409490a627SAlexander V. Chernikov 	}
9419490a627SAlexander V. Chernikov 
9429490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
9439490a627SAlexander V. Chernikov 
9449490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
9459490a627SAlexander V. Chernikov 
9469490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
9479490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
9489490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
9499490a627SAlexander V. Chernikov 		return (EEXIST);
9509490a627SAlexander V. Chernikov 	}
9519490a627SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), &ti, aname);
9529490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
9539490a627SAlexander V. Chernikov 
9549490a627SAlexander V. Chernikov 	if (ta == NULL)
9559490a627SAlexander V. Chernikov 		return (ENOTSUP);
9569490a627SAlexander V. Chernikov 
9579490a627SAlexander V. Chernikov 	if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL)
9589490a627SAlexander V. Chernikov 		return (ENOMEM);
9599490a627SAlexander V. Chernikov 
9609490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
9619490a627SAlexander V. Chernikov 	if (ipfw_objhash_alloc_idx(ni, ti.set, &kidx) != 0) {
9629490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
9639490a627SAlexander V. Chernikov 		printf("Unable to allocate table index for table %s in set %u."
9649490a627SAlexander V. Chernikov 		    " Consider increasing net.inet.ip.fw.tables_max",
9659490a627SAlexander V. Chernikov 		    tname, ti.set);
9669490a627SAlexander V. Chernikov 		free_table_config(ni, tc);
9679490a627SAlexander V. Chernikov 		return (EBUSY);
9689490a627SAlexander V. Chernikov 	}
9699490a627SAlexander V. Chernikov 
9709490a627SAlexander V. Chernikov 	tc->no.kidx = kidx;
9719490a627SAlexander V. Chernikov 
9729490a627SAlexander V. Chernikov 	IPFW_WLOCK(ch);
9739490a627SAlexander V. Chernikov 	link_table(ch, tc);
9749490a627SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
9759490a627SAlexander V. Chernikov 
9769490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
9779490a627SAlexander V. Chernikov 
9789490a627SAlexander V. Chernikov 	return (0);
9799490a627SAlexander V. Chernikov }
9809490a627SAlexander V. Chernikov 
9819490a627SAlexander V. Chernikov 
9829490a627SAlexander V. Chernikov /*
983d3a4f924SAlexander V. Chernikov  * Checks if supplied buffer size is "reasonable".
984d3a4f924SAlexander V. Chernikov  * Permit valsize between current needed size and
985d3a4f924SAlexander V. Chernikov  * 2x  needed size + 1
986d3a4f924SAlexander V. Chernikov  */
987d3a4f924SAlexander V. Chernikov static int
988d3a4f924SAlexander V. Chernikov check_buffer(size_t items, size_t item_size, size_t header, size_t bufsize)
989d3a4f924SAlexander V. Chernikov {
990d3a4f924SAlexander V. Chernikov 	size_t sz_min, sz_max;
991d3a4f924SAlexander V. Chernikov 
992d3a4f924SAlexander V. Chernikov 	sz_min = items * item_size + header;
993d3a4f924SAlexander V. Chernikov 	sz_max = (2 * items + 1) * item_size + header;
994d3a4f924SAlexander V. Chernikov 
995d3a4f924SAlexander V. Chernikov 	if (bufsize < sz_min || bufsize > sz_max)
996d3a4f924SAlexander V. Chernikov 		return (EINVAL);
997d3a4f924SAlexander V. Chernikov 
998d3a4f924SAlexander V. Chernikov 	return (0);
999d3a4f924SAlexander V. Chernikov }
1000d3a4f924SAlexander V. Chernikov 
1001d3a4f924SAlexander V. Chernikov void
1002d3a4f924SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
1003d3a4f924SAlexander V. Chernikov {
1004d3a4f924SAlexander V. Chernikov 
1005d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
1006d3a4f924SAlexander V. Chernikov 	ti->set = oh->set;
1007d3a4f924SAlexander V. Chernikov 	ti->uidx = oh->idx;
1008d3a4f924SAlexander V. Chernikov 	ti->tlvs = &oh->ntlv;
1009d3a4f924SAlexander V. Chernikov 	ti->tlen = oh->ntlv.head.length;
1010d3a4f924SAlexander V. Chernikov }
1011d3a4f924SAlexander V. Chernikov 
10129f7d47b0SAlexander V. Chernikov static void
10139f7d47b0SAlexander V. Chernikov export_table_info(struct table_config *tc, ipfw_xtable_info *i)
10149f7d47b0SAlexander V. Chernikov {
10159f7d47b0SAlexander V. Chernikov 
10169f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
10179f7d47b0SAlexander V. Chernikov 	i->ftype = tc->ftype;
10189f7d47b0SAlexander V. Chernikov 	i->atype = tc->ta->idx;
10199f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
10209f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
10219f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
10229f7d47b0SAlexander V. Chernikov 	i->count = tc->count;
10239f7d47b0SAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_table_xentry);
1024f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
10259f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
10269f7d47b0SAlexander V. Chernikov }
10279f7d47b0SAlexander V. Chernikov 
10289f7d47b0SAlexander V. Chernikov static void
10299f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
10309f7d47b0SAlexander V. Chernikov     void *arg)
10313b3a8eb9SGleb Smirnoff {
10329f7d47b0SAlexander V. Chernikov 	ipfw_obj_lheader *olh;
10339f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
10343b3a8eb9SGleb Smirnoff 
10359f7d47b0SAlexander V. Chernikov 	olh = (ipfw_obj_lheader *)arg;
10369f7d47b0SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(caddr_t)(olh + 1) + olh->count;
10379f7d47b0SAlexander V. Chernikov 	olh->count++;
10389f7d47b0SAlexander V. Chernikov 
10399f7d47b0SAlexander V. Chernikov 	export_table_info((struct table_config *)no, i);
10409f7d47b0SAlexander V. Chernikov }
10419f7d47b0SAlexander V. Chernikov 
1042f1220db8SAlexander V. Chernikov /*
1043f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
1044f1220db8SAlexander V. Chernikov  * storage provided by @olh.
1045f1220db8SAlexander V. Chernikov  * Returns 0 on success.
1046f1220db8SAlexander V. Chernikov  */
1047f1220db8SAlexander V. Chernikov static int
1048f1220db8SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh)
10499f7d47b0SAlexander V. Chernikov {
10509f7d47b0SAlexander V. Chernikov 	uint32_t size;
10519f7d47b0SAlexander V. Chernikov 	uint32_t count;
10529f7d47b0SAlexander V. Chernikov 
10539f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
10549f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
1055f1220db8SAlexander V. Chernikov 	if (size > olh->size) {
1056f1220db8SAlexander V. Chernikov 		/* Store new values anyway */
1057f1220db8SAlexander V. Chernikov 		olh->count = count;
1058f1220db8SAlexander V. Chernikov 		olh->size = size;
1059f1220db8SAlexander V. Chernikov 		olh->objsize = sizeof(ipfw_xtable_info);
10609f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1061f1220db8SAlexander V. Chernikov 	}
10629f7d47b0SAlexander V. Chernikov 
10639f7d47b0SAlexander V. Chernikov 	olh->count = 0;
10649f7d47b0SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, olh);
10659f7d47b0SAlexander V. Chernikov 
10669f7d47b0SAlexander V. Chernikov 	olh->count = count;
10679f7d47b0SAlexander V. Chernikov 	olh->size = size;
10689f7d47b0SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
10699f7d47b0SAlexander V. Chernikov 
10703b3a8eb9SGleb Smirnoff 	return (0);
10713b3a8eb9SGleb Smirnoff }
10723b3a8eb9SGleb Smirnoff 
10733b3a8eb9SGleb Smirnoff int
1074b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
10753b3a8eb9SGleb Smirnoff {
1076b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
10773b3a8eb9SGleb Smirnoff 
1078b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1079b074b7bbSAlexander V. Chernikov 		return (ESRCH);
10809f7d47b0SAlexander V. Chernikov 	*cnt = tc->count;
10813b3a8eb9SGleb Smirnoff 	return (0);
10823b3a8eb9SGleb Smirnoff }
10833b3a8eb9SGleb Smirnoff 
10849f7d47b0SAlexander V. Chernikov 
10859f7d47b0SAlexander V. Chernikov int
10869f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
10873b3a8eb9SGleb Smirnoff {
10889f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
10899f7d47b0SAlexander V. Chernikov 
1090d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
1091d3a4f924SAlexander V. Chernikov 		*cnt = 0;
10929f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
1093d3a4f924SAlexander V. Chernikov 	}
10949f7d47b0SAlexander V. Chernikov 	*cnt = tc->count * sizeof(ipfw_table_xentry);
10959f7d47b0SAlexander V. Chernikov 	if (tc->count > 0)
10969f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
10979f7d47b0SAlexander V. Chernikov 	return (0);
10989f7d47b0SAlexander V. Chernikov }
10999f7d47b0SAlexander V. Chernikov 
11009f7d47b0SAlexander V. Chernikov static int
11019f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
11029f7d47b0SAlexander V. Chernikov {
11039f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
11049f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
11059f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
11063b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
11073b3a8eb9SGleb Smirnoff 
11089f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
11099f7d47b0SAlexander V. Chernikov 
11109f7d47b0SAlexander V. Chernikov 	tc = da->tc;
11119f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
11129f7d47b0SAlexander V. Chernikov 
11139f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
1114f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
11153b3a8eb9SGleb Smirnoff 		return (1);
1116f1220db8SAlexander V. Chernikov 	ent = da->ent++;
1117f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
1118f1220db8SAlexander V. Chernikov 	da->cnt++;
11199f7d47b0SAlexander V. Chernikov 
11209f7d47b0SAlexander V. Chernikov 	return (ta->dump_entry(tc->astate, da->ti, e, ent));
11213b3a8eb9SGleb Smirnoff }
11223b3a8eb9SGleb Smirnoff 
1123f1220db8SAlexander V. Chernikov /*
1124f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
1125f1220db8SAlexander V. Chernikov  */
11263b3a8eb9SGleb Smirnoff int
1127f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
1128f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
11293b3a8eb9SGleb Smirnoff {
1130b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
11319f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
11329f7d47b0SAlexander V. Chernikov 	struct dump_args da;
11333b3a8eb9SGleb Smirnoff 
11343b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
11353b3a8eb9SGleb Smirnoff 
1136b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1137b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
11389f7d47b0SAlexander V. Chernikov 
11399f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
11409f7d47b0SAlexander V. Chernikov 
11419f7d47b0SAlexander V. Chernikov 	if (ta->dump_entry == NULL)
11429f7d47b0SAlexander V. Chernikov 		return (0);	/* Legacy dump support is not necessary */
11439f7d47b0SAlexander V. Chernikov 
1144d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
11459f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
11469f7d47b0SAlexander V. Chernikov 	da.tc = tc;
1147f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
1148f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
11499f7d47b0SAlexander V. Chernikov 
11509f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
11519f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
1152f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
11539f7d47b0SAlexander V. Chernikov 
11543b3a8eb9SGleb Smirnoff 	return (0);
11553b3a8eb9SGleb Smirnoff }
11563b3a8eb9SGleb Smirnoff 
11579490a627SAlexander V. Chernikov /*
11589490a627SAlexander V. Chernikov  * Dumps table entry in eXtended format (current).
11599490a627SAlexander V. Chernikov  */
11603b3a8eb9SGleb Smirnoff static int
11619f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
11623b3a8eb9SGleb Smirnoff {
11639f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
11649f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
11659f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
11663b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
11673b3a8eb9SGleb Smirnoff 
11689f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
11699f7d47b0SAlexander V. Chernikov 
11709f7d47b0SAlexander V. Chernikov 	tc = da->tc;
11719f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
11729f7d47b0SAlexander V. Chernikov 
11733b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
1174f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
11753b3a8eb9SGleb Smirnoff 		return (1);
1176f1220db8SAlexander V. Chernikov 	xent = da->xent++;
11773b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
1178f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
1179f1220db8SAlexander V. Chernikov 	da->cnt++;
11809f7d47b0SAlexander V. Chernikov 
11819f7d47b0SAlexander V. Chernikov 	return (ta->dump_xentry(tc->astate, da->ti, e, xent));
11829f7d47b0SAlexander V. Chernikov }
11839f7d47b0SAlexander V. Chernikov 
11849f7d47b0SAlexander V. Chernikov /*
11859f7d47b0SAlexander V. Chernikov  * Table algorithms
11869f7d47b0SAlexander V. Chernikov  */
11873b3a8eb9SGleb Smirnoff 
11889f7d47b0SAlexander V. Chernikov /*
11899490a627SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name
11909f7d47b0SAlexander V. Chernikov  */
11919f7d47b0SAlexander V. Chernikov static struct table_algo *
11929490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
11939f7d47b0SAlexander V. Chernikov {
11949490a627SAlexander V. Chernikov 	int i, l;
11959490a627SAlexander V. Chernikov 	struct table_algo *ta;
11969f7d47b0SAlexander V. Chernikov 
11979f7d47b0SAlexander V. Chernikov 	/* Search by index */
11989f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
11999f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
12009f7d47b0SAlexander V. Chernikov 			return (NULL);
12019f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
12029f7d47b0SAlexander V. Chernikov 	}
12039f7d47b0SAlexander V. Chernikov 
12049490a627SAlexander V. Chernikov 	/* Search by name if supplied */
12059490a627SAlexander V. Chernikov 	if (name != NULL) {
12069490a627SAlexander V. Chernikov 		/* TODO: better search */
12079490a627SAlexander V. Chernikov 		for (i = 1; i <= tcfg->algo_count; i++) {
12089490a627SAlexander V. Chernikov 			ta = tcfg->algo[i];
12099490a627SAlexander V. Chernikov 
12109490a627SAlexander V. Chernikov 			/*
12119490a627SAlexander V. Chernikov 			 * One can supply additional algorithm
12129490a627SAlexander V. Chernikov 			 * parameters so we compare only the first word
12139490a627SAlexander V. Chernikov 			 * of supplied name:
12149490a627SAlexander V. Chernikov 			 * 'hash_cidr hsize=32'
12159490a627SAlexander V. Chernikov 			 * '^^^^^^^^^'
12169490a627SAlexander V. Chernikov 			 *
12179490a627SAlexander V. Chernikov 			 */
12189490a627SAlexander V. Chernikov 			l = strlen(ta->name);
12199490a627SAlexander V. Chernikov 			if (strncmp(name, ta->name, l) == 0) {
12209490a627SAlexander V. Chernikov 				if (name[l] == '\0' || name[l] == ' ')
12219490a627SAlexander V. Chernikov 					return (ta);
12229490a627SAlexander V. Chernikov 			}
12239490a627SAlexander V. Chernikov 		}
12249490a627SAlexander V. Chernikov 
12259490a627SAlexander V. Chernikov 		return (NULL);
12269490a627SAlexander V. Chernikov 	}
12279490a627SAlexander V. Chernikov 
12289f7d47b0SAlexander V. Chernikov 	/* Search by type */
12299f7d47b0SAlexander V. Chernikov 	switch (ti->type) {
12303b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
12319f7d47b0SAlexander V. Chernikov 		return (&radix_cidr);
12323b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
12339f7d47b0SAlexander V. Chernikov 		return (&radix_iface);
12343b3a8eb9SGleb Smirnoff 	}
12353b3a8eb9SGleb Smirnoff 
12369f7d47b0SAlexander V. Chernikov 	return (NULL);
12373b3a8eb9SGleb Smirnoff }
12383b3a8eb9SGleb Smirnoff 
12399f7d47b0SAlexander V. Chernikov void
12409f7d47b0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta)
12413b3a8eb9SGleb Smirnoff {
12429f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
12433b3a8eb9SGleb Smirnoff 
12449f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
1245b074b7bbSAlexander V. Chernikov 
12469f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
12479f7d47b0SAlexander V. Chernikov 
12489f7d47b0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta;
12499f7d47b0SAlexander V. Chernikov 	ta->idx = tcfg->algo_count;
12503b3a8eb9SGleb Smirnoff }
12513b3a8eb9SGleb Smirnoff 
12529f7d47b0SAlexander V. Chernikov 
1253b074b7bbSAlexander V. Chernikov /*
1254b074b7bbSAlexander V. Chernikov  * Tables rewriting code
1255b074b7bbSAlexander V. Chernikov  *
1256b074b7bbSAlexander V. Chernikov  */
1257b074b7bbSAlexander V. Chernikov 
1258b074b7bbSAlexander V. Chernikov /*
1259b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
1260b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
1261b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
1262b074b7bbSAlexander V. Chernikov  */
1263b074b7bbSAlexander V. Chernikov static int
1264b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
1265b074b7bbSAlexander V. Chernikov {
1266b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1267b074b7bbSAlexander V. Chernikov 	int skip;
1268b074b7bbSAlexander V. Chernikov 	uint16_t v;
1269b074b7bbSAlexander V. Chernikov 
1270b074b7bbSAlexander V. Chernikov 	skip = 1;
1271b074b7bbSAlexander V. Chernikov 
1272b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1273b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1274b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1275b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1276b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
1277b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
1278b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
1279b074b7bbSAlexander V. Chernikov 		skip = 0;
1280b074b7bbSAlexander V. Chernikov 
1281b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
1282b074b7bbSAlexander V. Chernikov 			/*
1283b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
1284b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
1285b074b7bbSAlexander V. Chernikov 			 */
1286b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
1287b074b7bbSAlexander V. Chernikov 			switch (v) {
1288b074b7bbSAlexander V. Chernikov 			case 0:
1289b074b7bbSAlexander V. Chernikov 			case 1:
1290b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
1291b074b7bbSAlexander V. Chernikov 				break;
1292b074b7bbSAlexander V. Chernikov 			case 2:
1293b074b7bbSAlexander V. Chernikov 			case 3:
1294b074b7bbSAlexander V. Chernikov 				/* src/dst port */
1295b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1296b074b7bbSAlexander V. Chernikov 				break;
1297b074b7bbSAlexander V. Chernikov 			case 4:
1298b074b7bbSAlexander V. Chernikov 				/* uid/gid */
1299b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1300b074b7bbSAlexander V. Chernikov 			case 5:
1301b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1302b074b7bbSAlexander V. Chernikov 				/* jid */
1303b074b7bbSAlexander V. Chernikov 			case 6:
1304b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1305b074b7bbSAlexander V. Chernikov 				/* dscp */
1306b074b7bbSAlexander V. Chernikov 				break;
1307b074b7bbSAlexander V. Chernikov 			}
1308b074b7bbSAlexander V. Chernikov 		}
1309b074b7bbSAlexander V. Chernikov 		break;
1310b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1311b074b7bbSAlexander V. Chernikov 	case O_RECV:
1312b074b7bbSAlexander V. Chernikov 	case O_VIA:
1313b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1314b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1315b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
1316b074b7bbSAlexander V. Chernikov 			break;
1317b074b7bbSAlexander V. Chernikov 
1318b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
1319b074b7bbSAlexander V. Chernikov 		*puidx = cmdif->p.glob;
1320b074b7bbSAlexander V. Chernikov 		skip = 0;
1321b074b7bbSAlexander V. Chernikov 		break;
1322b074b7bbSAlexander V. Chernikov 	}
1323b074b7bbSAlexander V. Chernikov 
1324b074b7bbSAlexander V. Chernikov 	return (skip);
1325b074b7bbSAlexander V. Chernikov }
1326b074b7bbSAlexander V. Chernikov 
1327b074b7bbSAlexander V. Chernikov /*
1328b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
1329b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
1330b074b7bbSAlexander V. Chernikov  */
1331b074b7bbSAlexander V. Chernikov static void
1332b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
1333b074b7bbSAlexander V. Chernikov {
1334b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1335b074b7bbSAlexander V. Chernikov 
1336b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1337b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1338b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1339b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1340b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
1341b074b7bbSAlexander V. Chernikov 		break;
1342b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1343b074b7bbSAlexander V. Chernikov 	case O_RECV:
1344b074b7bbSAlexander V. Chernikov 	case O_VIA:
1345b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1346b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1347b074b7bbSAlexander V. Chernikov 		cmdif->p.glob = idx;
1348b074b7bbSAlexander V. Chernikov 		break;
1349b074b7bbSAlexander V. Chernikov 	}
1350b074b7bbSAlexander V. Chernikov }
1351b074b7bbSAlexander V. Chernikov 
1352b074b7bbSAlexander V. Chernikov static char *
1353b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
1354b074b7bbSAlexander V. Chernikov {
13559f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1356b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
1357b074b7bbSAlexander V. Chernikov 	int l;
1358b074b7bbSAlexander V. Chernikov 
1359b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
1360b074b7bbSAlexander V. Chernikov 	pe = pa + len;
1361b074b7bbSAlexander V. Chernikov 	l = 0;
1362b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
13639f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
1364b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
1365b074b7bbSAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_NAME)
1366b074b7bbSAlexander V. Chernikov 			continue;
1367b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
1368b074b7bbSAlexander V. Chernikov 			continue;
1369b074b7bbSAlexander V. Chernikov 
1370b074b7bbSAlexander V. Chernikov 		return (ntlv->name);
1371b074b7bbSAlexander V. Chernikov 	}
1372b074b7bbSAlexander V. Chernikov 
1373b074b7bbSAlexander V. Chernikov 	return (NULL);
1374b074b7bbSAlexander V. Chernikov }
1375b074b7bbSAlexander V. Chernikov 
1376b074b7bbSAlexander V. Chernikov static struct table_config *
1377b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
1378b074b7bbSAlexander V. Chernikov {
1379b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1380b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1381b074b7bbSAlexander V. Chernikov 
1382b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1383b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1384b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1385b074b7bbSAlexander V. Chernikov 			return (NULL);
1386b074b7bbSAlexander V. Chernikov 	} else {
1387b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1388b074b7bbSAlexander V. Chernikov 		name = bname;
1389b074b7bbSAlexander V. Chernikov 	}
1390b074b7bbSAlexander V. Chernikov 
1391b074b7bbSAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, ti->set, name);
1392b074b7bbSAlexander V. Chernikov 
1393b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
1394b074b7bbSAlexander V. Chernikov }
1395b074b7bbSAlexander V. Chernikov 
1396b074b7bbSAlexander V. Chernikov static struct table_config *
13979f7d47b0SAlexander V. Chernikov alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
13989490a627SAlexander V. Chernikov     struct table_algo *ta, char *aname)
1399b074b7bbSAlexander V. Chernikov {
1400b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1401b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1402b074b7bbSAlexander V. Chernikov 	int error;
1403b074b7bbSAlexander V. Chernikov 
1404b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1405b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1406b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1407b074b7bbSAlexander V. Chernikov 			return (NULL);
1408b074b7bbSAlexander V. Chernikov 	} else {
1409b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1410b074b7bbSAlexander V. Chernikov 		name = bname;
1411b074b7bbSAlexander V. Chernikov 	}
1412b074b7bbSAlexander V. Chernikov 
1413b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
1414b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
1415b074b7bbSAlexander V. Chernikov 	tc->no.type = ti->type;
1416b074b7bbSAlexander V. Chernikov 	tc->no.set = ti->set;
14179f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
1418b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
1419b074b7bbSAlexander V. Chernikov 
1420b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
1421b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
1422b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
1423b074b7bbSAlexander V. Chernikov 	}
1424b074b7bbSAlexander V. Chernikov 
1425b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
14269490a627SAlexander V. Chernikov 	error = ta->init(&tc->astate, &tc->ti, aname);
1427b074b7bbSAlexander V. Chernikov 	if (error != 0) {
1428b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
1429b074b7bbSAlexander V. Chernikov 		return (NULL);
1430b074b7bbSAlexander V. Chernikov 	}
1431b074b7bbSAlexander V. Chernikov 
1432b074b7bbSAlexander V. Chernikov 	return (tc);
1433b074b7bbSAlexander V. Chernikov }
1434b074b7bbSAlexander V. Chernikov 
1435b074b7bbSAlexander V. Chernikov static void
1436b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
1437b074b7bbSAlexander V. Chernikov {
1438b074b7bbSAlexander V. Chernikov 
1439b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0)
14409f7d47b0SAlexander V. Chernikov 		tc->ta->destroy(&tc->astate, &tc->ti);
1441b074b7bbSAlexander V. Chernikov 
1442b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
1443b074b7bbSAlexander V. Chernikov }
1444b074b7bbSAlexander V. Chernikov 
1445b074b7bbSAlexander V. Chernikov /*
1446b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
1447b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
1448b074b7bbSAlexander V. Chernikov  */
1449b074b7bbSAlexander V. Chernikov static void
14509f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
1451b074b7bbSAlexander V. Chernikov {
1452b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
14539f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1454b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1455b074b7bbSAlexander V. Chernikov 
14569f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
14579f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1458b074b7bbSAlexander V. Chernikov 
14599f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1460b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1461b074b7bbSAlexander V. Chernikov 
1462b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
14639f7d47b0SAlexander V. Chernikov 
14649f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
14659f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
1466b074b7bbSAlexander V. Chernikov 
1467b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
1468b074b7bbSAlexander V. Chernikov }
1469b074b7bbSAlexander V. Chernikov 
1470b074b7bbSAlexander V. Chernikov /*
1471b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
1472b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
1473b074b7bbSAlexander V. Chernikov  */
1474b074b7bbSAlexander V. Chernikov static void
14759f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
1476b074b7bbSAlexander V. Chernikov {
1477b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
14789f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1479b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1480b074b7bbSAlexander V. Chernikov 
14819f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
14829f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1483b074b7bbSAlexander V. Chernikov 
14849f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1485b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1486b074b7bbSAlexander V. Chernikov 
14879f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
1488b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
14899f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
14909f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
1491b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
1492b074b7bbSAlexander V. Chernikov }
1493b074b7bbSAlexander V. Chernikov 
1494b074b7bbSAlexander V. Chernikov /*
1495b074b7bbSAlexander V. Chernikov  * Finds named object by @uidx number.
1496b074b7bbSAlexander V. Chernikov  * Refs found object, allocate new index for non-existing object.
14979490a627SAlexander V. Chernikov  * Fills in @oib with userland/kernel indexes.
14989490a627SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
1499b074b7bbSAlexander V. Chernikov  *
1500b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1501b074b7bbSAlexander V. Chernikov  */
1502b074b7bbSAlexander V. Chernikov static int
15039490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
15049490a627SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
1505b074b7bbSAlexander V. Chernikov {
1506b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
15079490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
15089490a627SAlexander V. Chernikov 	struct named_object *no;
15099490a627SAlexander V. Chernikov 	int error, l, cmdlen;
15109490a627SAlexander V. Chernikov 	ipfw_insn *cmd;
15119490a627SAlexander V. Chernikov 	struct obj_idx *pidx, *p;
15129490a627SAlexander V. Chernikov 
15139490a627SAlexander V. Chernikov 	pidx = *oib;
15149490a627SAlexander V. Chernikov 	l = rule->cmd_len;
15159490a627SAlexander V. Chernikov 	cmd = rule->cmd;
15169490a627SAlexander V. Chernikov 	cmdlen = 0;
15179490a627SAlexander V. Chernikov 	error = 0;
15189490a627SAlexander V. Chernikov 
15199490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
15209490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
15219490a627SAlexander V. Chernikov 
15229490a627SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
15239490a627SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
15249490a627SAlexander V. Chernikov 
15259490a627SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
15269490a627SAlexander V. Chernikov 			continue;
1527b074b7bbSAlexander V. Chernikov 
1528b074b7bbSAlexander V. Chernikov 		pidx->uidx = ti->uidx;
1529b074b7bbSAlexander V. Chernikov 		pidx->type = ti->type;
1530b074b7bbSAlexander V. Chernikov 
15319490a627SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
15329490a627SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
15339490a627SAlexander V. Chernikov 				/* Incompatible types */
15349490a627SAlexander V. Chernikov 				error = EINVAL;
15359490a627SAlexander V. Chernikov 				break;
15369490a627SAlexander V. Chernikov 			}
15379f7d47b0SAlexander V. Chernikov 
15389490a627SAlexander V. Chernikov 			/* Reference found table and save kidx */
15399490a627SAlexander V. Chernikov 			tc->no.refcnt++;
15409490a627SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
15419490a627SAlexander V. Chernikov 			pidx++;
15429490a627SAlexander V. Chernikov 			continue;
15439490a627SAlexander V. Chernikov 		}
15449490a627SAlexander V. Chernikov 
15459490a627SAlexander V. Chernikov 		/* Table not found. Allocate new index and save for later */
1546b074b7bbSAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, ti->set, &pidx->kidx) != 0) {
1547b074b7bbSAlexander V. Chernikov 			printf("Unable to allocate table index in set %u."
1548b074b7bbSAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max",
1549b074b7bbSAlexander V. Chernikov 			    ti->set);
15509490a627SAlexander V. Chernikov 			error = EBUSY;
15519490a627SAlexander V. Chernikov 			break;
1552b074b7bbSAlexander V. Chernikov 		}
1553b074b7bbSAlexander V. Chernikov 
1554b074b7bbSAlexander V. Chernikov 		ci->new_tables++;
15559490a627SAlexander V. Chernikov 		pidx->new = 1;
15569490a627SAlexander V. Chernikov 		pidx++;
1557b074b7bbSAlexander V. Chernikov 	}
1558b074b7bbSAlexander V. Chernikov 
15599490a627SAlexander V. Chernikov 	if (error != 0) {
15609490a627SAlexander V. Chernikov 		/* Unref everything we have already done */
15619490a627SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
15629490a627SAlexander V. Chernikov 			if (p->new != 0) {
15639490a627SAlexander V. Chernikov 				ipfw_objhash_free_idx(ni, ci->tableset,p->kidx);
15649490a627SAlexander V. Chernikov 				continue;
15659490a627SAlexander V. Chernikov 			}
1566b074b7bbSAlexander V. Chernikov 
15679490a627SAlexander V. Chernikov 			/* Find & unref by existing idx */
15689490a627SAlexander V. Chernikov 			no = ipfw_objhash_lookup_idx(ni, ci->tableset, p->kidx);
15699490a627SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
15709490a627SAlexander V. Chernikov 			    p->kidx));
1571b074b7bbSAlexander V. Chernikov 
15729490a627SAlexander V. Chernikov 			no->refcnt--;
15739490a627SAlexander V. Chernikov 		}
15749490a627SAlexander V. Chernikov 	}
15759490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1576b074b7bbSAlexander V. Chernikov 
15779490a627SAlexander V. Chernikov 	*oib = pidx;
15789490a627SAlexander V. Chernikov 
15799490a627SAlexander V. Chernikov 	return (error);
1580b074b7bbSAlexander V. Chernikov }
1581b074b7bbSAlexander V. Chernikov 
1582b074b7bbSAlexander V. Chernikov /*
1583b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
1584b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
1585b074b7bbSAlexander V. Chernikov  * Works for \d+ talbes only (e.g. for tables, converted
1586b074b7bbSAlexander V. Chernikov  * from old numbered system calls).
1587b074b7bbSAlexander V. Chernikov  *
1588b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1589b074b7bbSAlexander V. Chernikov  * Raises error on any other tables.
1590b074b7bbSAlexander V. Chernikov  */
1591b074b7bbSAlexander V. Chernikov int
1592b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
1593b074b7bbSAlexander V. Chernikov {
1594b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1595b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1596b074b7bbSAlexander V. Chernikov 	uint32_t set;
1597b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1598b074b7bbSAlexander V. Chernikov 	uint8_t type;
1599b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1600b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1601b074b7bbSAlexander V. Chernikov 
1602b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1603b074b7bbSAlexander V. Chernikov 
1604b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1605b074b7bbSAlexander V. Chernikov 
1606b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1607b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1608b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1609b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1610b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1611b074b7bbSAlexander V. Chernikov 
1612b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1613b074b7bbSAlexander V. Chernikov 			continue;
1614b074b7bbSAlexander V. Chernikov 
1615b074b7bbSAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_idx(ni, set, kidx)) == NULL)
1616b074b7bbSAlexander V. Chernikov 			return (1);
1617b074b7bbSAlexander V. Chernikov 
1618b074b7bbSAlexander V. Chernikov 		if (no->compat == 0)
1619b074b7bbSAlexander V. Chernikov 			return (2);
1620b074b7bbSAlexander V. Chernikov 
1621b074b7bbSAlexander V. Chernikov 		update_table_opcode(cmd, no->uidx);
1622b074b7bbSAlexander V. Chernikov 	}
1623b074b7bbSAlexander V. Chernikov 
1624b074b7bbSAlexander V. Chernikov 	return (0);
1625b074b7bbSAlexander V. Chernikov }
1626b074b7bbSAlexander V. Chernikov 
1627b074b7bbSAlexander V. Chernikov 
1628b074b7bbSAlexander V. Chernikov /*
1629b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
1630b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
1631b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
1632b074b7bbSAlexander V. Chernikov  *
1633b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
1634b074b7bbSAlexander V. Chernikov  */
1635b074b7bbSAlexander V. Chernikov int
1636b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
1637b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
1638b074b7bbSAlexander V. Chernikov {
1639b074b7bbSAlexander V. Chernikov 	int cmdlen, error, ftype, l;
1640b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1641b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
1642b074b7bbSAlexander V. Chernikov 	uint8_t type;
1643b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
16449f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
1645b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1646b074b7bbSAlexander V. Chernikov 	struct named_object *no, *no_n, *no_tmp;
16479490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
1648b074b7bbSAlexander V. Chernikov 	struct namedobjects_head nh;
1649b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
1650b074b7bbSAlexander V. Chernikov 
1651b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1652b074b7bbSAlexander V. Chernikov 
16539490a627SAlexander V. Chernikov 	/* Prepare queue to store configs */
16549490a627SAlexander V. Chernikov 	TAILQ_INIT(&nh);
16559490a627SAlexander V. Chernikov 
1656b074b7bbSAlexander V. Chernikov 	/*
1657b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
1658b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
1659b074b7bbSAlexander V. Chernikov 	 */
1660b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
1661b074b7bbSAlexander V. Chernikov 		/* Stack */
16629490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
1663b074b7bbSAlexander V. Chernikov 	} else
16649490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
1665b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1666b074b7bbSAlexander V. Chernikov 
16679490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
1668b074b7bbSAlexander V. Chernikov 	error = 0;
1669b074b7bbSAlexander V. Chernikov 
1670b074b7bbSAlexander V. Chernikov 	type = 0;
1671b074b7bbSAlexander V. Chernikov 	ftype = 0;
1672b074b7bbSAlexander V. Chernikov 
1673b074b7bbSAlexander V. Chernikov 	ci->tableset = TABLE_SET(ci->krule->set);
1674b074b7bbSAlexander V. Chernikov 
1675b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
1676b074b7bbSAlexander V. Chernikov 	ti.set = ci->tableset;
1677b074b7bbSAlexander V. Chernikov 	ti.tlvs = ci->tlvs;
1678b074b7bbSAlexander V. Chernikov 	ti.tlen = ci->tlen;
1679b074b7bbSAlexander V. Chernikov 
1680b074b7bbSAlexander V. Chernikov 	/*
16819490a627SAlexander V. Chernikov 	 * Stage 1: reference existing tables, determine number
16829490a627SAlexander V. Chernikov 	 * of tables we need to allocate and allocate indexes for each.
1683b074b7bbSAlexander V. Chernikov 	 */
16849490a627SAlexander V. Chernikov 	error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti);
1685b074b7bbSAlexander V. Chernikov 
1686b074b7bbSAlexander V. Chernikov 	if (error != 0) {
16879490a627SAlexander V. Chernikov 		if (pidx_first != ci->obuf)
16889490a627SAlexander V. Chernikov 			free(pidx_first, M_IPFW);
1689b074b7bbSAlexander V. Chernikov 
1690b074b7bbSAlexander V. Chernikov 		return (error);
1691b074b7bbSAlexander V. Chernikov 	}
1692b074b7bbSAlexander V. Chernikov 
1693b074b7bbSAlexander V. Chernikov 	/*
1694b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate table configs for every non-existent table
1695b074b7bbSAlexander V. Chernikov 	 */
1696b074b7bbSAlexander V. Chernikov 
16979f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
16989490a627SAlexander V. Chernikov 		for (p = pidx_first; p < pidx_last; p++) {
1699b074b7bbSAlexander V. Chernikov 			if (p->new == 0)
1700b074b7bbSAlexander V. Chernikov 				continue;
1701b074b7bbSAlexander V. Chernikov 
1702b074b7bbSAlexander V. Chernikov 			/* TODO: get name from TLV */
1703b074b7bbSAlexander V. Chernikov 			ti.uidx = p->uidx;
1704b074b7bbSAlexander V. Chernikov 			ti.type = p->type;
17059f7d47b0SAlexander V. Chernikov 			ti.atype = 0;
1706b074b7bbSAlexander V. Chernikov 
17079490a627SAlexander V. Chernikov 			ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL);
17089f7d47b0SAlexander V. Chernikov 			if (ta == NULL) {
17099f7d47b0SAlexander V. Chernikov 				error = ENOTSUP;
17109f7d47b0SAlexander V. Chernikov 				goto free;
17119f7d47b0SAlexander V. Chernikov 			}
17129490a627SAlexander V. Chernikov 			tc = alloc_table_config(ni, &ti, ta, NULL);
1713b074b7bbSAlexander V. Chernikov 
1714b074b7bbSAlexander V. Chernikov 			if (tc == NULL) {
1715b074b7bbSAlexander V. Chernikov 				error = ENOMEM;
1716b074b7bbSAlexander V. Chernikov 				goto free;
1717b074b7bbSAlexander V. Chernikov 			}
1718b074b7bbSAlexander V. Chernikov 
1719b074b7bbSAlexander V. Chernikov 			tc->no.kidx = p->kidx;
1720b074b7bbSAlexander V. Chernikov 			tc->no.refcnt = 1;
1721b074b7bbSAlexander V. Chernikov 
1722b074b7bbSAlexander V. Chernikov 			/* Add to list */
1723b074b7bbSAlexander V. Chernikov 			TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next);
1724b074b7bbSAlexander V. Chernikov 		}
1725b074b7bbSAlexander V. Chernikov 
1726b074b7bbSAlexander V. Chernikov 		/*
1727b074b7bbSAlexander V. Chernikov 		 * Stage 2.1: Check if we're going to create 2 tables
1728b074b7bbSAlexander V. Chernikov 		 * with the same name, but different table types.
1729b074b7bbSAlexander V. Chernikov 		 */
1730b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH(no, &nh, nn_next) {
1731b074b7bbSAlexander V. Chernikov 			TAILQ_FOREACH(no_tmp, &nh, nn_next) {
17329490a627SAlexander V. Chernikov 				if (ipfw_objhash_same_name(ni, no, no_tmp) == 0)
1733b074b7bbSAlexander V. Chernikov 					continue;
1734b074b7bbSAlexander V. Chernikov 				if (no->type != no_tmp->type) {
1735b074b7bbSAlexander V. Chernikov 					error = EINVAL;
1736b074b7bbSAlexander V. Chernikov 					goto free;
1737b074b7bbSAlexander V. Chernikov 				}
1738b074b7bbSAlexander V. Chernikov 			}
1739b074b7bbSAlexander V. Chernikov 		}
17409f7d47b0SAlexander V. Chernikov 	}
1741b074b7bbSAlexander V. Chernikov 
17429f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
17439f7d47b0SAlexander V. Chernikov 
17449f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
1745b074b7bbSAlexander V. Chernikov 		/*
1746b074b7bbSAlexander V. Chernikov 		 * Stage 3: link & reference new table configs
1747b074b7bbSAlexander V. Chernikov 		 */
1748b074b7bbSAlexander V. Chernikov 
1749b074b7bbSAlexander V. Chernikov 
1750b074b7bbSAlexander V. Chernikov 		/*
1751b074b7bbSAlexander V. Chernikov 		 * Step 3.1: Check if some tables we need to create have been
1752b074b7bbSAlexander V. Chernikov 		 * already created with different table type.
1753b074b7bbSAlexander V. Chernikov 		 */
1754b074b7bbSAlexander V. Chernikov 
1755b074b7bbSAlexander V. Chernikov 		error = 0;
1756b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1757b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1758b074b7bbSAlexander V. Chernikov 			if (no_n == NULL)
1759b074b7bbSAlexander V. Chernikov 				continue;
1760b074b7bbSAlexander V. Chernikov 
1761b074b7bbSAlexander V. Chernikov 			if (no_n->type != no->type) {
1762b074b7bbSAlexander V. Chernikov 				error = EINVAL;
1763b074b7bbSAlexander V. Chernikov 				break;
1764b074b7bbSAlexander V. Chernikov 			}
1765b074b7bbSAlexander V. Chernikov 
1766b074b7bbSAlexander V. Chernikov 		}
1767b074b7bbSAlexander V. Chernikov 
1768b074b7bbSAlexander V. Chernikov 		if (error != 0) {
1769b074b7bbSAlexander V. Chernikov 			/*
1770b074b7bbSAlexander V. Chernikov 			 * Someone has allocated table with different table type.
1771b074b7bbSAlexander V. Chernikov 			 * We have to rollback everything.
1772b074b7bbSAlexander V. Chernikov 			 */
1773b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(chain);
1774b074b7bbSAlexander V. Chernikov 			goto free;
1775b074b7bbSAlexander V. Chernikov 		}
1776b074b7bbSAlexander V. Chernikov 
1777b074b7bbSAlexander V. Chernikov 
1778b074b7bbSAlexander V. Chernikov 		/*
17799f7d47b0SAlexander V. Chernikov 		 * Attach new tables.
17809f7d47b0SAlexander V. Chernikov 		 * We need to set table pointers for each new table,
1781b074b7bbSAlexander V. Chernikov 		 * so we have to acquire main WLOCK.
1782b074b7bbSAlexander V. Chernikov 		 */
1783b074b7bbSAlexander V. Chernikov 		IPFW_WLOCK(chain);
1784b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1785b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1786b074b7bbSAlexander V. Chernikov 
17879490a627SAlexander V. Chernikov 			if (no_n == NULL) {
17889490a627SAlexander V. Chernikov 				/* New table. Attach to runtime hash */
17899490a627SAlexander V. Chernikov 				TAILQ_REMOVE(&nh, no, nn_next);
17909490a627SAlexander V. Chernikov 				link_table(chain, (struct table_config *)no);
1791b074b7bbSAlexander V. Chernikov 				continue;
1792b074b7bbSAlexander V. Chernikov 			}
1793b074b7bbSAlexander V. Chernikov 
17949490a627SAlexander V. Chernikov 			/*
17959490a627SAlexander V. Chernikov 			 * Newly-allocated table with the same type.
17969490a627SAlexander V. Chernikov 			 * Reference it and update out @pidx array
17979490a627SAlexander V. Chernikov 			 * rewrite info.
17989490a627SAlexander V. Chernikov 			 */
17999490a627SAlexander V. Chernikov 			no_n->refcnt++;
18009490a627SAlexander V. Chernikov 			/* Keep oib array in sync: update kidx */
18019490a627SAlexander V. Chernikov 			for (p = pidx_first; p < pidx_last; p++) {
18029490a627SAlexander V. Chernikov 				if (p->kidx != no->kidx)
18039490a627SAlexander V. Chernikov 					continue;
18049490a627SAlexander V. Chernikov 				/* Update kidx */
18059490a627SAlexander V. Chernikov 				p->kidx = no_n->kidx;
18069490a627SAlexander V. Chernikov 				break;
18079490a627SAlexander V. Chernikov 			}
1808b074b7bbSAlexander V. Chernikov 		}
1809b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(chain);
18109f7d47b0SAlexander V. Chernikov 	}
1811b074b7bbSAlexander V. Chernikov 
1812b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
1813b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
1814b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
1815b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
18169490a627SAlexander V. Chernikov 	p = pidx_first;
1817b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1818b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1819b074b7bbSAlexander V. Chernikov 
1820b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
1821b074b7bbSAlexander V. Chernikov 			continue;
18229490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
18239490a627SAlexander V. Chernikov 		p++;
1824b074b7bbSAlexander V. Chernikov 	}
1825b074b7bbSAlexander V. Chernikov 
1826b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
1827b074b7bbSAlexander V. Chernikov 
1828b074b7bbSAlexander V. Chernikov 	error = 0;
1829b074b7bbSAlexander V. Chernikov 
1830b074b7bbSAlexander V. Chernikov 	/*
1831b074b7bbSAlexander V. Chernikov 	 * Stage 4: free resources
1832b074b7bbSAlexander V. Chernikov 	 */
1833b074b7bbSAlexander V. Chernikov free:
18349490a627SAlexander V. Chernikov 	if (!TAILQ_EMPTY(&nh)) {
18359490a627SAlexander V. Chernikov 		/* Free indexes first */
18369490a627SAlexander V. Chernikov 		IPFW_UH_WLOCK(chain);
18379490a627SAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
18389490a627SAlexander V. Chernikov 			ipfw_objhash_free_idx(ni, ci->tableset, no->kidx);
18399490a627SAlexander V. Chernikov 		}
18409490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
18419490a627SAlexander V. Chernikov 		/* Free configs */
1842b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp)
1843b074b7bbSAlexander V. Chernikov 			free_table_config(ni, tc);
18449490a627SAlexander V. Chernikov 	}
1845b074b7bbSAlexander V. Chernikov 
18469490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
18479490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
1848b074b7bbSAlexander V. Chernikov 
1849b074b7bbSAlexander V. Chernikov 	return (error);
1850b074b7bbSAlexander V. Chernikov }
1851b074b7bbSAlexander V. Chernikov 
1852b074b7bbSAlexander V. Chernikov /*
1853b074b7bbSAlexander V. Chernikov  * Remove references from every table used in @rule.
1854b074b7bbSAlexander V. Chernikov  */
1855b074b7bbSAlexander V. Chernikov void
1856b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
1857b074b7bbSAlexander V. Chernikov {
1858b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1859b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1860b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1861b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1862b074b7bbSAlexander V. Chernikov 	uint32_t set;
1863b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1864b074b7bbSAlexander V. Chernikov 	uint8_t type;
1865b074b7bbSAlexander V. Chernikov 
1866b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1867b074b7bbSAlexander V. Chernikov 
1868b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1869b074b7bbSAlexander V. Chernikov 
1870b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1871b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1872b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1873b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1874b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1875b074b7bbSAlexander V. Chernikov 
1876b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1877b074b7bbSAlexander V. Chernikov 			continue;
1878b074b7bbSAlexander V. Chernikov 
1879b074b7bbSAlexander V. Chernikov 		no = ipfw_objhash_lookup_idx(ni, set, kidx);
1880b074b7bbSAlexander V. Chernikov 
1881b074b7bbSAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
1882b074b7bbSAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
1883b074b7bbSAlexander V. Chernikov 		    no->type, type, kidx));
1884b074b7bbSAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
1885b074b7bbSAlexander V. Chernikov 		    kidx, no->refcnt));
1886b074b7bbSAlexander V. Chernikov 
1887b074b7bbSAlexander V. Chernikov 		no->refcnt--;
1888b074b7bbSAlexander V. Chernikov 	}
1889b074b7bbSAlexander V. Chernikov }
1890b074b7bbSAlexander V. Chernikov 
1891b074b7bbSAlexander V. Chernikov 
1892b074b7bbSAlexander V. Chernikov /*
1893b074b7bbSAlexander V. Chernikov  * Removes table bindings for every rule in rule chain @head.
1894b074b7bbSAlexander V. Chernikov  */
1895b074b7bbSAlexander V. Chernikov void
1896b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head)
1897b074b7bbSAlexander V. Chernikov {
1898b074b7bbSAlexander V. Chernikov 	struct ip_fw *rule;
1899b074b7bbSAlexander V. Chernikov 
1900b074b7bbSAlexander V. Chernikov 	while ((rule = head) != NULL) {
1901b074b7bbSAlexander V. Chernikov 		head = head->x_next;
1902b074b7bbSAlexander V. Chernikov 		ipfw_unbind_table_rule(chain, rule);
1903b074b7bbSAlexander V. Chernikov 	}
1904b074b7bbSAlexander V. Chernikov }
1905b074b7bbSAlexander V. Chernikov 
1906b074b7bbSAlexander V. Chernikov 
19073b3a8eb9SGleb Smirnoff /* end of file */
1908