xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision 2d99a349)
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);
1052d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
1062d99a349SAlexander V. Chernikov     struct sockopt_data *sd);
107f1220db8SAlexander V. Chernikov static void export_table_info(struct table_config *tc, ipfw_xtable_info *i);
108f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg);
109b074b7bbSAlexander V. Chernikov 
1102d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
1112d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
112d3a4f924SAlexander V. Chernikov 
1139f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1149490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
115b074b7bbSAlexander V. Chernikov 
116b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
117b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1189f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
119b074b7bbSAlexander V. Chernikov 
120b074b7bbSAlexander V. Chernikov 
1213b3a8eb9SGleb Smirnoff 
1223b3a8eb9SGleb Smirnoff int
123b074b7bbSAlexander V. Chernikov ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
124b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
1253b3a8eb9SGleb Smirnoff {
126b074b7bbSAlexander V. Chernikov 	struct table_config *tc, *tc_new;
1279f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
128b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
129b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1309f7d47b0SAlexander V. Chernikov 	int error;
1319f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
1323b3a8eb9SGleb Smirnoff 
1339f7d47b0SAlexander V. Chernikov #if 0
134b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
1353b3a8eb9SGleb Smirnoff 		return (EINVAL);
1363b3a8eb9SGleb Smirnoff #endif
1379f7d47b0SAlexander V. Chernikov 
1389f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1399f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1409f7d47b0SAlexander V. Chernikov 
1419f7d47b0SAlexander V. Chernikov 	/*
1429f7d47b0SAlexander V. Chernikov 	 * Find and reference existing table.
1439f7d47b0SAlexander V. Chernikov 	 */
1449f7d47b0SAlexander V. Chernikov 	ta = NULL;
1459f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
1469f7d47b0SAlexander V. Chernikov 		/* check table type */
1479f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
1489f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1493b3a8eb9SGleb Smirnoff 			return (EINVAL);
1503b3a8eb9SGleb Smirnoff 		}
1513b3a8eb9SGleb Smirnoff 
1529f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
1539f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
1549f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
1559f7d47b0SAlexander V. Chernikov 	}
1569f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1573b3a8eb9SGleb Smirnoff 
1589f7d47b0SAlexander V. Chernikov 	tc_new = NULL;
1599f7d47b0SAlexander V. Chernikov 	if (ta == NULL) {
1609f7d47b0SAlexander V. Chernikov 		/* Table not found. We have to create new one */
1619490a627SAlexander V. Chernikov 		if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL)
1629f7d47b0SAlexander V. Chernikov 			return (ENOTSUP);
1633b3a8eb9SGleb Smirnoff 
1649490a627SAlexander V. Chernikov 		tc_new = alloc_table_config(ni, ti, ta, NULL);
1659f7d47b0SAlexander V. Chernikov 		if (tc_new == NULL)
1669f7d47b0SAlexander V. Chernikov 			return (ENOMEM);
1679f7d47b0SAlexander V. Chernikov 	}
1683b3a8eb9SGleb Smirnoff 
1699f7d47b0SAlexander V. Chernikov 	/* Prepare record (allocate memory) */
1709f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
1719f7d47b0SAlexander V. Chernikov 	error = ta->prepare_add(tei, &ta_buf);
1729f7d47b0SAlexander V. Chernikov 	if (error != 0) {
1739f7d47b0SAlexander V. Chernikov 		if (tc_new != NULL)
1749f7d47b0SAlexander V. Chernikov 			free_table_config(ni, tc_new);
1759f7d47b0SAlexander V. Chernikov 		return (error);
1763b3a8eb9SGleb Smirnoff 	}
1773b3a8eb9SGleb Smirnoff 
178b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1793b3a8eb9SGleb Smirnoff 
180b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1813b3a8eb9SGleb Smirnoff 
1829f7d47b0SAlexander V. Chernikov 	if (tc == NULL) {
1839f7d47b0SAlexander V. Chernikov 		/* Check if another table was allocated by other thread */
184b074b7bbSAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
1859f7d47b0SAlexander V. Chernikov 
1869f7d47b0SAlexander V. Chernikov 			/*
1879f7d47b0SAlexander V. Chernikov 			 * Check if algoritm is the same since we've
1889f7d47b0SAlexander V. Chernikov 			 * already allocated state using @ta algoritm
1899f7d47b0SAlexander V. Chernikov 			 * callbacks.
1909f7d47b0SAlexander V. Chernikov 			 */
1919f7d47b0SAlexander V. Chernikov 			if (tc->ta != ta) {
192b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
193b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
1943b3a8eb9SGleb Smirnoff 				return (EINVAL);
1953b3a8eb9SGleb Smirnoff 			}
1963b3a8eb9SGleb Smirnoff 		} else {
1973b3a8eb9SGleb Smirnoff 			/*
1989f7d47b0SAlexander V. Chernikov 			 * We're first to create this table.
199b074b7bbSAlexander V. Chernikov 			 * Set tc_new to zero not to free it afterwards.
2003b3a8eb9SGleb Smirnoff 			 */
201b074b7bbSAlexander V. Chernikov 			tc = tc_new;
202b074b7bbSAlexander V. Chernikov 			tc_new = NULL;
203b074b7bbSAlexander V. Chernikov 
204b074b7bbSAlexander V. Chernikov 			/* Allocate table index. */
205b074b7bbSAlexander V. Chernikov 			if (ipfw_objhash_alloc_idx(ni, ti->set, &kidx) != 0) {
206b074b7bbSAlexander V. Chernikov 				/* Index full. */
207b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
208b074b7bbSAlexander V. Chernikov 				printf("Unable to allocate index for table %s."
209b074b7bbSAlexander V. Chernikov 				    " Consider increasing "
210b074b7bbSAlexander V. Chernikov 				    "net.inet.ip.fw.tables_max",
211b074b7bbSAlexander V. Chernikov 				    tc->no.name);
212b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
213b074b7bbSAlexander V. Chernikov 				return (EBUSY);
2143b3a8eb9SGleb Smirnoff 			}
215b074b7bbSAlexander V. Chernikov 			/* Save kidx */
216b074b7bbSAlexander V. Chernikov 			tc->no.kidx = kidx;
217b074b7bbSAlexander V. Chernikov 		}
218b074b7bbSAlexander V. Chernikov 	} else {
2199f7d47b0SAlexander V. Chernikov 		/* Drop reference we've used in first search */
2209f7d47b0SAlexander V. Chernikov 		tc->no.refcnt--;
221b074b7bbSAlexander V. Chernikov 	}
222b074b7bbSAlexander V. Chernikov 
223b074b7bbSAlexander V. Chernikov 	/* We've got valid table in @tc. Let's add data */
2249f7d47b0SAlexander V. Chernikov 	kidx = tc->no.kidx;
2259f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2269f7d47b0SAlexander V. Chernikov 
227b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
228b074b7bbSAlexander V. Chernikov 
229b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0) {
230b074b7bbSAlexander V. Chernikov 		link_table(ch, tc);
231b074b7bbSAlexander V. Chernikov 	}
232b074b7bbSAlexander V. Chernikov 
2339f7d47b0SAlexander V. Chernikov 	error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2343b3a8eb9SGleb Smirnoff 
2353b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2369f7d47b0SAlexander V. Chernikov 
2379f7d47b0SAlexander V. Chernikov 	if (error == 0)
2389f7d47b0SAlexander V. Chernikov 		tc->count++;
2399f7d47b0SAlexander V. Chernikov 
240b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
241b074b7bbSAlexander V. Chernikov 
242b074b7bbSAlexander V. Chernikov 	if (tc_new != NULL)
243b074b7bbSAlexander V. Chernikov 		free_table_config(ni, tc);
2443b3a8eb9SGleb Smirnoff 
2459f7d47b0SAlexander V. Chernikov 	if (error != 0)
2469f7d47b0SAlexander V. Chernikov 		ta->flush_entry(tei, &ta_buf);
247b074b7bbSAlexander V. Chernikov 
2489f7d47b0SAlexander V. Chernikov 	return (error);
2493b3a8eb9SGleb Smirnoff }
2503b3a8eb9SGleb Smirnoff 
2513b3a8eb9SGleb Smirnoff int
252b074b7bbSAlexander V. Chernikov ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
253b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
2543b3a8eb9SGleb Smirnoff {
255b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2569f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
257b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
258b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2599f7d47b0SAlexander V. Chernikov 	int error;
2609f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
2613b3a8eb9SGleb Smirnoff 
2629f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
263b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
264b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
2659f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2663b3a8eb9SGleb Smirnoff 		return (ESRCH);
2673b3a8eb9SGleb Smirnoff 	}
2683b3a8eb9SGleb Smirnoff 
269b074b7bbSAlexander V. Chernikov 	if (tc->no.type != ti->type) {
2709f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2713b3a8eb9SGleb Smirnoff 		return (EINVAL);
2723b3a8eb9SGleb Smirnoff 	}
2739f7d47b0SAlexander V. Chernikov 
2749f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2759f7d47b0SAlexander V. Chernikov 
2769f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
2779f7d47b0SAlexander V. Chernikov 	if ((error = ta->prepare_del(tei, &ta_buf)) != 0) {
2789f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2799f7d47b0SAlexander V. Chernikov 		return (error);
2809f7d47b0SAlexander V. Chernikov 	}
2819f7d47b0SAlexander V. Chernikov 
282b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
283b074b7bbSAlexander V. Chernikov 
284b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
2859f7d47b0SAlexander V. Chernikov 	error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2863b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2873b3a8eb9SGleb Smirnoff 
2889f7d47b0SAlexander V. Chernikov 	if (error == 0)
2899f7d47b0SAlexander V. Chernikov 		tc->count--;
290b074b7bbSAlexander V. Chernikov 
2919f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
2923b3a8eb9SGleb Smirnoff 
2939f7d47b0SAlexander V. Chernikov 	if (error != 0)
2949f7d47b0SAlexander V. Chernikov 		return (error);
2953b3a8eb9SGleb Smirnoff 
2969f7d47b0SAlexander V. Chernikov 	ta->flush_entry(tei, &ta_buf);
2973b3a8eb9SGleb Smirnoff 	return (0);
2983b3a8eb9SGleb Smirnoff }
2993b3a8eb9SGleb Smirnoff 
300b074b7bbSAlexander V. Chernikov /*
3019f7d47b0SAlexander V. Chernikov  * Flushes all entries in given table.
302b074b7bbSAlexander V. Chernikov  */
3033b3a8eb9SGleb Smirnoff int
304b074b7bbSAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
3053b3a8eb9SGleb Smirnoff {
306b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
307b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
3089f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
3099f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
3109f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
311b074b7bbSAlexander V. Chernikov 	int error;
312b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
3133b3a8eb9SGleb Smirnoff 
3143b3a8eb9SGleb Smirnoff 	/*
3159f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
316b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
3173b3a8eb9SGleb Smirnoff 	 */
318b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
319b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
320b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
321b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
322b074b7bbSAlexander V. Chernikov 		return (ESRCH);
323b074b7bbSAlexander V. Chernikov 	}
3249f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
325b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
326b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3273b3a8eb9SGleb Smirnoff 
328b074b7bbSAlexander V. Chernikov 	/*
3299f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
3309490a627SAlexander V. Chernikov 	 * TODO: pass startup parametes somehow.
331b074b7bbSAlexander V. Chernikov 	 */
3329f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
3339490a627SAlexander V. Chernikov 	if ((error = ta->init(&astate_new, &ti_new, NULL)) != 0) {
334b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
335b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
336b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
337b074b7bbSAlexander V. Chernikov 		return (error);
338b074b7bbSAlexander V. Chernikov 	}
339b074b7bbSAlexander V. Chernikov 
340b074b7bbSAlexander V. Chernikov 	/*
341b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
342b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
343b074b7bbSAlexander V. Chernikov 	 */
344b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
345b074b7bbSAlexander V. Chernikov 
346b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
347b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
3489f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
349b074b7bbSAlexander V. Chernikov 
3509f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
3519f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
3529f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
3539f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
354b074b7bbSAlexander V. Chernikov 
3559f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
3569f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
3579f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
3589f7d47b0SAlexander V. Chernikov 	tc->count = 0;
359b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
360b074b7bbSAlexander V. Chernikov 
361b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3623b3a8eb9SGleb Smirnoff 
363b074b7bbSAlexander V. Chernikov 	/*
364b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
365b074b7bbSAlexander V. Chernikov 	 */
3669f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
3673b3a8eb9SGleb Smirnoff 
3683b3a8eb9SGleb Smirnoff 	return (0);
3693b3a8eb9SGleb Smirnoff }
3703b3a8eb9SGleb Smirnoff 
371b074b7bbSAlexander V. Chernikov /*
3729f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
373b074b7bbSAlexander V. Chernikov  */
374b074b7bbSAlexander V. Chernikov int
3759f7d47b0SAlexander V. Chernikov ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
376b074b7bbSAlexander V. Chernikov {
377b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
378b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
379b074b7bbSAlexander V. Chernikov 
380b074b7bbSAlexander V. Chernikov 	ti->set = TABLE_SET(ti->set);
381b074b7bbSAlexander V. Chernikov 
382b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
383b074b7bbSAlexander V. Chernikov 
384b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
385b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
386b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
387b074b7bbSAlexander V. Chernikov 		return (ESRCH);
388b074b7bbSAlexander V. Chernikov 	}
389b074b7bbSAlexander V. Chernikov 
3909f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
3919f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
392b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
393b074b7bbSAlexander V. Chernikov 		return (EBUSY);
394b074b7bbSAlexander V. Chernikov 	}
395b074b7bbSAlexander V. Chernikov 
396b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
397b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
398b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
399b074b7bbSAlexander V. Chernikov 
400b074b7bbSAlexander V. Chernikov 	/* Free obj index */
401b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.set, tc->no.kidx) != 0)
402b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
403b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
404b074b7bbSAlexander V. Chernikov 
405b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
406b074b7bbSAlexander V. Chernikov 
407b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
408b074b7bbSAlexander V. Chernikov 
409b074b7bbSAlexander V. Chernikov 	return (0);
410b074b7bbSAlexander V. Chernikov }
411b074b7bbSAlexander V. Chernikov 
412b074b7bbSAlexander V. Chernikov static void
413b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
414b074b7bbSAlexander V. Chernikov     void *arg)
415b074b7bbSAlexander V. Chernikov {
416b074b7bbSAlexander V. Chernikov 
417b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
418b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->set, no->kidx) != 0)
419b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
420b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
421b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
422b074b7bbSAlexander V. Chernikov }
423b074b7bbSAlexander V. Chernikov 
4243b3a8eb9SGleb Smirnoff void
4253b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
4263b3a8eb9SGleb Smirnoff {
4273b3a8eb9SGleb Smirnoff 
428b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
429b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
430b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
431b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
432b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
433b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
4343b3a8eb9SGleb Smirnoff 
4353b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
4369f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
4379f7d47b0SAlexander V. Chernikov 
4389f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
439b074b7bbSAlexander V. Chernikov 
440b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
441b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
4423b3a8eb9SGleb Smirnoff }
4433b3a8eb9SGleb Smirnoff 
4443b3a8eb9SGleb Smirnoff int
4453b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
4463b3a8eb9SGleb Smirnoff {
447b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
448b074b7bbSAlexander V. Chernikov 
4493b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
4509f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
4519f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
452b074b7bbSAlexander V. Chernikov 
453b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
454b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
455b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
456b074b7bbSAlexander V. Chernikov 
4579f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
4589f7d47b0SAlexander V. Chernikov 
4593b3a8eb9SGleb Smirnoff 	return (0);
4603b3a8eb9SGleb Smirnoff }
4613b3a8eb9SGleb Smirnoff 
4623b3a8eb9SGleb Smirnoff int
4633b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
4643b3a8eb9SGleb Smirnoff {
4653b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
466b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
4679f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
468b074b7bbSAlexander V. Chernikov 	int new_blocks;
4693b3a8eb9SGleb Smirnoff 
4703b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
4713b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
4723b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
4733b3a8eb9SGleb Smirnoff 
4743b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
4759f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
4769f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
4779f7d47b0SAlexander V. Chernikov 
478b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
4793b3a8eb9SGleb Smirnoff 
4809f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
4813b3a8eb9SGleb Smirnoff 
4823b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
483b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
484b074b7bbSAlexander V. Chernikov 
4859f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
4869f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
4879f7d47b0SAlexander V. Chernikov 
4889f7d47b0SAlexander V. Chernikov 		/*
4899f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
4909f7d47b0SAlexander V. Chernikov 		 */
4919f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
492b074b7bbSAlexander V. Chernikov 		return (EINVAL);
493b074b7bbSAlexander V. Chernikov 	}
4943b3a8eb9SGleb Smirnoff 
4959f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
4969f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
4979f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
4983b3a8eb9SGleb Smirnoff 
4999f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
5009f7d47b0SAlexander V. Chernikov 
5019f7d47b0SAlexander V. Chernikov 	/* Change pointers */
5029f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
5039f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
5049f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
5053b3a8eb9SGleb Smirnoff 
5063b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
5073b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
5083b3a8eb9SGleb Smirnoff 
5093b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
5109f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5113b3a8eb9SGleb Smirnoff 
5123b3a8eb9SGleb Smirnoff 	/* Free old pointers */
5139f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
514b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
5153b3a8eb9SGleb Smirnoff 
5163b3a8eb9SGleb Smirnoff 	return (0);
5173b3a8eb9SGleb Smirnoff }
5183b3a8eb9SGleb Smirnoff 
5193b3a8eb9SGleb Smirnoff int
5203b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
5213b3a8eb9SGleb Smirnoff     uint32_t *val)
5223b3a8eb9SGleb Smirnoff {
5239f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
5243b3a8eb9SGleb Smirnoff 
5259f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
5269f7d47b0SAlexander V. Chernikov 
5279f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
5283b3a8eb9SGleb Smirnoff }
5299f7d47b0SAlexander V. Chernikov 
5309f7d47b0SAlexander V. Chernikov int
5319f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
5329f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
5339f7d47b0SAlexander V. Chernikov {
5349f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
5359f7d47b0SAlexander V. Chernikov 
5369f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
5379f7d47b0SAlexander V. Chernikov 
5389f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
5399f7d47b0SAlexander V. Chernikov }
5409f7d47b0SAlexander V. Chernikov 
5419f7d47b0SAlexander V. Chernikov /*
5429f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
5439f7d47b0SAlexander V. Chernikov  *
5449f7d47b0SAlexander V. Chernikov  */
5459f7d47b0SAlexander V. Chernikov 
546f1220db8SAlexander V. Chernikov /*
547d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
548d3a4f924SAlexander V. Chernikov  */
549d3a4f924SAlexander V. Chernikov 
550d3a4f924SAlexander V. Chernikov /*
551d3a4f924SAlexander V. Chernikov  * Get buffer size needed to list info for all tables.
552d3a4f924SAlexander V. Chernikov  * Data layout:
553d3a4f924SAlexander V. Chernikov  * Request: [ empty ], size = sizeof(ipfw_obj_lheader)
554d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ]
555d3a4f924SAlexander V. Chernikov  *
556d3a4f924SAlexander V. Chernikov  * Returns 0 on success
557f1220db8SAlexander V. Chernikov  */
558f1220db8SAlexander V. Chernikov int
5592d99a349SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
560f1220db8SAlexander V. Chernikov {
561f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
562f1220db8SAlexander V. Chernikov 
5632d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
5642d99a349SAlexander V. Chernikov 	if (olh == NULL)
565d3a4f924SAlexander V. Chernikov 		return (EINVAL);
566d3a4f924SAlexander V. Chernikov 
567f1220db8SAlexander V. Chernikov 	olh->size = sizeof(*olh); /* Make export_table store needed size */
568f1220db8SAlexander V. Chernikov 
569f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
5702d99a349SAlexander V. Chernikov 	export_tables(ch, olh, sd);
571f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
572f1220db8SAlexander V. Chernikov 
5732d99a349SAlexander V. Chernikov 	return (0);
574f1220db8SAlexander V. Chernikov }
575f1220db8SAlexander V. Chernikov 
576d3a4f924SAlexander V. Chernikov /*
577d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
578d3a4f924SAlexander V. Chernikov  * Data layout:
579d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
580d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
581d3a4f924SAlexander V. Chernikov  *
582d3a4f924SAlexander V. Chernikov  * Returns 0 on success
583d3a4f924SAlexander V. Chernikov  */
584f1220db8SAlexander V. Chernikov int
5852d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
586f1220db8SAlexander V. Chernikov {
587f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
588d3a4f924SAlexander V. Chernikov 	uint32_t sz;
589f1220db8SAlexander V. Chernikov 	int error;
590f1220db8SAlexander V. Chernikov 
5912d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
5922d99a349SAlexander V. Chernikov 	if (olh == NULL)
593d3a4f924SAlexander V. Chernikov 		return (EINVAL);
594d3a4f924SAlexander V. Chernikov 
595f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
596f1220db8SAlexander V. Chernikov 	sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
5972d99a349SAlexander V. Chernikov 
5982d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
5992d99a349SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
6002d99a349SAlexander V. Chernikov 		return (ENOMEM);
6012d99a349SAlexander V. Chernikov 	}
6022d99a349SAlexander V. Chernikov 
6032d99a349SAlexander V. Chernikov 	error = export_tables(ch, olh, sd);
604f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
605f1220db8SAlexander V. Chernikov 
606f1220db8SAlexander V. Chernikov 	return (error);
607f1220db8SAlexander V. Chernikov }
608f1220db8SAlexander V. Chernikov 
609f1220db8SAlexander V. Chernikov /*
6102d99a349SAlexander V. Chernikov  * Store table info to buffer provided by @sd.
611d3a4f924SAlexander V. Chernikov  * Data layout:
612d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
613d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
614d3a4f924SAlexander V. Chernikov  *
615d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
616d3a4f924SAlexander V. Chernikov  */
617d3a4f924SAlexander V. Chernikov int
6182d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
619d3a4f924SAlexander V. Chernikov {
620d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
621d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
622d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
623d3a4f924SAlexander V. Chernikov 	size_t sz;
624d3a4f924SAlexander V. Chernikov 
625d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
6262d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
6272d99a349SAlexander V. Chernikov 	if (oh == NULL)
628d3a4f924SAlexander V. Chernikov 		return (EINVAL);
629d3a4f924SAlexander V. Chernikov 
630d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
631d3a4f924SAlexander V. Chernikov 
632d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
633d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
634d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
635d3a4f924SAlexander V. Chernikov 		return (ESRCH);
636d3a4f924SAlexander V. Chernikov 	}
637d3a4f924SAlexander V. Chernikov 
638d3a4f924SAlexander V. Chernikov 	export_table_info(tc, (ipfw_xtable_info *)(oh + 1));
639d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
640d3a4f924SAlexander V. Chernikov 
6412d99a349SAlexander V. Chernikov 	return (0);
642d3a4f924SAlexander V. Chernikov }
643d3a4f924SAlexander V. Chernikov 
644f1220db8SAlexander V. Chernikov struct dump_args {
645f1220db8SAlexander V. Chernikov 	struct table_info *ti;
646f1220db8SAlexander V. Chernikov 	struct table_config *tc;
6472d99a349SAlexander V. Chernikov 	struct sockopt_data *sd;
648f1220db8SAlexander V. Chernikov 	uint32_t cnt;
649f1220db8SAlexander V. Chernikov 	uint16_t uidx;
6502d99a349SAlexander V. Chernikov 	ipfw_table_entry *ent;
6512d99a349SAlexander V. Chernikov 	uint32_t size;
652f1220db8SAlexander V. Chernikov };
653f1220db8SAlexander V. Chernikov 
654f1220db8SAlexander V. Chernikov int
6552d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
6562d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
657f1220db8SAlexander V. Chernikov {
658d3a4f924SAlexander V. Chernikov 	int error;
659d3a4f924SAlexander V. Chernikov 
660d3a4f924SAlexander V. Chernikov 	switch (op3->version) {
661d3a4f924SAlexander V. Chernikov 	case 0:
6622d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sd);
663d3a4f924SAlexander V. Chernikov 		break;
664d3a4f924SAlexander V. Chernikov 	case 1:
6652d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sd);
666d3a4f924SAlexander V. Chernikov 		break;
667d3a4f924SAlexander V. Chernikov 	default:
668d3a4f924SAlexander V. Chernikov 		error = ENOTSUP;
669d3a4f924SAlexander V. Chernikov 	}
670d3a4f924SAlexander V. Chernikov 
671d3a4f924SAlexander V. Chernikov 	return (error);
672d3a4f924SAlexander V. Chernikov }
673d3a4f924SAlexander V. Chernikov 
674d3a4f924SAlexander V. Chernikov /*
675d3a4f924SAlexander V. Chernikov  * Dumps all table data
6762d99a349SAlexander V. Chernikov  * Data layout (version 1)(current):
6772d99a349SAlexander V. Chernikov  * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
6782d99a349SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ]
679d3a4f924SAlexander V. Chernikov  *
680d3a4f924SAlexander V. Chernikov  * Returns 0 on success
681d3a4f924SAlexander V. Chernikov  */
682d3a4f924SAlexander V. Chernikov static int
6832d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
684d3a4f924SAlexander V. Chernikov {
685f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
686f1220db8SAlexander V. Chernikov 	ipfw_xtable_info *i;
687f1220db8SAlexander V. Chernikov 	struct tid_info ti;
688f1220db8SAlexander V. Chernikov 	struct table_config *tc;
689f1220db8SAlexander V. Chernikov 	struct table_algo *ta;
690f1220db8SAlexander V. Chernikov 	struct dump_args da;
691d3a4f924SAlexander V. Chernikov 	uint32_t sz;
692f1220db8SAlexander V. Chernikov 
6932d99a349SAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
6942d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
6952d99a349SAlexander V. Chernikov 	if (oh == NULL)
696d3a4f924SAlexander V. Chernikov 		return (EINVAL);
697d3a4f924SAlexander V. Chernikov 
6982d99a349SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
699d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
700f1220db8SAlexander V. Chernikov 
701f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
702f1220db8SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
703f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
704f1220db8SAlexander V. Chernikov 		return (ESRCH);
705f1220db8SAlexander V. Chernikov 	}
706f1220db8SAlexander V. Chernikov 	export_table_info(tc, i);
7072d99a349SAlexander V. Chernikov 	sz = tc->count;
7082d99a349SAlexander V. Chernikov 
7092d99a349SAlexander V. Chernikov 	if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) {
7102d99a349SAlexander V. Chernikov 
7112d99a349SAlexander V. Chernikov 		/*
7122d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
7132d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
7142d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
7152d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
7162d99a349SAlexander V. Chernikov 		 */
717f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
7182d99a349SAlexander V. Chernikov 		return (ENOMEM);
719f1220db8SAlexander V. Chernikov 	}
720f1220db8SAlexander V. Chernikov 
721f1220db8SAlexander V. Chernikov 	/*
722f1220db8SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
723f1220db8SAlexander V. Chernikov 	 */
724d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
725f1220db8SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
726f1220db8SAlexander V. Chernikov 	da.tc = tc;
7272d99a349SAlexander V. Chernikov 	da.sd = sd;
728f1220db8SAlexander V. Chernikov 
729f1220db8SAlexander V. Chernikov 	ta = tc->ta;
730f1220db8SAlexander V. Chernikov 
731f1220db8SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
732f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
733f1220db8SAlexander V. Chernikov 
734f1220db8SAlexander V. Chernikov 	return (0);
735f1220db8SAlexander V. Chernikov }
736f1220db8SAlexander V. Chernikov 
737d3a4f924SAlexander V. Chernikov /*
738d3a4f924SAlexander V. Chernikov  * Dumps all table data
7392d99a349SAlexander V. Chernikov  * Data layout (version 0)(legacy):
740d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
741d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
742d3a4f924SAlexander V. Chernikov  *
743d3a4f924SAlexander V. Chernikov  * Returns 0 on success
744d3a4f924SAlexander V. Chernikov  */
745d3a4f924SAlexander V. Chernikov static int
7462d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
747d3a4f924SAlexander V. Chernikov {
748d3a4f924SAlexander V. Chernikov 	ipfw_xtable *xtbl;
749d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
750d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
751d3a4f924SAlexander V. Chernikov 	struct table_algo *ta;
752d3a4f924SAlexander V. Chernikov 	struct dump_args da;
753d3a4f924SAlexander V. Chernikov 	size_t sz;
754d3a4f924SAlexander V. Chernikov 
7552d99a349SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
7562d99a349SAlexander V. Chernikov 	if (xtbl == NULL)
757d3a4f924SAlexander V. Chernikov 		return (EINVAL);
758d3a4f924SAlexander V. Chernikov 
759d3a4f924SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
760d3a4f924SAlexander V. Chernikov 	ti.set = 0; /* XXX: No way to specify set */
761d3a4f924SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
762d3a4f924SAlexander V. Chernikov 
763d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
764d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
765d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
766d3a4f924SAlexander V. Chernikov 		return (0);
767d3a4f924SAlexander V. Chernikov 	}
768d3a4f924SAlexander V. Chernikov 	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
7692d99a349SAlexander V. Chernikov 
7702d99a349SAlexander V. Chernikov 	xtbl->cnt = tc->count;
7712d99a349SAlexander V. Chernikov 	xtbl->size = sz;
7722d99a349SAlexander V. Chernikov 	xtbl->type = tc->no.type;
7732d99a349SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
7742d99a349SAlexander V. Chernikov 
7752d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
7762d99a349SAlexander V. Chernikov 
7772d99a349SAlexander V. Chernikov 		/*
7782d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
7792d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
7802d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
7812d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
7822d99a349SAlexander V. Chernikov 		 */
783d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
7842d99a349SAlexander V. Chernikov 		return (ENOMEM);
785d3a4f924SAlexander V. Chernikov 	}
786d3a4f924SAlexander V. Chernikov 
787d3a4f924SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
788d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
789d3a4f924SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
790d3a4f924SAlexander V. Chernikov 	da.tc = tc;
7912d99a349SAlexander V. Chernikov 	da.sd = sd;
7922d99a349SAlexander V. Chernikov 
793d3a4f924SAlexander V. Chernikov 	ta = tc->ta;
794d3a4f924SAlexander V. Chernikov 
795d3a4f924SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
796d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
797d3a4f924SAlexander V. Chernikov 
7982d99a349SAlexander V. Chernikov 	return (0);
799d3a4f924SAlexander V. Chernikov }
800d3a4f924SAlexander V. Chernikov 
801d3a4f924SAlexander V. Chernikov /*
8029490a627SAlexander V. Chernikov  * High-level setsockopt cmds
8039490a627SAlexander V. Chernikov  */
8049490a627SAlexander V. Chernikov int
8059490a627SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt,
8069490a627SAlexander V. Chernikov     ip_fw3_opheader *op3)
8079490a627SAlexander V. Chernikov {
8089490a627SAlexander V. Chernikov 
8099490a627SAlexander V. Chernikov 	return (ENOTSUP);
8109490a627SAlexander V. Chernikov }
8119490a627SAlexander V. Chernikov 
8129490a627SAlexander V. Chernikov /*
8139490a627SAlexander V. Chernikov  * Creates new table.
8149490a627SAlexander V. Chernikov  * Data layout:
8159490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
8169490a627SAlexander V. Chernikov  *
8179490a627SAlexander V. Chernikov  * Returns 0 on success
8189490a627SAlexander V. Chernikov  */
8199490a627SAlexander V. Chernikov int
8209490a627SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
8219490a627SAlexander V. Chernikov     ip_fw3_opheader *op3)
8229490a627SAlexander V. Chernikov {
8239490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
8249490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
8259490a627SAlexander V. Chernikov 	char *tname, *aname;
8269490a627SAlexander V. Chernikov 	struct tid_info ti;
8279490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
8289490a627SAlexander V. Chernikov 	struct table_config *tc;
8299490a627SAlexander V. Chernikov 	struct table_algo *ta;
8309490a627SAlexander V. Chernikov 	uint16_t kidx;
8319490a627SAlexander V. Chernikov 
8329490a627SAlexander V. Chernikov 	if (sopt->sopt_valsize < sizeof(*oh) + sizeof(ipfw_xtable_info))
8339490a627SAlexander V. Chernikov 		return (EINVAL);
8349490a627SAlexander V. Chernikov 
8359490a627SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
8369490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
8379490a627SAlexander V. Chernikov 
8389490a627SAlexander V. Chernikov 	/*
8399490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
8402d99a349SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
8419490a627SAlexander V. Chernikov 	 */
8429490a627SAlexander V. Chernikov 	tname = i->tablename;
8439490a627SAlexander V. Chernikov 	aname = i->algoname;
8449490a627SAlexander V. Chernikov 	if (strnlen(tname, sizeof(i->tablename)) == sizeof(i->tablename) ||
8459490a627SAlexander V. Chernikov 	    tname[0] == '\0' ||
8469490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
8479490a627SAlexander V. Chernikov 		return (EINVAL);
8489490a627SAlexander V. Chernikov 
8499490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
8509490a627SAlexander V. Chernikov 		/* Use default algorithm */
8519490a627SAlexander V. Chernikov 		aname = NULL;
8529490a627SAlexander V. Chernikov 	}
8539490a627SAlexander V. Chernikov 
8549490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
8559490a627SAlexander V. Chernikov 
8569490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
8579490a627SAlexander V. Chernikov 
8589490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
8599490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
8609490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
8619490a627SAlexander V. Chernikov 		return (EEXIST);
8629490a627SAlexander V. Chernikov 	}
8639490a627SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), &ti, aname);
8649490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
8659490a627SAlexander V. Chernikov 
8669490a627SAlexander V. Chernikov 	if (ta == NULL)
8679490a627SAlexander V. Chernikov 		return (ENOTSUP);
8689490a627SAlexander V. Chernikov 
8699490a627SAlexander V. Chernikov 	if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL)
8709490a627SAlexander V. Chernikov 		return (ENOMEM);
8719490a627SAlexander V. Chernikov 
8729490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
8739490a627SAlexander V. Chernikov 	if (ipfw_objhash_alloc_idx(ni, ti.set, &kidx) != 0) {
8749490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
8759490a627SAlexander V. Chernikov 		printf("Unable to allocate table index for table %s in set %u."
8769490a627SAlexander V. Chernikov 		    " Consider increasing net.inet.ip.fw.tables_max",
8779490a627SAlexander V. Chernikov 		    tname, ti.set);
8789490a627SAlexander V. Chernikov 		free_table_config(ni, tc);
8799490a627SAlexander V. Chernikov 		return (EBUSY);
8809490a627SAlexander V. Chernikov 	}
8819490a627SAlexander V. Chernikov 
8829490a627SAlexander V. Chernikov 	tc->no.kidx = kidx;
8839490a627SAlexander V. Chernikov 
8849490a627SAlexander V. Chernikov 	IPFW_WLOCK(ch);
8859490a627SAlexander V. Chernikov 	link_table(ch, tc);
8869490a627SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
8879490a627SAlexander V. Chernikov 
8889490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
8899490a627SAlexander V. Chernikov 
8909490a627SAlexander V. Chernikov 	return (0);
8919490a627SAlexander V. Chernikov }
8929490a627SAlexander V. Chernikov 
893d3a4f924SAlexander V. Chernikov void
894d3a4f924SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
895d3a4f924SAlexander V. Chernikov {
896d3a4f924SAlexander V. Chernikov 
897d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
898d3a4f924SAlexander V. Chernikov 	ti->set = oh->set;
899d3a4f924SAlexander V. Chernikov 	ti->uidx = oh->idx;
900d3a4f924SAlexander V. Chernikov 	ti->tlvs = &oh->ntlv;
901d3a4f924SAlexander V. Chernikov 	ti->tlen = oh->ntlv.head.length;
902d3a4f924SAlexander V. Chernikov }
903d3a4f924SAlexander V. Chernikov 
9049f7d47b0SAlexander V. Chernikov static void
9059f7d47b0SAlexander V. Chernikov export_table_info(struct table_config *tc, ipfw_xtable_info *i)
9069f7d47b0SAlexander V. Chernikov {
9079f7d47b0SAlexander V. Chernikov 
9089f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
9099f7d47b0SAlexander V. Chernikov 	i->ftype = tc->ftype;
9109f7d47b0SAlexander V. Chernikov 	i->atype = tc->ta->idx;
9119f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
9129f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
9139f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
9149f7d47b0SAlexander V. Chernikov 	i->count = tc->count;
9159f7d47b0SAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_table_xentry);
916f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
9179f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
9189f7d47b0SAlexander V. Chernikov }
9199f7d47b0SAlexander V. Chernikov 
9209f7d47b0SAlexander V. Chernikov static void
9219f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
9229f7d47b0SAlexander V. Chernikov     void *arg)
9233b3a8eb9SGleb Smirnoff {
9249f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
9252d99a349SAlexander V. Chernikov 	struct sockopt_data *sd;
9263b3a8eb9SGleb Smirnoff 
9272d99a349SAlexander V. Chernikov 	sd = (struct sockopt_data *)arg;
9282d99a349SAlexander V. Chernikov 	i = (ipfw_xtable_info *)ipfw_get_sopt_space(sd, sizeof(*i));
9292d99a349SAlexander V. Chernikov 	KASSERT(i == 0, ("previously checked buffer is not enough"));
9309f7d47b0SAlexander V. Chernikov 
9319f7d47b0SAlexander V. Chernikov 	export_table_info((struct table_config *)no, i);
9329f7d47b0SAlexander V. Chernikov }
9339f7d47b0SAlexander V. Chernikov 
934f1220db8SAlexander V. Chernikov /*
935f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
9362d99a349SAlexander V. Chernikov  * storage provided by @sd.
937f1220db8SAlexander V. Chernikov  * Returns 0 on success.
938f1220db8SAlexander V. Chernikov  */
939f1220db8SAlexander V. Chernikov static int
9402d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
9412d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
9429f7d47b0SAlexander V. Chernikov {
9439f7d47b0SAlexander V. Chernikov 	uint32_t size;
9449f7d47b0SAlexander V. Chernikov 	uint32_t count;
9459f7d47b0SAlexander V. Chernikov 
9469f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
9479f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
9482d99a349SAlexander V. Chernikov 
9492d99a349SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
950f1220db8SAlexander V. Chernikov 	olh->count = count;
951f1220db8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
9522d99a349SAlexander V. Chernikov 
9532d99a349SAlexander V. Chernikov 	if (size > olh->size) {
9542d99a349SAlexander V. Chernikov 		/* Store necessary size */
9552d99a349SAlexander V. Chernikov 		olh->size = size;
9569f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
957f1220db8SAlexander V. Chernikov 	}
9589f7d47b0SAlexander V. Chernikov 	olh->size = size;
9592d99a349SAlexander V. Chernikov 
9602d99a349SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, sd);
9619f7d47b0SAlexander V. Chernikov 
9623b3a8eb9SGleb Smirnoff 	return (0);
9633b3a8eb9SGleb Smirnoff }
9643b3a8eb9SGleb Smirnoff 
9653b3a8eb9SGleb Smirnoff int
966b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
9673b3a8eb9SGleb Smirnoff {
968b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
9693b3a8eb9SGleb Smirnoff 
970b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
971b074b7bbSAlexander V. Chernikov 		return (ESRCH);
9729f7d47b0SAlexander V. Chernikov 	*cnt = tc->count;
9733b3a8eb9SGleb Smirnoff 	return (0);
9743b3a8eb9SGleb Smirnoff }
9753b3a8eb9SGleb Smirnoff 
9769f7d47b0SAlexander V. Chernikov 
9779f7d47b0SAlexander V. Chernikov int
9789f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
9793b3a8eb9SGleb Smirnoff {
9809f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
9819f7d47b0SAlexander V. Chernikov 
982d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
983d3a4f924SAlexander V. Chernikov 		*cnt = 0;
9849f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
985d3a4f924SAlexander V. Chernikov 	}
9869f7d47b0SAlexander V. Chernikov 	*cnt = tc->count * sizeof(ipfw_table_xentry);
9879f7d47b0SAlexander V. Chernikov 	if (tc->count > 0)
9889f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
9899f7d47b0SAlexander V. Chernikov 	return (0);
9909f7d47b0SAlexander V. Chernikov }
9919f7d47b0SAlexander V. Chernikov 
9929f7d47b0SAlexander V. Chernikov static int
9939f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
9949f7d47b0SAlexander V. Chernikov {
9959f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
9969f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
9979f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
9983b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
9993b3a8eb9SGleb Smirnoff 
10009f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
10019f7d47b0SAlexander V. Chernikov 
10029f7d47b0SAlexander V. Chernikov 	tc = da->tc;
10039f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
10049f7d47b0SAlexander V. Chernikov 
10059f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
1006f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
10073b3a8eb9SGleb Smirnoff 		return (1);
1008f1220db8SAlexander V. Chernikov 	ent = da->ent++;
1009f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
1010f1220db8SAlexander V. Chernikov 	da->cnt++;
10119f7d47b0SAlexander V. Chernikov 
10129f7d47b0SAlexander V. Chernikov 	return (ta->dump_entry(tc->astate, da->ti, e, ent));
10133b3a8eb9SGleb Smirnoff }
10143b3a8eb9SGleb Smirnoff 
1015f1220db8SAlexander V. Chernikov /*
1016f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
1017f1220db8SAlexander V. Chernikov  */
10183b3a8eb9SGleb Smirnoff int
1019f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
1020f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
10213b3a8eb9SGleb Smirnoff {
1022b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
10239f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
10249f7d47b0SAlexander V. Chernikov 	struct dump_args da;
10253b3a8eb9SGleb Smirnoff 
10263b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
10273b3a8eb9SGleb Smirnoff 
1028b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1029b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
10309f7d47b0SAlexander V. Chernikov 
10319f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
10329f7d47b0SAlexander V. Chernikov 
10339f7d47b0SAlexander V. Chernikov 	if (ta->dump_entry == NULL)
10349f7d47b0SAlexander V. Chernikov 		return (0);	/* Legacy dump support is not necessary */
10359f7d47b0SAlexander V. Chernikov 
1036d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
10379f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
10389f7d47b0SAlexander V. Chernikov 	da.tc = tc;
1039f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
1040f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
10419f7d47b0SAlexander V. Chernikov 
10429f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
10439f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
1044f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
10459f7d47b0SAlexander V. Chernikov 
10463b3a8eb9SGleb Smirnoff 	return (0);
10473b3a8eb9SGleb Smirnoff }
10483b3a8eb9SGleb Smirnoff 
10499490a627SAlexander V. Chernikov /*
10509490a627SAlexander V. Chernikov  * Dumps table entry in eXtended format (current).
10519490a627SAlexander V. Chernikov  */
10523b3a8eb9SGleb Smirnoff static int
10539f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
10543b3a8eb9SGleb Smirnoff {
10559f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
10569f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
10579f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
10583b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
10593b3a8eb9SGleb Smirnoff 
10609f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
10619f7d47b0SAlexander V. Chernikov 
10629f7d47b0SAlexander V. Chernikov 	tc = da->tc;
10639f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
10649f7d47b0SAlexander V. Chernikov 
10652d99a349SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
10663b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
10672d99a349SAlexander V. Chernikov 	if (xent == NULL)
10683b3a8eb9SGleb Smirnoff 		return (1);
10693b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
1070f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
10719f7d47b0SAlexander V. Chernikov 
10729f7d47b0SAlexander V. Chernikov 	return (ta->dump_xentry(tc->astate, da->ti, e, xent));
10739f7d47b0SAlexander V. Chernikov }
10749f7d47b0SAlexander V. Chernikov 
10759f7d47b0SAlexander V. Chernikov /*
10769f7d47b0SAlexander V. Chernikov  * Table algorithms
10779f7d47b0SAlexander V. Chernikov  */
10783b3a8eb9SGleb Smirnoff 
10799f7d47b0SAlexander V. Chernikov /*
10809490a627SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name
10819f7d47b0SAlexander V. Chernikov  */
10829f7d47b0SAlexander V. Chernikov static struct table_algo *
10839490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
10849f7d47b0SAlexander V. Chernikov {
10859490a627SAlexander V. Chernikov 	int i, l;
10869490a627SAlexander V. Chernikov 	struct table_algo *ta;
10879f7d47b0SAlexander V. Chernikov 
10889f7d47b0SAlexander V. Chernikov 	/* Search by index */
10899f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
10909f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
10919f7d47b0SAlexander V. Chernikov 			return (NULL);
10929f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
10939f7d47b0SAlexander V. Chernikov 	}
10949f7d47b0SAlexander V. Chernikov 
10959490a627SAlexander V. Chernikov 	/* Search by name if supplied */
10969490a627SAlexander V. Chernikov 	if (name != NULL) {
10979490a627SAlexander V. Chernikov 		/* TODO: better search */
10989490a627SAlexander V. Chernikov 		for (i = 1; i <= tcfg->algo_count; i++) {
10999490a627SAlexander V. Chernikov 			ta = tcfg->algo[i];
11009490a627SAlexander V. Chernikov 
11019490a627SAlexander V. Chernikov 			/*
11029490a627SAlexander V. Chernikov 			 * One can supply additional algorithm
11039490a627SAlexander V. Chernikov 			 * parameters so we compare only the first word
11049490a627SAlexander V. Chernikov 			 * of supplied name:
11059490a627SAlexander V. Chernikov 			 * 'hash_cidr hsize=32'
11069490a627SAlexander V. Chernikov 			 * '^^^^^^^^^'
11079490a627SAlexander V. Chernikov 			 *
11089490a627SAlexander V. Chernikov 			 */
11099490a627SAlexander V. Chernikov 			l = strlen(ta->name);
11109490a627SAlexander V. Chernikov 			if (strncmp(name, ta->name, l) == 0) {
11119490a627SAlexander V. Chernikov 				if (name[l] == '\0' || name[l] == ' ')
11129490a627SAlexander V. Chernikov 					return (ta);
11139490a627SAlexander V. Chernikov 			}
11149490a627SAlexander V. Chernikov 		}
11159490a627SAlexander V. Chernikov 
11169490a627SAlexander V. Chernikov 		return (NULL);
11179490a627SAlexander V. Chernikov 	}
11189490a627SAlexander V. Chernikov 
11199f7d47b0SAlexander V. Chernikov 	/* Search by type */
11209f7d47b0SAlexander V. Chernikov 	switch (ti->type) {
11213b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
11229f7d47b0SAlexander V. Chernikov 		return (&radix_cidr);
11233b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
11249f7d47b0SAlexander V. Chernikov 		return (&radix_iface);
11253b3a8eb9SGleb Smirnoff 	}
11263b3a8eb9SGleb Smirnoff 
11279f7d47b0SAlexander V. Chernikov 	return (NULL);
11283b3a8eb9SGleb Smirnoff }
11293b3a8eb9SGleb Smirnoff 
11309f7d47b0SAlexander V. Chernikov void
11319f7d47b0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta)
11323b3a8eb9SGleb Smirnoff {
11339f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
11343b3a8eb9SGleb Smirnoff 
11359f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
1136b074b7bbSAlexander V. Chernikov 
11379f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
11389f7d47b0SAlexander V. Chernikov 
11399f7d47b0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta;
11409f7d47b0SAlexander V. Chernikov 	ta->idx = tcfg->algo_count;
11413b3a8eb9SGleb Smirnoff }
11423b3a8eb9SGleb Smirnoff 
11439f7d47b0SAlexander V. Chernikov 
1144b074b7bbSAlexander V. Chernikov /*
1145b074b7bbSAlexander V. Chernikov  * Tables rewriting code
1146b074b7bbSAlexander V. Chernikov  *
1147b074b7bbSAlexander V. Chernikov  */
1148b074b7bbSAlexander V. Chernikov 
1149b074b7bbSAlexander V. Chernikov /*
1150b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
1151b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
1152b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
1153b074b7bbSAlexander V. Chernikov  */
1154b074b7bbSAlexander V. Chernikov static int
1155b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
1156b074b7bbSAlexander V. Chernikov {
1157b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1158b074b7bbSAlexander V. Chernikov 	int skip;
1159b074b7bbSAlexander V. Chernikov 	uint16_t v;
1160b074b7bbSAlexander V. Chernikov 
1161b074b7bbSAlexander V. Chernikov 	skip = 1;
1162b074b7bbSAlexander V. Chernikov 
1163b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1164b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1165b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1166b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1167b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
1168b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
1169b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
1170b074b7bbSAlexander V. Chernikov 		skip = 0;
1171b074b7bbSAlexander V. Chernikov 
1172b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
1173b074b7bbSAlexander V. Chernikov 			/*
1174b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
1175b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
1176b074b7bbSAlexander V. Chernikov 			 */
1177b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
1178b074b7bbSAlexander V. Chernikov 			switch (v) {
1179b074b7bbSAlexander V. Chernikov 			case 0:
1180b074b7bbSAlexander V. Chernikov 			case 1:
1181b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
1182b074b7bbSAlexander V. Chernikov 				break;
1183b074b7bbSAlexander V. Chernikov 			case 2:
1184b074b7bbSAlexander V. Chernikov 			case 3:
1185b074b7bbSAlexander V. Chernikov 				/* src/dst port */
1186b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1187b074b7bbSAlexander V. Chernikov 				break;
1188b074b7bbSAlexander V. Chernikov 			case 4:
1189b074b7bbSAlexander V. Chernikov 				/* uid/gid */
1190b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1191b074b7bbSAlexander V. Chernikov 			case 5:
1192b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1193b074b7bbSAlexander V. Chernikov 				/* jid */
1194b074b7bbSAlexander V. Chernikov 			case 6:
1195b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1196b074b7bbSAlexander V. Chernikov 				/* dscp */
1197b074b7bbSAlexander V. Chernikov 				break;
1198b074b7bbSAlexander V. Chernikov 			}
1199b074b7bbSAlexander V. Chernikov 		}
1200b074b7bbSAlexander V. Chernikov 		break;
1201b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1202b074b7bbSAlexander V. Chernikov 	case O_RECV:
1203b074b7bbSAlexander V. Chernikov 	case O_VIA:
1204b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1205b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1206b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
1207b074b7bbSAlexander V. Chernikov 			break;
1208b074b7bbSAlexander V. Chernikov 
1209b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
1210b074b7bbSAlexander V. Chernikov 		*puidx = cmdif->p.glob;
1211b074b7bbSAlexander V. Chernikov 		skip = 0;
1212b074b7bbSAlexander V. Chernikov 		break;
1213b074b7bbSAlexander V. Chernikov 	}
1214b074b7bbSAlexander V. Chernikov 
1215b074b7bbSAlexander V. Chernikov 	return (skip);
1216b074b7bbSAlexander V. Chernikov }
1217b074b7bbSAlexander V. Chernikov 
1218b074b7bbSAlexander V. Chernikov /*
1219b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
1220b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
1221b074b7bbSAlexander V. Chernikov  */
1222b074b7bbSAlexander V. Chernikov static void
1223b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
1224b074b7bbSAlexander V. Chernikov {
1225b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1226b074b7bbSAlexander V. Chernikov 
1227b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1228b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1229b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1230b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1231b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
1232b074b7bbSAlexander V. Chernikov 		break;
1233b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1234b074b7bbSAlexander V. Chernikov 	case O_RECV:
1235b074b7bbSAlexander V. Chernikov 	case O_VIA:
1236b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1237b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1238b074b7bbSAlexander V. Chernikov 		cmdif->p.glob = idx;
1239b074b7bbSAlexander V. Chernikov 		break;
1240b074b7bbSAlexander V. Chernikov 	}
1241b074b7bbSAlexander V. Chernikov }
1242b074b7bbSAlexander V. Chernikov 
1243b074b7bbSAlexander V. Chernikov static char *
1244b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
1245b074b7bbSAlexander V. Chernikov {
12469f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1247b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
1248b074b7bbSAlexander V. Chernikov 	int l;
1249b074b7bbSAlexander V. Chernikov 
1250b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
1251b074b7bbSAlexander V. Chernikov 	pe = pa + len;
1252b074b7bbSAlexander V. Chernikov 	l = 0;
1253b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
12549f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
1255b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
1256b074b7bbSAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_NAME)
1257b074b7bbSAlexander V. Chernikov 			continue;
1258b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
1259b074b7bbSAlexander V. Chernikov 			continue;
1260b074b7bbSAlexander V. Chernikov 
1261b074b7bbSAlexander V. Chernikov 		return (ntlv->name);
1262b074b7bbSAlexander V. Chernikov 	}
1263b074b7bbSAlexander V. Chernikov 
1264b074b7bbSAlexander V. Chernikov 	return (NULL);
1265b074b7bbSAlexander V. Chernikov }
1266b074b7bbSAlexander V. Chernikov 
1267b074b7bbSAlexander V. Chernikov static struct table_config *
1268b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
1269b074b7bbSAlexander V. Chernikov {
1270b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1271b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1272b074b7bbSAlexander V. Chernikov 
1273b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1274b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1275b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1276b074b7bbSAlexander V. Chernikov 			return (NULL);
1277b074b7bbSAlexander V. Chernikov 	} else {
1278b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1279b074b7bbSAlexander V. Chernikov 		name = bname;
1280b074b7bbSAlexander V. Chernikov 	}
1281b074b7bbSAlexander V. Chernikov 
1282b074b7bbSAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, ti->set, name);
1283b074b7bbSAlexander V. Chernikov 
1284b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
1285b074b7bbSAlexander V. Chernikov }
1286b074b7bbSAlexander V. Chernikov 
1287b074b7bbSAlexander V. Chernikov static struct table_config *
12889f7d47b0SAlexander V. Chernikov alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
12899490a627SAlexander V. Chernikov     struct table_algo *ta, char *aname)
1290b074b7bbSAlexander V. Chernikov {
1291b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1292b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1293b074b7bbSAlexander V. Chernikov 	int error;
1294b074b7bbSAlexander V. Chernikov 
1295b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1296b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1297b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1298b074b7bbSAlexander V. Chernikov 			return (NULL);
1299b074b7bbSAlexander V. Chernikov 	} else {
1300b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1301b074b7bbSAlexander V. Chernikov 		name = bname;
1302b074b7bbSAlexander V. Chernikov 	}
1303b074b7bbSAlexander V. Chernikov 
1304b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
1305b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
1306b074b7bbSAlexander V. Chernikov 	tc->no.type = ti->type;
1307b074b7bbSAlexander V. Chernikov 	tc->no.set = ti->set;
13089f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
1309b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
1310b074b7bbSAlexander V. Chernikov 
1311b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
1312b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
1313b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
1314b074b7bbSAlexander V. Chernikov 	}
1315b074b7bbSAlexander V. Chernikov 
1316b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
13179490a627SAlexander V. Chernikov 	error = ta->init(&tc->astate, &tc->ti, aname);
1318b074b7bbSAlexander V. Chernikov 	if (error != 0) {
1319b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
1320b074b7bbSAlexander V. Chernikov 		return (NULL);
1321b074b7bbSAlexander V. Chernikov 	}
1322b074b7bbSAlexander V. Chernikov 
1323b074b7bbSAlexander V. Chernikov 	return (tc);
1324b074b7bbSAlexander V. Chernikov }
1325b074b7bbSAlexander V. Chernikov 
1326b074b7bbSAlexander V. Chernikov static void
1327b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
1328b074b7bbSAlexander V. Chernikov {
1329b074b7bbSAlexander V. Chernikov 
1330b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0)
13319f7d47b0SAlexander V. Chernikov 		tc->ta->destroy(&tc->astate, &tc->ti);
1332b074b7bbSAlexander V. Chernikov 
1333b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
1334b074b7bbSAlexander V. Chernikov }
1335b074b7bbSAlexander V. Chernikov 
1336b074b7bbSAlexander V. Chernikov /*
1337b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
1338b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
1339b074b7bbSAlexander V. Chernikov  */
1340b074b7bbSAlexander V. Chernikov static void
13419f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
1342b074b7bbSAlexander V. Chernikov {
1343b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
13449f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1345b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1346b074b7bbSAlexander V. Chernikov 
13479f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
13489f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1349b074b7bbSAlexander V. Chernikov 
13509f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1351b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1352b074b7bbSAlexander V. Chernikov 
1353b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
13549f7d47b0SAlexander V. Chernikov 
13559f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
13569f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
1357b074b7bbSAlexander V. Chernikov 
1358b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
1359b074b7bbSAlexander V. Chernikov }
1360b074b7bbSAlexander V. Chernikov 
1361b074b7bbSAlexander V. Chernikov /*
1362b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
1363b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
1364b074b7bbSAlexander V. Chernikov  */
1365b074b7bbSAlexander V. Chernikov static void
13669f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
1367b074b7bbSAlexander V. Chernikov {
1368b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
13699f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1370b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1371b074b7bbSAlexander V. Chernikov 
13729f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
13739f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1374b074b7bbSAlexander V. Chernikov 
13759f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1376b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1377b074b7bbSAlexander V. Chernikov 
13789f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
1379b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
13809f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
13819f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
1382b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
1383b074b7bbSAlexander V. Chernikov }
1384b074b7bbSAlexander V. Chernikov 
1385b074b7bbSAlexander V. Chernikov /*
1386b074b7bbSAlexander V. Chernikov  * Finds named object by @uidx number.
1387b074b7bbSAlexander V. Chernikov  * Refs found object, allocate new index for non-existing object.
13889490a627SAlexander V. Chernikov  * Fills in @oib with userland/kernel indexes.
13899490a627SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
1390b074b7bbSAlexander V. Chernikov  *
1391b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1392b074b7bbSAlexander V. Chernikov  */
1393b074b7bbSAlexander V. Chernikov static int
13949490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
13959490a627SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
1396b074b7bbSAlexander V. Chernikov {
1397b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
13989490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
13999490a627SAlexander V. Chernikov 	struct named_object *no;
14009490a627SAlexander V. Chernikov 	int error, l, cmdlen;
14019490a627SAlexander V. Chernikov 	ipfw_insn *cmd;
14029490a627SAlexander V. Chernikov 	struct obj_idx *pidx, *p;
14039490a627SAlexander V. Chernikov 
14049490a627SAlexander V. Chernikov 	pidx = *oib;
14059490a627SAlexander V. Chernikov 	l = rule->cmd_len;
14069490a627SAlexander V. Chernikov 	cmd = rule->cmd;
14079490a627SAlexander V. Chernikov 	cmdlen = 0;
14089490a627SAlexander V. Chernikov 	error = 0;
14099490a627SAlexander V. Chernikov 
14109490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
14119490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
14129490a627SAlexander V. Chernikov 
14139490a627SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
14149490a627SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
14159490a627SAlexander V. Chernikov 
14169490a627SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
14179490a627SAlexander V. Chernikov 			continue;
1418b074b7bbSAlexander V. Chernikov 
1419b074b7bbSAlexander V. Chernikov 		pidx->uidx = ti->uidx;
1420b074b7bbSAlexander V. Chernikov 		pidx->type = ti->type;
1421b074b7bbSAlexander V. Chernikov 
14229490a627SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
14239490a627SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
14249490a627SAlexander V. Chernikov 				/* Incompatible types */
14259490a627SAlexander V. Chernikov 				error = EINVAL;
14269490a627SAlexander V. Chernikov 				break;
14279490a627SAlexander V. Chernikov 			}
14289f7d47b0SAlexander V. Chernikov 
14299490a627SAlexander V. Chernikov 			/* Reference found table and save kidx */
14309490a627SAlexander V. Chernikov 			tc->no.refcnt++;
14319490a627SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
14329490a627SAlexander V. Chernikov 			pidx++;
14339490a627SAlexander V. Chernikov 			continue;
14349490a627SAlexander V. Chernikov 		}
14359490a627SAlexander V. Chernikov 
14369490a627SAlexander V. Chernikov 		/* Table not found. Allocate new index and save for later */
1437b074b7bbSAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, ti->set, &pidx->kidx) != 0) {
1438b074b7bbSAlexander V. Chernikov 			printf("Unable to allocate table index in set %u."
1439b074b7bbSAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max",
1440b074b7bbSAlexander V. Chernikov 			    ti->set);
14419490a627SAlexander V. Chernikov 			error = EBUSY;
14429490a627SAlexander V. Chernikov 			break;
1443b074b7bbSAlexander V. Chernikov 		}
1444b074b7bbSAlexander V. Chernikov 
1445b074b7bbSAlexander V. Chernikov 		ci->new_tables++;
14469490a627SAlexander V. Chernikov 		pidx->new = 1;
14479490a627SAlexander V. Chernikov 		pidx++;
1448b074b7bbSAlexander V. Chernikov 	}
1449b074b7bbSAlexander V. Chernikov 
14509490a627SAlexander V. Chernikov 	if (error != 0) {
14519490a627SAlexander V. Chernikov 		/* Unref everything we have already done */
14529490a627SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
14539490a627SAlexander V. Chernikov 			if (p->new != 0) {
14549490a627SAlexander V. Chernikov 				ipfw_objhash_free_idx(ni, ci->tableset,p->kidx);
14559490a627SAlexander V. Chernikov 				continue;
14569490a627SAlexander V. Chernikov 			}
1457b074b7bbSAlexander V. Chernikov 
14589490a627SAlexander V. Chernikov 			/* Find & unref by existing idx */
14599490a627SAlexander V. Chernikov 			no = ipfw_objhash_lookup_idx(ni, ci->tableset, p->kidx);
14609490a627SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
14619490a627SAlexander V. Chernikov 			    p->kidx));
1462b074b7bbSAlexander V. Chernikov 
14639490a627SAlexander V. Chernikov 			no->refcnt--;
14649490a627SAlexander V. Chernikov 		}
14659490a627SAlexander V. Chernikov 	}
14669490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1467b074b7bbSAlexander V. Chernikov 
14689490a627SAlexander V. Chernikov 	*oib = pidx;
14699490a627SAlexander V. Chernikov 
14709490a627SAlexander V. Chernikov 	return (error);
1471b074b7bbSAlexander V. Chernikov }
1472b074b7bbSAlexander V. Chernikov 
1473b074b7bbSAlexander V. Chernikov /*
1474b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
1475b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
1476b074b7bbSAlexander V. Chernikov  * Works for \d+ talbes only (e.g. for tables, converted
1477b074b7bbSAlexander V. Chernikov  * from old numbered system calls).
1478b074b7bbSAlexander V. Chernikov  *
1479b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1480b074b7bbSAlexander V. Chernikov  * Raises error on any other tables.
1481b074b7bbSAlexander V. Chernikov  */
1482b074b7bbSAlexander V. Chernikov int
1483b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
1484b074b7bbSAlexander V. Chernikov {
1485b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1486b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1487b074b7bbSAlexander V. Chernikov 	uint32_t set;
1488b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1489b074b7bbSAlexander V. Chernikov 	uint8_t type;
1490b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1491b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1492b074b7bbSAlexander V. Chernikov 
1493b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1494b074b7bbSAlexander V. Chernikov 
1495b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1496b074b7bbSAlexander V. Chernikov 
1497b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1498b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1499b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1500b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1501b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1502b074b7bbSAlexander V. Chernikov 
1503b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1504b074b7bbSAlexander V. Chernikov 			continue;
1505b074b7bbSAlexander V. Chernikov 
1506b074b7bbSAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_idx(ni, set, kidx)) == NULL)
1507b074b7bbSAlexander V. Chernikov 			return (1);
1508b074b7bbSAlexander V. Chernikov 
1509b074b7bbSAlexander V. Chernikov 		if (no->compat == 0)
1510b074b7bbSAlexander V. Chernikov 			return (2);
1511b074b7bbSAlexander V. Chernikov 
1512b074b7bbSAlexander V. Chernikov 		update_table_opcode(cmd, no->uidx);
1513b074b7bbSAlexander V. Chernikov 	}
1514b074b7bbSAlexander V. Chernikov 
1515b074b7bbSAlexander V. Chernikov 	return (0);
1516b074b7bbSAlexander V. Chernikov }
1517b074b7bbSAlexander V. Chernikov 
1518b074b7bbSAlexander V. Chernikov 
1519b074b7bbSAlexander V. Chernikov /*
1520b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
1521b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
1522b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
1523b074b7bbSAlexander V. Chernikov  *
1524b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
1525b074b7bbSAlexander V. Chernikov  */
1526b074b7bbSAlexander V. Chernikov int
1527b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
1528b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
1529b074b7bbSAlexander V. Chernikov {
1530b074b7bbSAlexander V. Chernikov 	int cmdlen, error, ftype, l;
1531b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1532b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
1533b074b7bbSAlexander V. Chernikov 	uint8_t type;
1534b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
15359f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
1536b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1537b074b7bbSAlexander V. Chernikov 	struct named_object *no, *no_n, *no_tmp;
15389490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
1539b074b7bbSAlexander V. Chernikov 	struct namedobjects_head nh;
1540b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
1541b074b7bbSAlexander V. Chernikov 
1542b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1543b074b7bbSAlexander V. Chernikov 
15449490a627SAlexander V. Chernikov 	/* Prepare queue to store configs */
15459490a627SAlexander V. Chernikov 	TAILQ_INIT(&nh);
15469490a627SAlexander V. Chernikov 
1547b074b7bbSAlexander V. Chernikov 	/*
1548b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
1549b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
1550b074b7bbSAlexander V. Chernikov 	 */
1551b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
1552b074b7bbSAlexander V. Chernikov 		/* Stack */
15539490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
1554b074b7bbSAlexander V. Chernikov 	} else
15559490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
1556b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1557b074b7bbSAlexander V. Chernikov 
15589490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
1559b074b7bbSAlexander V. Chernikov 	error = 0;
1560b074b7bbSAlexander V. Chernikov 
1561b074b7bbSAlexander V. Chernikov 	type = 0;
1562b074b7bbSAlexander V. Chernikov 	ftype = 0;
1563b074b7bbSAlexander V. Chernikov 
1564b074b7bbSAlexander V. Chernikov 	ci->tableset = TABLE_SET(ci->krule->set);
1565b074b7bbSAlexander V. Chernikov 
1566b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
1567b074b7bbSAlexander V. Chernikov 	ti.set = ci->tableset;
1568b074b7bbSAlexander V. Chernikov 	ti.tlvs = ci->tlvs;
1569b074b7bbSAlexander V. Chernikov 	ti.tlen = ci->tlen;
1570b074b7bbSAlexander V. Chernikov 
1571b074b7bbSAlexander V. Chernikov 	/*
15729490a627SAlexander V. Chernikov 	 * Stage 1: reference existing tables, determine number
15739490a627SAlexander V. Chernikov 	 * of tables we need to allocate and allocate indexes for each.
1574b074b7bbSAlexander V. Chernikov 	 */
15759490a627SAlexander V. Chernikov 	error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti);
1576b074b7bbSAlexander V. Chernikov 
1577b074b7bbSAlexander V. Chernikov 	if (error != 0) {
15789490a627SAlexander V. Chernikov 		if (pidx_first != ci->obuf)
15799490a627SAlexander V. Chernikov 			free(pidx_first, M_IPFW);
1580b074b7bbSAlexander V. Chernikov 
1581b074b7bbSAlexander V. Chernikov 		return (error);
1582b074b7bbSAlexander V. Chernikov 	}
1583b074b7bbSAlexander V. Chernikov 
1584b074b7bbSAlexander V. Chernikov 	/*
1585b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate table configs for every non-existent table
1586b074b7bbSAlexander V. Chernikov 	 */
1587b074b7bbSAlexander V. Chernikov 
15889f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
15899490a627SAlexander V. Chernikov 		for (p = pidx_first; p < pidx_last; p++) {
1590b074b7bbSAlexander V. Chernikov 			if (p->new == 0)
1591b074b7bbSAlexander V. Chernikov 				continue;
1592b074b7bbSAlexander V. Chernikov 
1593b074b7bbSAlexander V. Chernikov 			/* TODO: get name from TLV */
1594b074b7bbSAlexander V. Chernikov 			ti.uidx = p->uidx;
1595b074b7bbSAlexander V. Chernikov 			ti.type = p->type;
15969f7d47b0SAlexander V. Chernikov 			ti.atype = 0;
1597b074b7bbSAlexander V. Chernikov 
15989490a627SAlexander V. Chernikov 			ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL);
15999f7d47b0SAlexander V. Chernikov 			if (ta == NULL) {
16009f7d47b0SAlexander V. Chernikov 				error = ENOTSUP;
16019f7d47b0SAlexander V. Chernikov 				goto free;
16029f7d47b0SAlexander V. Chernikov 			}
16039490a627SAlexander V. Chernikov 			tc = alloc_table_config(ni, &ti, ta, NULL);
1604b074b7bbSAlexander V. Chernikov 
1605b074b7bbSAlexander V. Chernikov 			if (tc == NULL) {
1606b074b7bbSAlexander V. Chernikov 				error = ENOMEM;
1607b074b7bbSAlexander V. Chernikov 				goto free;
1608b074b7bbSAlexander V. Chernikov 			}
1609b074b7bbSAlexander V. Chernikov 
1610b074b7bbSAlexander V. Chernikov 			tc->no.kidx = p->kidx;
1611b074b7bbSAlexander V. Chernikov 			tc->no.refcnt = 1;
1612b074b7bbSAlexander V. Chernikov 
1613b074b7bbSAlexander V. Chernikov 			/* Add to list */
1614b074b7bbSAlexander V. Chernikov 			TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next);
1615b074b7bbSAlexander V. Chernikov 		}
1616b074b7bbSAlexander V. Chernikov 
1617b074b7bbSAlexander V. Chernikov 		/*
1618b074b7bbSAlexander V. Chernikov 		 * Stage 2.1: Check if we're going to create 2 tables
1619b074b7bbSAlexander V. Chernikov 		 * with the same name, but different table types.
1620b074b7bbSAlexander V. Chernikov 		 */
1621b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH(no, &nh, nn_next) {
1622b074b7bbSAlexander V. Chernikov 			TAILQ_FOREACH(no_tmp, &nh, nn_next) {
16239490a627SAlexander V. Chernikov 				if (ipfw_objhash_same_name(ni, no, no_tmp) == 0)
1624b074b7bbSAlexander V. Chernikov 					continue;
1625b074b7bbSAlexander V. Chernikov 				if (no->type != no_tmp->type) {
1626b074b7bbSAlexander V. Chernikov 					error = EINVAL;
1627b074b7bbSAlexander V. Chernikov 					goto free;
1628b074b7bbSAlexander V. Chernikov 				}
1629b074b7bbSAlexander V. Chernikov 			}
1630b074b7bbSAlexander V. Chernikov 		}
16319f7d47b0SAlexander V. Chernikov 	}
1632b074b7bbSAlexander V. Chernikov 
16339f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
16349f7d47b0SAlexander V. Chernikov 
16359f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
1636b074b7bbSAlexander V. Chernikov 		/*
1637b074b7bbSAlexander V. Chernikov 		 * Stage 3: link & reference new table configs
1638b074b7bbSAlexander V. Chernikov 		 */
1639b074b7bbSAlexander V. Chernikov 
1640b074b7bbSAlexander V. Chernikov 
1641b074b7bbSAlexander V. Chernikov 		/*
1642b074b7bbSAlexander V. Chernikov 		 * Step 3.1: Check if some tables we need to create have been
1643b074b7bbSAlexander V. Chernikov 		 * already created with different table type.
1644b074b7bbSAlexander V. Chernikov 		 */
1645b074b7bbSAlexander V. Chernikov 
1646b074b7bbSAlexander V. Chernikov 		error = 0;
1647b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1648b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1649b074b7bbSAlexander V. Chernikov 			if (no_n == NULL)
1650b074b7bbSAlexander V. Chernikov 				continue;
1651b074b7bbSAlexander V. Chernikov 
1652b074b7bbSAlexander V. Chernikov 			if (no_n->type != no->type) {
1653b074b7bbSAlexander V. Chernikov 				error = EINVAL;
1654b074b7bbSAlexander V. Chernikov 				break;
1655b074b7bbSAlexander V. Chernikov 			}
1656b074b7bbSAlexander V. Chernikov 
1657b074b7bbSAlexander V. Chernikov 		}
1658b074b7bbSAlexander V. Chernikov 
1659b074b7bbSAlexander V. Chernikov 		if (error != 0) {
1660b074b7bbSAlexander V. Chernikov 			/*
1661b074b7bbSAlexander V. Chernikov 			 * Someone has allocated table with different table type.
1662b074b7bbSAlexander V. Chernikov 			 * We have to rollback everything.
1663b074b7bbSAlexander V. Chernikov 			 */
1664b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(chain);
1665b074b7bbSAlexander V. Chernikov 			goto free;
1666b074b7bbSAlexander V. Chernikov 		}
1667b074b7bbSAlexander V. Chernikov 
1668b074b7bbSAlexander V. Chernikov 
1669b074b7bbSAlexander V. Chernikov 		/*
16709f7d47b0SAlexander V. Chernikov 		 * Attach new tables.
16719f7d47b0SAlexander V. Chernikov 		 * We need to set table pointers for each new table,
1672b074b7bbSAlexander V. Chernikov 		 * so we have to acquire main WLOCK.
1673b074b7bbSAlexander V. Chernikov 		 */
1674b074b7bbSAlexander V. Chernikov 		IPFW_WLOCK(chain);
1675b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1676b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1677b074b7bbSAlexander V. Chernikov 
16789490a627SAlexander V. Chernikov 			if (no_n == NULL) {
16799490a627SAlexander V. Chernikov 				/* New table. Attach to runtime hash */
16809490a627SAlexander V. Chernikov 				TAILQ_REMOVE(&nh, no, nn_next);
16819490a627SAlexander V. Chernikov 				link_table(chain, (struct table_config *)no);
1682b074b7bbSAlexander V. Chernikov 				continue;
1683b074b7bbSAlexander V. Chernikov 			}
1684b074b7bbSAlexander V. Chernikov 
16859490a627SAlexander V. Chernikov 			/*
16869490a627SAlexander V. Chernikov 			 * Newly-allocated table with the same type.
16879490a627SAlexander V. Chernikov 			 * Reference it and update out @pidx array
16889490a627SAlexander V. Chernikov 			 * rewrite info.
16899490a627SAlexander V. Chernikov 			 */
16909490a627SAlexander V. Chernikov 			no_n->refcnt++;
16919490a627SAlexander V. Chernikov 			/* Keep oib array in sync: update kidx */
16929490a627SAlexander V. Chernikov 			for (p = pidx_first; p < pidx_last; p++) {
16939490a627SAlexander V. Chernikov 				if (p->kidx != no->kidx)
16949490a627SAlexander V. Chernikov 					continue;
16959490a627SAlexander V. Chernikov 				/* Update kidx */
16969490a627SAlexander V. Chernikov 				p->kidx = no_n->kidx;
16979490a627SAlexander V. Chernikov 				break;
16989490a627SAlexander V. Chernikov 			}
1699b074b7bbSAlexander V. Chernikov 		}
1700b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(chain);
17019f7d47b0SAlexander V. Chernikov 	}
1702b074b7bbSAlexander V. Chernikov 
1703b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
1704b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
1705b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
1706b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
17079490a627SAlexander V. Chernikov 	p = pidx_first;
1708b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1709b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1710b074b7bbSAlexander V. Chernikov 
1711b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
1712b074b7bbSAlexander V. Chernikov 			continue;
17139490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
17149490a627SAlexander V. Chernikov 		p++;
1715b074b7bbSAlexander V. Chernikov 	}
1716b074b7bbSAlexander V. Chernikov 
1717b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
1718b074b7bbSAlexander V. Chernikov 
1719b074b7bbSAlexander V. Chernikov 	error = 0;
1720b074b7bbSAlexander V. Chernikov 
1721b074b7bbSAlexander V. Chernikov 	/*
1722b074b7bbSAlexander V. Chernikov 	 * Stage 4: free resources
1723b074b7bbSAlexander V. Chernikov 	 */
1724b074b7bbSAlexander V. Chernikov free:
17259490a627SAlexander V. Chernikov 	if (!TAILQ_EMPTY(&nh)) {
17269490a627SAlexander V. Chernikov 		/* Free indexes first */
17279490a627SAlexander V. Chernikov 		IPFW_UH_WLOCK(chain);
17289490a627SAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
17299490a627SAlexander V. Chernikov 			ipfw_objhash_free_idx(ni, ci->tableset, no->kidx);
17309490a627SAlexander V. Chernikov 		}
17319490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
17329490a627SAlexander V. Chernikov 		/* Free configs */
1733b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp)
1734b074b7bbSAlexander V. Chernikov 			free_table_config(ni, tc);
17359490a627SAlexander V. Chernikov 	}
1736b074b7bbSAlexander V. Chernikov 
17379490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
17389490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
1739b074b7bbSAlexander V. Chernikov 
1740b074b7bbSAlexander V. Chernikov 	return (error);
1741b074b7bbSAlexander V. Chernikov }
1742b074b7bbSAlexander V. Chernikov 
1743b074b7bbSAlexander V. Chernikov /*
1744b074b7bbSAlexander V. Chernikov  * Remove references from every table used in @rule.
1745b074b7bbSAlexander V. Chernikov  */
1746b074b7bbSAlexander V. Chernikov void
1747b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
1748b074b7bbSAlexander V. Chernikov {
1749b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1750b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1751b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1752b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1753b074b7bbSAlexander V. Chernikov 	uint32_t set;
1754b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1755b074b7bbSAlexander V. Chernikov 	uint8_t type;
1756b074b7bbSAlexander V. Chernikov 
1757b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1758b074b7bbSAlexander V. Chernikov 
1759b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1760b074b7bbSAlexander V. Chernikov 
1761b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1762b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1763b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1764b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1765b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1766b074b7bbSAlexander V. Chernikov 
1767b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1768b074b7bbSAlexander V. Chernikov 			continue;
1769b074b7bbSAlexander V. Chernikov 
1770b074b7bbSAlexander V. Chernikov 		no = ipfw_objhash_lookup_idx(ni, set, kidx);
1771b074b7bbSAlexander V. Chernikov 
1772b074b7bbSAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
1773b074b7bbSAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
1774b074b7bbSAlexander V. Chernikov 		    no->type, type, kidx));
1775b074b7bbSAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
1776b074b7bbSAlexander V. Chernikov 		    kidx, no->refcnt));
1777b074b7bbSAlexander V. Chernikov 
1778b074b7bbSAlexander V. Chernikov 		no->refcnt--;
1779b074b7bbSAlexander V. Chernikov 	}
1780b074b7bbSAlexander V. Chernikov }
1781b074b7bbSAlexander V. Chernikov 
1782b074b7bbSAlexander V. Chernikov 
1783b074b7bbSAlexander V. Chernikov /*
1784b074b7bbSAlexander V. Chernikov  * Removes table bindings for every rule in rule chain @head.
1785b074b7bbSAlexander V. Chernikov  */
1786b074b7bbSAlexander V. Chernikov void
1787b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head)
1788b074b7bbSAlexander V. Chernikov {
1789b074b7bbSAlexander V. Chernikov 	struct ip_fw *rule;
1790b074b7bbSAlexander V. Chernikov 
1791b074b7bbSAlexander V. Chernikov 	while ((rule = head) != NULL) {
1792b074b7bbSAlexander V. Chernikov 		head = head->x_next;
1793b074b7bbSAlexander V. Chernikov 		ipfw_unbind_table_rule(chain, rule);
1794b074b7bbSAlexander V. Chernikov 	}
1795b074b7bbSAlexander V. Chernikov }
1796b074b7bbSAlexander V. Chernikov 
1797b074b7bbSAlexander V. Chernikov 
17983b3a8eb9SGleb Smirnoff /* end of file */
1799