xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision 1832a7b3)
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 /*
30563b5ab1SAlexander V. Chernikov  * Lookup table support for ipfw.
313b3a8eb9SGleb Smirnoff  *
32ac35ff17SAlexander V. Chernikov  * This file contains handlers for all generic tables' operations:
33563b5ab1SAlexander V. Chernikov  * add/del/flush entries, list/dump tables etc..
343b3a8eb9SGleb Smirnoff  *
35563b5ab1SAlexander V. Chernikov  * Table data modification is protected by both UH and runtimg lock
36563b5ab1SAlexander V. Chernikov  * while reading configuration/data is protected by UH lock.
37563b5ab1SAlexander V. Chernikov  *
38563b5ab1SAlexander V. Chernikov  * Lookup algorithms for all table types are located in ip_fw_table_algo.c
393b3a8eb9SGleb Smirnoff  */
403b3a8eb9SGleb Smirnoff 
413b3a8eb9SGleb Smirnoff #include "opt_ipfw.h"
423b3a8eb9SGleb Smirnoff 
433b3a8eb9SGleb Smirnoff #include <sys/param.h>
443b3a8eb9SGleb Smirnoff #include <sys/systm.h>
453b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
463b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
473b3a8eb9SGleb Smirnoff #include <sys/lock.h>
483b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
493b3a8eb9SGleb Smirnoff #include <sys/socket.h>
50f1220db8SAlexander V. Chernikov #include <sys/socketvar.h>
513b3a8eb9SGleb Smirnoff #include <sys/queue.h>
523b3a8eb9SGleb Smirnoff #include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
533b3a8eb9SGleb Smirnoff #include <net/route.h>
543b3a8eb9SGleb Smirnoff #include <net/vnet.h>
553b3a8eb9SGleb Smirnoff 
563b3a8eb9SGleb Smirnoff #include <netinet/in.h>
573b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
583b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
593b3a8eb9SGleb Smirnoff 
603b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
61ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
623b3a8eb9SGleb Smirnoff 
633b3a8eb9SGleb Smirnoff 
643b3a8eb9SGleb Smirnoff  /*
65b074b7bbSAlexander V. Chernikov  * Table has the following `type` concepts:
66b074b7bbSAlexander V. Chernikov  *
679f7d47b0SAlexander V. Chernikov  * `no.type` represents lookup key type (cidr, ifp, uid, etc..)
689f7d47b0SAlexander V. Chernikov  * `ta->atype` represents exact lookup algorithm.
69b074b7bbSAlexander V. Chernikov  *     For example, we can use more efficient search schemes if we plan
70b074b7bbSAlexander V. Chernikov  *     to use some specific table for storing host-routes only.
719490a627SAlexander V. Chernikov  * `ftype` (at the moment )is pure userland field helping to properly
729490a627SAlexander V. Chernikov  *     format value data e.g. "value is IPv4 nexthop" or "value is DSCP"
739490a627SAlexander V. Chernikov  *     or "value is port".
74b074b7bbSAlexander V. Chernikov  *
75b074b7bbSAlexander V. Chernikov  */
76b074b7bbSAlexander V. Chernikov struct table_config {
77b074b7bbSAlexander V. Chernikov 	struct named_object	no;
78ac35ff17SAlexander V. Chernikov 	uint8_t		vtype;		/* format table type */
79b074b7bbSAlexander V. Chernikov 	uint8_t		linked;		/* 1 if already linked */
809f7d47b0SAlexander V. Chernikov 	uint16_t	spare0;
81b074b7bbSAlexander V. Chernikov 	uint32_t	count;		/* Number of records */
82b074b7bbSAlexander V. Chernikov 	char		tablename[64];	/* table name */
839f7d47b0SAlexander V. Chernikov 	struct table_algo	*ta;	/* Callbacks for given algo */
849f7d47b0SAlexander V. Chernikov 	void		*astate;	/* algorithm state */
859f7d47b0SAlexander V. Chernikov 	struct table_info	ti;	/* data to put to table_info */
86b074b7bbSAlexander V. Chernikov };
87b074b7bbSAlexander V. Chernikov #define	TABLE_SET(set)	((V_fw_tables_sets != 0) ? set : 0)
88b074b7bbSAlexander V. Chernikov 
89b074b7bbSAlexander V. Chernikov struct tables_config {
90b074b7bbSAlexander V. Chernikov 	struct namedobj_instance	*namehash;
919f7d47b0SAlexander V. Chernikov 	int				algo_count;
929f7d47b0SAlexander V. Chernikov 	struct table_algo 		*algo[256];
93b074b7bbSAlexander V. Chernikov };
94b074b7bbSAlexander V. Chernikov 
95b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni,
96b074b7bbSAlexander V. Chernikov     struct tid_info *ti);
97b074b7bbSAlexander V. Chernikov static struct table_config *alloc_table_config(struct namedobj_instance *ni,
989490a627SAlexander V. Chernikov     struct tid_info *ti, struct table_algo *ta, char *adata);
99b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni,
100b074b7bbSAlexander V. Chernikov     struct table_config *tc);
101b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
102b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
103b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type);
1042d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
1052d99a349SAlexander V. Chernikov     struct sockopt_data *sd);
106ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
107ac35ff17SAlexander V. Chernikov     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);
112ac35ff17SAlexander V. Chernikov static int ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
113ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
114ac35ff17SAlexander V. Chernikov static int ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
115ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
116ac35ff17SAlexander V. Chernikov 
117ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
118d3a4f924SAlexander V. Chernikov 
1199f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1209490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
121b074b7bbSAlexander V. Chernikov 
122b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
123b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1249f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
125b074b7bbSAlexander V. Chernikov 
126b074b7bbSAlexander V. Chernikov 
1273b3a8eb9SGleb Smirnoff 
1283b3a8eb9SGleb Smirnoff int
1291832a7b3SAlexander V. Chernikov add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
130b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
1313b3a8eb9SGleb Smirnoff {
132b074b7bbSAlexander V. Chernikov 	struct table_config *tc, *tc_new;
1339f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
134b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
135b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1369f7d47b0SAlexander V. Chernikov 	int error;
1379f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
1383b3a8eb9SGleb Smirnoff 
1399f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1409f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1419f7d47b0SAlexander V. Chernikov 
1429f7d47b0SAlexander V. Chernikov 	/*
1439f7d47b0SAlexander V. Chernikov 	 * Find and reference existing table.
1449f7d47b0SAlexander V. Chernikov 	 */
1459f7d47b0SAlexander V. Chernikov 	ta = NULL;
1469f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
1479f7d47b0SAlexander V. Chernikov 		/* check table type */
1489f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
1499f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1503b3a8eb9SGleb Smirnoff 			return (EINVAL);
1513b3a8eb9SGleb Smirnoff 		}
1523b3a8eb9SGleb Smirnoff 
1539f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
1549f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
1559f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
1569f7d47b0SAlexander V. Chernikov 	}
1579f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1583b3a8eb9SGleb Smirnoff 
1599f7d47b0SAlexander V. Chernikov 	tc_new = NULL;
160ac35ff17SAlexander V. Chernikov 	if (tc == NULL) {
1619f7d47b0SAlexander V. Chernikov 		/* Table not found. We have to create new one */
1629490a627SAlexander V. Chernikov 		if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL)
1639f7d47b0SAlexander V. Chernikov 			return (ENOTSUP);
1643b3a8eb9SGleb Smirnoff 
1659490a627SAlexander V. Chernikov 		tc_new = alloc_table_config(ni, ti, ta, NULL);
1669f7d47b0SAlexander V. Chernikov 		if (tc_new == NULL)
1679f7d47b0SAlexander V. Chernikov 			return (ENOMEM);
1689f7d47b0SAlexander V. Chernikov 	}
1693b3a8eb9SGleb Smirnoff 
1709f7d47b0SAlexander V. Chernikov 	/* Prepare record (allocate memory) */
1719f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
1729f7d47b0SAlexander V. Chernikov 	error = ta->prepare_add(tei, &ta_buf);
1739f7d47b0SAlexander V. Chernikov 	if (error != 0) {
1749f7d47b0SAlexander V. Chernikov 		if (tc_new != NULL)
1759f7d47b0SAlexander V. Chernikov 			free_table_config(ni, tc_new);
1769f7d47b0SAlexander V. Chernikov 		return (error);
1773b3a8eb9SGleb Smirnoff 	}
1783b3a8eb9SGleb Smirnoff 
179b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1803b3a8eb9SGleb Smirnoff 
181b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1823b3a8eb9SGleb Smirnoff 
1839f7d47b0SAlexander V. Chernikov 	if (tc == NULL) {
184ac35ff17SAlexander V. Chernikov 		/* Check if another table has been allocated by other thread */
185b074b7bbSAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
1869f7d47b0SAlexander V. Chernikov 
1879f7d47b0SAlexander V. Chernikov 			/*
1889f7d47b0SAlexander V. Chernikov 			 * Check if algoritm is the same since we've
1899f7d47b0SAlexander V. Chernikov 			 * already allocated state using @ta algoritm
1909f7d47b0SAlexander V. Chernikov 			 * callbacks.
1919f7d47b0SAlexander V. Chernikov 			 */
1929f7d47b0SAlexander V. Chernikov 			if (tc->ta != ta) {
193b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
194ac35ff17SAlexander V. Chernikov 				error = EINVAL;
195ac35ff17SAlexander V. Chernikov 				goto done;
1963b3a8eb9SGleb Smirnoff 			}
1973b3a8eb9SGleb Smirnoff 		} else {
198ac35ff17SAlexander V. Chernikov 			/* Table still does not exists */
199b074b7bbSAlexander V. Chernikov 
200b074b7bbSAlexander V. Chernikov 			/* Allocate table index. */
201ac35ff17SAlexander V. Chernikov 			if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
202b074b7bbSAlexander V. Chernikov 				/* Index full. */
203b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
204ac35ff17SAlexander V. Chernikov 				printf("Unable to allocate index for table %s"
205ac35ff17SAlexander V. Chernikov 				    "in set %u. Consider increasing "
206b074b7bbSAlexander V. Chernikov 				    "net.inet.ip.fw.tables_max",
207ac35ff17SAlexander V. Chernikov 				    tc_new->no.name, ti->set);
208ac35ff17SAlexander V. Chernikov 				error = EBUSY;
209ac35ff17SAlexander V. Chernikov 				goto done;
2103b3a8eb9SGleb Smirnoff 			}
211ac35ff17SAlexander V. Chernikov 
212ac35ff17SAlexander V. Chernikov 			/* Set tc_new to zero not to free it afterwards. */
213ac35ff17SAlexander V. Chernikov 			tc = tc_new;
214ac35ff17SAlexander V. Chernikov 			tc_new = NULL;
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 
229ac35ff17SAlexander V. Chernikov 	if (tc->linked == 0)
230b074b7bbSAlexander V. Chernikov 		link_table(ch, tc);
231b074b7bbSAlexander V. Chernikov 
2329f7d47b0SAlexander V. Chernikov 	error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2333b3a8eb9SGleb Smirnoff 
2343b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2359f7d47b0SAlexander V. Chernikov 
236ac35ff17SAlexander V. Chernikov 	/* Update number of records. */
237ac35ff17SAlexander V. Chernikov 	if (error == 0 && (tei->flags & TEI_FLAGS_UPDATED) == 0)
2389f7d47b0SAlexander V. Chernikov 		tc->count++;
2399f7d47b0SAlexander V. Chernikov 
240b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
241b074b7bbSAlexander V. Chernikov 
242ac35ff17SAlexander V. Chernikov done:
243b074b7bbSAlexander V. Chernikov 	if (tc_new != NULL)
244ac35ff17SAlexander V. Chernikov 		free_table_config(ni, tc_new);
245ac35ff17SAlexander V. Chernikov 	/* Run cleaning callback anyway */
2469f7d47b0SAlexander V. Chernikov 	ta->flush_entry(tei, &ta_buf);
247b074b7bbSAlexander V. Chernikov 
2489f7d47b0SAlexander V. Chernikov 	return (error);
2493b3a8eb9SGleb Smirnoff }
2503b3a8eb9SGleb Smirnoff 
2513b3a8eb9SGleb Smirnoff int
2521832a7b3SAlexander V. Chernikov 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 	ta->flush_entry(tei, &ta_buf);
294ac35ff17SAlexander V. Chernikov 
295ac35ff17SAlexander V. Chernikov 	return (error);
296ac35ff17SAlexander V. Chernikov }
297ac35ff17SAlexander V. Chernikov 
298ac35ff17SAlexander V. Chernikov int
299ac35ff17SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
300ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
301ac35ff17SAlexander V. Chernikov {
302ac35ff17SAlexander V. Chernikov 	int error;
303ac35ff17SAlexander V. Chernikov 
304ac35ff17SAlexander V. Chernikov 	switch (op3->version) {
305ac35ff17SAlexander V. Chernikov 	case 0:
306ac35ff17SAlexander V. Chernikov 		error = ipfw_modify_table_v0(ch, op3, sd);
307ac35ff17SAlexander V. Chernikov 		break;
308ac35ff17SAlexander V. Chernikov 	case 1:
309ac35ff17SAlexander V. Chernikov 		error = ipfw_modify_table_v1(ch, op3, sd);
310ac35ff17SAlexander V. Chernikov 		break;
311ac35ff17SAlexander V. Chernikov 	default:
312ac35ff17SAlexander V. Chernikov 		error = ENOTSUP;
313ac35ff17SAlexander V. Chernikov 	}
314ac35ff17SAlexander V. Chernikov 
315ac35ff17SAlexander V. Chernikov 	return (error);
316ac35ff17SAlexander V. Chernikov }
317ac35ff17SAlexander V. Chernikov 
318ac35ff17SAlexander V. Chernikov /*
319ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
320ac35ff17SAlexander V. Chernikov  * Data layout (v0):
321ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ipfw_table_xentry ]
322ac35ff17SAlexander V. Chernikov  *
323ac35ff17SAlexander V. Chernikov  * Returns 0 on success
324ac35ff17SAlexander V. Chernikov  */
325ac35ff17SAlexander V. Chernikov static int
326ac35ff17SAlexander V. Chernikov ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
327ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
328ac35ff17SAlexander V. Chernikov {
329ac35ff17SAlexander V. Chernikov 	ipfw_table_xentry *xent;
330ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
331ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
332ac35ff17SAlexander V. Chernikov 	int error, hdrlen, read;
333ac35ff17SAlexander V. Chernikov 
334ac35ff17SAlexander V. Chernikov 	hdrlen = offsetof(ipfw_table_xentry, k);
335ac35ff17SAlexander V. Chernikov 
336ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
337ac35ff17SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*op3) + hdrlen))
338ac35ff17SAlexander V. Chernikov 		return (EINVAL);
339ac35ff17SAlexander V. Chernikov 
340ac35ff17SAlexander V. Chernikov 	read = sizeof(ip_fw3_opheader);
341ac35ff17SAlexander V. Chernikov 
342ac35ff17SAlexander V. Chernikov 	/* Check if xentry len field is valid */
343ac35ff17SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)(op3 + 1);
344ac35ff17SAlexander V. Chernikov 	if (xent->len < hdrlen || xent->len + read > sd->valsize)
345ac35ff17SAlexander V. Chernikov 		return (EINVAL);
346ac35ff17SAlexander V. Chernikov 
347ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
348ac35ff17SAlexander V. Chernikov 	tei.paddr = &xent->k;
349ac35ff17SAlexander V. Chernikov 	tei.masklen = xent->masklen;
350ac35ff17SAlexander V. Chernikov 	tei.value = xent->value;
351ac35ff17SAlexander V. Chernikov 	/* Old requests compability */
352ac35ff17SAlexander V. Chernikov 	if (xent->type == IPFW_TABLE_CIDR) {
353ac35ff17SAlexander V. Chernikov 		if (xent->len - hdrlen == sizeof(in_addr_t))
354ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET;
355ac35ff17SAlexander V. Chernikov 		else
356ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET6;
357ac35ff17SAlexander V. Chernikov 	}
358ac35ff17SAlexander V. Chernikov 
359ac35ff17SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
360ac35ff17SAlexander V. Chernikov 	ti.uidx = xent->tbl;
361ac35ff17SAlexander V. Chernikov 	ti.type = xent->type;
362ac35ff17SAlexander V. Chernikov 
363ac35ff17SAlexander V. Chernikov 	error = (op3->opcode == IP_FW_TABLE_XADD) ?
3641832a7b3SAlexander V. Chernikov 	    add_table_entry(ch, &ti, &tei) :
3651832a7b3SAlexander V. Chernikov 	    del_table_entry(ch, &ti, &tei);
366ac35ff17SAlexander V. Chernikov 
367ac35ff17SAlexander V. Chernikov 	return (error);
368ac35ff17SAlexander V. Chernikov }
369ac35ff17SAlexander V. Chernikov 
370ac35ff17SAlexander V. Chernikov /*
371ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
372ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
373ac35ff17SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_tentry ]
374ac35ff17SAlexander V. Chernikov  *
375ac35ff17SAlexander V. Chernikov  * Returns 0 on success
376ac35ff17SAlexander V. Chernikov  */
377ac35ff17SAlexander V. Chernikov static int
378ac35ff17SAlexander V. Chernikov ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
379ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
380ac35ff17SAlexander V. Chernikov {
381ac35ff17SAlexander V. Chernikov 	ipfw_obj_tentry *tent;
382ac35ff17SAlexander V. Chernikov 	ipfw_obj_header *oh;
383ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
384ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
385ac35ff17SAlexander V. Chernikov 	int error, read;
386ac35ff17SAlexander V. Chernikov 
387ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
388ac35ff17SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*tent)))
389ac35ff17SAlexander V. Chernikov 		return (EINVAL);
390ac35ff17SAlexander V. Chernikov 
391ac35ff17SAlexander V. Chernikov 	/* Check if passed data is too long */
392ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sd->kavail)
393ac35ff17SAlexander V. Chernikov 		return (EINVAL);
394ac35ff17SAlexander V. Chernikov 
395ac35ff17SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
396ac35ff17SAlexander V. Chernikov 
397ac35ff17SAlexander V. Chernikov 	/* Basic length checks for TLVs */
398ac35ff17SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
399ac35ff17SAlexander V. Chernikov 		return (EINVAL);
400ac35ff17SAlexander V. Chernikov 
401ac35ff17SAlexander V. Chernikov 	read = sizeof(*oh);
402ac35ff17SAlexander V. Chernikov 
403ac35ff17SAlexander V. Chernikov 	/* Assume tentry may grow to support larger keys */
404ac35ff17SAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(oh + 1);
405ac35ff17SAlexander V. Chernikov 	if (tent->head.length < sizeof(*tent) ||
406ac35ff17SAlexander V. Chernikov 	    tent->head.length + read > sd->valsize)
407ac35ff17SAlexander V. Chernikov 		return (EINVAL);
408ac35ff17SAlexander V. Chernikov 
409ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
410ac35ff17SAlexander V. Chernikov 	tei.paddr = &tent->k;
411ac35ff17SAlexander V. Chernikov 	tei.subtype = tent->subtype;
412ac35ff17SAlexander V. Chernikov 	tei.masklen = tent->masklen;
413ac35ff17SAlexander V. Chernikov 	if (tent->flags & IPFW_TF_UPDATE)
414ac35ff17SAlexander V. Chernikov 		tei.flags |= TEI_FLAGS_UPDATE;
415ac35ff17SAlexander V. Chernikov 	tei.value = tent->value;
416ac35ff17SAlexander V. Chernikov 
417ac35ff17SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
418ac35ff17SAlexander V. Chernikov 	ti.uidx = tent->idx;
419ac35ff17SAlexander V. Chernikov 	ti.type = oh->ntlv.type;
420ac35ff17SAlexander V. Chernikov 	ti.tlvs = &oh->ntlv;
421ac35ff17SAlexander V. Chernikov 	ti.tlen = oh->ntlv.head.length;
422ac35ff17SAlexander V. Chernikov 
423ac35ff17SAlexander V. Chernikov 	error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
4241832a7b3SAlexander V. Chernikov 	    add_table_entry(ch, &ti, &tei) :
4251832a7b3SAlexander V. Chernikov 	    del_table_entry(ch, &ti, &tei);
426ac35ff17SAlexander V. Chernikov 
427ac35ff17SAlexander V. Chernikov 	return (error);
428ac35ff17SAlexander V. Chernikov }
429ac35ff17SAlexander V. Chernikov 
430ac35ff17SAlexander V. Chernikov int
431ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
432ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
433ac35ff17SAlexander V. Chernikov {
434ac35ff17SAlexander V. Chernikov 	int error;
435ac35ff17SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
436ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
437ac35ff17SAlexander V. Chernikov 
438ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh))
439ac35ff17SAlexander V. Chernikov 		return (EINVAL);
440ac35ff17SAlexander V. Chernikov 
441ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
442ac35ff17SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
443ac35ff17SAlexander V. Chernikov 
4441832a7b3SAlexander V. Chernikov 	if (op3->opcode == IP_FW_TABLE_XDESTROY)
445ac35ff17SAlexander V. Chernikov 		error = destroy_table(ch, &ti);
4461832a7b3SAlexander V. Chernikov 	else if (op3->opcode == IP_FW_TABLE_XFLUSH)
447ac35ff17SAlexander V. Chernikov 		error = flush_table(ch, &ti);
448ac35ff17SAlexander V. Chernikov 	else
449ac35ff17SAlexander V. Chernikov 		return (ENOTSUP);
450ac35ff17SAlexander V. Chernikov 
451ac35ff17SAlexander V. Chernikov 	return (error);
4523b3a8eb9SGleb Smirnoff }
4533b3a8eb9SGleb Smirnoff 
454b074b7bbSAlexander V. Chernikov /*
4559f7d47b0SAlexander V. Chernikov  * Flushes all entries in given table.
456ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
457ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ]
458ac35ff17SAlexander V. Chernikov  *
459ac35ff17SAlexander V. Chernikov  * Returns 0 on success
460b074b7bbSAlexander V. Chernikov  */
4611832a7b3SAlexander V. Chernikov int
462ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
4633b3a8eb9SGleb Smirnoff {
464b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
465b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
4669f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
4679f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
4689f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
469b074b7bbSAlexander V. Chernikov 	int error;
470b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
4713b3a8eb9SGleb Smirnoff 
4723b3a8eb9SGleb Smirnoff 	/*
4739f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
474b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
4753b3a8eb9SGleb Smirnoff 	 */
476b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
477b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
478b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
479b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
480b074b7bbSAlexander V. Chernikov 		return (ESRCH);
481b074b7bbSAlexander V. Chernikov 	}
4829f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
483b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
484b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
4853b3a8eb9SGleb Smirnoff 
486b074b7bbSAlexander V. Chernikov 	/*
4879f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
4889490a627SAlexander V. Chernikov 	 * TODO: pass startup parametes somehow.
489b074b7bbSAlexander V. Chernikov 	 */
4909f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
4919490a627SAlexander V. Chernikov 	if ((error = ta->init(&astate_new, &ti_new, NULL)) != 0) {
492b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
493b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
494b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
495b074b7bbSAlexander V. Chernikov 		return (error);
496b074b7bbSAlexander V. Chernikov 	}
497b074b7bbSAlexander V. Chernikov 
498b074b7bbSAlexander V. Chernikov 	/*
499b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
500b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
501b074b7bbSAlexander V. Chernikov 	 */
502b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
503b074b7bbSAlexander V. Chernikov 
504b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
505b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
5069f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
507b074b7bbSAlexander V. Chernikov 
5089f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
5099f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
5109f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
5119f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
512b074b7bbSAlexander V. Chernikov 
5139f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
5149f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
5159f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
5169f7d47b0SAlexander V. Chernikov 	tc->count = 0;
517b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
518b074b7bbSAlexander V. Chernikov 
519b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5203b3a8eb9SGleb Smirnoff 
521b074b7bbSAlexander V. Chernikov 	/*
522b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
523b074b7bbSAlexander V. Chernikov 	 */
5249f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
5253b3a8eb9SGleb Smirnoff 
5263b3a8eb9SGleb Smirnoff 	return (0);
5273b3a8eb9SGleb Smirnoff }
5283b3a8eb9SGleb Smirnoff 
529b074b7bbSAlexander V. Chernikov /*
5309f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
531ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
532ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ]
533ac35ff17SAlexander V. Chernikov  *
534ac35ff17SAlexander V. Chernikov  * Returns 0 on success
535b074b7bbSAlexander V. Chernikov  */
536ac35ff17SAlexander V. Chernikov static int
537ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
538b074b7bbSAlexander V. Chernikov {
539b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
540b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
541b074b7bbSAlexander V. Chernikov 
542b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
543b074b7bbSAlexander V. Chernikov 
544b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
545b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
546b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
547b074b7bbSAlexander V. Chernikov 		return (ESRCH);
548b074b7bbSAlexander V. Chernikov 	}
549b074b7bbSAlexander V. Chernikov 
5509f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
5519f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
552b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
553b074b7bbSAlexander V. Chernikov 		return (EBUSY);
554b074b7bbSAlexander V. Chernikov 	}
555b074b7bbSAlexander V. Chernikov 
556b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
557b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
558b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
559b074b7bbSAlexander V. Chernikov 
560b074b7bbSAlexander V. Chernikov 	/* Free obj index */
561ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0)
562b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
563b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
564b074b7bbSAlexander V. Chernikov 
565b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
566b074b7bbSAlexander V. Chernikov 
567b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
568b074b7bbSAlexander V. Chernikov 
569b074b7bbSAlexander V. Chernikov 	return (0);
570b074b7bbSAlexander V. Chernikov }
571b074b7bbSAlexander V. Chernikov 
572b074b7bbSAlexander V. Chernikov static void
573b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
574b074b7bbSAlexander V. Chernikov     void *arg)
575b074b7bbSAlexander V. Chernikov {
576b074b7bbSAlexander V. Chernikov 
577b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
578ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->kidx) != 0)
579b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
580b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
581b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
582b074b7bbSAlexander V. Chernikov }
583b074b7bbSAlexander V. Chernikov 
5843b3a8eb9SGleb Smirnoff void
5853b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
5863b3a8eb9SGleb Smirnoff {
5873b3a8eb9SGleb Smirnoff 
588b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
589b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
590b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
591b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
592b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
593b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5943b3a8eb9SGleb Smirnoff 
5953b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
5969f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
5979f7d47b0SAlexander V. Chernikov 
5989f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
599b074b7bbSAlexander V. Chernikov 
600b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
601b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
6023b3a8eb9SGleb Smirnoff }
6033b3a8eb9SGleb Smirnoff 
6043b3a8eb9SGleb Smirnoff int
6053b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
6063b3a8eb9SGleb Smirnoff {
607b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
608b074b7bbSAlexander V. Chernikov 
6093b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
6109f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
6119f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
612b074b7bbSAlexander V. Chernikov 
613b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
614b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
615b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
616b074b7bbSAlexander V. Chernikov 
6179f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
6189f7d47b0SAlexander V. Chernikov 
6193b3a8eb9SGleb Smirnoff 	return (0);
6203b3a8eb9SGleb Smirnoff }
6213b3a8eb9SGleb Smirnoff 
6223b3a8eb9SGleb Smirnoff int
6233b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
6243b3a8eb9SGleb Smirnoff {
6253b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
626b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
6279f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
628b074b7bbSAlexander V. Chernikov 	int new_blocks;
6293b3a8eb9SGleb Smirnoff 
6303b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
6313b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
6323b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
6333b3a8eb9SGleb Smirnoff 
6343b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
6359f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
6369f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
6379f7d47b0SAlexander V. Chernikov 
638b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
6393b3a8eb9SGleb Smirnoff 
6409f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
6413b3a8eb9SGleb Smirnoff 
6423b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
643b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
644b074b7bbSAlexander V. Chernikov 
6459f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
6469f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
6479f7d47b0SAlexander V. Chernikov 
6489f7d47b0SAlexander V. Chernikov 		/*
6499f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
6509f7d47b0SAlexander V. Chernikov 		 */
6519f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
652b074b7bbSAlexander V. Chernikov 		return (EINVAL);
653b074b7bbSAlexander V. Chernikov 	}
6543b3a8eb9SGleb Smirnoff 
6559f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
6569f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
6579f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
6583b3a8eb9SGleb Smirnoff 
6599f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
6609f7d47b0SAlexander V. Chernikov 
6619f7d47b0SAlexander V. Chernikov 	/* Change pointers */
6629f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
6639f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
6649f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
6653b3a8eb9SGleb Smirnoff 
6663b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
6673b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
6683b3a8eb9SGleb Smirnoff 
6693b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
6709f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
6713b3a8eb9SGleb Smirnoff 
6723b3a8eb9SGleb Smirnoff 	/* Free old pointers */
6739f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
674b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
6753b3a8eb9SGleb Smirnoff 
6763b3a8eb9SGleb Smirnoff 	return (0);
6773b3a8eb9SGleb Smirnoff }
6783b3a8eb9SGleb Smirnoff 
6793b3a8eb9SGleb Smirnoff int
6803b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
6813b3a8eb9SGleb Smirnoff     uint32_t *val)
6823b3a8eb9SGleb Smirnoff {
6839f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
6843b3a8eb9SGleb Smirnoff 
6859f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
6869f7d47b0SAlexander V. Chernikov 
6879f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
6883b3a8eb9SGleb Smirnoff }
6899f7d47b0SAlexander V. Chernikov 
6909f7d47b0SAlexander V. Chernikov int
6919f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
6929f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
6939f7d47b0SAlexander V. Chernikov {
6949f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
6959f7d47b0SAlexander V. Chernikov 
6969f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
6979f7d47b0SAlexander V. Chernikov 
6989f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
6999f7d47b0SAlexander V. Chernikov }
7009f7d47b0SAlexander V. Chernikov 
7019f7d47b0SAlexander V. Chernikov /*
7029f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
7039f7d47b0SAlexander V. Chernikov  *
7049f7d47b0SAlexander V. Chernikov  */
7059f7d47b0SAlexander V. Chernikov 
706f1220db8SAlexander V. Chernikov /*
707d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
708d3a4f924SAlexander V. Chernikov  */
709d3a4f924SAlexander V. Chernikov 
710d3a4f924SAlexander V. Chernikov /*
711d3a4f924SAlexander V. Chernikov  * Get buffer size needed to list info for all tables.
712ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
713d3a4f924SAlexander V. Chernikov  * Request: [ empty ], size = sizeof(ipfw_obj_lheader)
714d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ]
715d3a4f924SAlexander V. Chernikov  *
716d3a4f924SAlexander V. Chernikov  * Returns 0 on success
717f1220db8SAlexander V. Chernikov  */
718f1220db8SAlexander V. Chernikov int
7192d99a349SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
720f1220db8SAlexander V. Chernikov {
721f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
722f1220db8SAlexander V. Chernikov 
7232d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
7242d99a349SAlexander V. Chernikov 	if (olh == NULL)
725d3a4f924SAlexander V. Chernikov 		return (EINVAL);
726d3a4f924SAlexander V. Chernikov 
727f1220db8SAlexander V. Chernikov 	olh->size = sizeof(*olh); /* Make export_table store needed size */
728f1220db8SAlexander V. Chernikov 
729f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
7302d99a349SAlexander V. Chernikov 	export_tables(ch, olh, sd);
731f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
732f1220db8SAlexander V. Chernikov 
7332d99a349SAlexander V. Chernikov 	return (0);
734f1220db8SAlexander V. Chernikov }
735f1220db8SAlexander V. Chernikov 
736d3a4f924SAlexander V. Chernikov /*
737d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
738ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
739d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
740d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
741d3a4f924SAlexander V. Chernikov  *
742d3a4f924SAlexander V. Chernikov  * Returns 0 on success
743d3a4f924SAlexander V. Chernikov  */
744f1220db8SAlexander V. Chernikov int
7452d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
746f1220db8SAlexander V. Chernikov {
747f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
748d3a4f924SAlexander V. Chernikov 	uint32_t sz;
749f1220db8SAlexander V. Chernikov 	int error;
750f1220db8SAlexander V. Chernikov 
7512d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
7522d99a349SAlexander V. Chernikov 	if (olh == NULL)
753d3a4f924SAlexander V. Chernikov 		return (EINVAL);
754d3a4f924SAlexander V. Chernikov 
755f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
756f1220db8SAlexander V. Chernikov 	sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
7572d99a349SAlexander V. Chernikov 
7582d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
7592d99a349SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
7602d99a349SAlexander V. Chernikov 		return (ENOMEM);
7612d99a349SAlexander V. Chernikov 	}
7622d99a349SAlexander V. Chernikov 
7632d99a349SAlexander V. Chernikov 	error = export_tables(ch, olh, sd);
764f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
765f1220db8SAlexander V. Chernikov 
766f1220db8SAlexander V. Chernikov 	return (error);
767f1220db8SAlexander V. Chernikov }
768f1220db8SAlexander V. Chernikov 
769f1220db8SAlexander V. Chernikov /*
7702d99a349SAlexander V. Chernikov  * Store table info to buffer provided by @sd.
771ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
772d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
773d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
774d3a4f924SAlexander V. Chernikov  *
775d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
776d3a4f924SAlexander V. Chernikov  */
777d3a4f924SAlexander V. Chernikov int
7782d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
779d3a4f924SAlexander V. Chernikov {
780d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
781d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
782d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
783d3a4f924SAlexander V. Chernikov 	size_t sz;
784d3a4f924SAlexander V. Chernikov 
785d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
7862d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
7872d99a349SAlexander V. Chernikov 	if (oh == NULL)
788d3a4f924SAlexander V. Chernikov 		return (EINVAL);
789d3a4f924SAlexander V. Chernikov 
790d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
791d3a4f924SAlexander V. Chernikov 
792d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
793d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
794d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
795d3a4f924SAlexander V. Chernikov 		return (ESRCH);
796d3a4f924SAlexander V. Chernikov 	}
797d3a4f924SAlexander V. Chernikov 
798ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1));
799d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
800d3a4f924SAlexander V. Chernikov 
8012d99a349SAlexander V. Chernikov 	return (0);
802d3a4f924SAlexander V. Chernikov }
803d3a4f924SAlexander V. Chernikov 
804f1220db8SAlexander V. Chernikov struct dump_args {
805f1220db8SAlexander V. Chernikov 	struct table_info *ti;
806f1220db8SAlexander V. Chernikov 	struct table_config *tc;
8072d99a349SAlexander V. Chernikov 	struct sockopt_data *sd;
808f1220db8SAlexander V. Chernikov 	uint32_t cnt;
809f1220db8SAlexander V. Chernikov 	uint16_t uidx;
8102d99a349SAlexander V. Chernikov 	ipfw_table_entry *ent;
8112d99a349SAlexander V. Chernikov 	uint32_t size;
812f1220db8SAlexander V. Chernikov };
813f1220db8SAlexander V. Chernikov 
814f1220db8SAlexander V. Chernikov int
8152d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
8162d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
817f1220db8SAlexander V. Chernikov {
818d3a4f924SAlexander V. Chernikov 	int error;
819d3a4f924SAlexander V. Chernikov 
820d3a4f924SAlexander V. Chernikov 	switch (op3->version) {
821d3a4f924SAlexander V. Chernikov 	case 0:
8222d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sd);
823d3a4f924SAlexander V. Chernikov 		break;
824d3a4f924SAlexander V. Chernikov 	case 1:
8252d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sd);
826d3a4f924SAlexander V. Chernikov 		break;
827d3a4f924SAlexander V. Chernikov 	default:
828d3a4f924SAlexander V. Chernikov 		error = ENOTSUP;
829d3a4f924SAlexander V. Chernikov 	}
830d3a4f924SAlexander V. Chernikov 
831d3a4f924SAlexander V. Chernikov 	return (error);
832d3a4f924SAlexander V. Chernikov }
833d3a4f924SAlexander V. Chernikov 
834d3a4f924SAlexander V. Chernikov /*
835d3a4f924SAlexander V. Chernikov  * Dumps all table data
836ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
8372d99a349SAlexander V. Chernikov  * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
8382d99a349SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ]
839d3a4f924SAlexander V. Chernikov  *
840d3a4f924SAlexander V. Chernikov  * Returns 0 on success
841d3a4f924SAlexander V. Chernikov  */
842d3a4f924SAlexander V. Chernikov static int
8432d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
844d3a4f924SAlexander V. Chernikov {
845f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
846f1220db8SAlexander V. Chernikov 	ipfw_xtable_info *i;
847f1220db8SAlexander V. Chernikov 	struct tid_info ti;
848f1220db8SAlexander V. Chernikov 	struct table_config *tc;
849f1220db8SAlexander V. Chernikov 	struct table_algo *ta;
850f1220db8SAlexander V. Chernikov 	struct dump_args da;
851d3a4f924SAlexander V. Chernikov 	uint32_t sz;
852f1220db8SAlexander V. Chernikov 
8532d99a349SAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
8542d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
8552d99a349SAlexander V. Chernikov 	if (oh == NULL)
856d3a4f924SAlexander V. Chernikov 		return (EINVAL);
857d3a4f924SAlexander V. Chernikov 
8582d99a349SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
859d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
860f1220db8SAlexander V. Chernikov 
861f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
862f1220db8SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
863f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
864f1220db8SAlexander V. Chernikov 		return (ESRCH);
865f1220db8SAlexander V. Chernikov 	}
866ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, i);
8672d99a349SAlexander V. Chernikov 	sz = tc->count;
8682d99a349SAlexander V. Chernikov 
8692d99a349SAlexander V. Chernikov 	if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) {
8702d99a349SAlexander V. Chernikov 
8712d99a349SAlexander V. Chernikov 		/*
8722d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
8732d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
8742d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
8752d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
8762d99a349SAlexander V. Chernikov 		 */
877f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
8782d99a349SAlexander V. Chernikov 		return (ENOMEM);
879f1220db8SAlexander V. Chernikov 	}
880f1220db8SAlexander V. Chernikov 
881f1220db8SAlexander V. Chernikov 	/*
882f1220db8SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
883f1220db8SAlexander V. Chernikov 	 */
884d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
885f1220db8SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
886f1220db8SAlexander V. Chernikov 	da.tc = tc;
8872d99a349SAlexander V. Chernikov 	da.sd = sd;
888f1220db8SAlexander V. Chernikov 
889f1220db8SAlexander V. Chernikov 	ta = tc->ta;
890f1220db8SAlexander V. Chernikov 
891f1220db8SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
892f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
893f1220db8SAlexander V. Chernikov 
894f1220db8SAlexander V. Chernikov 	return (0);
895f1220db8SAlexander V. Chernikov }
896f1220db8SAlexander V. Chernikov 
897d3a4f924SAlexander V. Chernikov /*
898d3a4f924SAlexander V. Chernikov  * Dumps all table data
8992d99a349SAlexander V. Chernikov  * Data layout (version 0)(legacy):
900d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
901d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
902d3a4f924SAlexander V. Chernikov  *
903d3a4f924SAlexander V. Chernikov  * Returns 0 on success
904d3a4f924SAlexander V. Chernikov  */
905d3a4f924SAlexander V. Chernikov static int
9062d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
907d3a4f924SAlexander V. Chernikov {
908d3a4f924SAlexander V. Chernikov 	ipfw_xtable *xtbl;
909d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
910d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
911d3a4f924SAlexander V. Chernikov 	struct table_algo *ta;
912d3a4f924SAlexander V. Chernikov 	struct dump_args da;
913d3a4f924SAlexander V. Chernikov 	size_t sz;
914d3a4f924SAlexander V. Chernikov 
9152d99a349SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
9162d99a349SAlexander V. Chernikov 	if (xtbl == NULL)
917d3a4f924SAlexander V. Chernikov 		return (EINVAL);
918d3a4f924SAlexander V. Chernikov 
919d3a4f924SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
920d3a4f924SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
921d3a4f924SAlexander V. Chernikov 
922d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
923d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
924d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
925d3a4f924SAlexander V. Chernikov 		return (0);
926d3a4f924SAlexander V. Chernikov 	}
927d3a4f924SAlexander V. Chernikov 	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
9282d99a349SAlexander V. Chernikov 
9292d99a349SAlexander V. Chernikov 	xtbl->cnt = tc->count;
9302d99a349SAlexander V. Chernikov 	xtbl->size = sz;
9312d99a349SAlexander V. Chernikov 	xtbl->type = tc->no.type;
9322d99a349SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
9332d99a349SAlexander V. Chernikov 
9342d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
9352d99a349SAlexander V. Chernikov 
9362d99a349SAlexander V. Chernikov 		/*
9372d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
9382d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
9392d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
9402d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
9412d99a349SAlexander V. Chernikov 		 */
942d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
9432d99a349SAlexander V. Chernikov 		return (ENOMEM);
944d3a4f924SAlexander V. Chernikov 	}
945d3a4f924SAlexander V. Chernikov 
946d3a4f924SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
947d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
948d3a4f924SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
949d3a4f924SAlexander V. Chernikov 	da.tc = tc;
9502d99a349SAlexander V. Chernikov 	da.sd = sd;
9512d99a349SAlexander V. Chernikov 
952d3a4f924SAlexander V. Chernikov 	ta = tc->ta;
953d3a4f924SAlexander V. Chernikov 
954d3a4f924SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
955d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
956d3a4f924SAlexander V. Chernikov 
9572d99a349SAlexander V. Chernikov 	return (0);
958d3a4f924SAlexander V. Chernikov }
959d3a4f924SAlexander V. Chernikov 
960d3a4f924SAlexander V. Chernikov /*
9619490a627SAlexander V. Chernikov  * Creates new table.
962ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
9639490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
9649490a627SAlexander V. Chernikov  *
9659490a627SAlexander V. Chernikov  * Returns 0 on success
9669490a627SAlexander V. Chernikov  */
9679490a627SAlexander V. Chernikov int
968ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
969ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
9709490a627SAlexander V. Chernikov {
9719490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
9729490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
9739490a627SAlexander V. Chernikov 	char *tname, *aname;
9749490a627SAlexander V. Chernikov 	struct tid_info ti;
9759490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
9769490a627SAlexander V. Chernikov 	struct table_config *tc;
9779490a627SAlexander V. Chernikov 	struct table_algo *ta;
9789490a627SAlexander V. Chernikov 	uint16_t kidx;
9799490a627SAlexander V. Chernikov 
980ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
9819490a627SAlexander V. Chernikov 		return (EINVAL);
9829490a627SAlexander V. Chernikov 
983ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)sd->kbuf;
9849490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
9859490a627SAlexander V. Chernikov 
9869490a627SAlexander V. Chernikov 	/*
9879490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
9882d99a349SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
9899490a627SAlexander V. Chernikov 	 */
990ac35ff17SAlexander V. Chernikov 	tname = oh->ntlv.name;
9919490a627SAlexander V. Chernikov 	aname = i->algoname;
992ac35ff17SAlexander V. Chernikov 	if (ipfw_check_table_name(tname) != 0 ||
9939490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
9949490a627SAlexander V. Chernikov 		return (EINVAL);
9959490a627SAlexander V. Chernikov 
9969490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
9979490a627SAlexander V. Chernikov 		/* Use default algorithm */
9989490a627SAlexander V. Chernikov 		aname = NULL;
9999490a627SAlexander V. Chernikov 	}
10009490a627SAlexander V. Chernikov 
10019490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1002ac35ff17SAlexander V. Chernikov 	ti.type = i->type;
10039490a627SAlexander V. Chernikov 
10049490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
10059490a627SAlexander V. Chernikov 
10069490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
10079490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
10089490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
10099490a627SAlexander V. Chernikov 		return (EEXIST);
10109490a627SAlexander V. Chernikov 	}
10119490a627SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), &ti, aname);
10129490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
10139490a627SAlexander V. Chernikov 
10149490a627SAlexander V. Chernikov 	if (ta == NULL)
10159490a627SAlexander V. Chernikov 		return (ENOTSUP);
10169490a627SAlexander V. Chernikov 
10179490a627SAlexander V. Chernikov 	if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL)
10189490a627SAlexander V. Chernikov 		return (ENOMEM);
1019ac35ff17SAlexander V. Chernikov 	/* TODO: move inside alloc_table_config() */
1020ac35ff17SAlexander V. Chernikov 	tc->vtype = i->vtype;
10219490a627SAlexander V. Chernikov 
10229490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1023ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
10249490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
10259490a627SAlexander V. Chernikov 		printf("Unable to allocate table index for table %s in set %u."
10269490a627SAlexander V. Chernikov 		    " Consider increasing net.inet.ip.fw.tables_max",
10279490a627SAlexander V. Chernikov 		    tname, ti.set);
10289490a627SAlexander V. Chernikov 		free_table_config(ni, tc);
10299490a627SAlexander V. Chernikov 		return (EBUSY);
10309490a627SAlexander V. Chernikov 	}
10319490a627SAlexander V. Chernikov 
10329490a627SAlexander V. Chernikov 	tc->no.kidx = kidx;
10339490a627SAlexander V. Chernikov 
10349490a627SAlexander V. Chernikov 	IPFW_WLOCK(ch);
10359490a627SAlexander V. Chernikov 	link_table(ch, tc);
10369490a627SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
10379490a627SAlexander V. Chernikov 
10389490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
10399490a627SAlexander V. Chernikov 
10409490a627SAlexander V. Chernikov 	return (0);
10419490a627SAlexander V. Chernikov }
10429490a627SAlexander V. Chernikov 
1043d3a4f924SAlexander V. Chernikov void
1044d3a4f924SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
1045d3a4f924SAlexander V. Chernikov {
1046d3a4f924SAlexander V. Chernikov 
1047d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
1048d3a4f924SAlexander V. Chernikov 	ti->set = oh->set;
1049d3a4f924SAlexander V. Chernikov 	ti->uidx = oh->idx;
1050d3a4f924SAlexander V. Chernikov 	ti->tlvs = &oh->ntlv;
1051d3a4f924SAlexander V. Chernikov 	ti->tlen = oh->ntlv.head.length;
1052d3a4f924SAlexander V. Chernikov }
1053d3a4f924SAlexander V. Chernikov 
1054563b5ab1SAlexander V. Chernikov int
1055563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
1056563b5ab1SAlexander V. Chernikov     struct sockopt_data *sd)
1057563b5ab1SAlexander V. Chernikov {
1058563b5ab1SAlexander V. Chernikov 	struct namedobj_instance *ni;
1059563b5ab1SAlexander V. Chernikov 	struct named_object *no;
1060563b5ab1SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1061563b5ab1SAlexander V. Chernikov 
1062563b5ab1SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1063563b5ab1SAlexander V. Chernikov 
1064ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_kidx(ni, kidx);
1065563b5ab1SAlexander V. Chernikov 	KASSERT(no != NULL, ("invalid table kidx passed"));
1066563b5ab1SAlexander V. Chernikov 
1067563b5ab1SAlexander V. Chernikov 	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
1068563b5ab1SAlexander V. Chernikov 	if (ntlv == NULL)
1069563b5ab1SAlexander V. Chernikov 		return (ENOMEM);
1070563b5ab1SAlexander V. Chernikov 
1071563b5ab1SAlexander V. Chernikov 	ntlv->head.type = IPFW_TLV_TBL_NAME;
1072563b5ab1SAlexander V. Chernikov 	ntlv->head.length = sizeof(*ntlv);
1073563b5ab1SAlexander V. Chernikov 	ntlv->idx = no->kidx;
1074563b5ab1SAlexander V. Chernikov 	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
1075563b5ab1SAlexander V. Chernikov 
1076563b5ab1SAlexander V. Chernikov 	return (0);
1077563b5ab1SAlexander V. Chernikov }
1078563b5ab1SAlexander V. Chernikov 
10799f7d47b0SAlexander V. Chernikov static void
1080ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
1081ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i)
10829f7d47b0SAlexander V. Chernikov {
1083ac35ff17SAlexander V. Chernikov 	struct table_info *ti;
10849f7d47b0SAlexander V. Chernikov 
10859f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
1086ac35ff17SAlexander V. Chernikov 	i->vtype = tc->vtype;
10879f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
10889f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
10899f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
10909f7d47b0SAlexander V. Chernikov 	i->count = tc->count;
10919f7d47b0SAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_table_xentry);
1092f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
10939f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
1094ac35ff17SAlexander V. Chernikov 	if (tc->ta->print_config != NULL) {
1095ac35ff17SAlexander V. Chernikov 		/* Use algo function to print table config to string */
1096ac35ff17SAlexander V. Chernikov 		ti = KIDX_TO_TI(ch, tc->no.kidx);
1097ac35ff17SAlexander V. Chernikov 		tc->ta->print_config(tc->astate, ti, i->algoname,
1098ac35ff17SAlexander V. Chernikov 		    sizeof(i->algoname));
1099ac35ff17SAlexander V. Chernikov 	} else
1100ac35ff17SAlexander V. Chernikov 		strlcpy(i->algoname, tc->ta->name, sizeof(i->algoname));
11019f7d47b0SAlexander V. Chernikov }
11029f7d47b0SAlexander V. Chernikov 
1103ac35ff17SAlexander V. Chernikov struct dump_table_args {
1104ac35ff17SAlexander V. Chernikov 	struct ip_fw_chain *ch;
1105ac35ff17SAlexander V. Chernikov 	struct sockopt_data *sd;
1106ac35ff17SAlexander V. Chernikov };
1107ac35ff17SAlexander V. Chernikov 
11089f7d47b0SAlexander V. Chernikov static void
11099f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
11109f7d47b0SAlexander V. Chernikov     void *arg)
11113b3a8eb9SGleb Smirnoff {
11129f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
1113ac35ff17SAlexander V. Chernikov 	struct dump_table_args *dta;
11143b3a8eb9SGleb Smirnoff 
1115ac35ff17SAlexander V. Chernikov 	dta = (struct dump_table_args *)arg;
1116ac35ff17SAlexander V. Chernikov 
1117ac35ff17SAlexander V. Chernikov 	i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
11182d99a349SAlexander V. Chernikov 	KASSERT(i == 0, ("previously checked buffer is not enough"));
11199f7d47b0SAlexander V. Chernikov 
1120ac35ff17SAlexander V. Chernikov 	export_table_info(dta->ch, (struct table_config *)no, i);
11219f7d47b0SAlexander V. Chernikov }
11229f7d47b0SAlexander V. Chernikov 
1123f1220db8SAlexander V. Chernikov /*
1124f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
11252d99a349SAlexander V. Chernikov  * storage provided by @sd.
1126f1220db8SAlexander V. Chernikov  * Returns 0 on success.
1127f1220db8SAlexander V. Chernikov  */
1128f1220db8SAlexander V. Chernikov static int
11292d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
11302d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
11319f7d47b0SAlexander V. Chernikov {
11329f7d47b0SAlexander V. Chernikov 	uint32_t size;
11339f7d47b0SAlexander V. Chernikov 	uint32_t count;
1134ac35ff17SAlexander V. Chernikov 	struct dump_table_args dta;
11359f7d47b0SAlexander V. Chernikov 
11369f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
11379f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
11382d99a349SAlexander V. Chernikov 
11392d99a349SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
1140f1220db8SAlexander V. Chernikov 	olh->count = count;
1141f1220db8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
11422d99a349SAlexander V. Chernikov 
11432d99a349SAlexander V. Chernikov 	if (size > olh->size) {
11442d99a349SAlexander V. Chernikov 		/* Store necessary size */
11452d99a349SAlexander V. Chernikov 		olh->size = size;
11469f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1147f1220db8SAlexander V. Chernikov 	}
11489f7d47b0SAlexander V. Chernikov 	olh->size = size;
11492d99a349SAlexander V. Chernikov 
1150ac35ff17SAlexander V. Chernikov 	dta.ch = ch;
1151ac35ff17SAlexander V. Chernikov 	dta.sd = sd;
1152ac35ff17SAlexander V. Chernikov 
1153ac35ff17SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta);
11549f7d47b0SAlexander V. Chernikov 
11553b3a8eb9SGleb Smirnoff 	return (0);
11563b3a8eb9SGleb Smirnoff }
11573b3a8eb9SGleb Smirnoff 
11583b3a8eb9SGleb Smirnoff int
1159b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
11603b3a8eb9SGleb Smirnoff {
1161b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
11623b3a8eb9SGleb Smirnoff 
1163b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1164b074b7bbSAlexander V. Chernikov 		return (ESRCH);
11659f7d47b0SAlexander V. Chernikov 	*cnt = tc->count;
11663b3a8eb9SGleb Smirnoff 	return (0);
11673b3a8eb9SGleb Smirnoff }
11683b3a8eb9SGleb Smirnoff 
11699f7d47b0SAlexander V. Chernikov 
11709f7d47b0SAlexander V. Chernikov int
11719f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
11723b3a8eb9SGleb Smirnoff {
11739f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
11749f7d47b0SAlexander V. Chernikov 
1175d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
1176d3a4f924SAlexander V. Chernikov 		*cnt = 0;
11779f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
1178d3a4f924SAlexander V. Chernikov 	}
11799f7d47b0SAlexander V. Chernikov 	*cnt = tc->count * sizeof(ipfw_table_xentry);
11809f7d47b0SAlexander V. Chernikov 	if (tc->count > 0)
11819f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
11829f7d47b0SAlexander V. Chernikov 	return (0);
11839f7d47b0SAlexander V. Chernikov }
11849f7d47b0SAlexander V. Chernikov 
11859f7d47b0SAlexander V. Chernikov static int
11869f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
11879f7d47b0SAlexander V. Chernikov {
11889f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
11899f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
11909f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
11913b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
11923b3a8eb9SGleb Smirnoff 
11939f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
11949f7d47b0SAlexander V. Chernikov 
11959f7d47b0SAlexander V. Chernikov 	tc = da->tc;
11969f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
11979f7d47b0SAlexander V. Chernikov 
11989f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
1199f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
12003b3a8eb9SGleb Smirnoff 		return (1);
1201f1220db8SAlexander V. Chernikov 	ent = da->ent++;
1202f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
1203f1220db8SAlexander V. Chernikov 	da->cnt++;
12049f7d47b0SAlexander V. Chernikov 
12059f7d47b0SAlexander V. Chernikov 	return (ta->dump_entry(tc->astate, da->ti, e, ent));
12063b3a8eb9SGleb Smirnoff }
12073b3a8eb9SGleb Smirnoff 
1208f1220db8SAlexander V. Chernikov /*
1209f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
1210f1220db8SAlexander V. Chernikov  */
12113b3a8eb9SGleb Smirnoff int
1212f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
1213f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
12143b3a8eb9SGleb Smirnoff {
1215b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
12169f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
12179f7d47b0SAlexander V. Chernikov 	struct dump_args da;
12183b3a8eb9SGleb Smirnoff 
12193b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
12203b3a8eb9SGleb Smirnoff 
1221b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1222b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
12239f7d47b0SAlexander V. Chernikov 
12249f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
12259f7d47b0SAlexander V. Chernikov 
12269f7d47b0SAlexander V. Chernikov 	if (ta->dump_entry == NULL)
12279f7d47b0SAlexander V. Chernikov 		return (0);	/* Legacy dump support is not necessary */
12289f7d47b0SAlexander V. Chernikov 
1229d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
12309f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
12319f7d47b0SAlexander V. Chernikov 	da.tc = tc;
1232f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
1233f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
12349f7d47b0SAlexander V. Chernikov 
12359f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
12369f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
1237f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
12389f7d47b0SAlexander V. Chernikov 
12393b3a8eb9SGleb Smirnoff 	return (0);
12403b3a8eb9SGleb Smirnoff }
12413b3a8eb9SGleb Smirnoff 
12429490a627SAlexander V. Chernikov /*
12439490a627SAlexander V. Chernikov  * Dumps table entry in eXtended format (current).
12449490a627SAlexander V. Chernikov  */
12453b3a8eb9SGleb Smirnoff static int
12469f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
12473b3a8eb9SGleb Smirnoff {
12489f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
12499f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
12509f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
12513b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
12523b3a8eb9SGleb Smirnoff 
12539f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
12549f7d47b0SAlexander V. Chernikov 
12559f7d47b0SAlexander V. Chernikov 	tc = da->tc;
12569f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
12579f7d47b0SAlexander V. Chernikov 
12582d99a349SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
12593b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
12602d99a349SAlexander V. Chernikov 	if (xent == NULL)
12613b3a8eb9SGleb Smirnoff 		return (1);
12623b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
1263f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
12649f7d47b0SAlexander V. Chernikov 
12659f7d47b0SAlexander V. Chernikov 	return (ta->dump_xentry(tc->astate, da->ti, e, xent));
12669f7d47b0SAlexander V. Chernikov }
12679f7d47b0SAlexander V. Chernikov 
12689f7d47b0SAlexander V. Chernikov /*
12699f7d47b0SAlexander V. Chernikov  * Table algorithms
12709f7d47b0SAlexander V. Chernikov  */
12713b3a8eb9SGleb Smirnoff 
12729f7d47b0SAlexander V. Chernikov /*
12739490a627SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name
12749f7d47b0SAlexander V. Chernikov  */
12759f7d47b0SAlexander V. Chernikov static struct table_algo *
12769490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
12779f7d47b0SAlexander V. Chernikov {
12789490a627SAlexander V. Chernikov 	int i, l;
12799490a627SAlexander V. Chernikov 	struct table_algo *ta;
12809f7d47b0SAlexander V. Chernikov 
12819f7d47b0SAlexander V. Chernikov 	/* Search by index */
12829f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
12839f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
12849f7d47b0SAlexander V. Chernikov 			return (NULL);
12859f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
12869f7d47b0SAlexander V. Chernikov 	}
12879f7d47b0SAlexander V. Chernikov 
12889490a627SAlexander V. Chernikov 	/* Search by name if supplied */
12899490a627SAlexander V. Chernikov 	if (name != NULL) {
12909490a627SAlexander V. Chernikov 		/* TODO: better search */
12919490a627SAlexander V. Chernikov 		for (i = 1; i <= tcfg->algo_count; i++) {
12929490a627SAlexander V. Chernikov 			ta = tcfg->algo[i];
12939490a627SAlexander V. Chernikov 
12949490a627SAlexander V. Chernikov 			/*
12959490a627SAlexander V. Chernikov 			 * One can supply additional algorithm
12969490a627SAlexander V. Chernikov 			 * parameters so we compare only the first word
12979490a627SAlexander V. Chernikov 			 * of supplied name:
12989490a627SAlexander V. Chernikov 			 * 'hash_cidr hsize=32'
12999490a627SAlexander V. Chernikov 			 * '^^^^^^^^^'
13009490a627SAlexander V. Chernikov 			 *
13019490a627SAlexander V. Chernikov 			 */
13029490a627SAlexander V. Chernikov 			l = strlen(ta->name);
13039490a627SAlexander V. Chernikov 			if (strncmp(name, ta->name, l) == 0) {
13049490a627SAlexander V. Chernikov 				if (name[l] == '\0' || name[l] == ' ')
13059490a627SAlexander V. Chernikov 					return (ta);
13069490a627SAlexander V. Chernikov 			}
13079490a627SAlexander V. Chernikov 		}
13089490a627SAlexander V. Chernikov 
13099490a627SAlexander V. Chernikov 		return (NULL);
13109490a627SAlexander V. Chernikov 	}
13119490a627SAlexander V. Chernikov 
13129f7d47b0SAlexander V. Chernikov 	/* Search by type */
13139f7d47b0SAlexander V. Chernikov 	switch (ti->type) {
13143b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
13159f7d47b0SAlexander V. Chernikov 		return (&radix_cidr);
13163b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
13179f7d47b0SAlexander V. Chernikov 		return (&radix_iface);
13183b3a8eb9SGleb Smirnoff 	}
13193b3a8eb9SGleb Smirnoff 
13209f7d47b0SAlexander V. Chernikov 	return (NULL);
13213b3a8eb9SGleb Smirnoff }
13223b3a8eb9SGleb Smirnoff 
13239f7d47b0SAlexander V. Chernikov void
13249f7d47b0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta)
13253b3a8eb9SGleb Smirnoff {
13269f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
13273b3a8eb9SGleb Smirnoff 
13289f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
1329b074b7bbSAlexander V. Chernikov 
13309f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
13319f7d47b0SAlexander V. Chernikov 
13329f7d47b0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta;
13339f7d47b0SAlexander V. Chernikov 	ta->idx = tcfg->algo_count;
13343b3a8eb9SGleb Smirnoff }
13353b3a8eb9SGleb Smirnoff 
13369f7d47b0SAlexander V. Chernikov 
1337b074b7bbSAlexander V. Chernikov /*
1338b074b7bbSAlexander V. Chernikov  * Tables rewriting code
1339b074b7bbSAlexander V. Chernikov  *
1340b074b7bbSAlexander V. Chernikov  */
1341b074b7bbSAlexander V. Chernikov 
1342b074b7bbSAlexander V. Chernikov /*
1343b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
1344b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
1345b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
1346b074b7bbSAlexander V. Chernikov  */
1347b074b7bbSAlexander V. Chernikov static int
1348b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
1349b074b7bbSAlexander V. Chernikov {
1350b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1351b074b7bbSAlexander V. Chernikov 	int skip;
1352b074b7bbSAlexander V. Chernikov 	uint16_t v;
1353b074b7bbSAlexander V. Chernikov 
1354b074b7bbSAlexander V. Chernikov 	skip = 1;
1355b074b7bbSAlexander V. Chernikov 
1356b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1357b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1358b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1359b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1360b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
1361b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
1362b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
1363b074b7bbSAlexander V. Chernikov 		skip = 0;
1364b074b7bbSAlexander V. Chernikov 
1365b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
1366b074b7bbSAlexander V. Chernikov 			/*
1367b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
1368b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
1369b074b7bbSAlexander V. Chernikov 			 */
1370b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
1371b074b7bbSAlexander V. Chernikov 			switch (v) {
1372b074b7bbSAlexander V. Chernikov 			case 0:
1373b074b7bbSAlexander V. Chernikov 			case 1:
1374b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
1375b074b7bbSAlexander V. Chernikov 				break;
1376b074b7bbSAlexander V. Chernikov 			case 2:
1377b074b7bbSAlexander V. Chernikov 			case 3:
1378b074b7bbSAlexander V. Chernikov 				/* src/dst port */
1379b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1380b074b7bbSAlexander V. Chernikov 				break;
1381b074b7bbSAlexander V. Chernikov 			case 4:
1382b074b7bbSAlexander V. Chernikov 				/* uid/gid */
1383b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1384b074b7bbSAlexander V. Chernikov 			case 5:
1385b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1386b074b7bbSAlexander V. Chernikov 				/* jid */
1387b074b7bbSAlexander V. Chernikov 			case 6:
1388b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1389b074b7bbSAlexander V. Chernikov 				/* dscp */
1390b074b7bbSAlexander V. Chernikov 				break;
1391b074b7bbSAlexander V. Chernikov 			}
1392b074b7bbSAlexander V. Chernikov 		}
1393b074b7bbSAlexander V. Chernikov 		break;
1394b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1395b074b7bbSAlexander V. Chernikov 	case O_RECV:
1396b074b7bbSAlexander V. Chernikov 	case O_VIA:
1397b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1398b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1399b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
1400b074b7bbSAlexander V. Chernikov 			break;
1401b074b7bbSAlexander V. Chernikov 
1402b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
1403b074b7bbSAlexander V. Chernikov 		*puidx = cmdif->p.glob;
1404b074b7bbSAlexander V. Chernikov 		skip = 0;
1405b074b7bbSAlexander V. Chernikov 		break;
1406b074b7bbSAlexander V. Chernikov 	}
1407b074b7bbSAlexander V. Chernikov 
1408b074b7bbSAlexander V. Chernikov 	return (skip);
1409b074b7bbSAlexander V. Chernikov }
1410b074b7bbSAlexander V. Chernikov 
1411b074b7bbSAlexander V. Chernikov /*
1412b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
1413b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
1414b074b7bbSAlexander V. Chernikov  */
1415b074b7bbSAlexander V. Chernikov static void
1416b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
1417b074b7bbSAlexander V. Chernikov {
1418b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1419b074b7bbSAlexander V. Chernikov 
1420b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1421b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1422b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1423b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1424b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
1425b074b7bbSAlexander V. Chernikov 		break;
1426b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1427b074b7bbSAlexander V. Chernikov 	case O_RECV:
1428b074b7bbSAlexander V. Chernikov 	case O_VIA:
1429b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1430b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1431b074b7bbSAlexander V. Chernikov 		cmdif->p.glob = idx;
1432b074b7bbSAlexander V. Chernikov 		break;
1433b074b7bbSAlexander V. Chernikov 	}
1434b074b7bbSAlexander V. Chernikov }
1435b074b7bbSAlexander V. Chernikov 
1436ac35ff17SAlexander V. Chernikov /*
1437ac35ff17SAlexander V. Chernikov  * Checks table name for validity.
1438ac35ff17SAlexander V. Chernikov  * Enforce basic length checks, the rest
1439ac35ff17SAlexander V. Chernikov  * should be done in userland.
1440ac35ff17SAlexander V. Chernikov  *
1441ac35ff17SAlexander V. Chernikov  * Returns 0 if name is considered valid.
1442ac35ff17SAlexander V. Chernikov  */
1443ac35ff17SAlexander V. Chernikov int
1444ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name)
1445ac35ff17SAlexander V. Chernikov {
1446ac35ff17SAlexander V. Chernikov 	int nsize;
1447ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv = NULL;
1448ac35ff17SAlexander V. Chernikov 
1449ac35ff17SAlexander V. Chernikov 	nsize = sizeof(ntlv->name);
1450ac35ff17SAlexander V. Chernikov 
1451ac35ff17SAlexander V. Chernikov 	if (strnlen(name, nsize) == nsize)
1452ac35ff17SAlexander V. Chernikov 		return (EINVAL);
1453ac35ff17SAlexander V. Chernikov 
1454ac35ff17SAlexander V. Chernikov 	if (name[0] == '\0')
1455ac35ff17SAlexander V. Chernikov 		return (EINVAL);
1456ac35ff17SAlexander V. Chernikov 
1457ac35ff17SAlexander V. Chernikov 	/*
1458ac35ff17SAlexander V. Chernikov 	 * TODO: do some more complicated checks
1459ac35ff17SAlexander V. Chernikov 	 */
1460ac35ff17SAlexander V. Chernikov 
1461ac35ff17SAlexander V. Chernikov 	return (0);
1462ac35ff17SAlexander V. Chernikov }
1463ac35ff17SAlexander V. Chernikov 
1464ac35ff17SAlexander V. Chernikov /*
1465ac35ff17SAlexander V. Chernikov  * Find tablename TLV by @uid.
1466ac35ff17SAlexander V. Chernikov  * Check @tlvs for valid data inside.
1467ac35ff17SAlexander V. Chernikov  *
1468ac35ff17SAlexander V. Chernikov  * Returns pointer to found TLV or NULL.
1469ac35ff17SAlexander V. Chernikov  */
1470ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv *
1471b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
1472b074b7bbSAlexander V. Chernikov {
14739f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1474b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
1475b074b7bbSAlexander V. Chernikov 	int l;
1476b074b7bbSAlexander V. Chernikov 
1477b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
1478b074b7bbSAlexander V. Chernikov 	pe = pa + len;
1479b074b7bbSAlexander V. Chernikov 	l = 0;
1480b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
14819f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
1482b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
1483ac35ff17SAlexander V. Chernikov 
1484ac35ff17SAlexander V. Chernikov 		if (l != sizeof(*ntlv))
1485ac35ff17SAlexander V. Chernikov 			return (NULL);
1486ac35ff17SAlexander V. Chernikov 
1487563b5ab1SAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_TBL_NAME)
1488b074b7bbSAlexander V. Chernikov 			continue;
1489ac35ff17SAlexander V. Chernikov 
1490b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
1491b074b7bbSAlexander V. Chernikov 			continue;
1492b074b7bbSAlexander V. Chernikov 
1493ac35ff17SAlexander V. Chernikov 		if (ipfw_check_table_name(ntlv->name) != 0)
1494ac35ff17SAlexander V. Chernikov 			return (NULL);
1495ac35ff17SAlexander V. Chernikov 
1496ac35ff17SAlexander V. Chernikov 		return (ntlv);
1497b074b7bbSAlexander V. Chernikov 	}
1498b074b7bbSAlexander V. Chernikov 
1499b074b7bbSAlexander V. Chernikov 	return (NULL);
1500b074b7bbSAlexander V. Chernikov }
1501b074b7bbSAlexander V. Chernikov 
1502ac35ff17SAlexander V. Chernikov /*
1503ac35ff17SAlexander V. Chernikov  * Finds table config based on either legacy index
1504ac35ff17SAlexander V. Chernikov  * or name in ntlv.
1505ac35ff17SAlexander V. Chernikov  * Note @ti structure contains unchecked data from userland.
1506ac35ff17SAlexander V. Chernikov  *
1507ac35ff17SAlexander V. Chernikov  * Returns pointer to table_config or NULL.
1508ac35ff17SAlexander V. Chernikov  */
1509b074b7bbSAlexander V. Chernikov static struct table_config *
1510b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
1511b074b7bbSAlexander V. Chernikov {
1512b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1513b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1514ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1515ac35ff17SAlexander V. Chernikov 	uint32_t set;
1516b074b7bbSAlexander V. Chernikov 
1517b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1518ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1519ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
1520b074b7bbSAlexander V. Chernikov 			return (NULL);
1521ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
1522ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
1523b074b7bbSAlexander V. Chernikov 	} else {
1524b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1525b074b7bbSAlexander V. Chernikov 		name = bname;
1526ac35ff17SAlexander V. Chernikov 		set = 0;
1527b074b7bbSAlexander V. Chernikov 	}
1528b074b7bbSAlexander V. Chernikov 
1529ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, set, name);
1530b074b7bbSAlexander V. Chernikov 
1531b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
1532b074b7bbSAlexander V. Chernikov }
1533b074b7bbSAlexander V. Chernikov 
1534b074b7bbSAlexander V. Chernikov static struct table_config *
15359f7d47b0SAlexander V. Chernikov alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
15369490a627SAlexander V. Chernikov     struct table_algo *ta, char *aname)
1537b074b7bbSAlexander V. Chernikov {
1538b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1539b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1540b074b7bbSAlexander V. Chernikov 	int error;
1541ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1542ac35ff17SAlexander V. Chernikov 	uint32_t set;
1543b074b7bbSAlexander V. Chernikov 
1544b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1545ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1546ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
1547b074b7bbSAlexander V. Chernikov 			return (NULL);
1548ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
1549ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
1550b074b7bbSAlexander V. Chernikov 	} else {
1551b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1552b074b7bbSAlexander V. Chernikov 		name = bname;
1553ac35ff17SAlexander V. Chernikov 		set = 0;
1554b074b7bbSAlexander V. Chernikov 	}
1555b074b7bbSAlexander V. Chernikov 
1556b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
1557b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
1558b074b7bbSAlexander V. Chernikov 	tc->no.type = ti->type;
1559ac35ff17SAlexander V. Chernikov 	tc->no.set = set;
15609f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
1561b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
1562ac35ff17SAlexander V. Chernikov 	/* Set default value type to u32 for compability reasons */
1563ac35ff17SAlexander V. Chernikov 	tc->vtype = IPFW_VTYPE_U32;
1564b074b7bbSAlexander V. Chernikov 
1565b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
1566b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
1567b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
1568b074b7bbSAlexander V. Chernikov 	}
1569b074b7bbSAlexander V. Chernikov 
1570b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
15719490a627SAlexander V. Chernikov 	error = ta->init(&tc->astate, &tc->ti, aname);
1572b074b7bbSAlexander V. Chernikov 	if (error != 0) {
1573b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
1574b074b7bbSAlexander V. Chernikov 		return (NULL);
1575b074b7bbSAlexander V. Chernikov 	}
1576b074b7bbSAlexander V. Chernikov 
1577b074b7bbSAlexander V. Chernikov 	return (tc);
1578b074b7bbSAlexander V. Chernikov }
1579b074b7bbSAlexander V. Chernikov 
1580b074b7bbSAlexander V. Chernikov static void
1581b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
1582b074b7bbSAlexander V. Chernikov {
1583b074b7bbSAlexander V. Chernikov 
1584b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0)
15859f7d47b0SAlexander V. Chernikov 		tc->ta->destroy(&tc->astate, &tc->ti);
1586b074b7bbSAlexander V. Chernikov 
1587b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
1588b074b7bbSAlexander V. Chernikov }
1589b074b7bbSAlexander V. Chernikov 
1590b074b7bbSAlexander V. Chernikov /*
1591b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
1592b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
1593b074b7bbSAlexander V. Chernikov  */
1594b074b7bbSAlexander V. Chernikov static void
15959f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
1596b074b7bbSAlexander V. Chernikov {
1597b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
15989f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1599b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1600b074b7bbSAlexander V. Chernikov 
16019f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
16029f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1603b074b7bbSAlexander V. Chernikov 
16049f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1605b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1606b074b7bbSAlexander V. Chernikov 
1607b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
16089f7d47b0SAlexander V. Chernikov 
16099f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
16109f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
1611b074b7bbSAlexander V. Chernikov 
1612b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
1613b074b7bbSAlexander V. Chernikov }
1614b074b7bbSAlexander V. Chernikov 
1615b074b7bbSAlexander V. Chernikov /*
1616b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
1617b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
1618b074b7bbSAlexander V. Chernikov  */
1619b074b7bbSAlexander V. Chernikov static void
16209f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
1621b074b7bbSAlexander V. Chernikov {
1622b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
16239f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1624b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1625b074b7bbSAlexander V. Chernikov 
16269f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
16279f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1628b074b7bbSAlexander V. Chernikov 
16299f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1630b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1631b074b7bbSAlexander V. Chernikov 
16329f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
1633b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
16349f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
16359f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
1636b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
1637b074b7bbSAlexander V. Chernikov }
1638b074b7bbSAlexander V. Chernikov 
1639b074b7bbSAlexander V. Chernikov /*
1640b074b7bbSAlexander V. Chernikov  * Finds named object by @uidx number.
1641b074b7bbSAlexander V. Chernikov  * Refs found object, allocate new index for non-existing object.
16429490a627SAlexander V. Chernikov  * Fills in @oib with userland/kernel indexes.
16439490a627SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
1644b074b7bbSAlexander V. Chernikov  *
1645b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1646b074b7bbSAlexander V. Chernikov  */
1647b074b7bbSAlexander V. Chernikov static int
16489490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
16499490a627SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
1650b074b7bbSAlexander V. Chernikov {
1651b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
16529490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
16539490a627SAlexander V. Chernikov 	struct named_object *no;
16549490a627SAlexander V. Chernikov 	int error, l, cmdlen;
16559490a627SAlexander V. Chernikov 	ipfw_insn *cmd;
16569490a627SAlexander V. Chernikov 	struct obj_idx *pidx, *p;
16579490a627SAlexander V. Chernikov 
16589490a627SAlexander V. Chernikov 	pidx = *oib;
16599490a627SAlexander V. Chernikov 	l = rule->cmd_len;
16609490a627SAlexander V. Chernikov 	cmd = rule->cmd;
16619490a627SAlexander V. Chernikov 	cmdlen = 0;
16629490a627SAlexander V. Chernikov 	error = 0;
16639490a627SAlexander V. Chernikov 
16649490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
16659490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
16669490a627SAlexander V. Chernikov 
16679490a627SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
16689490a627SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
16699490a627SAlexander V. Chernikov 
16709490a627SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
16719490a627SAlexander V. Chernikov 			continue;
1672b074b7bbSAlexander V. Chernikov 
1673b074b7bbSAlexander V. Chernikov 		pidx->uidx = ti->uidx;
1674b074b7bbSAlexander V. Chernikov 		pidx->type = ti->type;
1675b074b7bbSAlexander V. Chernikov 
16769490a627SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
16779490a627SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
16789490a627SAlexander V. Chernikov 				/* Incompatible types */
16799490a627SAlexander V. Chernikov 				error = EINVAL;
16809490a627SAlexander V. Chernikov 				break;
16819490a627SAlexander V. Chernikov 			}
16829f7d47b0SAlexander V. Chernikov 
16839490a627SAlexander V. Chernikov 			/* Reference found table and save kidx */
16849490a627SAlexander V. Chernikov 			tc->no.refcnt++;
16859490a627SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
16869490a627SAlexander V. Chernikov 			pidx++;
16879490a627SAlexander V. Chernikov 			continue;
16889490a627SAlexander V. Chernikov 		}
16899490a627SAlexander V. Chernikov 
16909490a627SAlexander V. Chernikov 		/* Table not found. Allocate new index and save for later */
1691ac35ff17SAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) {
1692ac35ff17SAlexander V. Chernikov 			printf("Unable to allocate table %s index in set %u."
1693b074b7bbSAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max",
1694ac35ff17SAlexander V. Chernikov 			    "", ti->set);
16959490a627SAlexander V. Chernikov 			error = EBUSY;
16969490a627SAlexander V. Chernikov 			break;
1697b074b7bbSAlexander V. Chernikov 		}
1698b074b7bbSAlexander V. Chernikov 
1699b074b7bbSAlexander V. Chernikov 		ci->new_tables++;
17009490a627SAlexander V. Chernikov 		pidx->new = 1;
17019490a627SAlexander V. Chernikov 		pidx++;
1702b074b7bbSAlexander V. Chernikov 	}
1703b074b7bbSAlexander V. Chernikov 
17049490a627SAlexander V. Chernikov 	if (error != 0) {
17059490a627SAlexander V. Chernikov 		/* Unref everything we have already done */
17069490a627SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
17079490a627SAlexander V. Chernikov 			if (p->new != 0) {
1708ac35ff17SAlexander V. Chernikov 				ipfw_objhash_free_idx(ni, p->kidx);
17099490a627SAlexander V. Chernikov 				continue;
17109490a627SAlexander V. Chernikov 			}
1711b074b7bbSAlexander V. Chernikov 
17129490a627SAlexander V. Chernikov 			/* Find & unref by existing idx */
1713ac35ff17SAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
17149490a627SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
17159490a627SAlexander V. Chernikov 			    p->kidx));
1716b074b7bbSAlexander V. Chernikov 
17179490a627SAlexander V. Chernikov 			no->refcnt--;
17189490a627SAlexander V. Chernikov 		}
17199490a627SAlexander V. Chernikov 	}
17209490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1721b074b7bbSAlexander V. Chernikov 
17229490a627SAlexander V. Chernikov 	*oib = pidx;
17239490a627SAlexander V. Chernikov 
17249490a627SAlexander V. Chernikov 	return (error);
1725b074b7bbSAlexander V. Chernikov }
1726b074b7bbSAlexander V. Chernikov 
1727b074b7bbSAlexander V. Chernikov /*
1728b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
1729b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
1730b074b7bbSAlexander V. Chernikov  * Works for \d+ talbes only (e.g. for tables, converted
1731b074b7bbSAlexander V. Chernikov  * from old numbered system calls).
1732b074b7bbSAlexander V. Chernikov  *
1733b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1734b074b7bbSAlexander V. Chernikov  * Raises error on any other tables.
1735b074b7bbSAlexander V. Chernikov  */
1736b074b7bbSAlexander V. Chernikov int
1737b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
1738b074b7bbSAlexander V. Chernikov {
17391832a7b3SAlexander V. Chernikov 	int cmdlen, error, l;
1740b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
17411832a7b3SAlexander V. Chernikov 	uint16_t kidx, uidx;
1742b074b7bbSAlexander V. Chernikov 	uint8_t type;
1743b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1744b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1745b074b7bbSAlexander V. Chernikov 
1746b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
17471832a7b3SAlexander V. Chernikov 	error = 0;
1748b074b7bbSAlexander V. Chernikov 
1749b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1750b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1751b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1752b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1753b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1754b074b7bbSAlexander V. Chernikov 
1755b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1756b074b7bbSAlexander V. Chernikov 			continue;
1757b074b7bbSAlexander V. Chernikov 
1758ac35ff17SAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL)
1759b074b7bbSAlexander V. Chernikov 			return (1);
1760b074b7bbSAlexander V. Chernikov 
17611832a7b3SAlexander V. Chernikov 		uidx = no->uidx;
17621832a7b3SAlexander V. Chernikov 		if (no->compat == 0) {
1763b074b7bbSAlexander V. Chernikov 
17641832a7b3SAlexander V. Chernikov 			/*
17651832a7b3SAlexander V. Chernikov 			 * We are called via legacy opcode.
17661832a7b3SAlexander V. Chernikov 			 * Save error and show table as fake number
17671832a7b3SAlexander V. Chernikov 			 * not to make ipfw(8) hang.
17681832a7b3SAlexander V. Chernikov 			 */
17691832a7b3SAlexander V. Chernikov 			uidx = 65535;
17701832a7b3SAlexander V. Chernikov 			error = 2;
1771b074b7bbSAlexander V. Chernikov 		}
1772b074b7bbSAlexander V. Chernikov 
17731832a7b3SAlexander V. Chernikov 		update_table_opcode(cmd, uidx);
17741832a7b3SAlexander V. Chernikov 	}
17751832a7b3SAlexander V. Chernikov 
17761832a7b3SAlexander V. Chernikov 	return (error);
1777b074b7bbSAlexander V. Chernikov }
1778b074b7bbSAlexander V. Chernikov 
1779563b5ab1SAlexander V. Chernikov /*
1780563b5ab1SAlexander V. Chernikov  * Sets every table kidx in @bmask which is used in rule @rule.
1781563b5ab1SAlexander V. Chernikov  *
1782563b5ab1SAlexander V. Chernikov  * Returns number of newly-referenced tables.
1783563b5ab1SAlexander V. Chernikov  */
1784563b5ab1SAlexander V. Chernikov int
1785563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
1786563b5ab1SAlexander V. Chernikov     uint32_t *bmask)
1787563b5ab1SAlexander V. Chernikov {
1788563b5ab1SAlexander V. Chernikov 	int cmdlen, l, count;
1789563b5ab1SAlexander V. Chernikov 	ipfw_insn *cmd;
1790563b5ab1SAlexander V. Chernikov 	uint16_t kidx;
1791563b5ab1SAlexander V. Chernikov 	uint8_t type;
1792563b5ab1SAlexander V. Chernikov 
1793563b5ab1SAlexander V. Chernikov 	l = rule->cmd_len;
1794563b5ab1SAlexander V. Chernikov 	cmd = rule->cmd;
1795563b5ab1SAlexander V. Chernikov 	cmdlen = 0;
1796563b5ab1SAlexander V. Chernikov 	count = 0;
1797563b5ab1SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1798563b5ab1SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1799563b5ab1SAlexander V. Chernikov 
1800563b5ab1SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1801563b5ab1SAlexander V. Chernikov 			continue;
1802563b5ab1SAlexander V. Chernikov 
1803563b5ab1SAlexander V. Chernikov 		if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
1804563b5ab1SAlexander V. Chernikov 			count++;
1805563b5ab1SAlexander V. Chernikov 
1806563b5ab1SAlexander V. Chernikov 		bmask[kidx / 32] |= 1 << (kidx % 32);
1807563b5ab1SAlexander V. Chernikov 	}
1808563b5ab1SAlexander V. Chernikov 
1809563b5ab1SAlexander V. Chernikov 	return (count);
1810563b5ab1SAlexander V. Chernikov }
1811563b5ab1SAlexander V. Chernikov 
1812563b5ab1SAlexander V. Chernikov 
1813b074b7bbSAlexander V. Chernikov 
1814b074b7bbSAlexander V. Chernikov /*
1815b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
1816b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
1817b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
1818b074b7bbSAlexander V. Chernikov  *
1819b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
1820b074b7bbSAlexander V. Chernikov  */
1821b074b7bbSAlexander V. Chernikov int
1822b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
1823b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
1824b074b7bbSAlexander V. Chernikov {
1825b074b7bbSAlexander V. Chernikov 	int cmdlen, error, ftype, l;
1826b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1827b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
1828b074b7bbSAlexander V. Chernikov 	uint8_t type;
1829b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
18309f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
1831b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1832b074b7bbSAlexander V. Chernikov 	struct named_object *no, *no_n, *no_tmp;
18339490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
1834b074b7bbSAlexander V. Chernikov 	struct namedobjects_head nh;
1835b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
1836b074b7bbSAlexander V. Chernikov 
1837b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1838b074b7bbSAlexander V. Chernikov 
18399490a627SAlexander V. Chernikov 	/* Prepare queue to store configs */
18409490a627SAlexander V. Chernikov 	TAILQ_INIT(&nh);
18419490a627SAlexander V. Chernikov 
1842b074b7bbSAlexander V. Chernikov 	/*
1843b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
1844b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
1845b074b7bbSAlexander V. Chernikov 	 */
1846b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
1847b074b7bbSAlexander V. Chernikov 		/* Stack */
18489490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
1849b074b7bbSAlexander V. Chernikov 	} else
18509490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
1851b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1852b074b7bbSAlexander V. Chernikov 
18539490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
1854b074b7bbSAlexander V. Chernikov 	error = 0;
1855b074b7bbSAlexander V. Chernikov 
1856b074b7bbSAlexander V. Chernikov 	type = 0;
1857b074b7bbSAlexander V. Chernikov 	ftype = 0;
1858b074b7bbSAlexander V. Chernikov 
1859b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
18601832a7b3SAlexander V. Chernikov 
18611832a7b3SAlexander V. Chernikov 	/*
18621832a7b3SAlexander V. Chernikov 	 * Use default set for looking up tables (old way) or
18631832a7b3SAlexander V. Chernikov 	 * use set rule is assigned to (new way).
18641832a7b3SAlexander V. Chernikov 	 */
18651832a7b3SAlexander V. Chernikov 	ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0;
18666c2997ffSAlexander V. Chernikov 	if (ci->ctlv != NULL) {
18676c2997ffSAlexander V. Chernikov 		ti.tlvs = (void *)(ci->ctlv + 1);
18686c2997ffSAlexander V. Chernikov 		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
18696c2997ffSAlexander V. Chernikov 	}
1870b074b7bbSAlexander V. Chernikov 
1871b074b7bbSAlexander V. Chernikov 	/*
18729490a627SAlexander V. Chernikov 	 * Stage 1: reference existing tables, determine number
18739490a627SAlexander V. Chernikov 	 * of tables we need to allocate and allocate indexes for each.
1874b074b7bbSAlexander V. Chernikov 	 */
18759490a627SAlexander V. Chernikov 	error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti);
1876b074b7bbSAlexander V. Chernikov 
1877b074b7bbSAlexander V. Chernikov 	if (error != 0) {
18789490a627SAlexander V. Chernikov 		if (pidx_first != ci->obuf)
18799490a627SAlexander V. Chernikov 			free(pidx_first, M_IPFW);
1880b074b7bbSAlexander V. Chernikov 
1881b074b7bbSAlexander V. Chernikov 		return (error);
1882b074b7bbSAlexander V. Chernikov 	}
1883b074b7bbSAlexander V. Chernikov 
1884b074b7bbSAlexander V. Chernikov 	/*
1885b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate table configs for every non-existent table
1886b074b7bbSAlexander V. Chernikov 	 */
1887b074b7bbSAlexander V. Chernikov 
18889f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
18899490a627SAlexander V. Chernikov 		for (p = pidx_first; p < pidx_last; p++) {
1890b074b7bbSAlexander V. Chernikov 			if (p->new == 0)
1891b074b7bbSAlexander V. Chernikov 				continue;
1892b074b7bbSAlexander V. Chernikov 
1893b074b7bbSAlexander V. Chernikov 			ti.uidx = p->uidx;
1894b074b7bbSAlexander V. Chernikov 			ti.type = p->type;
18959f7d47b0SAlexander V. Chernikov 			ti.atype = 0;
1896b074b7bbSAlexander V. Chernikov 
18979490a627SAlexander V. Chernikov 			ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL);
18989f7d47b0SAlexander V. Chernikov 			if (ta == NULL) {
18999f7d47b0SAlexander V. Chernikov 				error = ENOTSUP;
19009f7d47b0SAlexander V. Chernikov 				goto free;
19019f7d47b0SAlexander V. Chernikov 			}
19029490a627SAlexander V. Chernikov 			tc = alloc_table_config(ni, &ti, ta, NULL);
1903b074b7bbSAlexander V. Chernikov 
1904b074b7bbSAlexander V. Chernikov 			if (tc == NULL) {
1905b074b7bbSAlexander V. Chernikov 				error = ENOMEM;
1906b074b7bbSAlexander V. Chernikov 				goto free;
1907b074b7bbSAlexander V. Chernikov 			}
1908b074b7bbSAlexander V. Chernikov 
1909b074b7bbSAlexander V. Chernikov 			tc->no.kidx = p->kidx;
1910b074b7bbSAlexander V. Chernikov 			tc->no.refcnt = 1;
1911b074b7bbSAlexander V. Chernikov 
1912b074b7bbSAlexander V. Chernikov 			/* Add to list */
1913b074b7bbSAlexander V. Chernikov 			TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next);
1914b074b7bbSAlexander V. Chernikov 		}
1915b074b7bbSAlexander V. Chernikov 
1916b074b7bbSAlexander V. Chernikov 		/*
1917b074b7bbSAlexander V. Chernikov 		 * Stage 2.1: Check if we're going to create 2 tables
1918b074b7bbSAlexander V. Chernikov 		 * with the same name, but different table types.
1919b074b7bbSAlexander V. Chernikov 		 */
1920b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH(no, &nh, nn_next) {
1921b074b7bbSAlexander V. Chernikov 			TAILQ_FOREACH(no_tmp, &nh, nn_next) {
19229490a627SAlexander V. Chernikov 				if (ipfw_objhash_same_name(ni, no, no_tmp) == 0)
1923b074b7bbSAlexander V. Chernikov 					continue;
1924b074b7bbSAlexander V. Chernikov 				if (no->type != no_tmp->type) {
1925b074b7bbSAlexander V. Chernikov 					error = EINVAL;
1926b074b7bbSAlexander V. Chernikov 					goto free;
1927b074b7bbSAlexander V. Chernikov 				}
1928b074b7bbSAlexander V. Chernikov 			}
1929b074b7bbSAlexander V. Chernikov 		}
19309f7d47b0SAlexander V. Chernikov 	}
1931b074b7bbSAlexander V. Chernikov 
19329f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
19339f7d47b0SAlexander V. Chernikov 
19349f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
1935b074b7bbSAlexander V. Chernikov 		/*
1936b074b7bbSAlexander V. Chernikov 		 * Stage 3: link & reference new table configs
1937b074b7bbSAlexander V. Chernikov 		 */
1938b074b7bbSAlexander V. Chernikov 
1939b074b7bbSAlexander V. Chernikov 
1940b074b7bbSAlexander V. Chernikov 		/*
1941b074b7bbSAlexander V. Chernikov 		 * Step 3.1: Check if some tables we need to create have been
1942b074b7bbSAlexander V. Chernikov 		 * already created with different table type.
1943b074b7bbSAlexander V. Chernikov 		 */
1944b074b7bbSAlexander V. Chernikov 
1945b074b7bbSAlexander V. Chernikov 		error = 0;
1946b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1947b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1948b074b7bbSAlexander V. Chernikov 			if (no_n == NULL)
1949b074b7bbSAlexander V. Chernikov 				continue;
1950b074b7bbSAlexander V. Chernikov 
1951b074b7bbSAlexander V. Chernikov 			if (no_n->type != no->type) {
1952b074b7bbSAlexander V. Chernikov 				error = EINVAL;
1953b074b7bbSAlexander V. Chernikov 				break;
1954b074b7bbSAlexander V. Chernikov 			}
1955b074b7bbSAlexander V. Chernikov 
1956b074b7bbSAlexander V. Chernikov 		}
1957b074b7bbSAlexander V. Chernikov 
1958b074b7bbSAlexander V. Chernikov 		if (error != 0) {
1959b074b7bbSAlexander V. Chernikov 			/*
1960b074b7bbSAlexander V. Chernikov 			 * Someone has allocated table with different table type.
1961b074b7bbSAlexander V. Chernikov 			 * We have to rollback everything.
1962b074b7bbSAlexander V. Chernikov 			 */
1963b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(chain);
1964b074b7bbSAlexander V. Chernikov 			goto free;
1965b074b7bbSAlexander V. Chernikov 		}
1966b074b7bbSAlexander V. Chernikov 
1967b074b7bbSAlexander V. Chernikov 		/*
19689f7d47b0SAlexander V. Chernikov 		 * Attach new tables.
19699f7d47b0SAlexander V. Chernikov 		 * We need to set table pointers for each new table,
1970b074b7bbSAlexander V. Chernikov 		 * so we have to acquire main WLOCK.
1971b074b7bbSAlexander V. Chernikov 		 */
1972b074b7bbSAlexander V. Chernikov 		IPFW_WLOCK(chain);
1973b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1974b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1975b074b7bbSAlexander V. Chernikov 
19769490a627SAlexander V. Chernikov 			if (no_n == NULL) {
19779490a627SAlexander V. Chernikov 				/* New table. Attach to runtime hash */
19789490a627SAlexander V. Chernikov 				TAILQ_REMOVE(&nh, no, nn_next);
19799490a627SAlexander V. Chernikov 				link_table(chain, (struct table_config *)no);
1980b074b7bbSAlexander V. Chernikov 				continue;
1981b074b7bbSAlexander V. Chernikov 			}
1982b074b7bbSAlexander V. Chernikov 
19839490a627SAlexander V. Chernikov 			/*
19849490a627SAlexander V. Chernikov 			 * Newly-allocated table with the same type.
19859490a627SAlexander V. Chernikov 			 * Reference it and update out @pidx array
19869490a627SAlexander V. Chernikov 			 * rewrite info.
19879490a627SAlexander V. Chernikov 			 */
19889490a627SAlexander V. Chernikov 			no_n->refcnt++;
19899490a627SAlexander V. Chernikov 			/* Keep oib array in sync: update kidx */
19909490a627SAlexander V. Chernikov 			for (p = pidx_first; p < pidx_last; p++) {
19919490a627SAlexander V. Chernikov 				if (p->kidx != no->kidx)
19929490a627SAlexander V. Chernikov 					continue;
19939490a627SAlexander V. Chernikov 				/* Update kidx */
19949490a627SAlexander V. Chernikov 				p->kidx = no_n->kidx;
19959490a627SAlexander V. Chernikov 				break;
19969490a627SAlexander V. Chernikov 			}
1997b074b7bbSAlexander V. Chernikov 		}
1998b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(chain);
19999f7d47b0SAlexander V. Chernikov 	}
2000b074b7bbSAlexander V. Chernikov 
2001b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
2002b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
2003b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
2004b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
20059490a627SAlexander V. Chernikov 	p = pidx_first;
2006b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2007b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
2008b074b7bbSAlexander V. Chernikov 
2009b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
2010b074b7bbSAlexander V. Chernikov 			continue;
20119490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
20129490a627SAlexander V. Chernikov 		p++;
2013b074b7bbSAlexander V. Chernikov 	}
2014b074b7bbSAlexander V. Chernikov 
2015b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
2016b074b7bbSAlexander V. Chernikov 
2017b074b7bbSAlexander V. Chernikov 	error = 0;
2018b074b7bbSAlexander V. Chernikov 
2019b074b7bbSAlexander V. Chernikov 	/*
2020b074b7bbSAlexander V. Chernikov 	 * Stage 4: free resources
2021b074b7bbSAlexander V. Chernikov 	 */
2022b074b7bbSAlexander V. Chernikov free:
20239490a627SAlexander V. Chernikov 	if (!TAILQ_EMPTY(&nh)) {
20249490a627SAlexander V. Chernikov 		/* Free indexes first */
20259490a627SAlexander V. Chernikov 		IPFW_UH_WLOCK(chain);
20269490a627SAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
2027ac35ff17SAlexander V. Chernikov 			ipfw_objhash_free_idx(ni, no->kidx);
20289490a627SAlexander V. Chernikov 		}
20299490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
20309490a627SAlexander V. Chernikov 		/* Free configs */
2031b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp)
2032b074b7bbSAlexander V. Chernikov 			free_table_config(ni, tc);
20339490a627SAlexander V. Chernikov 	}
2034b074b7bbSAlexander V. Chernikov 
20359490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
20369490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
2037b074b7bbSAlexander V. Chernikov 
2038b074b7bbSAlexander V. Chernikov 	return (error);
2039b074b7bbSAlexander V. Chernikov }
2040b074b7bbSAlexander V. Chernikov 
2041b074b7bbSAlexander V. Chernikov /*
2042b074b7bbSAlexander V. Chernikov  * Remove references from every table used in @rule.
2043b074b7bbSAlexander V. Chernikov  */
2044b074b7bbSAlexander V. Chernikov void
2045b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
2046b074b7bbSAlexander V. Chernikov {
2047b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
2048b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
2049b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
2050b074b7bbSAlexander V. Chernikov 	struct named_object *no;
2051b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2052b074b7bbSAlexander V. Chernikov 	uint8_t type;
2053b074b7bbSAlexander V. Chernikov 
2054b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
2055b074b7bbSAlexander V. Chernikov 
2056b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
2057b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
2058b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
2059b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2060b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
2061b074b7bbSAlexander V. Chernikov 
2062b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
2063b074b7bbSAlexander V. Chernikov 			continue;
2064b074b7bbSAlexander V. Chernikov 
2065ac35ff17SAlexander V. Chernikov 		no = ipfw_objhash_lookup_kidx(ni, kidx);
2066b074b7bbSAlexander V. Chernikov 
2067b074b7bbSAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
2068b074b7bbSAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
2069b074b7bbSAlexander V. Chernikov 		    no->type, type, kidx));
2070b074b7bbSAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
2071b074b7bbSAlexander V. Chernikov 		    kidx, no->refcnt));
2072b074b7bbSAlexander V. Chernikov 
2073b074b7bbSAlexander V. Chernikov 		no->refcnt--;
2074b074b7bbSAlexander V. Chernikov 	}
2075b074b7bbSAlexander V. Chernikov }
2076b074b7bbSAlexander V. Chernikov 
2077b074b7bbSAlexander V. Chernikov 
2078b074b7bbSAlexander V. Chernikov /*
2079b074b7bbSAlexander V. Chernikov  * Removes table bindings for every rule in rule chain @head.
2080b074b7bbSAlexander V. Chernikov  */
2081b074b7bbSAlexander V. Chernikov void
2082b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head)
2083b074b7bbSAlexander V. Chernikov {
2084b074b7bbSAlexander V. Chernikov 	struct ip_fw *rule;
2085b074b7bbSAlexander V. Chernikov 
2086b074b7bbSAlexander V. Chernikov 	while ((rule = head) != NULL) {
2087b074b7bbSAlexander V. Chernikov 		head = head->x_next;
2088b074b7bbSAlexander V. Chernikov 		ipfw_unbind_table_rule(chain, rule);
2089b074b7bbSAlexander V. Chernikov 	}
2090b074b7bbSAlexander V. Chernikov }
2091b074b7bbSAlexander V. Chernikov 
2092b074b7bbSAlexander V. Chernikov 
20933b3a8eb9SGleb Smirnoff /* end of file */
2094