xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision 563b5ab1)
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  *
32563b5ab1SAlexander V. Chernikov  * This file containg 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;
78b074b7bbSAlexander V. Chernikov 	uint8_t		ftype;		/* 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);
106f1220db8SAlexander V. Chernikov static void export_table_info(struct table_config *tc, ipfw_xtable_info *i);
107f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg);
108b074b7bbSAlexander V. Chernikov 
1092d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
1102d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
111d3a4f924SAlexander V. Chernikov 
1129f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1139490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
114b074b7bbSAlexander V. Chernikov 
115b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
116b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1179f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
118b074b7bbSAlexander V. Chernikov 
119b074b7bbSAlexander V. Chernikov 
1203b3a8eb9SGleb Smirnoff 
1213b3a8eb9SGleb Smirnoff int
122b074b7bbSAlexander V. Chernikov ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
123b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
1243b3a8eb9SGleb Smirnoff {
125b074b7bbSAlexander V. Chernikov 	struct table_config *tc, *tc_new;
1269f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
127b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
128b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1299f7d47b0SAlexander V. Chernikov 	int error;
1309f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
1313b3a8eb9SGleb Smirnoff 
1329f7d47b0SAlexander V. Chernikov #if 0
133b074b7bbSAlexander V. Chernikov 	if (ti->uidx >= V_fw_tables_max)
1343b3a8eb9SGleb Smirnoff 		return (EINVAL);
1353b3a8eb9SGleb Smirnoff #endif
1369f7d47b0SAlexander V. Chernikov 
1379f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1389f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1399f7d47b0SAlexander V. Chernikov 
1409f7d47b0SAlexander V. Chernikov 	/*
1419f7d47b0SAlexander V. Chernikov 	 * Find and reference existing table.
1429f7d47b0SAlexander V. Chernikov 	 */
1439f7d47b0SAlexander V. Chernikov 	ta = NULL;
1449f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
1459f7d47b0SAlexander V. Chernikov 		/* check table type */
1469f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
1479f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1483b3a8eb9SGleb Smirnoff 			return (EINVAL);
1493b3a8eb9SGleb Smirnoff 		}
1503b3a8eb9SGleb Smirnoff 
1519f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
1529f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
1539f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
1549f7d47b0SAlexander V. Chernikov 	}
1559f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1563b3a8eb9SGleb Smirnoff 
1579f7d47b0SAlexander V. Chernikov 	tc_new = NULL;
1589f7d47b0SAlexander V. Chernikov 	if (ta == NULL) {
1599f7d47b0SAlexander V. Chernikov 		/* Table not found. We have to create new one */
1609490a627SAlexander V. Chernikov 		if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL)
1619f7d47b0SAlexander V. Chernikov 			return (ENOTSUP);
1623b3a8eb9SGleb Smirnoff 
1639490a627SAlexander V. Chernikov 		tc_new = alloc_table_config(ni, ti, ta, NULL);
1649f7d47b0SAlexander V. Chernikov 		if (tc_new == NULL)
1659f7d47b0SAlexander V. Chernikov 			return (ENOMEM);
1669f7d47b0SAlexander V. Chernikov 	}
1673b3a8eb9SGleb Smirnoff 
1689f7d47b0SAlexander V. Chernikov 	/* Prepare record (allocate memory) */
1699f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
1709f7d47b0SAlexander V. Chernikov 	error = ta->prepare_add(tei, &ta_buf);
1719f7d47b0SAlexander V. Chernikov 	if (error != 0) {
1729f7d47b0SAlexander V. Chernikov 		if (tc_new != NULL)
1739f7d47b0SAlexander V. Chernikov 			free_table_config(ni, tc_new);
1749f7d47b0SAlexander V. Chernikov 		return (error);
1753b3a8eb9SGleb Smirnoff 	}
1763b3a8eb9SGleb Smirnoff 
177b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1783b3a8eb9SGleb Smirnoff 
179b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1803b3a8eb9SGleb Smirnoff 
1819f7d47b0SAlexander V. Chernikov 	if (tc == NULL) {
1829f7d47b0SAlexander V. Chernikov 		/* Check if another table was allocated by other thread */
183b074b7bbSAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
1849f7d47b0SAlexander V. Chernikov 
1859f7d47b0SAlexander V. Chernikov 			/*
1869f7d47b0SAlexander V. Chernikov 			 * Check if algoritm is the same since we've
1879f7d47b0SAlexander V. Chernikov 			 * already allocated state using @ta algoritm
1889f7d47b0SAlexander V. Chernikov 			 * callbacks.
1899f7d47b0SAlexander V. Chernikov 			 */
1909f7d47b0SAlexander V. Chernikov 			if (tc->ta != ta) {
191b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
192b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
1933b3a8eb9SGleb Smirnoff 				return (EINVAL);
1943b3a8eb9SGleb Smirnoff 			}
1953b3a8eb9SGleb Smirnoff 		} else {
1963b3a8eb9SGleb Smirnoff 			/*
1979f7d47b0SAlexander V. Chernikov 			 * We're first to create this table.
198b074b7bbSAlexander V. Chernikov 			 * Set tc_new to zero not to free it afterwards.
1993b3a8eb9SGleb Smirnoff 			 */
200b074b7bbSAlexander V. Chernikov 			tc = tc_new;
201b074b7bbSAlexander V. Chernikov 			tc_new = NULL;
202b074b7bbSAlexander V. Chernikov 
203b074b7bbSAlexander V. Chernikov 			/* Allocate table index. */
204b074b7bbSAlexander V. Chernikov 			if (ipfw_objhash_alloc_idx(ni, ti->set, &kidx) != 0) {
205b074b7bbSAlexander V. Chernikov 				/* Index full. */
206b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
207b074b7bbSAlexander V. Chernikov 				printf("Unable to allocate index for table %s."
208b074b7bbSAlexander V. Chernikov 				    " Consider increasing "
209b074b7bbSAlexander V. Chernikov 				    "net.inet.ip.fw.tables_max",
210b074b7bbSAlexander V. Chernikov 				    tc->no.name);
211b074b7bbSAlexander V. Chernikov 				free_table_config(ni, tc);
212b074b7bbSAlexander V. Chernikov 				return (EBUSY);
2133b3a8eb9SGleb Smirnoff 			}
214b074b7bbSAlexander V. Chernikov 			/* Save kidx */
215b074b7bbSAlexander V. Chernikov 			tc->no.kidx = kidx;
216b074b7bbSAlexander V. Chernikov 		}
217b074b7bbSAlexander V. Chernikov 	} else {
2189f7d47b0SAlexander V. Chernikov 		/* Drop reference we've used in first search */
2199f7d47b0SAlexander V. Chernikov 		tc->no.refcnt--;
220b074b7bbSAlexander V. Chernikov 	}
221b074b7bbSAlexander V. Chernikov 
222b074b7bbSAlexander V. Chernikov 	/* We've got valid table in @tc. Let's add data */
2239f7d47b0SAlexander V. Chernikov 	kidx = tc->no.kidx;
2249f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2259f7d47b0SAlexander V. Chernikov 
226b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
227b074b7bbSAlexander V. Chernikov 
228b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0) {
229b074b7bbSAlexander V. Chernikov 		link_table(ch, tc);
230b074b7bbSAlexander V. Chernikov 	}
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 
2369f7d47b0SAlexander V. Chernikov 	if (error == 0)
2379f7d47b0SAlexander V. Chernikov 		tc->count++;
2389f7d47b0SAlexander V. Chernikov 
239b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
240b074b7bbSAlexander V. Chernikov 
241b074b7bbSAlexander V. Chernikov 	if (tc_new != NULL)
242b074b7bbSAlexander V. Chernikov 		free_table_config(ni, tc);
2433b3a8eb9SGleb Smirnoff 
2449f7d47b0SAlexander V. Chernikov 	if (error != 0)
2459f7d47b0SAlexander V. Chernikov 		ta->flush_entry(tei, &ta_buf);
246b074b7bbSAlexander V. Chernikov 
2479f7d47b0SAlexander V. Chernikov 	return (error);
2483b3a8eb9SGleb Smirnoff }
2493b3a8eb9SGleb Smirnoff 
2503b3a8eb9SGleb Smirnoff int
251b074b7bbSAlexander V. Chernikov ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
252b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
2533b3a8eb9SGleb Smirnoff {
254b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2559f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
256b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
257b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2589f7d47b0SAlexander V. Chernikov 	int error;
2599f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
2603b3a8eb9SGleb Smirnoff 
2619f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
262b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
263b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
2649f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2653b3a8eb9SGleb Smirnoff 		return (ESRCH);
2663b3a8eb9SGleb Smirnoff 	}
2673b3a8eb9SGleb Smirnoff 
268b074b7bbSAlexander V. Chernikov 	if (tc->no.type != ti->type) {
2699f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2703b3a8eb9SGleb Smirnoff 		return (EINVAL);
2713b3a8eb9SGleb Smirnoff 	}
2729f7d47b0SAlexander V. Chernikov 
2739f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2749f7d47b0SAlexander V. Chernikov 
2759f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
2769f7d47b0SAlexander V. Chernikov 	if ((error = ta->prepare_del(tei, &ta_buf)) != 0) {
2779f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2789f7d47b0SAlexander V. Chernikov 		return (error);
2799f7d47b0SAlexander V. Chernikov 	}
2809f7d47b0SAlexander V. Chernikov 
281b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
282b074b7bbSAlexander V. Chernikov 
283b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
2849f7d47b0SAlexander V. Chernikov 	error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2853b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2863b3a8eb9SGleb Smirnoff 
2879f7d47b0SAlexander V. Chernikov 	if (error == 0)
2889f7d47b0SAlexander V. Chernikov 		tc->count--;
289b074b7bbSAlexander V. Chernikov 
2909f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
2913b3a8eb9SGleb Smirnoff 
2929f7d47b0SAlexander V. Chernikov 	if (error != 0)
2939f7d47b0SAlexander V. Chernikov 		return (error);
2943b3a8eb9SGleb Smirnoff 
2959f7d47b0SAlexander V. Chernikov 	ta->flush_entry(tei, &ta_buf);
2963b3a8eb9SGleb Smirnoff 	return (0);
2973b3a8eb9SGleb Smirnoff }
2983b3a8eb9SGleb Smirnoff 
299b074b7bbSAlexander V. Chernikov /*
3009f7d47b0SAlexander V. Chernikov  * Flushes all entries in given table.
301b074b7bbSAlexander V. Chernikov  */
3023b3a8eb9SGleb Smirnoff int
303b074b7bbSAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
3043b3a8eb9SGleb Smirnoff {
305b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
306b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
3079f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
3089f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
3099f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
310b074b7bbSAlexander V. Chernikov 	int error;
311b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
3123b3a8eb9SGleb Smirnoff 
3133b3a8eb9SGleb Smirnoff 	/*
3149f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
315b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
3163b3a8eb9SGleb Smirnoff 	 */
317b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
318b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
319b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
320b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
321b074b7bbSAlexander V. Chernikov 		return (ESRCH);
322b074b7bbSAlexander V. Chernikov 	}
3239f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
324b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
325b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3263b3a8eb9SGleb Smirnoff 
327b074b7bbSAlexander V. Chernikov 	/*
3289f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
3299490a627SAlexander V. Chernikov 	 * TODO: pass startup parametes somehow.
330b074b7bbSAlexander V. Chernikov 	 */
3319f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
3329490a627SAlexander V. Chernikov 	if ((error = ta->init(&astate_new, &ti_new, NULL)) != 0) {
333b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
334b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
335b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
336b074b7bbSAlexander V. Chernikov 		return (error);
337b074b7bbSAlexander V. Chernikov 	}
338b074b7bbSAlexander V. Chernikov 
339b074b7bbSAlexander V. Chernikov 	/*
340b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
341b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
342b074b7bbSAlexander V. Chernikov 	 */
343b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
344b074b7bbSAlexander V. Chernikov 
345b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
346b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
3479f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
348b074b7bbSAlexander V. Chernikov 
3499f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
3509f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
3519f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
3529f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
353b074b7bbSAlexander V. Chernikov 
3549f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
3559f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
3569f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
3579f7d47b0SAlexander V. Chernikov 	tc->count = 0;
358b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
359b074b7bbSAlexander V. Chernikov 
360b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3613b3a8eb9SGleb Smirnoff 
362b074b7bbSAlexander V. Chernikov 	/*
363b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
364b074b7bbSAlexander V. Chernikov 	 */
3659f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
3663b3a8eb9SGleb Smirnoff 
3673b3a8eb9SGleb Smirnoff 	return (0);
3683b3a8eb9SGleb Smirnoff }
3693b3a8eb9SGleb Smirnoff 
370b074b7bbSAlexander V. Chernikov /*
3719f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
372b074b7bbSAlexander V. Chernikov  */
373b074b7bbSAlexander V. Chernikov int
3749f7d47b0SAlexander V. Chernikov ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
375b074b7bbSAlexander V. Chernikov {
376b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
377b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
378b074b7bbSAlexander V. Chernikov 
379b074b7bbSAlexander V. Chernikov 	ti->set = TABLE_SET(ti->set);
380b074b7bbSAlexander V. Chernikov 
381b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
382b074b7bbSAlexander V. Chernikov 
383b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
384b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
385b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
386b074b7bbSAlexander V. Chernikov 		return (ESRCH);
387b074b7bbSAlexander V. Chernikov 	}
388b074b7bbSAlexander V. Chernikov 
3899f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
3909f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
391b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
392b074b7bbSAlexander V. Chernikov 		return (EBUSY);
393b074b7bbSAlexander V. Chernikov 	}
394b074b7bbSAlexander V. Chernikov 
395b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
396b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
397b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
398b074b7bbSAlexander V. Chernikov 
399b074b7bbSAlexander V. Chernikov 	/* Free obj index */
400b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.set, tc->no.kidx) != 0)
401b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
402b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
403b074b7bbSAlexander V. Chernikov 
404b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
405b074b7bbSAlexander V. Chernikov 
406b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
407b074b7bbSAlexander V. Chernikov 
408b074b7bbSAlexander V. Chernikov 	return (0);
409b074b7bbSAlexander V. Chernikov }
410b074b7bbSAlexander V. Chernikov 
411b074b7bbSAlexander V. Chernikov static void
412b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
413b074b7bbSAlexander V. Chernikov     void *arg)
414b074b7bbSAlexander V. Chernikov {
415b074b7bbSAlexander V. Chernikov 
416b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
417b074b7bbSAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->set, no->kidx) != 0)
418b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
419b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
420b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
421b074b7bbSAlexander V. Chernikov }
422b074b7bbSAlexander V. Chernikov 
4233b3a8eb9SGleb Smirnoff void
4243b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
4253b3a8eb9SGleb Smirnoff {
4263b3a8eb9SGleb Smirnoff 
427b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
428b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
429b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
430b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
431b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
432b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
4333b3a8eb9SGleb Smirnoff 
4343b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
4359f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
4369f7d47b0SAlexander V. Chernikov 
4379f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
438b074b7bbSAlexander V. Chernikov 
439b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
440b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
4413b3a8eb9SGleb Smirnoff }
4423b3a8eb9SGleb Smirnoff 
4433b3a8eb9SGleb Smirnoff int
4443b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
4453b3a8eb9SGleb Smirnoff {
446b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
447b074b7bbSAlexander V. Chernikov 
4483b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
4499f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
4509f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
451b074b7bbSAlexander V. Chernikov 
452b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
453b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
454b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
455b074b7bbSAlexander V. Chernikov 
4569f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
4579f7d47b0SAlexander V. Chernikov 
4583b3a8eb9SGleb Smirnoff 	return (0);
4593b3a8eb9SGleb Smirnoff }
4603b3a8eb9SGleb Smirnoff 
4613b3a8eb9SGleb Smirnoff int
4623b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
4633b3a8eb9SGleb Smirnoff {
4643b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
465b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
4669f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
467b074b7bbSAlexander V. Chernikov 	int new_blocks;
4683b3a8eb9SGleb Smirnoff 
4693b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
4703b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
4713b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
4723b3a8eb9SGleb Smirnoff 
4733b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
4749f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
4759f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
4769f7d47b0SAlexander V. Chernikov 
477b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
4783b3a8eb9SGleb Smirnoff 
4799f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
4803b3a8eb9SGleb Smirnoff 
4813b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
482b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
483b074b7bbSAlexander V. Chernikov 
4849f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
4859f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
4869f7d47b0SAlexander V. Chernikov 
4879f7d47b0SAlexander V. Chernikov 		/*
4889f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
4899f7d47b0SAlexander V. Chernikov 		 */
4909f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
491b074b7bbSAlexander V. Chernikov 		return (EINVAL);
492b074b7bbSAlexander V. Chernikov 	}
4933b3a8eb9SGleb Smirnoff 
4949f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
4959f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
4969f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
4973b3a8eb9SGleb Smirnoff 
4989f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
4999f7d47b0SAlexander V. Chernikov 
5009f7d47b0SAlexander V. Chernikov 	/* Change pointers */
5019f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
5029f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
5039f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
5043b3a8eb9SGleb Smirnoff 
5053b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
5063b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
5073b3a8eb9SGleb Smirnoff 
5083b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
5099f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5103b3a8eb9SGleb Smirnoff 
5113b3a8eb9SGleb Smirnoff 	/* Free old pointers */
5129f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
513b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
5143b3a8eb9SGleb Smirnoff 
5153b3a8eb9SGleb Smirnoff 	return (0);
5163b3a8eb9SGleb Smirnoff }
5173b3a8eb9SGleb Smirnoff 
5183b3a8eb9SGleb Smirnoff int
5193b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
5203b3a8eb9SGleb Smirnoff     uint32_t *val)
5213b3a8eb9SGleb Smirnoff {
5229f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
5233b3a8eb9SGleb Smirnoff 
5249f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
5259f7d47b0SAlexander V. Chernikov 
5269f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
5273b3a8eb9SGleb Smirnoff }
5289f7d47b0SAlexander V. Chernikov 
5299f7d47b0SAlexander V. Chernikov int
5309f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
5319f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
5329f7d47b0SAlexander V. Chernikov {
5339f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
5349f7d47b0SAlexander V. Chernikov 
5359f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
5369f7d47b0SAlexander V. Chernikov 
5379f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
5389f7d47b0SAlexander V. Chernikov }
5399f7d47b0SAlexander V. Chernikov 
5409f7d47b0SAlexander V. Chernikov /*
5419f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
5429f7d47b0SAlexander V. Chernikov  *
5439f7d47b0SAlexander V. Chernikov  */
5449f7d47b0SAlexander V. Chernikov 
545f1220db8SAlexander V. Chernikov /*
546d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
547d3a4f924SAlexander V. Chernikov  */
548d3a4f924SAlexander V. Chernikov 
549d3a4f924SAlexander V. Chernikov /*
550d3a4f924SAlexander V. Chernikov  * Get buffer size needed to list info for all tables.
551d3a4f924SAlexander V. Chernikov  * Data layout:
552d3a4f924SAlexander V. Chernikov  * Request: [ empty ], size = sizeof(ipfw_obj_lheader)
553d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ]
554d3a4f924SAlexander V. Chernikov  *
555d3a4f924SAlexander V. Chernikov  * Returns 0 on success
556f1220db8SAlexander V. Chernikov  */
557f1220db8SAlexander V. Chernikov int
5582d99a349SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
559f1220db8SAlexander V. Chernikov {
560f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
561f1220db8SAlexander V. Chernikov 
5622d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
5632d99a349SAlexander V. Chernikov 	if (olh == NULL)
564d3a4f924SAlexander V. Chernikov 		return (EINVAL);
565d3a4f924SAlexander V. Chernikov 
566f1220db8SAlexander V. Chernikov 	olh->size = sizeof(*olh); /* Make export_table store needed size */
567f1220db8SAlexander V. Chernikov 
568f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
5692d99a349SAlexander V. Chernikov 	export_tables(ch, olh, sd);
570f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
571f1220db8SAlexander V. Chernikov 
5722d99a349SAlexander V. Chernikov 	return (0);
573f1220db8SAlexander V. Chernikov }
574f1220db8SAlexander V. Chernikov 
575d3a4f924SAlexander V. Chernikov /*
576d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
577d3a4f924SAlexander V. Chernikov  * Data layout:
578d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
579d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
580d3a4f924SAlexander V. Chernikov  *
581d3a4f924SAlexander V. Chernikov  * Returns 0 on success
582d3a4f924SAlexander V. Chernikov  */
583f1220db8SAlexander V. Chernikov int
5842d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
585f1220db8SAlexander V. Chernikov {
586f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
587d3a4f924SAlexander V. Chernikov 	uint32_t sz;
588f1220db8SAlexander V. Chernikov 	int error;
589f1220db8SAlexander V. Chernikov 
5902d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
5912d99a349SAlexander V. Chernikov 	if (olh == NULL)
592d3a4f924SAlexander V. Chernikov 		return (EINVAL);
593d3a4f924SAlexander V. Chernikov 
594f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
595f1220db8SAlexander V. Chernikov 	sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
5962d99a349SAlexander V. Chernikov 
5972d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
5982d99a349SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
5992d99a349SAlexander V. Chernikov 		return (ENOMEM);
6002d99a349SAlexander V. Chernikov 	}
6012d99a349SAlexander V. Chernikov 
6022d99a349SAlexander V. Chernikov 	error = export_tables(ch, olh, sd);
603f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
604f1220db8SAlexander V. Chernikov 
605f1220db8SAlexander V. Chernikov 	return (error);
606f1220db8SAlexander V. Chernikov }
607f1220db8SAlexander V. Chernikov 
608f1220db8SAlexander V. Chernikov /*
6092d99a349SAlexander V. Chernikov  * Store table info to buffer provided by @sd.
610d3a4f924SAlexander V. Chernikov  * Data layout:
611d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
612d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
613d3a4f924SAlexander V. Chernikov  *
614d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
615d3a4f924SAlexander V. Chernikov  */
616d3a4f924SAlexander V. Chernikov int
6172d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
618d3a4f924SAlexander V. Chernikov {
619d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
620d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
621d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
622d3a4f924SAlexander V. Chernikov 	size_t sz;
623d3a4f924SAlexander V. Chernikov 
624d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
6252d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
6262d99a349SAlexander V. Chernikov 	if (oh == NULL)
627d3a4f924SAlexander V. Chernikov 		return (EINVAL);
628d3a4f924SAlexander V. Chernikov 
629d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
630d3a4f924SAlexander V. Chernikov 
631d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
632d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
633d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
634d3a4f924SAlexander V. Chernikov 		return (ESRCH);
635d3a4f924SAlexander V. Chernikov 	}
636d3a4f924SAlexander V. Chernikov 
637d3a4f924SAlexander V. Chernikov 	export_table_info(tc, (ipfw_xtable_info *)(oh + 1));
638d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
639d3a4f924SAlexander V. Chernikov 
6402d99a349SAlexander V. Chernikov 	return (0);
641d3a4f924SAlexander V. Chernikov }
642d3a4f924SAlexander V. Chernikov 
643f1220db8SAlexander V. Chernikov struct dump_args {
644f1220db8SAlexander V. Chernikov 	struct table_info *ti;
645f1220db8SAlexander V. Chernikov 	struct table_config *tc;
6462d99a349SAlexander V. Chernikov 	struct sockopt_data *sd;
647f1220db8SAlexander V. Chernikov 	uint32_t cnt;
648f1220db8SAlexander V. Chernikov 	uint16_t uidx;
6492d99a349SAlexander V. Chernikov 	ipfw_table_entry *ent;
6502d99a349SAlexander V. Chernikov 	uint32_t size;
651f1220db8SAlexander V. Chernikov };
652f1220db8SAlexander V. Chernikov 
653f1220db8SAlexander V. Chernikov int
6542d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
6552d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
656f1220db8SAlexander V. Chernikov {
657d3a4f924SAlexander V. Chernikov 	int error;
658d3a4f924SAlexander V. Chernikov 
659d3a4f924SAlexander V. Chernikov 	switch (op3->version) {
660d3a4f924SAlexander V. Chernikov 	case 0:
6612d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sd);
662d3a4f924SAlexander V. Chernikov 		break;
663d3a4f924SAlexander V. Chernikov 	case 1:
6642d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sd);
665d3a4f924SAlexander V. Chernikov 		break;
666d3a4f924SAlexander V. Chernikov 	default:
667d3a4f924SAlexander V. Chernikov 		error = ENOTSUP;
668d3a4f924SAlexander V. Chernikov 	}
669d3a4f924SAlexander V. Chernikov 
670d3a4f924SAlexander V. Chernikov 	return (error);
671d3a4f924SAlexander V. Chernikov }
672d3a4f924SAlexander V. Chernikov 
673d3a4f924SAlexander V. Chernikov /*
674d3a4f924SAlexander V. Chernikov  * Dumps all table data
6752d99a349SAlexander V. Chernikov  * Data layout (version 1)(current):
6762d99a349SAlexander V. Chernikov  * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
6772d99a349SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ]
678d3a4f924SAlexander V. Chernikov  *
679d3a4f924SAlexander V. Chernikov  * Returns 0 on success
680d3a4f924SAlexander V. Chernikov  */
681d3a4f924SAlexander V. Chernikov static int
6822d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
683d3a4f924SAlexander V. Chernikov {
684f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
685f1220db8SAlexander V. Chernikov 	ipfw_xtable_info *i;
686f1220db8SAlexander V. Chernikov 	struct tid_info ti;
687f1220db8SAlexander V. Chernikov 	struct table_config *tc;
688f1220db8SAlexander V. Chernikov 	struct table_algo *ta;
689f1220db8SAlexander V. Chernikov 	struct dump_args da;
690d3a4f924SAlexander V. Chernikov 	uint32_t sz;
691f1220db8SAlexander V. Chernikov 
6922d99a349SAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
6932d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
6942d99a349SAlexander V. Chernikov 	if (oh == NULL)
695d3a4f924SAlexander V. Chernikov 		return (EINVAL);
696d3a4f924SAlexander V. Chernikov 
6972d99a349SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
698d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
699f1220db8SAlexander V. Chernikov 
700f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
701f1220db8SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
702f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
703f1220db8SAlexander V. Chernikov 		return (ESRCH);
704f1220db8SAlexander V. Chernikov 	}
705f1220db8SAlexander V. Chernikov 	export_table_info(tc, i);
7062d99a349SAlexander V. Chernikov 	sz = tc->count;
7072d99a349SAlexander V. Chernikov 
7082d99a349SAlexander V. Chernikov 	if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) {
7092d99a349SAlexander V. Chernikov 
7102d99a349SAlexander V. Chernikov 		/*
7112d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
7122d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
7132d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
7142d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
7152d99a349SAlexander V. Chernikov 		 */
716f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
7172d99a349SAlexander V. Chernikov 		return (ENOMEM);
718f1220db8SAlexander V. Chernikov 	}
719f1220db8SAlexander V. Chernikov 
720f1220db8SAlexander V. Chernikov 	/*
721f1220db8SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
722f1220db8SAlexander V. Chernikov 	 */
723d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
724f1220db8SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
725f1220db8SAlexander V. Chernikov 	da.tc = tc;
7262d99a349SAlexander V. Chernikov 	da.sd = sd;
727f1220db8SAlexander V. Chernikov 
728f1220db8SAlexander V. Chernikov 	ta = tc->ta;
729f1220db8SAlexander V. Chernikov 
730f1220db8SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
731f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
732f1220db8SAlexander V. Chernikov 
733f1220db8SAlexander V. Chernikov 	return (0);
734f1220db8SAlexander V. Chernikov }
735f1220db8SAlexander V. Chernikov 
736d3a4f924SAlexander V. Chernikov /*
737d3a4f924SAlexander V. Chernikov  * Dumps all table data
7382d99a349SAlexander V. Chernikov  * Data layout (version 0)(legacy):
739d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
740d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
741d3a4f924SAlexander V. Chernikov  *
742d3a4f924SAlexander V. Chernikov  * Returns 0 on success
743d3a4f924SAlexander V. Chernikov  */
744d3a4f924SAlexander V. Chernikov static int
7452d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
746d3a4f924SAlexander V. Chernikov {
747d3a4f924SAlexander V. Chernikov 	ipfw_xtable *xtbl;
748d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
749d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
750d3a4f924SAlexander V. Chernikov 	struct table_algo *ta;
751d3a4f924SAlexander V. Chernikov 	struct dump_args da;
752d3a4f924SAlexander V. Chernikov 	size_t sz;
753d3a4f924SAlexander V. Chernikov 
7542d99a349SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
7552d99a349SAlexander V. Chernikov 	if (xtbl == NULL)
756d3a4f924SAlexander V. Chernikov 		return (EINVAL);
757d3a4f924SAlexander V. Chernikov 
758d3a4f924SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
759d3a4f924SAlexander V. Chernikov 	ti.set = 0; /* XXX: No way to specify set */
760d3a4f924SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
761d3a4f924SAlexander V. Chernikov 
762d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
763d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
764d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
765d3a4f924SAlexander V. Chernikov 		return (0);
766d3a4f924SAlexander V. Chernikov 	}
767d3a4f924SAlexander V. Chernikov 	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
7682d99a349SAlexander V. Chernikov 
7692d99a349SAlexander V. Chernikov 	xtbl->cnt = tc->count;
7702d99a349SAlexander V. Chernikov 	xtbl->size = sz;
7712d99a349SAlexander V. Chernikov 	xtbl->type = tc->no.type;
7722d99a349SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
7732d99a349SAlexander V. Chernikov 
7742d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
7752d99a349SAlexander V. Chernikov 
7762d99a349SAlexander V. Chernikov 		/*
7772d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
7782d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
7792d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
7802d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
7812d99a349SAlexander V. Chernikov 		 */
782d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
7832d99a349SAlexander V. Chernikov 		return (ENOMEM);
784d3a4f924SAlexander V. Chernikov 	}
785d3a4f924SAlexander V. Chernikov 
786d3a4f924SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
787d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
788d3a4f924SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
789d3a4f924SAlexander V. Chernikov 	da.tc = tc;
7902d99a349SAlexander V. Chernikov 	da.sd = sd;
7912d99a349SAlexander V. Chernikov 
792d3a4f924SAlexander V. Chernikov 	ta = tc->ta;
793d3a4f924SAlexander V. Chernikov 
794d3a4f924SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
795d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
796d3a4f924SAlexander V. Chernikov 
7972d99a349SAlexander V. Chernikov 	return (0);
798d3a4f924SAlexander V. Chernikov }
799d3a4f924SAlexander V. Chernikov 
800d3a4f924SAlexander V. Chernikov /*
8019490a627SAlexander V. Chernikov  * High-level setsockopt cmds
8029490a627SAlexander V. Chernikov  */
8039490a627SAlexander V. Chernikov int
8049490a627SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt,
8059490a627SAlexander V. Chernikov     ip_fw3_opheader *op3)
8069490a627SAlexander V. Chernikov {
8079490a627SAlexander V. Chernikov 
8089490a627SAlexander V. Chernikov 	return (ENOTSUP);
8099490a627SAlexander V. Chernikov }
8109490a627SAlexander V. Chernikov 
8119490a627SAlexander V. Chernikov /*
8129490a627SAlexander V. Chernikov  * Creates new table.
8139490a627SAlexander V. Chernikov  * Data layout:
8149490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
8159490a627SAlexander V. Chernikov  *
8169490a627SAlexander V. Chernikov  * Returns 0 on success
8179490a627SAlexander V. Chernikov  */
8189490a627SAlexander V. Chernikov int
8199490a627SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
8209490a627SAlexander V. Chernikov     ip_fw3_opheader *op3)
8219490a627SAlexander V. Chernikov {
8229490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
8239490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
8249490a627SAlexander V. Chernikov 	char *tname, *aname;
8259490a627SAlexander V. Chernikov 	struct tid_info ti;
8269490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
8279490a627SAlexander V. Chernikov 	struct table_config *tc;
8289490a627SAlexander V. Chernikov 	struct table_algo *ta;
8299490a627SAlexander V. Chernikov 	uint16_t kidx;
8309490a627SAlexander V. Chernikov 
8319490a627SAlexander V. Chernikov 	if (sopt->sopt_valsize < sizeof(*oh) + sizeof(ipfw_xtable_info))
8329490a627SAlexander V. Chernikov 		return (EINVAL);
8339490a627SAlexander V. Chernikov 
8349490a627SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
8359490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
8369490a627SAlexander V. Chernikov 
8379490a627SAlexander V. Chernikov 	/*
8389490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
8392d99a349SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
8409490a627SAlexander V. Chernikov 	 */
8419490a627SAlexander V. Chernikov 	tname = i->tablename;
8429490a627SAlexander V. Chernikov 	aname = i->algoname;
8439490a627SAlexander V. Chernikov 	if (strnlen(tname, sizeof(i->tablename)) == sizeof(i->tablename) ||
8449490a627SAlexander V. Chernikov 	    tname[0] == '\0' ||
8459490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
8469490a627SAlexander V. Chernikov 		return (EINVAL);
8479490a627SAlexander V. Chernikov 
8489490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
8499490a627SAlexander V. Chernikov 		/* Use default algorithm */
8509490a627SAlexander V. Chernikov 		aname = NULL;
8519490a627SAlexander V. Chernikov 	}
8529490a627SAlexander V. Chernikov 
8539490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
8549490a627SAlexander V. Chernikov 
8559490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
8569490a627SAlexander V. Chernikov 
8579490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
8589490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
8599490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
8609490a627SAlexander V. Chernikov 		return (EEXIST);
8619490a627SAlexander V. Chernikov 	}
8629490a627SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), &ti, aname);
8639490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
8649490a627SAlexander V. Chernikov 
8659490a627SAlexander V. Chernikov 	if (ta == NULL)
8669490a627SAlexander V. Chernikov 		return (ENOTSUP);
8679490a627SAlexander V. Chernikov 
8689490a627SAlexander V. Chernikov 	if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL)
8699490a627SAlexander V. Chernikov 		return (ENOMEM);
8709490a627SAlexander V. Chernikov 
8719490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
8729490a627SAlexander V. Chernikov 	if (ipfw_objhash_alloc_idx(ni, ti.set, &kidx) != 0) {
8739490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
8749490a627SAlexander V. Chernikov 		printf("Unable to allocate table index for table %s in set %u."
8759490a627SAlexander V. Chernikov 		    " Consider increasing net.inet.ip.fw.tables_max",
8769490a627SAlexander V. Chernikov 		    tname, ti.set);
8779490a627SAlexander V. Chernikov 		free_table_config(ni, tc);
8789490a627SAlexander V. Chernikov 		return (EBUSY);
8799490a627SAlexander V. Chernikov 	}
8809490a627SAlexander V. Chernikov 
8819490a627SAlexander V. Chernikov 	tc->no.kidx = kidx;
8829490a627SAlexander V. Chernikov 
8839490a627SAlexander V. Chernikov 	IPFW_WLOCK(ch);
8849490a627SAlexander V. Chernikov 	link_table(ch, tc);
8859490a627SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
8869490a627SAlexander V. Chernikov 
8879490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
8889490a627SAlexander V. Chernikov 
8899490a627SAlexander V. Chernikov 	return (0);
8909490a627SAlexander V. Chernikov }
8919490a627SAlexander V. Chernikov 
892d3a4f924SAlexander V. Chernikov void
893d3a4f924SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
894d3a4f924SAlexander V. Chernikov {
895d3a4f924SAlexander V. Chernikov 
896d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
897d3a4f924SAlexander V. Chernikov 	ti->set = oh->set;
898d3a4f924SAlexander V. Chernikov 	ti->uidx = oh->idx;
899d3a4f924SAlexander V. Chernikov 	ti->tlvs = &oh->ntlv;
900d3a4f924SAlexander V. Chernikov 	ti->tlen = oh->ntlv.head.length;
901d3a4f924SAlexander V. Chernikov }
902d3a4f924SAlexander V. Chernikov 
903563b5ab1SAlexander V. Chernikov int
904563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
905563b5ab1SAlexander V. Chernikov     struct sockopt_data *sd)
906563b5ab1SAlexander V. Chernikov {
907563b5ab1SAlexander V. Chernikov 	struct namedobj_instance *ni;
908563b5ab1SAlexander V. Chernikov 	struct named_object *no;
909563b5ab1SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
910563b5ab1SAlexander V. Chernikov 
911563b5ab1SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
912563b5ab1SAlexander V. Chernikov 
913563b5ab1SAlexander V. Chernikov 	no = ipfw_objhash_lookup_idx(ni, 0, kidx);
914563b5ab1SAlexander V. Chernikov 	KASSERT(no != NULL, ("invalid table kidx passed"));
915563b5ab1SAlexander V. Chernikov 
916563b5ab1SAlexander V. Chernikov 	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
917563b5ab1SAlexander V. Chernikov 	if (ntlv == NULL)
918563b5ab1SAlexander V. Chernikov 		return (ENOMEM);
919563b5ab1SAlexander V. Chernikov 
920563b5ab1SAlexander V. Chernikov 	ntlv->head.type = IPFW_TLV_TBL_NAME;
921563b5ab1SAlexander V. Chernikov 	ntlv->head.length = sizeof(*ntlv);
922563b5ab1SAlexander V. Chernikov 	ntlv->idx = no->kidx;
923563b5ab1SAlexander V. Chernikov 	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
924563b5ab1SAlexander V. Chernikov 
925563b5ab1SAlexander V. Chernikov 	return (0);
926563b5ab1SAlexander V. Chernikov }
927563b5ab1SAlexander V. Chernikov 
9289f7d47b0SAlexander V. Chernikov static void
9299f7d47b0SAlexander V. Chernikov export_table_info(struct table_config *tc, ipfw_xtable_info *i)
9309f7d47b0SAlexander V. Chernikov {
9319f7d47b0SAlexander V. Chernikov 
9329f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
9339f7d47b0SAlexander V. Chernikov 	i->ftype = tc->ftype;
9349f7d47b0SAlexander V. Chernikov 	i->atype = tc->ta->idx;
9359f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
9369f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
9379f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
9389f7d47b0SAlexander V. Chernikov 	i->count = tc->count;
9399f7d47b0SAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_table_xentry);
940f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
9419f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
9429f7d47b0SAlexander V. Chernikov }
9439f7d47b0SAlexander V. Chernikov 
9449f7d47b0SAlexander V. Chernikov static void
9459f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
9469f7d47b0SAlexander V. Chernikov     void *arg)
9473b3a8eb9SGleb Smirnoff {
9489f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
9492d99a349SAlexander V. Chernikov 	struct sockopt_data *sd;
9503b3a8eb9SGleb Smirnoff 
9512d99a349SAlexander V. Chernikov 	sd = (struct sockopt_data *)arg;
9522d99a349SAlexander V. Chernikov 	i = (ipfw_xtable_info *)ipfw_get_sopt_space(sd, sizeof(*i));
9532d99a349SAlexander V. Chernikov 	KASSERT(i == 0, ("previously checked buffer is not enough"));
9549f7d47b0SAlexander V. Chernikov 
9559f7d47b0SAlexander V. Chernikov 	export_table_info((struct table_config *)no, i);
9569f7d47b0SAlexander V. Chernikov }
9579f7d47b0SAlexander V. Chernikov 
958f1220db8SAlexander V. Chernikov /*
959f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
9602d99a349SAlexander V. Chernikov  * storage provided by @sd.
961f1220db8SAlexander V. Chernikov  * Returns 0 on success.
962f1220db8SAlexander V. Chernikov  */
963f1220db8SAlexander V. Chernikov static int
9642d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
9652d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
9669f7d47b0SAlexander V. Chernikov {
9679f7d47b0SAlexander V. Chernikov 	uint32_t size;
9689f7d47b0SAlexander V. Chernikov 	uint32_t count;
9699f7d47b0SAlexander V. Chernikov 
9709f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
9719f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
9722d99a349SAlexander V. Chernikov 
9732d99a349SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
974f1220db8SAlexander V. Chernikov 	olh->count = count;
975f1220db8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
9762d99a349SAlexander V. Chernikov 
9772d99a349SAlexander V. Chernikov 	if (size > olh->size) {
9782d99a349SAlexander V. Chernikov 		/* Store necessary size */
9792d99a349SAlexander V. Chernikov 		olh->size = size;
9809f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
981f1220db8SAlexander V. Chernikov 	}
9829f7d47b0SAlexander V. Chernikov 	olh->size = size;
9832d99a349SAlexander V. Chernikov 
9842d99a349SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, sd);
9859f7d47b0SAlexander V. Chernikov 
9863b3a8eb9SGleb Smirnoff 	return (0);
9873b3a8eb9SGleb Smirnoff }
9883b3a8eb9SGleb Smirnoff 
9893b3a8eb9SGleb Smirnoff int
990b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
9913b3a8eb9SGleb Smirnoff {
992b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
9933b3a8eb9SGleb Smirnoff 
994b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
995b074b7bbSAlexander V. Chernikov 		return (ESRCH);
9969f7d47b0SAlexander V. Chernikov 	*cnt = tc->count;
9973b3a8eb9SGleb Smirnoff 	return (0);
9983b3a8eb9SGleb Smirnoff }
9993b3a8eb9SGleb Smirnoff 
10009f7d47b0SAlexander V. Chernikov 
10019f7d47b0SAlexander V. Chernikov int
10029f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
10033b3a8eb9SGleb Smirnoff {
10049f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
10059f7d47b0SAlexander V. Chernikov 
1006d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
1007d3a4f924SAlexander V. Chernikov 		*cnt = 0;
10089f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
1009d3a4f924SAlexander V. Chernikov 	}
10109f7d47b0SAlexander V. Chernikov 	*cnt = tc->count * sizeof(ipfw_table_xentry);
10119f7d47b0SAlexander V. Chernikov 	if (tc->count > 0)
10129f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
10139f7d47b0SAlexander V. Chernikov 	return (0);
10149f7d47b0SAlexander V. Chernikov }
10159f7d47b0SAlexander V. Chernikov 
10169f7d47b0SAlexander V. Chernikov static int
10179f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
10189f7d47b0SAlexander V. Chernikov {
10199f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
10209f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
10219f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
10223b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
10233b3a8eb9SGleb Smirnoff 
10249f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
10259f7d47b0SAlexander V. Chernikov 
10269f7d47b0SAlexander V. Chernikov 	tc = da->tc;
10279f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
10289f7d47b0SAlexander V. Chernikov 
10299f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
1030f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
10313b3a8eb9SGleb Smirnoff 		return (1);
1032f1220db8SAlexander V. Chernikov 	ent = da->ent++;
1033f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
1034f1220db8SAlexander V. Chernikov 	da->cnt++;
10359f7d47b0SAlexander V. Chernikov 
10369f7d47b0SAlexander V. Chernikov 	return (ta->dump_entry(tc->astate, da->ti, e, ent));
10373b3a8eb9SGleb Smirnoff }
10383b3a8eb9SGleb Smirnoff 
1039f1220db8SAlexander V. Chernikov /*
1040f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
1041f1220db8SAlexander V. Chernikov  */
10423b3a8eb9SGleb Smirnoff int
1043f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
1044f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
10453b3a8eb9SGleb Smirnoff {
1046b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
10479f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
10489f7d47b0SAlexander V. Chernikov 	struct dump_args da;
10493b3a8eb9SGleb Smirnoff 
10503b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
10513b3a8eb9SGleb Smirnoff 
1052b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1053b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
10549f7d47b0SAlexander V. Chernikov 
10559f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
10569f7d47b0SAlexander V. Chernikov 
10579f7d47b0SAlexander V. Chernikov 	if (ta->dump_entry == NULL)
10589f7d47b0SAlexander V. Chernikov 		return (0);	/* Legacy dump support is not necessary */
10599f7d47b0SAlexander V. Chernikov 
1060d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
10619f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
10629f7d47b0SAlexander V. Chernikov 	da.tc = tc;
1063f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
1064f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
10659f7d47b0SAlexander V. Chernikov 
10669f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
10679f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
1068f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
10699f7d47b0SAlexander V. Chernikov 
10703b3a8eb9SGleb Smirnoff 	return (0);
10713b3a8eb9SGleb Smirnoff }
10723b3a8eb9SGleb Smirnoff 
10739490a627SAlexander V. Chernikov /*
10749490a627SAlexander V. Chernikov  * Dumps table entry in eXtended format (current).
10759490a627SAlexander V. Chernikov  */
10763b3a8eb9SGleb Smirnoff static int
10779f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
10783b3a8eb9SGleb Smirnoff {
10799f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
10809f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
10819f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
10823b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
10833b3a8eb9SGleb Smirnoff 
10849f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
10859f7d47b0SAlexander V. Chernikov 
10869f7d47b0SAlexander V. Chernikov 	tc = da->tc;
10879f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
10889f7d47b0SAlexander V. Chernikov 
10892d99a349SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
10903b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
10912d99a349SAlexander V. Chernikov 	if (xent == NULL)
10923b3a8eb9SGleb Smirnoff 		return (1);
10933b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
1094f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
10959f7d47b0SAlexander V. Chernikov 
10969f7d47b0SAlexander V. Chernikov 	return (ta->dump_xentry(tc->astate, da->ti, e, xent));
10979f7d47b0SAlexander V. Chernikov }
10989f7d47b0SAlexander V. Chernikov 
10999f7d47b0SAlexander V. Chernikov /*
11009f7d47b0SAlexander V. Chernikov  * Table algorithms
11019f7d47b0SAlexander V. Chernikov  */
11023b3a8eb9SGleb Smirnoff 
11039f7d47b0SAlexander V. Chernikov /*
11049490a627SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name
11059f7d47b0SAlexander V. Chernikov  */
11069f7d47b0SAlexander V. Chernikov static struct table_algo *
11079490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
11089f7d47b0SAlexander V. Chernikov {
11099490a627SAlexander V. Chernikov 	int i, l;
11109490a627SAlexander V. Chernikov 	struct table_algo *ta;
11119f7d47b0SAlexander V. Chernikov 
11129f7d47b0SAlexander V. Chernikov 	/* Search by index */
11139f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
11149f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
11159f7d47b0SAlexander V. Chernikov 			return (NULL);
11169f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
11179f7d47b0SAlexander V. Chernikov 	}
11189f7d47b0SAlexander V. Chernikov 
11199490a627SAlexander V. Chernikov 	/* Search by name if supplied */
11209490a627SAlexander V. Chernikov 	if (name != NULL) {
11219490a627SAlexander V. Chernikov 		/* TODO: better search */
11229490a627SAlexander V. Chernikov 		for (i = 1; i <= tcfg->algo_count; i++) {
11239490a627SAlexander V. Chernikov 			ta = tcfg->algo[i];
11249490a627SAlexander V. Chernikov 
11259490a627SAlexander V. Chernikov 			/*
11269490a627SAlexander V. Chernikov 			 * One can supply additional algorithm
11279490a627SAlexander V. Chernikov 			 * parameters so we compare only the first word
11289490a627SAlexander V. Chernikov 			 * of supplied name:
11299490a627SAlexander V. Chernikov 			 * 'hash_cidr hsize=32'
11309490a627SAlexander V. Chernikov 			 * '^^^^^^^^^'
11319490a627SAlexander V. Chernikov 			 *
11329490a627SAlexander V. Chernikov 			 */
11339490a627SAlexander V. Chernikov 			l = strlen(ta->name);
11349490a627SAlexander V. Chernikov 			if (strncmp(name, ta->name, l) == 0) {
11359490a627SAlexander V. Chernikov 				if (name[l] == '\0' || name[l] == ' ')
11369490a627SAlexander V. Chernikov 					return (ta);
11379490a627SAlexander V. Chernikov 			}
11389490a627SAlexander V. Chernikov 		}
11399490a627SAlexander V. Chernikov 
11409490a627SAlexander V. Chernikov 		return (NULL);
11419490a627SAlexander V. Chernikov 	}
11429490a627SAlexander V. Chernikov 
11439f7d47b0SAlexander V. Chernikov 	/* Search by type */
11449f7d47b0SAlexander V. Chernikov 	switch (ti->type) {
11453b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
11469f7d47b0SAlexander V. Chernikov 		return (&radix_cidr);
11473b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
11489f7d47b0SAlexander V. Chernikov 		return (&radix_iface);
11493b3a8eb9SGleb Smirnoff 	}
11503b3a8eb9SGleb Smirnoff 
11519f7d47b0SAlexander V. Chernikov 	return (NULL);
11523b3a8eb9SGleb Smirnoff }
11533b3a8eb9SGleb Smirnoff 
11549f7d47b0SAlexander V. Chernikov void
11559f7d47b0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta)
11563b3a8eb9SGleb Smirnoff {
11579f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
11583b3a8eb9SGleb Smirnoff 
11599f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
1160b074b7bbSAlexander V. Chernikov 
11619f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
11629f7d47b0SAlexander V. Chernikov 
11639f7d47b0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta;
11649f7d47b0SAlexander V. Chernikov 	ta->idx = tcfg->algo_count;
11653b3a8eb9SGleb Smirnoff }
11663b3a8eb9SGleb Smirnoff 
11679f7d47b0SAlexander V. Chernikov 
1168b074b7bbSAlexander V. Chernikov /*
1169b074b7bbSAlexander V. Chernikov  * Tables rewriting code
1170b074b7bbSAlexander V. Chernikov  *
1171b074b7bbSAlexander V. Chernikov  */
1172b074b7bbSAlexander V. Chernikov 
1173b074b7bbSAlexander V. Chernikov /*
1174b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
1175b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
1176b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
1177b074b7bbSAlexander V. Chernikov  */
1178b074b7bbSAlexander V. Chernikov static int
1179b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
1180b074b7bbSAlexander V. Chernikov {
1181b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1182b074b7bbSAlexander V. Chernikov 	int skip;
1183b074b7bbSAlexander V. Chernikov 	uint16_t v;
1184b074b7bbSAlexander V. Chernikov 
1185b074b7bbSAlexander V. Chernikov 	skip = 1;
1186b074b7bbSAlexander V. Chernikov 
1187b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1188b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1189b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1190b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1191b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
1192b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
1193b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
1194b074b7bbSAlexander V. Chernikov 		skip = 0;
1195b074b7bbSAlexander V. Chernikov 
1196b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
1197b074b7bbSAlexander V. Chernikov 			/*
1198b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
1199b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
1200b074b7bbSAlexander V. Chernikov 			 */
1201b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
1202b074b7bbSAlexander V. Chernikov 			switch (v) {
1203b074b7bbSAlexander V. Chernikov 			case 0:
1204b074b7bbSAlexander V. Chernikov 			case 1:
1205b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
1206b074b7bbSAlexander V. Chernikov 				break;
1207b074b7bbSAlexander V. Chernikov 			case 2:
1208b074b7bbSAlexander V. Chernikov 			case 3:
1209b074b7bbSAlexander V. Chernikov 				/* src/dst port */
1210b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1211b074b7bbSAlexander V. Chernikov 				break;
1212b074b7bbSAlexander V. Chernikov 			case 4:
1213b074b7bbSAlexander V. Chernikov 				/* uid/gid */
1214b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1215b074b7bbSAlexander V. Chernikov 			case 5:
1216b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1217b074b7bbSAlexander V. Chernikov 				/* jid */
1218b074b7bbSAlexander V. Chernikov 			case 6:
1219b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1220b074b7bbSAlexander V. Chernikov 				/* dscp */
1221b074b7bbSAlexander V. Chernikov 				break;
1222b074b7bbSAlexander V. Chernikov 			}
1223b074b7bbSAlexander V. Chernikov 		}
1224b074b7bbSAlexander V. Chernikov 		break;
1225b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1226b074b7bbSAlexander V. Chernikov 	case O_RECV:
1227b074b7bbSAlexander V. Chernikov 	case O_VIA:
1228b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1229b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1230b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
1231b074b7bbSAlexander V. Chernikov 			break;
1232b074b7bbSAlexander V. Chernikov 
1233b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
1234b074b7bbSAlexander V. Chernikov 		*puidx = cmdif->p.glob;
1235b074b7bbSAlexander V. Chernikov 		skip = 0;
1236b074b7bbSAlexander V. Chernikov 		break;
1237b074b7bbSAlexander V. Chernikov 	}
1238b074b7bbSAlexander V. Chernikov 
1239b074b7bbSAlexander V. Chernikov 	return (skip);
1240b074b7bbSAlexander V. Chernikov }
1241b074b7bbSAlexander V. Chernikov 
1242b074b7bbSAlexander V. Chernikov /*
1243b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
1244b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
1245b074b7bbSAlexander V. Chernikov  */
1246b074b7bbSAlexander V. Chernikov static void
1247b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
1248b074b7bbSAlexander V. Chernikov {
1249b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1250b074b7bbSAlexander V. Chernikov 
1251b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1252b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1253b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1254b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1255b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
1256b074b7bbSAlexander V. Chernikov 		break;
1257b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1258b074b7bbSAlexander V. Chernikov 	case O_RECV:
1259b074b7bbSAlexander V. Chernikov 	case O_VIA:
1260b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1261b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1262b074b7bbSAlexander V. Chernikov 		cmdif->p.glob = idx;
1263b074b7bbSAlexander V. Chernikov 		break;
1264b074b7bbSAlexander V. Chernikov 	}
1265b074b7bbSAlexander V. Chernikov }
1266b074b7bbSAlexander V. Chernikov 
1267b074b7bbSAlexander V. Chernikov static char *
1268b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
1269b074b7bbSAlexander V. Chernikov {
12709f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1271b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
1272b074b7bbSAlexander V. Chernikov 	int l;
1273b074b7bbSAlexander V. Chernikov 
1274b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
1275b074b7bbSAlexander V. Chernikov 	pe = pa + len;
1276b074b7bbSAlexander V. Chernikov 	l = 0;
1277b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
12789f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
1279b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
1280563b5ab1SAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_TBL_NAME)
1281b074b7bbSAlexander V. Chernikov 			continue;
1282b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
1283b074b7bbSAlexander V. Chernikov 			continue;
1284b074b7bbSAlexander V. Chernikov 
1285b074b7bbSAlexander V. Chernikov 		return (ntlv->name);
1286b074b7bbSAlexander V. Chernikov 	}
1287b074b7bbSAlexander V. Chernikov 
1288b074b7bbSAlexander V. Chernikov 	return (NULL);
1289b074b7bbSAlexander V. Chernikov }
1290b074b7bbSAlexander V. Chernikov 
1291b074b7bbSAlexander V. Chernikov static struct table_config *
1292b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
1293b074b7bbSAlexander V. Chernikov {
1294b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1295b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1296b074b7bbSAlexander V. Chernikov 
1297b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1298b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1299b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1300b074b7bbSAlexander V. Chernikov 			return (NULL);
1301b074b7bbSAlexander V. Chernikov 	} else {
1302b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1303b074b7bbSAlexander V. Chernikov 		name = bname;
1304b074b7bbSAlexander V. Chernikov 	}
1305b074b7bbSAlexander V. Chernikov 
1306b074b7bbSAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, ti->set, name);
1307b074b7bbSAlexander V. Chernikov 
1308b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
1309b074b7bbSAlexander V. Chernikov }
1310b074b7bbSAlexander V. Chernikov 
1311b074b7bbSAlexander V. Chernikov static struct table_config *
13129f7d47b0SAlexander V. Chernikov alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
13139490a627SAlexander V. Chernikov     struct table_algo *ta, char *aname)
1314b074b7bbSAlexander V. Chernikov {
1315b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1316b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1317b074b7bbSAlexander V. Chernikov 	int error;
1318b074b7bbSAlexander V. Chernikov 
1319b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1320b074b7bbSAlexander V. Chernikov 		name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1321b074b7bbSAlexander V. Chernikov 		if (name == NULL)
1322b074b7bbSAlexander V. Chernikov 			return (NULL);
1323b074b7bbSAlexander V. Chernikov 	} else {
1324b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1325b074b7bbSAlexander V. Chernikov 		name = bname;
1326b074b7bbSAlexander V. Chernikov 	}
1327b074b7bbSAlexander V. Chernikov 
1328b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
1329b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
1330b074b7bbSAlexander V. Chernikov 	tc->no.type = ti->type;
1331b074b7bbSAlexander V. Chernikov 	tc->no.set = ti->set;
13329f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
1333b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
1334b074b7bbSAlexander V. Chernikov 
1335b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
1336b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
1337b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
1338b074b7bbSAlexander V. Chernikov 	}
1339b074b7bbSAlexander V. Chernikov 
1340b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
13419490a627SAlexander V. Chernikov 	error = ta->init(&tc->astate, &tc->ti, aname);
1342b074b7bbSAlexander V. Chernikov 	if (error != 0) {
1343b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
1344b074b7bbSAlexander V. Chernikov 		return (NULL);
1345b074b7bbSAlexander V. Chernikov 	}
1346b074b7bbSAlexander V. Chernikov 
1347b074b7bbSAlexander V. Chernikov 	return (tc);
1348b074b7bbSAlexander V. Chernikov }
1349b074b7bbSAlexander V. Chernikov 
1350b074b7bbSAlexander V. Chernikov static void
1351b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
1352b074b7bbSAlexander V. Chernikov {
1353b074b7bbSAlexander V. Chernikov 
1354b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0)
13559f7d47b0SAlexander V. Chernikov 		tc->ta->destroy(&tc->astate, &tc->ti);
1356b074b7bbSAlexander V. Chernikov 
1357b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
1358b074b7bbSAlexander V. Chernikov }
1359b074b7bbSAlexander V. Chernikov 
1360b074b7bbSAlexander V. Chernikov /*
1361b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
1362b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
1363b074b7bbSAlexander V. Chernikov  */
1364b074b7bbSAlexander V. Chernikov static void
13659f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
1366b074b7bbSAlexander V. Chernikov {
1367b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
13689f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1369b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1370b074b7bbSAlexander V. Chernikov 
13719f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
13729f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1373b074b7bbSAlexander V. Chernikov 
13749f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1375b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1376b074b7bbSAlexander V. Chernikov 
1377b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
13789f7d47b0SAlexander V. Chernikov 
13799f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
13809f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
1381b074b7bbSAlexander V. Chernikov 
1382b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
1383b074b7bbSAlexander V. Chernikov }
1384b074b7bbSAlexander V. Chernikov 
1385b074b7bbSAlexander V. Chernikov /*
1386b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
1387b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
1388b074b7bbSAlexander V. Chernikov  */
1389b074b7bbSAlexander V. Chernikov static void
13909f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
1391b074b7bbSAlexander V. Chernikov {
1392b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
13939f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1394b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1395b074b7bbSAlexander V. Chernikov 
13969f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
13979f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1398b074b7bbSAlexander V. Chernikov 
13999f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1400b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1401b074b7bbSAlexander V. Chernikov 
14029f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
1403b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
14049f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
14059f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
1406b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
1407b074b7bbSAlexander V. Chernikov }
1408b074b7bbSAlexander V. Chernikov 
1409b074b7bbSAlexander V. Chernikov /*
1410b074b7bbSAlexander V. Chernikov  * Finds named object by @uidx number.
1411b074b7bbSAlexander V. Chernikov  * Refs found object, allocate new index for non-existing object.
14129490a627SAlexander V. Chernikov  * Fills in @oib with userland/kernel indexes.
14139490a627SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
1414b074b7bbSAlexander V. Chernikov  *
1415b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1416b074b7bbSAlexander V. Chernikov  */
1417b074b7bbSAlexander V. Chernikov static int
14189490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
14199490a627SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
1420b074b7bbSAlexander V. Chernikov {
1421b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
14229490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
14239490a627SAlexander V. Chernikov 	struct named_object *no;
14249490a627SAlexander V. Chernikov 	int error, l, cmdlen;
14259490a627SAlexander V. Chernikov 	ipfw_insn *cmd;
14269490a627SAlexander V. Chernikov 	struct obj_idx *pidx, *p;
14279490a627SAlexander V. Chernikov 
14289490a627SAlexander V. Chernikov 	pidx = *oib;
14299490a627SAlexander V. Chernikov 	l = rule->cmd_len;
14309490a627SAlexander V. Chernikov 	cmd = rule->cmd;
14319490a627SAlexander V. Chernikov 	cmdlen = 0;
14329490a627SAlexander V. Chernikov 	error = 0;
14339490a627SAlexander V. Chernikov 
14349490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
14359490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
14369490a627SAlexander V. Chernikov 
14379490a627SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
14389490a627SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
14399490a627SAlexander V. Chernikov 
14409490a627SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
14419490a627SAlexander V. Chernikov 			continue;
1442b074b7bbSAlexander V. Chernikov 
1443b074b7bbSAlexander V. Chernikov 		pidx->uidx = ti->uidx;
1444b074b7bbSAlexander V. Chernikov 		pidx->type = ti->type;
1445b074b7bbSAlexander V. Chernikov 
14469490a627SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
14479490a627SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
14489490a627SAlexander V. Chernikov 				/* Incompatible types */
14499490a627SAlexander V. Chernikov 				error = EINVAL;
14509490a627SAlexander V. Chernikov 				break;
14519490a627SAlexander V. Chernikov 			}
14529f7d47b0SAlexander V. Chernikov 
14539490a627SAlexander V. Chernikov 			/* Reference found table and save kidx */
14549490a627SAlexander V. Chernikov 			tc->no.refcnt++;
14559490a627SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
14569490a627SAlexander V. Chernikov 			pidx++;
14579490a627SAlexander V. Chernikov 			continue;
14589490a627SAlexander V. Chernikov 		}
14599490a627SAlexander V. Chernikov 
14609490a627SAlexander V. Chernikov 		/* Table not found. Allocate new index and save for later */
1461b074b7bbSAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, ti->set, &pidx->kidx) != 0) {
1462b074b7bbSAlexander V. Chernikov 			printf("Unable to allocate table index in set %u."
1463b074b7bbSAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max",
1464b074b7bbSAlexander V. Chernikov 			    ti->set);
14659490a627SAlexander V. Chernikov 			error = EBUSY;
14669490a627SAlexander V. Chernikov 			break;
1467b074b7bbSAlexander V. Chernikov 		}
1468b074b7bbSAlexander V. Chernikov 
1469b074b7bbSAlexander V. Chernikov 		ci->new_tables++;
14709490a627SAlexander V. Chernikov 		pidx->new = 1;
14719490a627SAlexander V. Chernikov 		pidx++;
1472b074b7bbSAlexander V. Chernikov 	}
1473b074b7bbSAlexander V. Chernikov 
14749490a627SAlexander V. Chernikov 	if (error != 0) {
14759490a627SAlexander V. Chernikov 		/* Unref everything we have already done */
14769490a627SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
14779490a627SAlexander V. Chernikov 			if (p->new != 0) {
14789490a627SAlexander V. Chernikov 				ipfw_objhash_free_idx(ni, ci->tableset,p->kidx);
14799490a627SAlexander V. Chernikov 				continue;
14809490a627SAlexander V. Chernikov 			}
1481b074b7bbSAlexander V. Chernikov 
14829490a627SAlexander V. Chernikov 			/* Find & unref by existing idx */
14839490a627SAlexander V. Chernikov 			no = ipfw_objhash_lookup_idx(ni, ci->tableset, p->kidx);
14849490a627SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
14859490a627SAlexander V. Chernikov 			    p->kidx));
1486b074b7bbSAlexander V. Chernikov 
14879490a627SAlexander V. Chernikov 			no->refcnt--;
14889490a627SAlexander V. Chernikov 		}
14899490a627SAlexander V. Chernikov 	}
14909490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1491b074b7bbSAlexander V. Chernikov 
14929490a627SAlexander V. Chernikov 	*oib = pidx;
14939490a627SAlexander V. Chernikov 
14949490a627SAlexander V. Chernikov 	return (error);
1495b074b7bbSAlexander V. Chernikov }
1496b074b7bbSAlexander V. Chernikov 
1497b074b7bbSAlexander V. Chernikov /*
1498b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
1499b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
1500b074b7bbSAlexander V. Chernikov  * Works for \d+ talbes only (e.g. for tables, converted
1501b074b7bbSAlexander V. Chernikov  * from old numbered system calls).
1502b074b7bbSAlexander V. Chernikov  *
1503b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1504b074b7bbSAlexander V. Chernikov  * Raises error on any other tables.
1505b074b7bbSAlexander V. Chernikov  */
1506b074b7bbSAlexander V. Chernikov int
1507b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
1508b074b7bbSAlexander V. Chernikov {
1509b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1510b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1511b074b7bbSAlexander V. Chernikov 	uint32_t set;
1512b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1513b074b7bbSAlexander V. Chernikov 	uint8_t type;
1514b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1515b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1516b074b7bbSAlexander V. Chernikov 
1517b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1518b074b7bbSAlexander V. Chernikov 
1519b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1520b074b7bbSAlexander V. Chernikov 
1521b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1522b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1523b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1524b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1525b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1526b074b7bbSAlexander V. Chernikov 
1527b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1528b074b7bbSAlexander V. Chernikov 			continue;
1529b074b7bbSAlexander V. Chernikov 
1530b074b7bbSAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_idx(ni, set, kidx)) == NULL)
1531b074b7bbSAlexander V. Chernikov 			return (1);
1532b074b7bbSAlexander V. Chernikov 
1533b074b7bbSAlexander V. Chernikov 		if (no->compat == 0)
1534b074b7bbSAlexander V. Chernikov 			return (2);
1535b074b7bbSAlexander V. Chernikov 
1536b074b7bbSAlexander V. Chernikov 		update_table_opcode(cmd, no->uidx);
1537b074b7bbSAlexander V. Chernikov 	}
1538b074b7bbSAlexander V. Chernikov 
1539b074b7bbSAlexander V. Chernikov 	return (0);
1540b074b7bbSAlexander V. Chernikov }
1541b074b7bbSAlexander V. Chernikov 
1542563b5ab1SAlexander V. Chernikov /*
1543563b5ab1SAlexander V. Chernikov  * Sets every table kidx in @bmask which is used in rule @rule.
1544563b5ab1SAlexander V. Chernikov  *
1545563b5ab1SAlexander V. Chernikov  * Returns number of newly-referenced tables.
1546563b5ab1SAlexander V. Chernikov  */
1547563b5ab1SAlexander V. Chernikov int
1548563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
1549563b5ab1SAlexander V. Chernikov     uint32_t *bmask)
1550563b5ab1SAlexander V. Chernikov {
1551563b5ab1SAlexander V. Chernikov 	int cmdlen, l, count;
1552563b5ab1SAlexander V. Chernikov 	ipfw_insn *cmd;
1553563b5ab1SAlexander V. Chernikov 	uint16_t kidx;
1554563b5ab1SAlexander V. Chernikov 	uint8_t type;
1555563b5ab1SAlexander V. Chernikov 
1556563b5ab1SAlexander V. Chernikov 	l = rule->cmd_len;
1557563b5ab1SAlexander V. Chernikov 	cmd = rule->cmd;
1558563b5ab1SAlexander V. Chernikov 	cmdlen = 0;
1559563b5ab1SAlexander V. Chernikov 	count = 0;
1560563b5ab1SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1561563b5ab1SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1562563b5ab1SAlexander V. Chernikov 
1563563b5ab1SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1564563b5ab1SAlexander V. Chernikov 			continue;
1565563b5ab1SAlexander V. Chernikov 
1566563b5ab1SAlexander V. Chernikov 		if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
1567563b5ab1SAlexander V. Chernikov 			count++;
1568563b5ab1SAlexander V. Chernikov 
1569563b5ab1SAlexander V. Chernikov 		bmask[kidx / 32] |= 1 << (kidx % 32);
1570563b5ab1SAlexander V. Chernikov 	}
1571563b5ab1SAlexander V. Chernikov 
1572563b5ab1SAlexander V. Chernikov 	return (count);
1573563b5ab1SAlexander V. Chernikov }
1574563b5ab1SAlexander V. Chernikov 
1575563b5ab1SAlexander V. Chernikov 
1576b074b7bbSAlexander V. Chernikov 
1577b074b7bbSAlexander V. Chernikov /*
1578b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
1579b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
1580b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
1581b074b7bbSAlexander V. Chernikov  *
1582b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
1583b074b7bbSAlexander V. Chernikov  */
1584b074b7bbSAlexander V. Chernikov int
1585b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
1586b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
1587b074b7bbSAlexander V. Chernikov {
1588b074b7bbSAlexander V. Chernikov 	int cmdlen, error, ftype, l;
1589b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1590b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
1591b074b7bbSAlexander V. Chernikov 	uint8_t type;
1592b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
15939f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
1594b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1595b074b7bbSAlexander V. Chernikov 	struct named_object *no, *no_n, *no_tmp;
15969490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
1597b074b7bbSAlexander V. Chernikov 	struct namedobjects_head nh;
1598b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
1599b074b7bbSAlexander V. Chernikov 
1600b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1601b074b7bbSAlexander V. Chernikov 
16029490a627SAlexander V. Chernikov 	/* Prepare queue to store configs */
16039490a627SAlexander V. Chernikov 	TAILQ_INIT(&nh);
16049490a627SAlexander V. Chernikov 
1605b074b7bbSAlexander V. Chernikov 	/*
1606b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
1607b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
1608b074b7bbSAlexander V. Chernikov 	 */
1609b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
1610b074b7bbSAlexander V. Chernikov 		/* Stack */
16119490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
1612b074b7bbSAlexander V. Chernikov 	} else
16139490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
1614b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1615b074b7bbSAlexander V. Chernikov 
16169490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
1617b074b7bbSAlexander V. Chernikov 	error = 0;
1618b074b7bbSAlexander V. Chernikov 
1619b074b7bbSAlexander V. Chernikov 	type = 0;
1620b074b7bbSAlexander V. Chernikov 	ftype = 0;
1621b074b7bbSAlexander V. Chernikov 
1622b074b7bbSAlexander V. Chernikov 	ci->tableset = TABLE_SET(ci->krule->set);
1623b074b7bbSAlexander V. Chernikov 
1624b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
1625b074b7bbSAlexander V. Chernikov 	ti.set = ci->tableset;
1626b074b7bbSAlexander V. Chernikov 	ti.tlvs = ci->tlvs;
1627b074b7bbSAlexander V. Chernikov 	ti.tlen = ci->tlen;
1628b074b7bbSAlexander V. Chernikov 
1629b074b7bbSAlexander V. Chernikov 	/*
16309490a627SAlexander V. Chernikov 	 * Stage 1: reference existing tables, determine number
16319490a627SAlexander V. Chernikov 	 * of tables we need to allocate and allocate indexes for each.
1632b074b7bbSAlexander V. Chernikov 	 */
16339490a627SAlexander V. Chernikov 	error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti);
1634b074b7bbSAlexander V. Chernikov 
1635b074b7bbSAlexander V. Chernikov 	if (error != 0) {
16369490a627SAlexander V. Chernikov 		if (pidx_first != ci->obuf)
16379490a627SAlexander V. Chernikov 			free(pidx_first, M_IPFW);
1638b074b7bbSAlexander V. Chernikov 
1639b074b7bbSAlexander V. Chernikov 		return (error);
1640b074b7bbSAlexander V. Chernikov 	}
1641b074b7bbSAlexander V. Chernikov 
1642b074b7bbSAlexander V. Chernikov 	/*
1643b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate table configs for every non-existent table
1644b074b7bbSAlexander V. Chernikov 	 */
1645b074b7bbSAlexander V. Chernikov 
16469f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
16479490a627SAlexander V. Chernikov 		for (p = pidx_first; p < pidx_last; p++) {
1648b074b7bbSAlexander V. Chernikov 			if (p->new == 0)
1649b074b7bbSAlexander V. Chernikov 				continue;
1650b074b7bbSAlexander V. Chernikov 
1651b074b7bbSAlexander V. Chernikov 			/* TODO: get name from TLV */
1652b074b7bbSAlexander V. Chernikov 			ti.uidx = p->uidx;
1653b074b7bbSAlexander V. Chernikov 			ti.type = p->type;
16549f7d47b0SAlexander V. Chernikov 			ti.atype = 0;
1655b074b7bbSAlexander V. Chernikov 
16569490a627SAlexander V. Chernikov 			ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL);
16579f7d47b0SAlexander V. Chernikov 			if (ta == NULL) {
16589f7d47b0SAlexander V. Chernikov 				error = ENOTSUP;
16599f7d47b0SAlexander V. Chernikov 				goto free;
16609f7d47b0SAlexander V. Chernikov 			}
16619490a627SAlexander V. Chernikov 			tc = alloc_table_config(ni, &ti, ta, NULL);
1662b074b7bbSAlexander V. Chernikov 
1663b074b7bbSAlexander V. Chernikov 			if (tc == NULL) {
1664b074b7bbSAlexander V. Chernikov 				error = ENOMEM;
1665b074b7bbSAlexander V. Chernikov 				goto free;
1666b074b7bbSAlexander V. Chernikov 			}
1667b074b7bbSAlexander V. Chernikov 
1668b074b7bbSAlexander V. Chernikov 			tc->no.kidx = p->kidx;
1669b074b7bbSAlexander V. Chernikov 			tc->no.refcnt = 1;
1670b074b7bbSAlexander V. Chernikov 
1671b074b7bbSAlexander V. Chernikov 			/* Add to list */
1672b074b7bbSAlexander V. Chernikov 			TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next);
1673b074b7bbSAlexander V. Chernikov 		}
1674b074b7bbSAlexander V. Chernikov 
1675b074b7bbSAlexander V. Chernikov 		/*
1676b074b7bbSAlexander V. Chernikov 		 * Stage 2.1: Check if we're going to create 2 tables
1677b074b7bbSAlexander V. Chernikov 		 * with the same name, but different table types.
1678b074b7bbSAlexander V. Chernikov 		 */
1679b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH(no, &nh, nn_next) {
1680b074b7bbSAlexander V. Chernikov 			TAILQ_FOREACH(no_tmp, &nh, nn_next) {
16819490a627SAlexander V. Chernikov 				if (ipfw_objhash_same_name(ni, no, no_tmp) == 0)
1682b074b7bbSAlexander V. Chernikov 					continue;
1683b074b7bbSAlexander V. Chernikov 				if (no->type != no_tmp->type) {
1684b074b7bbSAlexander V. Chernikov 					error = EINVAL;
1685b074b7bbSAlexander V. Chernikov 					goto free;
1686b074b7bbSAlexander V. Chernikov 				}
1687b074b7bbSAlexander V. Chernikov 			}
1688b074b7bbSAlexander V. Chernikov 		}
16899f7d47b0SAlexander V. Chernikov 	}
1690b074b7bbSAlexander V. Chernikov 
16919f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
16929f7d47b0SAlexander V. Chernikov 
16939f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
1694b074b7bbSAlexander V. Chernikov 		/*
1695b074b7bbSAlexander V. Chernikov 		 * Stage 3: link & reference new table configs
1696b074b7bbSAlexander V. Chernikov 		 */
1697b074b7bbSAlexander V. Chernikov 
1698b074b7bbSAlexander V. Chernikov 
1699b074b7bbSAlexander V. Chernikov 		/*
1700b074b7bbSAlexander V. Chernikov 		 * Step 3.1: Check if some tables we need to create have been
1701b074b7bbSAlexander V. Chernikov 		 * already created with different table type.
1702b074b7bbSAlexander V. Chernikov 		 */
1703b074b7bbSAlexander V. Chernikov 
1704b074b7bbSAlexander V. Chernikov 		error = 0;
1705b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1706b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1707b074b7bbSAlexander V. Chernikov 			if (no_n == NULL)
1708b074b7bbSAlexander V. Chernikov 				continue;
1709b074b7bbSAlexander V. Chernikov 
1710b074b7bbSAlexander V. Chernikov 			if (no_n->type != no->type) {
1711b074b7bbSAlexander V. Chernikov 				error = EINVAL;
1712b074b7bbSAlexander V. Chernikov 				break;
1713b074b7bbSAlexander V. Chernikov 			}
1714b074b7bbSAlexander V. Chernikov 
1715b074b7bbSAlexander V. Chernikov 		}
1716b074b7bbSAlexander V. Chernikov 
1717b074b7bbSAlexander V. Chernikov 		if (error != 0) {
1718b074b7bbSAlexander V. Chernikov 			/*
1719b074b7bbSAlexander V. Chernikov 			 * Someone has allocated table with different table type.
1720b074b7bbSAlexander V. Chernikov 			 * We have to rollback everything.
1721b074b7bbSAlexander V. Chernikov 			 */
1722b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(chain);
1723b074b7bbSAlexander V. Chernikov 			goto free;
1724b074b7bbSAlexander V. Chernikov 		}
1725b074b7bbSAlexander V. Chernikov 
1726b074b7bbSAlexander V. Chernikov 
1727b074b7bbSAlexander V. Chernikov 		/*
17289f7d47b0SAlexander V. Chernikov 		 * Attach new tables.
17299f7d47b0SAlexander V. Chernikov 		 * We need to set table pointers for each new table,
1730b074b7bbSAlexander V. Chernikov 		 * so we have to acquire main WLOCK.
1731b074b7bbSAlexander V. Chernikov 		 */
1732b074b7bbSAlexander V. Chernikov 		IPFW_WLOCK(chain);
1733b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1734b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1735b074b7bbSAlexander V. Chernikov 
17369490a627SAlexander V. Chernikov 			if (no_n == NULL) {
17379490a627SAlexander V. Chernikov 				/* New table. Attach to runtime hash */
17389490a627SAlexander V. Chernikov 				TAILQ_REMOVE(&nh, no, nn_next);
17399490a627SAlexander V. Chernikov 				link_table(chain, (struct table_config *)no);
1740b074b7bbSAlexander V. Chernikov 				continue;
1741b074b7bbSAlexander V. Chernikov 			}
1742b074b7bbSAlexander V. Chernikov 
17439490a627SAlexander V. Chernikov 			/*
17449490a627SAlexander V. Chernikov 			 * Newly-allocated table with the same type.
17459490a627SAlexander V. Chernikov 			 * Reference it and update out @pidx array
17469490a627SAlexander V. Chernikov 			 * rewrite info.
17479490a627SAlexander V. Chernikov 			 */
17489490a627SAlexander V. Chernikov 			no_n->refcnt++;
17499490a627SAlexander V. Chernikov 			/* Keep oib array in sync: update kidx */
17509490a627SAlexander V. Chernikov 			for (p = pidx_first; p < pidx_last; p++) {
17519490a627SAlexander V. Chernikov 				if (p->kidx != no->kidx)
17529490a627SAlexander V. Chernikov 					continue;
17539490a627SAlexander V. Chernikov 				/* Update kidx */
17549490a627SAlexander V. Chernikov 				p->kidx = no_n->kidx;
17559490a627SAlexander V. Chernikov 				break;
17569490a627SAlexander V. Chernikov 			}
1757b074b7bbSAlexander V. Chernikov 		}
1758b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(chain);
17599f7d47b0SAlexander V. Chernikov 	}
1760b074b7bbSAlexander V. Chernikov 
1761b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
1762b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
1763b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
1764b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
17659490a627SAlexander V. Chernikov 	p = pidx_first;
1766b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1767b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1768b074b7bbSAlexander V. Chernikov 
1769b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
1770b074b7bbSAlexander V. Chernikov 			continue;
17719490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
17729490a627SAlexander V. Chernikov 		p++;
1773b074b7bbSAlexander V. Chernikov 	}
1774b074b7bbSAlexander V. Chernikov 
1775b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
1776b074b7bbSAlexander V. Chernikov 
1777b074b7bbSAlexander V. Chernikov 	error = 0;
1778b074b7bbSAlexander V. Chernikov 
1779b074b7bbSAlexander V. Chernikov 	/*
1780b074b7bbSAlexander V. Chernikov 	 * Stage 4: free resources
1781b074b7bbSAlexander V. Chernikov 	 */
1782b074b7bbSAlexander V. Chernikov free:
17839490a627SAlexander V. Chernikov 	if (!TAILQ_EMPTY(&nh)) {
17849490a627SAlexander V. Chernikov 		/* Free indexes first */
17859490a627SAlexander V. Chernikov 		IPFW_UH_WLOCK(chain);
17869490a627SAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
17879490a627SAlexander V. Chernikov 			ipfw_objhash_free_idx(ni, ci->tableset, no->kidx);
17889490a627SAlexander V. Chernikov 		}
17899490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
17909490a627SAlexander V. Chernikov 		/* Free configs */
1791b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp)
1792b074b7bbSAlexander V. Chernikov 			free_table_config(ni, tc);
17939490a627SAlexander V. Chernikov 	}
1794b074b7bbSAlexander V. Chernikov 
17959490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
17969490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
1797b074b7bbSAlexander V. Chernikov 
1798b074b7bbSAlexander V. Chernikov 	return (error);
1799b074b7bbSAlexander V. Chernikov }
1800b074b7bbSAlexander V. Chernikov 
1801b074b7bbSAlexander V. Chernikov /*
1802b074b7bbSAlexander V. Chernikov  * Remove references from every table used in @rule.
1803b074b7bbSAlexander V. Chernikov  */
1804b074b7bbSAlexander V. Chernikov void
1805b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
1806b074b7bbSAlexander V. Chernikov {
1807b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1808b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1809b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1810b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1811b074b7bbSAlexander V. Chernikov 	uint32_t set;
1812b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1813b074b7bbSAlexander V. Chernikov 	uint8_t type;
1814b074b7bbSAlexander V. Chernikov 
1815b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1816b074b7bbSAlexander V. Chernikov 
1817b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1818b074b7bbSAlexander V. Chernikov 
1819b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1820b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1821b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1822b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1823b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1824b074b7bbSAlexander V. Chernikov 
1825b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1826b074b7bbSAlexander V. Chernikov 			continue;
1827b074b7bbSAlexander V. Chernikov 
1828b074b7bbSAlexander V. Chernikov 		no = ipfw_objhash_lookup_idx(ni, set, kidx);
1829b074b7bbSAlexander V. Chernikov 
1830b074b7bbSAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
1831b074b7bbSAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
1832b074b7bbSAlexander V. Chernikov 		    no->type, type, kidx));
1833b074b7bbSAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
1834b074b7bbSAlexander V. Chernikov 		    kidx, no->refcnt));
1835b074b7bbSAlexander V. Chernikov 
1836b074b7bbSAlexander V. Chernikov 		no->refcnt--;
1837b074b7bbSAlexander V. Chernikov 	}
1838b074b7bbSAlexander V. Chernikov }
1839b074b7bbSAlexander V. Chernikov 
1840b074b7bbSAlexander V. Chernikov 
1841b074b7bbSAlexander V. Chernikov /*
1842b074b7bbSAlexander V. Chernikov  * Removes table bindings for every rule in rule chain @head.
1843b074b7bbSAlexander V. Chernikov  */
1844b074b7bbSAlexander V. Chernikov void
1845b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head)
1846b074b7bbSAlexander V. Chernikov {
1847b074b7bbSAlexander V. Chernikov 	struct ip_fw *rule;
1848b074b7bbSAlexander V. Chernikov 
1849b074b7bbSAlexander V. Chernikov 	while ((rule = head) != NULL) {
1850b074b7bbSAlexander V. Chernikov 		head = head->x_next;
1851b074b7bbSAlexander V. Chernikov 		ipfw_unbind_table_rule(chain, rule);
1852b074b7bbSAlexander V. Chernikov 	}
1853b074b7bbSAlexander V. Chernikov }
1854b074b7bbSAlexander V. Chernikov 
1855b074b7bbSAlexander V. Chernikov 
18563b3a8eb9SGleb Smirnoff /* end of file */
1857