xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision ac35ff17)
13b3a8eb9SGleb Smirnoff /*-
23b3a8eb9SGleb Smirnoff  * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko.
33b3a8eb9SGleb Smirnoff  *
43b3a8eb9SGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
53b3a8eb9SGleb Smirnoff  * modification, are permitted provided that the following conditions
63b3a8eb9SGleb Smirnoff  * are met:
73b3a8eb9SGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
83b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
93b3a8eb9SGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
103b3a8eb9SGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
113b3a8eb9SGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
123b3a8eb9SGleb Smirnoff  *
133b3a8eb9SGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
143b3a8eb9SGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
153b3a8eb9SGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
163b3a8eb9SGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
173b3a8eb9SGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
183b3a8eb9SGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
193b3a8eb9SGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
203b3a8eb9SGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
213b3a8eb9SGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
223b3a8eb9SGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
233b3a8eb9SGleb Smirnoff  * SUCH DAMAGE.
243b3a8eb9SGleb Smirnoff  */
253b3a8eb9SGleb Smirnoff 
263b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
273b3a8eb9SGleb Smirnoff __FBSDID("$FreeBSD$");
283b3a8eb9SGleb Smirnoff 
293b3a8eb9SGleb Smirnoff /*
30563b5ab1SAlexander V. Chernikov  * Lookup table support for ipfw.
313b3a8eb9SGleb Smirnoff  *
32ac35ff17SAlexander V. Chernikov  * This file contains handlers for all generic tables' operations:
33563b5ab1SAlexander V. Chernikov  * add/del/flush entries, list/dump tables etc..
343b3a8eb9SGleb Smirnoff  *
35563b5ab1SAlexander V. Chernikov  * Table data modification is protected by both UH and runtimg lock
36563b5ab1SAlexander V. Chernikov  * while reading configuration/data is protected by UH lock.
37563b5ab1SAlexander V. Chernikov  *
38563b5ab1SAlexander V. Chernikov  * Lookup algorithms for all table types are located in ip_fw_table_algo.c
393b3a8eb9SGleb Smirnoff  */
403b3a8eb9SGleb Smirnoff 
413b3a8eb9SGleb Smirnoff #include "opt_ipfw.h"
423b3a8eb9SGleb Smirnoff 
433b3a8eb9SGleb Smirnoff #include <sys/param.h>
443b3a8eb9SGleb Smirnoff #include <sys/systm.h>
453b3a8eb9SGleb Smirnoff #include <sys/malloc.h>
463b3a8eb9SGleb Smirnoff #include <sys/kernel.h>
473b3a8eb9SGleb Smirnoff #include <sys/lock.h>
483b3a8eb9SGleb Smirnoff #include <sys/rwlock.h>
493b3a8eb9SGleb Smirnoff #include <sys/socket.h>
50f1220db8SAlexander V. Chernikov #include <sys/socketvar.h>
513b3a8eb9SGleb Smirnoff #include <sys/queue.h>
523b3a8eb9SGleb Smirnoff #include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
533b3a8eb9SGleb Smirnoff #include <net/route.h>
543b3a8eb9SGleb Smirnoff #include <net/vnet.h>
553b3a8eb9SGleb Smirnoff 
563b3a8eb9SGleb Smirnoff #include <netinet/in.h>
573b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
583b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
593b3a8eb9SGleb Smirnoff 
603b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
61ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
623b3a8eb9SGleb Smirnoff 
633b3a8eb9SGleb Smirnoff 
643b3a8eb9SGleb Smirnoff  /*
65b074b7bbSAlexander V. Chernikov  * Table has the following `type` concepts:
66b074b7bbSAlexander V. Chernikov  *
679f7d47b0SAlexander V. Chernikov  * `no.type` represents lookup key type (cidr, ifp, uid, etc..)
689f7d47b0SAlexander V. Chernikov  * `ta->atype` represents exact lookup algorithm.
69b074b7bbSAlexander V. Chernikov  *     For example, we can use more efficient search schemes if we plan
70b074b7bbSAlexander V. Chernikov  *     to use some specific table for storing host-routes only.
719490a627SAlexander V. Chernikov  * `ftype` (at the moment )is pure userland field helping to properly
729490a627SAlexander V. Chernikov  *     format value data e.g. "value is IPv4 nexthop" or "value is DSCP"
739490a627SAlexander V. Chernikov  *     or "value is port".
74b074b7bbSAlexander V. Chernikov  *
75b074b7bbSAlexander V. Chernikov  */
76b074b7bbSAlexander V. Chernikov struct table_config {
77b074b7bbSAlexander V. Chernikov 	struct named_object	no;
78ac35ff17SAlexander V. Chernikov 	uint8_t		vtype;		/* format table type */
79b074b7bbSAlexander V. Chernikov 	uint8_t		linked;		/* 1 if already linked */
809f7d47b0SAlexander V. Chernikov 	uint16_t	spare0;
81b074b7bbSAlexander V. Chernikov 	uint32_t	count;		/* Number of records */
82b074b7bbSAlexander V. Chernikov 	char		tablename[64];	/* table name */
839f7d47b0SAlexander V. Chernikov 	struct table_algo	*ta;	/* Callbacks for given algo */
849f7d47b0SAlexander V. Chernikov 	void		*astate;	/* algorithm state */
859f7d47b0SAlexander V. Chernikov 	struct table_info	ti;	/* data to put to table_info */
86b074b7bbSAlexander V. Chernikov };
87b074b7bbSAlexander V. Chernikov #define	TABLE_SET(set)	((V_fw_tables_sets != 0) ? set : 0)
88b074b7bbSAlexander V. Chernikov 
89b074b7bbSAlexander V. Chernikov struct tables_config {
90b074b7bbSAlexander V. Chernikov 	struct namedobj_instance	*namehash;
919f7d47b0SAlexander V. Chernikov 	int				algo_count;
929f7d47b0SAlexander V. Chernikov 	struct table_algo 		*algo[256];
93b074b7bbSAlexander V. Chernikov };
94b074b7bbSAlexander V. Chernikov 
95b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni,
96b074b7bbSAlexander V. Chernikov     struct tid_info *ti);
97b074b7bbSAlexander V. Chernikov static struct table_config *alloc_table_config(struct namedobj_instance *ni,
989490a627SAlexander V. Chernikov     struct tid_info *ti, struct table_algo *ta, char *adata);
99b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni,
100b074b7bbSAlexander V. Chernikov     struct table_config *tc);
101b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
102b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
103b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type);
1042d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
1052d99a349SAlexander V. Chernikov     struct sockopt_data *sd);
106ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
107ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i);
108f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg);
109b074b7bbSAlexander V. Chernikov 
1102d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
1112d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
112ac35ff17SAlexander V. Chernikov static int ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
113ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
114ac35ff17SAlexander V. Chernikov static int ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
115ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
116ac35ff17SAlexander V. Chernikov 
117ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
118ac35ff17SAlexander V. Chernikov static int flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
119d3a4f924SAlexander V. Chernikov 
1209f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1219490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
122b074b7bbSAlexander V. Chernikov 
123b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
124b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1259f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
126b074b7bbSAlexander V. Chernikov 
127b074b7bbSAlexander V. Chernikov 
1283b3a8eb9SGleb Smirnoff 
1293b3a8eb9SGleb Smirnoff int
130b074b7bbSAlexander V. Chernikov ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
131b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
1323b3a8eb9SGleb Smirnoff {
133b074b7bbSAlexander V. Chernikov 	struct table_config *tc, *tc_new;
1349f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
135b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
136b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1379f7d47b0SAlexander V. Chernikov 	int error;
1389f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
1393b3a8eb9SGleb Smirnoff 
1409f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1419f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1429f7d47b0SAlexander V. Chernikov 
1439f7d47b0SAlexander V. Chernikov 	/*
1449f7d47b0SAlexander V. Chernikov 	 * Find and reference existing table.
1459f7d47b0SAlexander V. Chernikov 	 */
1469f7d47b0SAlexander V. Chernikov 	ta = NULL;
1479f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
1489f7d47b0SAlexander V. Chernikov 		/* check table type */
1499f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
1509f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1513b3a8eb9SGleb Smirnoff 			return (EINVAL);
1523b3a8eb9SGleb Smirnoff 		}
1533b3a8eb9SGleb Smirnoff 
1549f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
1559f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
1569f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
1579f7d47b0SAlexander V. Chernikov 	}
1589f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1593b3a8eb9SGleb Smirnoff 
1609f7d47b0SAlexander V. Chernikov 	tc_new = NULL;
161ac35ff17SAlexander V. Chernikov 	if (tc == NULL) {
1629f7d47b0SAlexander V. Chernikov 		/* Table not found. We have to create new one */
1639490a627SAlexander V. Chernikov 		if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL)
1649f7d47b0SAlexander V. Chernikov 			return (ENOTSUP);
1653b3a8eb9SGleb Smirnoff 
1669490a627SAlexander V. Chernikov 		tc_new = alloc_table_config(ni, ti, ta, NULL);
1679f7d47b0SAlexander V. Chernikov 		if (tc_new == NULL)
1689f7d47b0SAlexander V. Chernikov 			return (ENOMEM);
1699f7d47b0SAlexander V. Chernikov 	}
1703b3a8eb9SGleb Smirnoff 
1719f7d47b0SAlexander V. Chernikov 	/* Prepare record (allocate memory) */
1729f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
1739f7d47b0SAlexander V. Chernikov 	error = ta->prepare_add(tei, &ta_buf);
1749f7d47b0SAlexander V. Chernikov 	if (error != 0) {
1759f7d47b0SAlexander V. Chernikov 		if (tc_new != NULL)
1769f7d47b0SAlexander V. Chernikov 			free_table_config(ni, tc_new);
1779f7d47b0SAlexander V. Chernikov 		return (error);
1783b3a8eb9SGleb Smirnoff 	}
1793b3a8eb9SGleb Smirnoff 
180b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1813b3a8eb9SGleb Smirnoff 
182b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1833b3a8eb9SGleb Smirnoff 
1849f7d47b0SAlexander V. Chernikov 	if (tc == NULL) {
185ac35ff17SAlexander V. Chernikov 		/* Check if another table has been allocated by other thread */
186b074b7bbSAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
1879f7d47b0SAlexander V. Chernikov 
1889f7d47b0SAlexander V. Chernikov 			/*
1899f7d47b0SAlexander V. Chernikov 			 * Check if algoritm is the same since we've
1909f7d47b0SAlexander V. Chernikov 			 * already allocated state using @ta algoritm
1919f7d47b0SAlexander V. Chernikov 			 * callbacks.
1929f7d47b0SAlexander V. Chernikov 			 */
1939f7d47b0SAlexander V. Chernikov 			if (tc->ta != ta) {
194b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
195ac35ff17SAlexander V. Chernikov 				error = EINVAL;
196ac35ff17SAlexander V. Chernikov 				goto done;
1973b3a8eb9SGleb Smirnoff 			}
1983b3a8eb9SGleb Smirnoff 		} else {
199ac35ff17SAlexander V. Chernikov 			/* Table still does not exists */
200b074b7bbSAlexander V. Chernikov 
201b074b7bbSAlexander V. Chernikov 			/* Allocate table index. */
202ac35ff17SAlexander V. Chernikov 			if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
203b074b7bbSAlexander V. Chernikov 				/* Index full. */
204b074b7bbSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
205ac35ff17SAlexander V. Chernikov 				printf("Unable to allocate index for table %s"
206ac35ff17SAlexander V. Chernikov 				    "in set %u. Consider increasing "
207b074b7bbSAlexander V. Chernikov 				    "net.inet.ip.fw.tables_max",
208ac35ff17SAlexander V. Chernikov 				    tc_new->no.name, ti->set);
209ac35ff17SAlexander V. Chernikov 				error = EBUSY;
210ac35ff17SAlexander V. Chernikov 				goto done;
2113b3a8eb9SGleb Smirnoff 			}
212ac35ff17SAlexander V. Chernikov 
213ac35ff17SAlexander V. Chernikov 			/* Set tc_new to zero not to free it afterwards. */
214ac35ff17SAlexander V. Chernikov 			tc = tc_new;
215ac35ff17SAlexander V. Chernikov 			tc_new = NULL;
216b074b7bbSAlexander V. Chernikov 			/* Save kidx */
217b074b7bbSAlexander V. Chernikov 			tc->no.kidx = kidx;
218b074b7bbSAlexander V. Chernikov 		}
219b074b7bbSAlexander V. Chernikov 	} else {
2209f7d47b0SAlexander V. Chernikov 		/* Drop reference we've used in first search */
2219f7d47b0SAlexander V. Chernikov 		tc->no.refcnt--;
222b074b7bbSAlexander V. Chernikov 	}
223b074b7bbSAlexander V. Chernikov 
224b074b7bbSAlexander V. Chernikov 	/* We've got valid table in @tc. Let's add data */
2259f7d47b0SAlexander V. Chernikov 	kidx = tc->no.kidx;
2269f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2279f7d47b0SAlexander V. Chernikov 
228b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
229b074b7bbSAlexander V. Chernikov 
230ac35ff17SAlexander V. Chernikov 	if (tc->linked == 0)
231b074b7bbSAlexander V. Chernikov 		link_table(ch, tc);
232b074b7bbSAlexander V. Chernikov 
2339f7d47b0SAlexander V. Chernikov 	error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2343b3a8eb9SGleb Smirnoff 
2353b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2369f7d47b0SAlexander V. Chernikov 
237ac35ff17SAlexander V. Chernikov 	/* Update number of records. */
238ac35ff17SAlexander V. Chernikov 	if (error == 0 && (tei->flags & TEI_FLAGS_UPDATED) == 0)
2399f7d47b0SAlexander V. Chernikov 		tc->count++;
2409f7d47b0SAlexander V. Chernikov 
241b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
242b074b7bbSAlexander V. Chernikov 
243ac35ff17SAlexander V. Chernikov done:
244b074b7bbSAlexander V. Chernikov 	if (tc_new != NULL)
245ac35ff17SAlexander V. Chernikov 		free_table_config(ni, tc_new);
246ac35ff17SAlexander V. Chernikov 	/* Run cleaning callback anyway */
2479f7d47b0SAlexander V. Chernikov 	ta->flush_entry(tei, &ta_buf);
248b074b7bbSAlexander V. Chernikov 
2499f7d47b0SAlexander V. Chernikov 	return (error);
2503b3a8eb9SGleb Smirnoff }
2513b3a8eb9SGleb Smirnoff 
2523b3a8eb9SGleb Smirnoff int
253b074b7bbSAlexander V. Chernikov ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
254b074b7bbSAlexander V. Chernikov     struct tentry_info *tei)
2553b3a8eb9SGleb Smirnoff {
256b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2579f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
258b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
259b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2609f7d47b0SAlexander V. Chernikov 	int error;
2619f7d47b0SAlexander V. Chernikov 	char ta_buf[128];
2623b3a8eb9SGleb Smirnoff 
2639f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
264b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
265b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
2669f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2673b3a8eb9SGleb Smirnoff 		return (ESRCH);
2683b3a8eb9SGleb Smirnoff 	}
2693b3a8eb9SGleb Smirnoff 
270b074b7bbSAlexander V. Chernikov 	if (tc->no.type != ti->type) {
2719f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2723b3a8eb9SGleb Smirnoff 		return (EINVAL);
2733b3a8eb9SGleb Smirnoff 	}
2749f7d47b0SAlexander V. Chernikov 
2759f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
2769f7d47b0SAlexander V. Chernikov 
2779f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
2789f7d47b0SAlexander V. Chernikov 	if ((error = ta->prepare_del(tei, &ta_buf)) != 0) {
2799f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2809f7d47b0SAlexander V. Chernikov 		return (error);
2819f7d47b0SAlexander V. Chernikov 	}
2829f7d47b0SAlexander V. Chernikov 
283b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
284b074b7bbSAlexander V. Chernikov 
285b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
2869f7d47b0SAlexander V. Chernikov 	error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
2873b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2883b3a8eb9SGleb Smirnoff 
2899f7d47b0SAlexander V. Chernikov 	if (error == 0)
2909f7d47b0SAlexander V. Chernikov 		tc->count--;
291b074b7bbSAlexander V. Chernikov 
2929f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
2933b3a8eb9SGleb Smirnoff 
2949f7d47b0SAlexander V. Chernikov 	ta->flush_entry(tei, &ta_buf);
295ac35ff17SAlexander V. Chernikov 
296ac35ff17SAlexander V. Chernikov 	return (error);
297ac35ff17SAlexander V. Chernikov }
298ac35ff17SAlexander V. Chernikov 
299ac35ff17SAlexander V. Chernikov int
300ac35ff17SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
301ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
302ac35ff17SAlexander V. Chernikov {
303ac35ff17SAlexander V. Chernikov 	int error;
304ac35ff17SAlexander V. Chernikov 
305ac35ff17SAlexander V. Chernikov 	switch (op3->version) {
306ac35ff17SAlexander V. Chernikov 	case 0:
307ac35ff17SAlexander V. Chernikov 		error = ipfw_modify_table_v0(ch, op3, sd);
308ac35ff17SAlexander V. Chernikov 		break;
309ac35ff17SAlexander V. Chernikov 	case 1:
310ac35ff17SAlexander V. Chernikov 		error = ipfw_modify_table_v1(ch, op3, sd);
311ac35ff17SAlexander V. Chernikov 		break;
312ac35ff17SAlexander V. Chernikov 	default:
313ac35ff17SAlexander V. Chernikov 		error = ENOTSUP;
314ac35ff17SAlexander V. Chernikov 	}
315ac35ff17SAlexander V. Chernikov 
316ac35ff17SAlexander V. Chernikov 	return (error);
317ac35ff17SAlexander V. Chernikov }
318ac35ff17SAlexander V. Chernikov 
319ac35ff17SAlexander V. Chernikov /*
320ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
321ac35ff17SAlexander V. Chernikov  * Data layout (v0):
322ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ipfw_table_xentry ]
323ac35ff17SAlexander V. Chernikov  *
324ac35ff17SAlexander V. Chernikov  * Returns 0 on success
325ac35ff17SAlexander V. Chernikov  */
326ac35ff17SAlexander V. Chernikov static int
327ac35ff17SAlexander V. Chernikov ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
328ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
329ac35ff17SAlexander V. Chernikov {
330ac35ff17SAlexander V. Chernikov 	ipfw_table_xentry *xent;
331ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
332ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
333ac35ff17SAlexander V. Chernikov 	int error, hdrlen, read;
334ac35ff17SAlexander V. Chernikov 
335ac35ff17SAlexander V. Chernikov 	hdrlen = offsetof(ipfw_table_xentry, k);
336ac35ff17SAlexander V. Chernikov 
337ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
338ac35ff17SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*op3) + hdrlen))
339ac35ff17SAlexander V. Chernikov 		return (EINVAL);
340ac35ff17SAlexander V. Chernikov 
341ac35ff17SAlexander V. Chernikov 	read = sizeof(ip_fw3_opheader);
342ac35ff17SAlexander V. Chernikov 
343ac35ff17SAlexander V. Chernikov 	/* Check if xentry len field is valid */
344ac35ff17SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)(op3 + 1);
345ac35ff17SAlexander V. Chernikov 	if (xent->len < hdrlen || xent->len + read > sd->valsize)
346ac35ff17SAlexander V. Chernikov 		return (EINVAL);
347ac35ff17SAlexander V. Chernikov 
348ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
349ac35ff17SAlexander V. Chernikov 	tei.paddr = &xent->k;
350ac35ff17SAlexander V. Chernikov 	tei.masklen = xent->masklen;
351ac35ff17SAlexander V. Chernikov 	tei.value = xent->value;
352ac35ff17SAlexander V. Chernikov 	/* Old requests compability */
353ac35ff17SAlexander V. Chernikov 	if (xent->type == IPFW_TABLE_CIDR) {
354ac35ff17SAlexander V. Chernikov 		if (xent->len - hdrlen == sizeof(in_addr_t))
355ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET;
356ac35ff17SAlexander V. Chernikov 		else
357ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET6;
358ac35ff17SAlexander V. Chernikov 	}
359ac35ff17SAlexander V. Chernikov 
360ac35ff17SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
361ac35ff17SAlexander V. Chernikov 	ti.uidx = xent->tbl;
362ac35ff17SAlexander V. Chernikov 	ti.type = xent->type;
363ac35ff17SAlexander V. Chernikov 
364ac35ff17SAlexander V. Chernikov 	error = (op3->opcode == IP_FW_TABLE_XADD) ?
365ac35ff17SAlexander V. Chernikov 	    ipfw_add_table_entry(ch, &ti, &tei) :
366ac35ff17SAlexander V. Chernikov 	    ipfw_del_table_entry(ch, &ti, &tei);
367ac35ff17SAlexander V. Chernikov 
368ac35ff17SAlexander V. Chernikov 	return (error);
369ac35ff17SAlexander V. Chernikov }
370ac35ff17SAlexander V. Chernikov 
371ac35ff17SAlexander V. Chernikov /*
372ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
373ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
374ac35ff17SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_tentry ]
375ac35ff17SAlexander V. Chernikov  *
376ac35ff17SAlexander V. Chernikov  * Returns 0 on success
377ac35ff17SAlexander V. Chernikov  */
378ac35ff17SAlexander V. Chernikov static int
379ac35ff17SAlexander V. Chernikov ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
380ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
381ac35ff17SAlexander V. Chernikov {
382ac35ff17SAlexander V. Chernikov 	ipfw_obj_tentry *tent;
383ac35ff17SAlexander V. Chernikov 	ipfw_obj_header *oh;
384ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
385ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
386ac35ff17SAlexander V. Chernikov 	int error, read;
387ac35ff17SAlexander V. Chernikov 
388ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
389ac35ff17SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*tent)))
390ac35ff17SAlexander V. Chernikov 		return (EINVAL);
391ac35ff17SAlexander V. Chernikov 
392ac35ff17SAlexander V. Chernikov 	/* Check if passed data is too long */
393ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sd->kavail)
394ac35ff17SAlexander V. Chernikov 		return (EINVAL);
395ac35ff17SAlexander V. Chernikov 
396ac35ff17SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
397ac35ff17SAlexander V. Chernikov 
398ac35ff17SAlexander V. Chernikov 	/* Basic length checks for TLVs */
399ac35ff17SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
400ac35ff17SAlexander V. Chernikov 		return (EINVAL);
401ac35ff17SAlexander V. Chernikov 
402ac35ff17SAlexander V. Chernikov 	read = sizeof(*oh);
403ac35ff17SAlexander V. Chernikov 
404ac35ff17SAlexander V. Chernikov 	/* Assume tentry may grow to support larger keys */
405ac35ff17SAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(oh + 1);
406ac35ff17SAlexander V. Chernikov 	if (tent->head.length < sizeof(*tent) ||
407ac35ff17SAlexander V. Chernikov 	    tent->head.length + read > sd->valsize)
408ac35ff17SAlexander V. Chernikov 		return (EINVAL);
409ac35ff17SAlexander V. Chernikov 
410ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
411ac35ff17SAlexander V. Chernikov 	tei.paddr = &tent->k;
412ac35ff17SAlexander V. Chernikov 	tei.subtype = tent->subtype;
413ac35ff17SAlexander V. Chernikov 	tei.masklen = tent->masklen;
414ac35ff17SAlexander V. Chernikov 	if (tent->flags & IPFW_TF_UPDATE)
415ac35ff17SAlexander V. Chernikov 		tei.flags |= TEI_FLAGS_UPDATE;
416ac35ff17SAlexander V. Chernikov 	tei.value = tent->value;
417ac35ff17SAlexander V. Chernikov 
418ac35ff17SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
419ac35ff17SAlexander V. Chernikov 	ti.uidx = tent->idx;
420ac35ff17SAlexander V. Chernikov 	ti.type = oh->ntlv.type;
421ac35ff17SAlexander V. Chernikov 	ti.tlvs = &oh->ntlv;
422ac35ff17SAlexander V. Chernikov 	ti.tlen = oh->ntlv.head.length;
423ac35ff17SAlexander V. Chernikov 
424ac35ff17SAlexander V. Chernikov 	error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
425ac35ff17SAlexander V. Chernikov 	    ipfw_add_table_entry(ch, &ti, &tei) :
426ac35ff17SAlexander V. Chernikov 	    ipfw_del_table_entry(ch, &ti, &tei);
427ac35ff17SAlexander V. Chernikov 
428ac35ff17SAlexander V. Chernikov 	return (error);
429ac35ff17SAlexander V. Chernikov }
430ac35ff17SAlexander V. Chernikov 
431ac35ff17SAlexander V. Chernikov int
432ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
433ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
434ac35ff17SAlexander V. Chernikov {
435ac35ff17SAlexander V. Chernikov 	int error;
436ac35ff17SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
437ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
438ac35ff17SAlexander V. Chernikov 
439ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh))
440ac35ff17SAlexander V. Chernikov 		return (EINVAL);
441ac35ff17SAlexander V. Chernikov 
442ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
443ac35ff17SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
444ac35ff17SAlexander V. Chernikov 
445ac35ff17SAlexander V. Chernikov 	if (opt == IP_FW_TABLE_XDESTROY)
446ac35ff17SAlexander V. Chernikov 		error = destroy_table(ch, &ti);
447ac35ff17SAlexander V. Chernikov 	else if (opt == IP_FW_TABLE_XFLUSH)
448ac35ff17SAlexander V. Chernikov 		error = flush_table(ch, &ti);
449ac35ff17SAlexander V. Chernikov 	else
450ac35ff17SAlexander V. Chernikov 		return (ENOTSUP);
451ac35ff17SAlexander V. Chernikov 
452ac35ff17SAlexander V. Chernikov 	return (error);
4533b3a8eb9SGleb Smirnoff }
4543b3a8eb9SGleb Smirnoff 
455b074b7bbSAlexander V. Chernikov /*
4569f7d47b0SAlexander V. Chernikov  * Flushes all entries in given table.
457ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
458ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ]
459ac35ff17SAlexander V. Chernikov  *
460ac35ff17SAlexander V. Chernikov  * Returns 0 on success
461b074b7bbSAlexander V. Chernikov  */
462ac35ff17SAlexander V. Chernikov static int
463ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
4643b3a8eb9SGleb Smirnoff {
465b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
466b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
4679f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
4689f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
4699f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
470b074b7bbSAlexander V. Chernikov 	int error;
471b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
4723b3a8eb9SGleb Smirnoff 
4733b3a8eb9SGleb Smirnoff 	/*
4749f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
475b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
4763b3a8eb9SGleb Smirnoff 	 */
477b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
478b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
479b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
480b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
481b074b7bbSAlexander V. Chernikov 		return (ESRCH);
482b074b7bbSAlexander V. Chernikov 	}
4839f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
484b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
485b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
4863b3a8eb9SGleb Smirnoff 
487b074b7bbSAlexander V. Chernikov 	/*
4889f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
4899490a627SAlexander V. Chernikov 	 * TODO: pass startup parametes somehow.
490b074b7bbSAlexander V. Chernikov 	 */
4919f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
4929490a627SAlexander V. Chernikov 	if ((error = ta->init(&astate_new, &ti_new, NULL)) != 0) {
493b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
494b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
495b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
496b074b7bbSAlexander V. Chernikov 		return (error);
497b074b7bbSAlexander V. Chernikov 	}
498b074b7bbSAlexander V. Chernikov 
499b074b7bbSAlexander V. Chernikov 	/*
500b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
501b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
502b074b7bbSAlexander V. Chernikov 	 */
503b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
504b074b7bbSAlexander V. Chernikov 
505b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
506b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
5079f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
508b074b7bbSAlexander V. Chernikov 
5099f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
5109f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
5119f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
5129f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
513b074b7bbSAlexander V. Chernikov 
5149f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
5159f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
5169f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
5179f7d47b0SAlexander V. Chernikov 	tc->count = 0;
518b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
519b074b7bbSAlexander V. Chernikov 
520b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5213b3a8eb9SGleb Smirnoff 
522b074b7bbSAlexander V. Chernikov 	/*
523b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
524b074b7bbSAlexander V. Chernikov 	 */
5259f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
5263b3a8eb9SGleb Smirnoff 
5273b3a8eb9SGleb Smirnoff 	return (0);
5283b3a8eb9SGleb Smirnoff }
5293b3a8eb9SGleb Smirnoff 
530b074b7bbSAlexander V. Chernikov /*
5319f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
532ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
533ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ]
534ac35ff17SAlexander V. Chernikov  *
535ac35ff17SAlexander V. Chernikov  * Returns 0 on success
536b074b7bbSAlexander V. Chernikov  */
537ac35ff17SAlexander V. Chernikov static int
538ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
539b074b7bbSAlexander V. Chernikov {
540b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
541b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
542b074b7bbSAlexander V. Chernikov 
543b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
544b074b7bbSAlexander V. Chernikov 
545b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
546b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
547b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
548b074b7bbSAlexander V. Chernikov 		return (ESRCH);
549b074b7bbSAlexander V. Chernikov 	}
550b074b7bbSAlexander V. Chernikov 
5519f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
5529f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
553b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
554b074b7bbSAlexander V. Chernikov 		return (EBUSY);
555b074b7bbSAlexander V. Chernikov 	}
556b074b7bbSAlexander V. Chernikov 
557b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
558b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
559b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
560b074b7bbSAlexander V. Chernikov 
561b074b7bbSAlexander V. Chernikov 	/* Free obj index */
562ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0)
563b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
564b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
565b074b7bbSAlexander V. Chernikov 
566b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
567b074b7bbSAlexander V. Chernikov 
568b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
569b074b7bbSAlexander V. Chernikov 
570b074b7bbSAlexander V. Chernikov 	return (0);
571b074b7bbSAlexander V. Chernikov }
572b074b7bbSAlexander V. Chernikov 
573b074b7bbSAlexander V. Chernikov static void
574b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
575b074b7bbSAlexander V. Chernikov     void *arg)
576b074b7bbSAlexander V. Chernikov {
577b074b7bbSAlexander V. Chernikov 
578b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
579ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->kidx) != 0)
580b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
581b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
582b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
583b074b7bbSAlexander V. Chernikov }
584b074b7bbSAlexander V. Chernikov 
5853b3a8eb9SGleb Smirnoff void
5863b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
5873b3a8eb9SGleb Smirnoff {
5883b3a8eb9SGleb Smirnoff 
589b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
590b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
591b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
592b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
593b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
594b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
5953b3a8eb9SGleb Smirnoff 
5963b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
5979f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
5989f7d47b0SAlexander V. Chernikov 
5999f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
600b074b7bbSAlexander V. Chernikov 
601b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
602b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
6033b3a8eb9SGleb Smirnoff }
6043b3a8eb9SGleb Smirnoff 
6053b3a8eb9SGleb Smirnoff int
6063b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
6073b3a8eb9SGleb Smirnoff {
608b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
609b074b7bbSAlexander V. Chernikov 
6103b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
6119f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
6129f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
613b074b7bbSAlexander V. Chernikov 
614b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
615b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
616b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
617b074b7bbSAlexander V. Chernikov 
6189f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
6199f7d47b0SAlexander V. Chernikov 
6203b3a8eb9SGleb Smirnoff 	return (0);
6213b3a8eb9SGleb Smirnoff }
6223b3a8eb9SGleb Smirnoff 
6233b3a8eb9SGleb Smirnoff int
6243b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
6253b3a8eb9SGleb Smirnoff {
6263b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
627b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
6289f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
629b074b7bbSAlexander V. Chernikov 	int new_blocks;
6303b3a8eb9SGleb Smirnoff 
6313b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
6323b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
6333b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
6343b3a8eb9SGleb Smirnoff 
6353b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
6369f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
6379f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
6389f7d47b0SAlexander V. Chernikov 
639b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
6403b3a8eb9SGleb Smirnoff 
6419f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
6423b3a8eb9SGleb Smirnoff 
6433b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
644b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
645b074b7bbSAlexander V. Chernikov 
6469f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
6479f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
6489f7d47b0SAlexander V. Chernikov 
6499f7d47b0SAlexander V. Chernikov 		/*
6509f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
6519f7d47b0SAlexander V. Chernikov 		 */
6529f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
653b074b7bbSAlexander V. Chernikov 		return (EINVAL);
654b074b7bbSAlexander V. Chernikov 	}
6553b3a8eb9SGleb Smirnoff 
6569f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
6579f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
6589f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
6593b3a8eb9SGleb Smirnoff 
6609f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
6619f7d47b0SAlexander V. Chernikov 
6629f7d47b0SAlexander V. Chernikov 	/* Change pointers */
6639f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
6649f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
6659f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
6663b3a8eb9SGleb Smirnoff 
6673b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
6683b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
6693b3a8eb9SGleb Smirnoff 
6703b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
6719f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
6723b3a8eb9SGleb Smirnoff 
6733b3a8eb9SGleb Smirnoff 	/* Free old pointers */
6749f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
675b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
6763b3a8eb9SGleb Smirnoff 
6773b3a8eb9SGleb Smirnoff 	return (0);
6783b3a8eb9SGleb Smirnoff }
6793b3a8eb9SGleb Smirnoff 
6803b3a8eb9SGleb Smirnoff int
6813b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
6823b3a8eb9SGleb Smirnoff     uint32_t *val)
6833b3a8eb9SGleb Smirnoff {
6849f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
6853b3a8eb9SGleb Smirnoff 
6869f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
6879f7d47b0SAlexander V. Chernikov 
6889f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
6893b3a8eb9SGleb Smirnoff }
6909f7d47b0SAlexander V. Chernikov 
6919f7d47b0SAlexander V. Chernikov int
6929f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
6939f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
6949f7d47b0SAlexander V. Chernikov {
6959f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
6969f7d47b0SAlexander V. Chernikov 
6979f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
6989f7d47b0SAlexander V. Chernikov 
6999f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
7009f7d47b0SAlexander V. Chernikov }
7019f7d47b0SAlexander V. Chernikov 
7029f7d47b0SAlexander V. Chernikov /*
7039f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
7049f7d47b0SAlexander V. Chernikov  *
7059f7d47b0SAlexander V. Chernikov  */
7069f7d47b0SAlexander V. Chernikov 
707f1220db8SAlexander V. Chernikov /*
708d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
709d3a4f924SAlexander V. Chernikov  */
710d3a4f924SAlexander V. Chernikov 
711d3a4f924SAlexander V. Chernikov /*
712d3a4f924SAlexander V. Chernikov  * Get buffer size needed to list info for all tables.
713ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
714d3a4f924SAlexander V. Chernikov  * Request: [ empty ], size = sizeof(ipfw_obj_lheader)
715d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ]
716d3a4f924SAlexander V. Chernikov  *
717d3a4f924SAlexander V. Chernikov  * Returns 0 on success
718f1220db8SAlexander V. Chernikov  */
719f1220db8SAlexander V. Chernikov int
7202d99a349SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
721f1220db8SAlexander V. Chernikov {
722f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
723f1220db8SAlexander V. Chernikov 
7242d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
7252d99a349SAlexander V. Chernikov 	if (olh == NULL)
726d3a4f924SAlexander V. Chernikov 		return (EINVAL);
727d3a4f924SAlexander V. Chernikov 
728f1220db8SAlexander V. Chernikov 	olh->size = sizeof(*olh); /* Make export_table store needed size */
729f1220db8SAlexander V. Chernikov 
730f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
7312d99a349SAlexander V. Chernikov 	export_tables(ch, olh, sd);
732f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
733f1220db8SAlexander V. Chernikov 
7342d99a349SAlexander V. Chernikov 	return (0);
735f1220db8SAlexander V. Chernikov }
736f1220db8SAlexander V. Chernikov 
737d3a4f924SAlexander V. Chernikov /*
738d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
739ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
740d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
741d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
742d3a4f924SAlexander V. Chernikov  *
743d3a4f924SAlexander V. Chernikov  * Returns 0 on success
744d3a4f924SAlexander V. Chernikov  */
745f1220db8SAlexander V. Chernikov int
7462d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
747f1220db8SAlexander V. Chernikov {
748f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
749d3a4f924SAlexander V. Chernikov 	uint32_t sz;
750f1220db8SAlexander V. Chernikov 	int error;
751f1220db8SAlexander V. Chernikov 
7522d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
7532d99a349SAlexander V. Chernikov 	if (olh == NULL)
754d3a4f924SAlexander V. Chernikov 		return (EINVAL);
755d3a4f924SAlexander V. Chernikov 
756f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
757f1220db8SAlexander V. Chernikov 	sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
7582d99a349SAlexander V. Chernikov 
7592d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
7602d99a349SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
7612d99a349SAlexander V. Chernikov 		return (ENOMEM);
7622d99a349SAlexander V. Chernikov 	}
7632d99a349SAlexander V. Chernikov 
7642d99a349SAlexander V. Chernikov 	error = export_tables(ch, olh, sd);
765f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
766f1220db8SAlexander V. Chernikov 
767f1220db8SAlexander V. Chernikov 	return (error);
768f1220db8SAlexander V. Chernikov }
769f1220db8SAlexander V. Chernikov 
770f1220db8SAlexander V. Chernikov /*
7712d99a349SAlexander V. Chernikov  * Store table info to buffer provided by @sd.
772ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
773d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
774d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
775d3a4f924SAlexander V. Chernikov  *
776d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
777d3a4f924SAlexander V. Chernikov  */
778d3a4f924SAlexander V. Chernikov int
7792d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
780d3a4f924SAlexander V. Chernikov {
781d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
782d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
783d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
784d3a4f924SAlexander V. Chernikov 	size_t sz;
785d3a4f924SAlexander V. Chernikov 
786d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
7872d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
7882d99a349SAlexander V. Chernikov 	if (oh == NULL)
789d3a4f924SAlexander V. Chernikov 		return (EINVAL);
790d3a4f924SAlexander V. Chernikov 
791d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
792d3a4f924SAlexander V. Chernikov 
793d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
794d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
795d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
796d3a4f924SAlexander V. Chernikov 		return (ESRCH);
797d3a4f924SAlexander V. Chernikov 	}
798d3a4f924SAlexander V. Chernikov 
799ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1));
800d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
801d3a4f924SAlexander V. Chernikov 
8022d99a349SAlexander V. Chernikov 	return (0);
803d3a4f924SAlexander V. Chernikov }
804d3a4f924SAlexander V. Chernikov 
805f1220db8SAlexander V. Chernikov struct dump_args {
806f1220db8SAlexander V. Chernikov 	struct table_info *ti;
807f1220db8SAlexander V. Chernikov 	struct table_config *tc;
8082d99a349SAlexander V. Chernikov 	struct sockopt_data *sd;
809f1220db8SAlexander V. Chernikov 	uint32_t cnt;
810f1220db8SAlexander V. Chernikov 	uint16_t uidx;
8112d99a349SAlexander V. Chernikov 	ipfw_table_entry *ent;
8122d99a349SAlexander V. Chernikov 	uint32_t size;
813f1220db8SAlexander V. Chernikov };
814f1220db8SAlexander V. Chernikov 
815f1220db8SAlexander V. Chernikov int
8162d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
8172d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
818f1220db8SAlexander V. Chernikov {
819d3a4f924SAlexander V. Chernikov 	int error;
820d3a4f924SAlexander V. Chernikov 
821d3a4f924SAlexander V. Chernikov 	switch (op3->version) {
822d3a4f924SAlexander V. Chernikov 	case 0:
8232d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sd);
824d3a4f924SAlexander V. Chernikov 		break;
825d3a4f924SAlexander V. Chernikov 	case 1:
8262d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sd);
827d3a4f924SAlexander V. Chernikov 		break;
828d3a4f924SAlexander V. Chernikov 	default:
829d3a4f924SAlexander V. Chernikov 		error = ENOTSUP;
830d3a4f924SAlexander V. Chernikov 	}
831d3a4f924SAlexander V. Chernikov 
832d3a4f924SAlexander V. Chernikov 	return (error);
833d3a4f924SAlexander V. Chernikov }
834d3a4f924SAlexander V. Chernikov 
835d3a4f924SAlexander V. Chernikov /*
836d3a4f924SAlexander V. Chernikov  * Dumps all table data
837ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
8382d99a349SAlexander V. Chernikov  * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
8392d99a349SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ]
840d3a4f924SAlexander V. Chernikov  *
841d3a4f924SAlexander V. Chernikov  * Returns 0 on success
842d3a4f924SAlexander V. Chernikov  */
843d3a4f924SAlexander V. Chernikov static int
8442d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
845d3a4f924SAlexander V. Chernikov {
846f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
847f1220db8SAlexander V. Chernikov 	ipfw_xtable_info *i;
848f1220db8SAlexander V. Chernikov 	struct tid_info ti;
849f1220db8SAlexander V. Chernikov 	struct table_config *tc;
850f1220db8SAlexander V. Chernikov 	struct table_algo *ta;
851f1220db8SAlexander V. Chernikov 	struct dump_args da;
852d3a4f924SAlexander V. Chernikov 	uint32_t sz;
853f1220db8SAlexander V. Chernikov 
8542d99a349SAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
8552d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
8562d99a349SAlexander V. Chernikov 	if (oh == NULL)
857d3a4f924SAlexander V. Chernikov 		return (EINVAL);
858d3a4f924SAlexander V. Chernikov 
8592d99a349SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
860d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
861f1220db8SAlexander V. Chernikov 
862f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
863f1220db8SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
864f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
865f1220db8SAlexander V. Chernikov 		return (ESRCH);
866f1220db8SAlexander V. Chernikov 	}
867ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, i);
8682d99a349SAlexander V. Chernikov 	sz = tc->count;
8692d99a349SAlexander V. Chernikov 
8702d99a349SAlexander V. Chernikov 	if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) {
8712d99a349SAlexander V. Chernikov 
8722d99a349SAlexander V. Chernikov 		/*
8732d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
8742d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
8752d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
8762d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
8772d99a349SAlexander V. Chernikov 		 */
878f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
8792d99a349SAlexander V. Chernikov 		return (ENOMEM);
880f1220db8SAlexander V. Chernikov 	}
881f1220db8SAlexander V. Chernikov 
882f1220db8SAlexander V. Chernikov 	/*
883f1220db8SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
884f1220db8SAlexander V. Chernikov 	 */
885d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
886f1220db8SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
887f1220db8SAlexander V. Chernikov 	da.tc = tc;
8882d99a349SAlexander V. Chernikov 	da.sd = sd;
889f1220db8SAlexander V. Chernikov 
890f1220db8SAlexander V. Chernikov 	ta = tc->ta;
891f1220db8SAlexander V. Chernikov 
892f1220db8SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
893f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
894f1220db8SAlexander V. Chernikov 
895f1220db8SAlexander V. Chernikov 	return (0);
896f1220db8SAlexander V. Chernikov }
897f1220db8SAlexander V. Chernikov 
898d3a4f924SAlexander V. Chernikov /*
899d3a4f924SAlexander V. Chernikov  * Dumps all table data
9002d99a349SAlexander V. Chernikov  * Data layout (version 0)(legacy):
901d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
902d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
903d3a4f924SAlexander V. Chernikov  *
904d3a4f924SAlexander V. Chernikov  * Returns 0 on success
905d3a4f924SAlexander V. Chernikov  */
906d3a4f924SAlexander V. Chernikov static int
9072d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
908d3a4f924SAlexander V. Chernikov {
909d3a4f924SAlexander V. Chernikov 	ipfw_xtable *xtbl;
910d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
911d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
912d3a4f924SAlexander V. Chernikov 	struct table_algo *ta;
913d3a4f924SAlexander V. Chernikov 	struct dump_args da;
914d3a4f924SAlexander V. Chernikov 	size_t sz;
915d3a4f924SAlexander V. Chernikov 
9162d99a349SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
9172d99a349SAlexander V. Chernikov 	if (xtbl == NULL)
918d3a4f924SAlexander V. Chernikov 		return (EINVAL);
919d3a4f924SAlexander V. Chernikov 
920d3a4f924SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
921d3a4f924SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
922d3a4f924SAlexander V. Chernikov 
923d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
924d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
925d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
926d3a4f924SAlexander V. Chernikov 		return (0);
927d3a4f924SAlexander V. Chernikov 	}
928d3a4f924SAlexander V. Chernikov 	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
9292d99a349SAlexander V. Chernikov 
9302d99a349SAlexander V. Chernikov 	xtbl->cnt = tc->count;
9312d99a349SAlexander V. Chernikov 	xtbl->size = sz;
9322d99a349SAlexander V. Chernikov 	xtbl->type = tc->no.type;
9332d99a349SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
9342d99a349SAlexander V. Chernikov 
9352d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
9362d99a349SAlexander V. Chernikov 
9372d99a349SAlexander V. Chernikov 		/*
9382d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
9392d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
9402d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
9412d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
9422d99a349SAlexander V. Chernikov 		 */
943d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
9442d99a349SAlexander V. Chernikov 		return (ENOMEM);
945d3a4f924SAlexander V. Chernikov 	}
946d3a4f924SAlexander V. Chernikov 
947d3a4f924SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
948d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
949d3a4f924SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
950d3a4f924SAlexander V. Chernikov 	da.tc = tc;
9512d99a349SAlexander V. Chernikov 	da.sd = sd;
9522d99a349SAlexander V. Chernikov 
953d3a4f924SAlexander V. Chernikov 	ta = tc->ta;
954d3a4f924SAlexander V. Chernikov 
955d3a4f924SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
956d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
957d3a4f924SAlexander V. Chernikov 
9582d99a349SAlexander V. Chernikov 	return (0);
959d3a4f924SAlexander V. Chernikov }
960d3a4f924SAlexander V. Chernikov 
961d3a4f924SAlexander V. Chernikov /*
9629490a627SAlexander V. Chernikov  * Creates new table.
963ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
9649490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
9659490a627SAlexander V. Chernikov  *
9669490a627SAlexander V. Chernikov  * Returns 0 on success
9679490a627SAlexander V. Chernikov  */
9689490a627SAlexander V. Chernikov int
969ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
970ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
9719490a627SAlexander V. Chernikov {
9729490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
9739490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
9749490a627SAlexander V. Chernikov 	char *tname, *aname;
9759490a627SAlexander V. Chernikov 	struct tid_info ti;
9769490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
9779490a627SAlexander V. Chernikov 	struct table_config *tc;
9789490a627SAlexander V. Chernikov 	struct table_algo *ta;
9799490a627SAlexander V. Chernikov 	uint16_t kidx;
9809490a627SAlexander V. Chernikov 
981ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
9829490a627SAlexander V. Chernikov 		return (EINVAL);
9839490a627SAlexander V. Chernikov 
984ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)sd->kbuf;
9859490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
9869490a627SAlexander V. Chernikov 
9879490a627SAlexander V. Chernikov 	/*
9889490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
9892d99a349SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
9909490a627SAlexander V. Chernikov 	 */
991ac35ff17SAlexander V. Chernikov 	tname = oh->ntlv.name;
9929490a627SAlexander V. Chernikov 	aname = i->algoname;
993ac35ff17SAlexander V. Chernikov 	if (ipfw_check_table_name(tname) != 0 ||
9949490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
9959490a627SAlexander V. Chernikov 		return (EINVAL);
9969490a627SAlexander V. Chernikov 
9979490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
9989490a627SAlexander V. Chernikov 		/* Use default algorithm */
9999490a627SAlexander V. Chernikov 		aname = NULL;
10009490a627SAlexander V. Chernikov 	}
10019490a627SAlexander V. Chernikov 
10029490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1003ac35ff17SAlexander V. Chernikov 	/* Create table in set 0 by default */
1004ac35ff17SAlexander V. Chernikov 	ti->set = TABLE_SET(ti->set);
1005ac35ff17SAlexander V. Chernikov 	ti.type = i->type;
10069490a627SAlexander V. Chernikov 
10079490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
10089490a627SAlexander V. Chernikov 
10099490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
10109490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
10119490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
10129490a627SAlexander V. Chernikov 		return (EEXIST);
10139490a627SAlexander V. Chernikov 	}
10149490a627SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), &ti, aname);
10159490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
10169490a627SAlexander V. Chernikov 
10179490a627SAlexander V. Chernikov 	if (ta == NULL)
10189490a627SAlexander V. Chernikov 		return (ENOTSUP);
10199490a627SAlexander V. Chernikov 
10209490a627SAlexander V. Chernikov 	if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL)
10219490a627SAlexander V. Chernikov 		return (ENOMEM);
1022ac35ff17SAlexander V. Chernikov 	/* TODO: move inside alloc_table_config() */
1023ac35ff17SAlexander V. Chernikov 	tc->vtype = i->vtype;
10249490a627SAlexander V. Chernikov 
10259490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1026ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
10279490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
10289490a627SAlexander V. Chernikov 		printf("Unable to allocate table index for table %s in set %u."
10299490a627SAlexander V. Chernikov 		    " Consider increasing net.inet.ip.fw.tables_max",
10309490a627SAlexander V. Chernikov 		    tname, ti.set);
10319490a627SAlexander V. Chernikov 		free_table_config(ni, tc);
10329490a627SAlexander V. Chernikov 		return (EBUSY);
10339490a627SAlexander V. Chernikov 	}
10349490a627SAlexander V. Chernikov 
10359490a627SAlexander V. Chernikov 	tc->no.kidx = kidx;
10369490a627SAlexander V. Chernikov 
10379490a627SAlexander V. Chernikov 	IPFW_WLOCK(ch);
10389490a627SAlexander V. Chernikov 	link_table(ch, tc);
10399490a627SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
10409490a627SAlexander V. Chernikov 
10419490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
10429490a627SAlexander V. Chernikov 
10439490a627SAlexander V. Chernikov 	return (0);
10449490a627SAlexander V. Chernikov }
10459490a627SAlexander V. Chernikov 
1046d3a4f924SAlexander V. Chernikov void
1047d3a4f924SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
1048d3a4f924SAlexander V. Chernikov {
1049d3a4f924SAlexander V. Chernikov 
1050d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
1051d3a4f924SAlexander V. Chernikov 	ti->set = oh->set;
1052d3a4f924SAlexander V. Chernikov 	ti->uidx = oh->idx;
1053d3a4f924SAlexander V. Chernikov 	ti->tlvs = &oh->ntlv;
1054d3a4f924SAlexander V. Chernikov 	ti->tlen = oh->ntlv.head.length;
1055d3a4f924SAlexander V. Chernikov }
1056d3a4f924SAlexander V. Chernikov 
1057563b5ab1SAlexander V. Chernikov int
1058563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
1059563b5ab1SAlexander V. Chernikov     struct sockopt_data *sd)
1060563b5ab1SAlexander V. Chernikov {
1061563b5ab1SAlexander V. Chernikov 	struct namedobj_instance *ni;
1062563b5ab1SAlexander V. Chernikov 	struct named_object *no;
1063563b5ab1SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1064563b5ab1SAlexander V. Chernikov 
1065563b5ab1SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1066563b5ab1SAlexander V. Chernikov 
1067ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_kidx(ni, kidx);
1068563b5ab1SAlexander V. Chernikov 	KASSERT(no != NULL, ("invalid table kidx passed"));
1069563b5ab1SAlexander V. Chernikov 
1070563b5ab1SAlexander V. Chernikov 	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
1071563b5ab1SAlexander V. Chernikov 	if (ntlv == NULL)
1072563b5ab1SAlexander V. Chernikov 		return (ENOMEM);
1073563b5ab1SAlexander V. Chernikov 
1074563b5ab1SAlexander V. Chernikov 	ntlv->head.type = IPFW_TLV_TBL_NAME;
1075563b5ab1SAlexander V. Chernikov 	ntlv->head.length = sizeof(*ntlv);
1076563b5ab1SAlexander V. Chernikov 	ntlv->idx = no->kidx;
1077563b5ab1SAlexander V. Chernikov 	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
1078563b5ab1SAlexander V. Chernikov 
1079563b5ab1SAlexander V. Chernikov 	return (0);
1080563b5ab1SAlexander V. Chernikov }
1081563b5ab1SAlexander V. Chernikov 
10829f7d47b0SAlexander V. Chernikov static void
1083ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
1084ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i)
10859f7d47b0SAlexander V. Chernikov {
1086ac35ff17SAlexander V. Chernikov 	struct table_info *ti;
10879f7d47b0SAlexander V. Chernikov 
10889f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
1089ac35ff17SAlexander V. Chernikov 	i->vtype = tc->vtype;
10909f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
10919f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
10929f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
10939f7d47b0SAlexander V. Chernikov 	i->count = tc->count;
10949f7d47b0SAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_table_xentry);
1095f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
10969f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
1097ac35ff17SAlexander V. Chernikov 	if (tc->ta->print_config != NULL) {
1098ac35ff17SAlexander V. Chernikov 		/* Use algo function to print table config to string */
1099ac35ff17SAlexander V. Chernikov 		ti = KIDX_TO_TI(ch, tc->no.kidx);
1100ac35ff17SAlexander V. Chernikov 		tc->ta->print_config(tc->astate, ti, i->algoname,
1101ac35ff17SAlexander V. Chernikov 		    sizeof(i->algoname));
1102ac35ff17SAlexander V. Chernikov 	} else
1103ac35ff17SAlexander V. Chernikov 		strlcpy(i->algoname, tc->ta->name, sizeof(i->algoname));
11049f7d47b0SAlexander V. Chernikov }
11059f7d47b0SAlexander V. Chernikov 
1106ac35ff17SAlexander V. Chernikov struct dump_table_args {
1107ac35ff17SAlexander V. Chernikov 	struct ip_fw_chain *ch;
1108ac35ff17SAlexander V. Chernikov 	struct sockopt_data *sd;
1109ac35ff17SAlexander V. Chernikov };
1110ac35ff17SAlexander V. Chernikov 
11119f7d47b0SAlexander V. Chernikov static void
11129f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
11139f7d47b0SAlexander V. Chernikov     void *arg)
11143b3a8eb9SGleb Smirnoff {
11159f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
1116ac35ff17SAlexander V. Chernikov 	struct dump_table_args *dta;
11173b3a8eb9SGleb Smirnoff 
1118ac35ff17SAlexander V. Chernikov 	dta = (struct dump_table_args *)arg;
1119ac35ff17SAlexander V. Chernikov 
1120ac35ff17SAlexander V. Chernikov 	i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
11212d99a349SAlexander V. Chernikov 	KASSERT(i == 0, ("previously checked buffer is not enough"));
11229f7d47b0SAlexander V. Chernikov 
1123ac35ff17SAlexander V. Chernikov 	export_table_info(dta->ch, (struct table_config *)no, i);
11249f7d47b0SAlexander V. Chernikov }
11259f7d47b0SAlexander V. Chernikov 
1126f1220db8SAlexander V. Chernikov /*
1127f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
11282d99a349SAlexander V. Chernikov  * storage provided by @sd.
1129f1220db8SAlexander V. Chernikov  * Returns 0 on success.
1130f1220db8SAlexander V. Chernikov  */
1131f1220db8SAlexander V. Chernikov static int
11322d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
11332d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
11349f7d47b0SAlexander V. Chernikov {
11359f7d47b0SAlexander V. Chernikov 	uint32_t size;
11369f7d47b0SAlexander V. Chernikov 	uint32_t count;
1137ac35ff17SAlexander V. Chernikov 	struct dump_table_args dta;
11389f7d47b0SAlexander V. Chernikov 
11399f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
11409f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
11412d99a349SAlexander V. Chernikov 
11422d99a349SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
1143f1220db8SAlexander V. Chernikov 	olh->count = count;
1144f1220db8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
11452d99a349SAlexander V. Chernikov 
11462d99a349SAlexander V. Chernikov 	if (size > olh->size) {
11472d99a349SAlexander V. Chernikov 		/* Store necessary size */
11482d99a349SAlexander V. Chernikov 		olh->size = size;
11499f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1150f1220db8SAlexander V. Chernikov 	}
11519f7d47b0SAlexander V. Chernikov 	olh->size = size;
11522d99a349SAlexander V. Chernikov 
1153ac35ff17SAlexander V. Chernikov 	dta.ch = ch;
1154ac35ff17SAlexander V. Chernikov 	dta.sd = sd;
1155ac35ff17SAlexander V. Chernikov 
1156ac35ff17SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta);
11579f7d47b0SAlexander V. Chernikov 
11583b3a8eb9SGleb Smirnoff 	return (0);
11593b3a8eb9SGleb Smirnoff }
11603b3a8eb9SGleb Smirnoff 
11613b3a8eb9SGleb Smirnoff int
1162b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
11633b3a8eb9SGleb Smirnoff {
1164b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
11653b3a8eb9SGleb Smirnoff 
1166b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1167b074b7bbSAlexander V. Chernikov 		return (ESRCH);
11689f7d47b0SAlexander V. Chernikov 	*cnt = tc->count;
11693b3a8eb9SGleb Smirnoff 	return (0);
11703b3a8eb9SGleb Smirnoff }
11713b3a8eb9SGleb Smirnoff 
11729f7d47b0SAlexander V. Chernikov 
11739f7d47b0SAlexander V. Chernikov int
11749f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
11753b3a8eb9SGleb Smirnoff {
11769f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
11779f7d47b0SAlexander V. Chernikov 
1178d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
1179d3a4f924SAlexander V. Chernikov 		*cnt = 0;
11809f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
1181d3a4f924SAlexander V. Chernikov 	}
11829f7d47b0SAlexander V. Chernikov 	*cnt = tc->count * sizeof(ipfw_table_xentry);
11839f7d47b0SAlexander V. Chernikov 	if (tc->count > 0)
11849f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
11859f7d47b0SAlexander V. Chernikov 	return (0);
11869f7d47b0SAlexander V. Chernikov }
11879f7d47b0SAlexander V. Chernikov 
11889f7d47b0SAlexander V. Chernikov static int
11899f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
11909f7d47b0SAlexander V. Chernikov {
11919f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
11929f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
11939f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
11943b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
11953b3a8eb9SGleb Smirnoff 
11969f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
11979f7d47b0SAlexander V. Chernikov 
11989f7d47b0SAlexander V. Chernikov 	tc = da->tc;
11999f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
12009f7d47b0SAlexander V. Chernikov 
12019f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
1202f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
12033b3a8eb9SGleb Smirnoff 		return (1);
1204f1220db8SAlexander V. Chernikov 	ent = da->ent++;
1205f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
1206f1220db8SAlexander V. Chernikov 	da->cnt++;
12079f7d47b0SAlexander V. Chernikov 
12089f7d47b0SAlexander V. Chernikov 	return (ta->dump_entry(tc->astate, da->ti, e, ent));
12093b3a8eb9SGleb Smirnoff }
12103b3a8eb9SGleb Smirnoff 
1211f1220db8SAlexander V. Chernikov /*
1212f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
1213f1220db8SAlexander V. Chernikov  */
12143b3a8eb9SGleb Smirnoff int
1215f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
1216f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
12173b3a8eb9SGleb Smirnoff {
1218b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
12199f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
12209f7d47b0SAlexander V. Chernikov 	struct dump_args da;
12213b3a8eb9SGleb Smirnoff 
12223b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
12233b3a8eb9SGleb Smirnoff 
1224b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1225b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
12269f7d47b0SAlexander V. Chernikov 
12279f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
12289f7d47b0SAlexander V. Chernikov 
12299f7d47b0SAlexander V. Chernikov 	if (ta->dump_entry == NULL)
12309f7d47b0SAlexander V. Chernikov 		return (0);	/* Legacy dump support is not necessary */
12319f7d47b0SAlexander V. Chernikov 
1232d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
12339f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
12349f7d47b0SAlexander V. Chernikov 	da.tc = tc;
1235f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
1236f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
12379f7d47b0SAlexander V. Chernikov 
12389f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
12399f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
1240f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
12419f7d47b0SAlexander V. Chernikov 
12423b3a8eb9SGleb Smirnoff 	return (0);
12433b3a8eb9SGleb Smirnoff }
12443b3a8eb9SGleb Smirnoff 
12459490a627SAlexander V. Chernikov /*
12469490a627SAlexander V. Chernikov  * Dumps table entry in eXtended format (current).
12479490a627SAlexander V. Chernikov  */
12483b3a8eb9SGleb Smirnoff static int
12499f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
12503b3a8eb9SGleb Smirnoff {
12519f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
12529f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
12539f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
12543b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
12553b3a8eb9SGleb Smirnoff 
12569f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
12579f7d47b0SAlexander V. Chernikov 
12589f7d47b0SAlexander V. Chernikov 	tc = da->tc;
12599f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
12609f7d47b0SAlexander V. Chernikov 
12612d99a349SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
12623b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
12632d99a349SAlexander V. Chernikov 	if (xent == NULL)
12643b3a8eb9SGleb Smirnoff 		return (1);
12653b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
1266f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
12679f7d47b0SAlexander V. Chernikov 
12689f7d47b0SAlexander V. Chernikov 	return (ta->dump_xentry(tc->astate, da->ti, e, xent));
12699f7d47b0SAlexander V. Chernikov }
12709f7d47b0SAlexander V. Chernikov 
12719f7d47b0SAlexander V. Chernikov /*
12729f7d47b0SAlexander V. Chernikov  * Table algorithms
12739f7d47b0SAlexander V. Chernikov  */
12743b3a8eb9SGleb Smirnoff 
12759f7d47b0SAlexander V. Chernikov /*
12769490a627SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name
12779f7d47b0SAlexander V. Chernikov  */
12789f7d47b0SAlexander V. Chernikov static struct table_algo *
12799490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
12809f7d47b0SAlexander V. Chernikov {
12819490a627SAlexander V. Chernikov 	int i, l;
12829490a627SAlexander V. Chernikov 	struct table_algo *ta;
12839f7d47b0SAlexander V. Chernikov 
12849f7d47b0SAlexander V. Chernikov 	/* Search by index */
12859f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
12869f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
12879f7d47b0SAlexander V. Chernikov 			return (NULL);
12889f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
12899f7d47b0SAlexander V. Chernikov 	}
12909f7d47b0SAlexander V. Chernikov 
12919490a627SAlexander V. Chernikov 	/* Search by name if supplied */
12929490a627SAlexander V. Chernikov 	if (name != NULL) {
12939490a627SAlexander V. Chernikov 		/* TODO: better search */
12949490a627SAlexander V. Chernikov 		for (i = 1; i <= tcfg->algo_count; i++) {
12959490a627SAlexander V. Chernikov 			ta = tcfg->algo[i];
12969490a627SAlexander V. Chernikov 
12979490a627SAlexander V. Chernikov 			/*
12989490a627SAlexander V. Chernikov 			 * One can supply additional algorithm
12999490a627SAlexander V. Chernikov 			 * parameters so we compare only the first word
13009490a627SAlexander V. Chernikov 			 * of supplied name:
13019490a627SAlexander V. Chernikov 			 * 'hash_cidr hsize=32'
13029490a627SAlexander V. Chernikov 			 * '^^^^^^^^^'
13039490a627SAlexander V. Chernikov 			 *
13049490a627SAlexander V. Chernikov 			 */
13059490a627SAlexander V. Chernikov 			l = strlen(ta->name);
13069490a627SAlexander V. Chernikov 			if (strncmp(name, ta->name, l) == 0) {
13079490a627SAlexander V. Chernikov 				if (name[l] == '\0' || name[l] == ' ')
13089490a627SAlexander V. Chernikov 					return (ta);
13099490a627SAlexander V. Chernikov 			}
13109490a627SAlexander V. Chernikov 		}
13119490a627SAlexander V. Chernikov 
13129490a627SAlexander V. Chernikov 		return (NULL);
13139490a627SAlexander V. Chernikov 	}
13149490a627SAlexander V. Chernikov 
13159f7d47b0SAlexander V. Chernikov 	/* Search by type */
13169f7d47b0SAlexander V. Chernikov 	switch (ti->type) {
13173b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_CIDR:
13189f7d47b0SAlexander V. Chernikov 		return (&radix_cidr);
13193b3a8eb9SGleb Smirnoff 	case IPFW_TABLE_INTERFACE:
13209f7d47b0SAlexander V. Chernikov 		return (&radix_iface);
13213b3a8eb9SGleb Smirnoff 	}
13223b3a8eb9SGleb Smirnoff 
13239f7d47b0SAlexander V. Chernikov 	return (NULL);
13243b3a8eb9SGleb Smirnoff }
13253b3a8eb9SGleb Smirnoff 
13269f7d47b0SAlexander V. Chernikov void
13279f7d47b0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta)
13283b3a8eb9SGleb Smirnoff {
13299f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
13303b3a8eb9SGleb Smirnoff 
13319f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
1332b074b7bbSAlexander V. Chernikov 
13339f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
13349f7d47b0SAlexander V. Chernikov 
13359f7d47b0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta;
13369f7d47b0SAlexander V. Chernikov 	ta->idx = tcfg->algo_count;
13373b3a8eb9SGleb Smirnoff }
13383b3a8eb9SGleb Smirnoff 
13399f7d47b0SAlexander V. Chernikov 
1340b074b7bbSAlexander V. Chernikov /*
1341b074b7bbSAlexander V. Chernikov  * Tables rewriting code
1342b074b7bbSAlexander V. Chernikov  *
1343b074b7bbSAlexander V. Chernikov  */
1344b074b7bbSAlexander V. Chernikov 
1345b074b7bbSAlexander V. Chernikov /*
1346b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
1347b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
1348b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
1349b074b7bbSAlexander V. Chernikov  */
1350b074b7bbSAlexander V. Chernikov static int
1351b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
1352b074b7bbSAlexander V. Chernikov {
1353b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1354b074b7bbSAlexander V. Chernikov 	int skip;
1355b074b7bbSAlexander V. Chernikov 	uint16_t v;
1356b074b7bbSAlexander V. Chernikov 
1357b074b7bbSAlexander V. Chernikov 	skip = 1;
1358b074b7bbSAlexander V. Chernikov 
1359b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1360b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1361b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1362b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1363b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
1364b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
1365b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
1366b074b7bbSAlexander V. Chernikov 		skip = 0;
1367b074b7bbSAlexander V. Chernikov 
1368b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
1369b074b7bbSAlexander V. Chernikov 			/*
1370b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
1371b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
1372b074b7bbSAlexander V. Chernikov 			 */
1373b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
1374b074b7bbSAlexander V. Chernikov 			switch (v) {
1375b074b7bbSAlexander V. Chernikov 			case 0:
1376b074b7bbSAlexander V. Chernikov 			case 1:
1377b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
1378b074b7bbSAlexander V. Chernikov 				break;
1379b074b7bbSAlexander V. Chernikov 			case 2:
1380b074b7bbSAlexander V. Chernikov 			case 3:
1381b074b7bbSAlexander V. Chernikov 				/* src/dst port */
1382b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1383b074b7bbSAlexander V. Chernikov 				break;
1384b074b7bbSAlexander V. Chernikov 			case 4:
1385b074b7bbSAlexander V. Chernikov 				/* uid/gid */
1386b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1387b074b7bbSAlexander V. Chernikov 			case 5:
1388b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U32;
1389b074b7bbSAlexander V. Chernikov 				/* jid */
1390b074b7bbSAlexander V. Chernikov 			case 6:
1391b074b7bbSAlexander V. Chernikov 				//type = IPFW_TABLE_U16;
1392b074b7bbSAlexander V. Chernikov 				/* dscp */
1393b074b7bbSAlexander V. Chernikov 				break;
1394b074b7bbSAlexander V. Chernikov 			}
1395b074b7bbSAlexander V. Chernikov 		}
1396b074b7bbSAlexander V. Chernikov 		break;
1397b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1398b074b7bbSAlexander V. Chernikov 	case O_RECV:
1399b074b7bbSAlexander V. Chernikov 	case O_VIA:
1400b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1401b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1402b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
1403b074b7bbSAlexander V. Chernikov 			break;
1404b074b7bbSAlexander V. Chernikov 
1405b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
1406b074b7bbSAlexander V. Chernikov 		*puidx = cmdif->p.glob;
1407b074b7bbSAlexander V. Chernikov 		skip = 0;
1408b074b7bbSAlexander V. Chernikov 		break;
1409b074b7bbSAlexander V. Chernikov 	}
1410b074b7bbSAlexander V. Chernikov 
1411b074b7bbSAlexander V. Chernikov 	return (skip);
1412b074b7bbSAlexander V. Chernikov }
1413b074b7bbSAlexander V. Chernikov 
1414b074b7bbSAlexander V. Chernikov /*
1415b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
1416b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
1417b074b7bbSAlexander V. Chernikov  */
1418b074b7bbSAlexander V. Chernikov static void
1419b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
1420b074b7bbSAlexander V. Chernikov {
1421b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1422b074b7bbSAlexander V. Chernikov 
1423b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1424b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1425b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1426b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1427b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
1428b074b7bbSAlexander V. Chernikov 		break;
1429b074b7bbSAlexander V. Chernikov 	case O_XMIT:
1430b074b7bbSAlexander V. Chernikov 	case O_RECV:
1431b074b7bbSAlexander V. Chernikov 	case O_VIA:
1432b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
1433b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
1434b074b7bbSAlexander V. Chernikov 		cmdif->p.glob = idx;
1435b074b7bbSAlexander V. Chernikov 		break;
1436b074b7bbSAlexander V. Chernikov 	}
1437b074b7bbSAlexander V. Chernikov }
1438b074b7bbSAlexander V. Chernikov 
1439ac35ff17SAlexander V. Chernikov /*
1440ac35ff17SAlexander V. Chernikov  * Checks table name for validity.
1441ac35ff17SAlexander V. Chernikov  * Enforce basic length checks, the rest
1442ac35ff17SAlexander V. Chernikov  * should be done in userland.
1443ac35ff17SAlexander V. Chernikov  *
1444ac35ff17SAlexander V. Chernikov  * Returns 0 if name is considered valid.
1445ac35ff17SAlexander V. Chernikov  */
1446ac35ff17SAlexander V. Chernikov int
1447ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name)
1448ac35ff17SAlexander V. Chernikov {
1449ac35ff17SAlexander V. Chernikov 	int nsize;
1450ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv = NULL;
1451ac35ff17SAlexander V. Chernikov 
1452ac35ff17SAlexander V. Chernikov 	nsize = sizeof(ntlv->name);
1453ac35ff17SAlexander V. Chernikov 
1454ac35ff17SAlexander V. Chernikov 	if (strnlen(name, nsize) == nsize)
1455ac35ff17SAlexander V. Chernikov 		return (EINVAL);
1456ac35ff17SAlexander V. Chernikov 
1457ac35ff17SAlexander V. Chernikov 	if (name[0] == '\0')
1458ac35ff17SAlexander V. Chernikov 		return (EINVAL);
1459ac35ff17SAlexander V. Chernikov 
1460ac35ff17SAlexander V. Chernikov 	/*
1461ac35ff17SAlexander V. Chernikov 	 * TODO: do some more complicated checks
1462ac35ff17SAlexander V. Chernikov 	 */
1463ac35ff17SAlexander V. Chernikov 
1464ac35ff17SAlexander V. Chernikov 	return (0);
1465ac35ff17SAlexander V. Chernikov }
1466ac35ff17SAlexander V. Chernikov 
1467ac35ff17SAlexander V. Chernikov /*
1468ac35ff17SAlexander V. Chernikov  * Find tablename TLV by @uid.
1469ac35ff17SAlexander V. Chernikov  * Check @tlvs for valid data inside.
1470ac35ff17SAlexander V. Chernikov  *
1471ac35ff17SAlexander V. Chernikov  * Returns pointer to found TLV or NULL.
1472ac35ff17SAlexander V. Chernikov  */
1473ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv *
1474b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
1475b074b7bbSAlexander V. Chernikov {
14769f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1477b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
1478b074b7bbSAlexander V. Chernikov 	int l;
1479b074b7bbSAlexander V. Chernikov 
1480b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
1481b074b7bbSAlexander V. Chernikov 	pe = pa + len;
1482b074b7bbSAlexander V. Chernikov 	l = 0;
1483b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
14849f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
1485b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
1486ac35ff17SAlexander V. Chernikov 
1487ac35ff17SAlexander V. Chernikov 		if (l != sizeof(*ntlv))
1488ac35ff17SAlexander V. Chernikov 			return (NULL);
1489ac35ff17SAlexander V. Chernikov 
1490563b5ab1SAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_TBL_NAME)
1491b074b7bbSAlexander V. Chernikov 			continue;
1492ac35ff17SAlexander V. Chernikov 
1493b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
1494b074b7bbSAlexander V. Chernikov 			continue;
1495b074b7bbSAlexander V. Chernikov 
1496ac35ff17SAlexander V. Chernikov 		if (ipfw_check_table_name(ntlv->name) != 0)
1497ac35ff17SAlexander V. Chernikov 			return (NULL);
1498ac35ff17SAlexander V. Chernikov 
1499ac35ff17SAlexander V. Chernikov 		return (ntlv);
1500b074b7bbSAlexander V. Chernikov 	}
1501b074b7bbSAlexander V. Chernikov 
1502b074b7bbSAlexander V. Chernikov 	return (NULL);
1503b074b7bbSAlexander V. Chernikov }
1504b074b7bbSAlexander V. Chernikov 
1505ac35ff17SAlexander V. Chernikov /*
1506ac35ff17SAlexander V. Chernikov  * Finds table config based on either legacy index
1507ac35ff17SAlexander V. Chernikov  * or name in ntlv.
1508ac35ff17SAlexander V. Chernikov  * Note @ti structure contains unchecked data from userland.
1509ac35ff17SAlexander V. Chernikov  *
1510ac35ff17SAlexander V. Chernikov  * Returns pointer to table_config or NULL.
1511ac35ff17SAlexander V. Chernikov  */
1512b074b7bbSAlexander V. Chernikov static struct table_config *
1513b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
1514b074b7bbSAlexander V. Chernikov {
1515b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1516b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1517ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1518ac35ff17SAlexander V. Chernikov 	uint32_t set;
1519b074b7bbSAlexander V. Chernikov 
1520b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1521ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1522ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
1523b074b7bbSAlexander V. Chernikov 			return (NULL);
1524ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
1525ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
1526b074b7bbSAlexander V. Chernikov 	} else {
1527b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1528b074b7bbSAlexander V. Chernikov 		name = bname;
1529ac35ff17SAlexander V. Chernikov 		set = 0;
1530b074b7bbSAlexander V. Chernikov 	}
1531b074b7bbSAlexander V. Chernikov 
1532ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, set, name);
1533b074b7bbSAlexander V. Chernikov 
1534b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
1535b074b7bbSAlexander V. Chernikov }
1536b074b7bbSAlexander V. Chernikov 
1537b074b7bbSAlexander V. Chernikov static struct table_config *
15389f7d47b0SAlexander V. Chernikov alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
15399490a627SAlexander V. Chernikov     struct table_algo *ta, char *aname)
1540b074b7bbSAlexander V. Chernikov {
1541b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
1542b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1543b074b7bbSAlexander V. Chernikov 	int error;
1544ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1545ac35ff17SAlexander V. Chernikov 	uint32_t set;
1546b074b7bbSAlexander V. Chernikov 
1547b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
1548ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
1549ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
1550b074b7bbSAlexander V. Chernikov 			return (NULL);
1551ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
1552ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
1553b074b7bbSAlexander V. Chernikov 	} else {
1554b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
1555b074b7bbSAlexander V. Chernikov 		name = bname;
1556ac35ff17SAlexander V. Chernikov 		set = 0;
1557b074b7bbSAlexander V. Chernikov 	}
1558b074b7bbSAlexander V. Chernikov 
1559b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
1560b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
1561b074b7bbSAlexander V. Chernikov 	tc->no.type = ti->type;
1562ac35ff17SAlexander V. Chernikov 	tc->no.set = set;
15639f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
1564b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
1565ac35ff17SAlexander V. Chernikov 	/* Set default value type to u32 for compability reasons */
1566ac35ff17SAlexander V. Chernikov 	tc->vtype = IPFW_VTYPE_U32;
1567b074b7bbSAlexander V. Chernikov 
1568b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
1569b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
1570b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
1571b074b7bbSAlexander V. Chernikov 	}
1572b074b7bbSAlexander V. Chernikov 
1573b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
15749490a627SAlexander V. Chernikov 	error = ta->init(&tc->astate, &tc->ti, aname);
1575b074b7bbSAlexander V. Chernikov 	if (error != 0) {
1576b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
1577b074b7bbSAlexander V. Chernikov 		return (NULL);
1578b074b7bbSAlexander V. Chernikov 	}
1579b074b7bbSAlexander V. Chernikov 
1580b074b7bbSAlexander V. Chernikov 	return (tc);
1581b074b7bbSAlexander V. Chernikov }
1582b074b7bbSAlexander V. Chernikov 
1583b074b7bbSAlexander V. Chernikov static void
1584b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
1585b074b7bbSAlexander V. Chernikov {
1586b074b7bbSAlexander V. Chernikov 
1587b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0)
15889f7d47b0SAlexander V. Chernikov 		tc->ta->destroy(&tc->astate, &tc->ti);
1589b074b7bbSAlexander V. Chernikov 
1590b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
1591b074b7bbSAlexander V. Chernikov }
1592b074b7bbSAlexander V. Chernikov 
1593b074b7bbSAlexander V. Chernikov /*
1594b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
1595b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
1596b074b7bbSAlexander V. Chernikov  */
1597b074b7bbSAlexander V. Chernikov static void
15989f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
1599b074b7bbSAlexander V. Chernikov {
1600b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
16019f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1602b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1603b074b7bbSAlexander V. Chernikov 
16049f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
16059f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1606b074b7bbSAlexander V. Chernikov 
16079f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1608b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1609b074b7bbSAlexander V. Chernikov 
1610b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
16119f7d47b0SAlexander V. Chernikov 
16129f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
16139f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
1614b074b7bbSAlexander V. Chernikov 
1615b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
1616b074b7bbSAlexander V. Chernikov }
1617b074b7bbSAlexander V. Chernikov 
1618b074b7bbSAlexander V. Chernikov /*
1619b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
1620b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
1621b074b7bbSAlexander V. Chernikov  */
1622b074b7bbSAlexander V. Chernikov static void
16239f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
1624b074b7bbSAlexander V. Chernikov {
1625b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
16269f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
1627b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1628b074b7bbSAlexander V. Chernikov 
16299f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
16309f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
1631b074b7bbSAlexander V. Chernikov 
16329f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1633b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
1634b074b7bbSAlexander V. Chernikov 
16359f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
1636b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
16379f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
16389f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
1639b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
1640b074b7bbSAlexander V. Chernikov }
1641b074b7bbSAlexander V. Chernikov 
1642b074b7bbSAlexander V. Chernikov /*
1643b074b7bbSAlexander V. Chernikov  * Finds named object by @uidx number.
1644b074b7bbSAlexander V. Chernikov  * Refs found object, allocate new index for non-existing object.
16459490a627SAlexander V. Chernikov  * Fills in @oib with userland/kernel indexes.
16469490a627SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
1647b074b7bbSAlexander V. Chernikov  *
1648b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1649b074b7bbSAlexander V. Chernikov  */
1650b074b7bbSAlexander V. Chernikov static int
16519490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
16529490a627SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
1653b074b7bbSAlexander V. Chernikov {
1654b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
16559490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
16569490a627SAlexander V. Chernikov 	struct named_object *no;
16579490a627SAlexander V. Chernikov 	int error, l, cmdlen;
16589490a627SAlexander V. Chernikov 	ipfw_insn *cmd;
16599490a627SAlexander V. Chernikov 	struct obj_idx *pidx, *p;
16609490a627SAlexander V. Chernikov 
16619490a627SAlexander V. Chernikov 	pidx = *oib;
16629490a627SAlexander V. Chernikov 	l = rule->cmd_len;
16639490a627SAlexander V. Chernikov 	cmd = rule->cmd;
16649490a627SAlexander V. Chernikov 	cmdlen = 0;
16659490a627SAlexander V. Chernikov 	error = 0;
16669490a627SAlexander V. Chernikov 
16679490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
16689490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
16699490a627SAlexander V. Chernikov 
16709490a627SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
16719490a627SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
16729490a627SAlexander V. Chernikov 
16739490a627SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
16749490a627SAlexander V. Chernikov 			continue;
1675b074b7bbSAlexander V. Chernikov 
1676b074b7bbSAlexander V. Chernikov 		pidx->uidx = ti->uidx;
1677b074b7bbSAlexander V. Chernikov 		pidx->type = ti->type;
1678b074b7bbSAlexander V. Chernikov 
16799490a627SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
16809490a627SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
16819490a627SAlexander V. Chernikov 				/* Incompatible types */
16829490a627SAlexander V. Chernikov 				error = EINVAL;
16839490a627SAlexander V. Chernikov 				break;
16849490a627SAlexander V. Chernikov 			}
16859f7d47b0SAlexander V. Chernikov 
16869490a627SAlexander V. Chernikov 			/* Reference found table and save kidx */
16879490a627SAlexander V. Chernikov 			tc->no.refcnt++;
16889490a627SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
16899490a627SAlexander V. Chernikov 			pidx++;
16909490a627SAlexander V. Chernikov 			continue;
16919490a627SAlexander V. Chernikov 		}
16929490a627SAlexander V. Chernikov 
16939490a627SAlexander V. Chernikov 		/* Table not found. Allocate new index and save for later */
1694ac35ff17SAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) {
1695ac35ff17SAlexander V. Chernikov 			printf("Unable to allocate table %s index in set %u."
1696b074b7bbSAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max",
1697ac35ff17SAlexander V. Chernikov 			    "", ti->set);
16989490a627SAlexander V. Chernikov 			error = EBUSY;
16999490a627SAlexander V. Chernikov 			break;
1700b074b7bbSAlexander V. Chernikov 		}
1701b074b7bbSAlexander V. Chernikov 
1702b074b7bbSAlexander V. Chernikov 		ci->new_tables++;
17039490a627SAlexander V. Chernikov 		pidx->new = 1;
17049490a627SAlexander V. Chernikov 		pidx++;
1705b074b7bbSAlexander V. Chernikov 	}
1706b074b7bbSAlexander V. Chernikov 
17079490a627SAlexander V. Chernikov 	if (error != 0) {
17089490a627SAlexander V. Chernikov 		/* Unref everything we have already done */
17099490a627SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
17109490a627SAlexander V. Chernikov 			if (p->new != 0) {
1711ac35ff17SAlexander V. Chernikov 				ipfw_objhash_free_idx(ni, p->kidx);
17129490a627SAlexander V. Chernikov 				continue;
17139490a627SAlexander V. Chernikov 			}
1714b074b7bbSAlexander V. Chernikov 
17159490a627SAlexander V. Chernikov 			/* Find & unref by existing idx */
1716ac35ff17SAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
17179490a627SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
17189490a627SAlexander V. Chernikov 			    p->kidx));
1719b074b7bbSAlexander V. Chernikov 
17209490a627SAlexander V. Chernikov 			no->refcnt--;
17219490a627SAlexander V. Chernikov 		}
17229490a627SAlexander V. Chernikov 	}
17239490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1724b074b7bbSAlexander V. Chernikov 
17259490a627SAlexander V. Chernikov 	*oib = pidx;
17269490a627SAlexander V. Chernikov 
17279490a627SAlexander V. Chernikov 	return (error);
1728b074b7bbSAlexander V. Chernikov }
1729b074b7bbSAlexander V. Chernikov 
1730b074b7bbSAlexander V. Chernikov /*
1731b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
1732b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
1733b074b7bbSAlexander V. Chernikov  * Works for \d+ talbes only (e.g. for tables, converted
1734b074b7bbSAlexander V. Chernikov  * from old numbered system calls).
1735b074b7bbSAlexander V. Chernikov  *
1736b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
1737b074b7bbSAlexander V. Chernikov  * Raises error on any other tables.
1738b074b7bbSAlexander V. Chernikov  */
1739b074b7bbSAlexander V. Chernikov int
1740b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
1741b074b7bbSAlexander V. Chernikov {
1742b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
1743b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1744b074b7bbSAlexander V. Chernikov 	uint32_t set;
1745b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1746b074b7bbSAlexander V. Chernikov 	uint8_t type;
1747b074b7bbSAlexander V. Chernikov 	struct named_object *no;
1748b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1749b074b7bbSAlexander V. Chernikov 
1750b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1751b074b7bbSAlexander V. Chernikov 
1752b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
1753b074b7bbSAlexander V. Chernikov 
1754b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
1755b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
1756b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
1757b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1758b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1759b074b7bbSAlexander V. Chernikov 
1760b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1761b074b7bbSAlexander V. Chernikov 			continue;
1762b074b7bbSAlexander V. Chernikov 
1763ac35ff17SAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL)
1764b074b7bbSAlexander V. Chernikov 			return (1);
1765b074b7bbSAlexander V. Chernikov 
1766b074b7bbSAlexander V. Chernikov 		if (no->compat == 0)
1767b074b7bbSAlexander V. Chernikov 			return (2);
1768b074b7bbSAlexander V. Chernikov 
1769b074b7bbSAlexander V. Chernikov 		update_table_opcode(cmd, no->uidx);
1770b074b7bbSAlexander V. Chernikov 	}
1771b074b7bbSAlexander V. Chernikov 
1772b074b7bbSAlexander V. Chernikov 	return (0);
1773b074b7bbSAlexander V. Chernikov }
1774b074b7bbSAlexander V. Chernikov 
1775563b5ab1SAlexander V. Chernikov /*
1776563b5ab1SAlexander V. Chernikov  * Sets every table kidx in @bmask which is used in rule @rule.
1777563b5ab1SAlexander V. Chernikov  *
1778563b5ab1SAlexander V. Chernikov  * Returns number of newly-referenced tables.
1779563b5ab1SAlexander V. Chernikov  */
1780563b5ab1SAlexander V. Chernikov int
1781563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
1782563b5ab1SAlexander V. Chernikov     uint32_t *bmask)
1783563b5ab1SAlexander V. Chernikov {
1784563b5ab1SAlexander V. Chernikov 	int cmdlen, l, count;
1785563b5ab1SAlexander V. Chernikov 	ipfw_insn *cmd;
1786563b5ab1SAlexander V. Chernikov 	uint16_t kidx;
1787563b5ab1SAlexander V. Chernikov 	uint8_t type;
1788563b5ab1SAlexander V. Chernikov 
1789563b5ab1SAlexander V. Chernikov 	l = rule->cmd_len;
1790563b5ab1SAlexander V. Chernikov 	cmd = rule->cmd;
1791563b5ab1SAlexander V. Chernikov 	cmdlen = 0;
1792563b5ab1SAlexander V. Chernikov 	count = 0;
1793563b5ab1SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1794563b5ab1SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1795563b5ab1SAlexander V. Chernikov 
1796563b5ab1SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1797563b5ab1SAlexander V. Chernikov 			continue;
1798563b5ab1SAlexander V. Chernikov 
1799563b5ab1SAlexander V. Chernikov 		if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
1800563b5ab1SAlexander V. Chernikov 			count++;
1801563b5ab1SAlexander V. Chernikov 
1802563b5ab1SAlexander V. Chernikov 		bmask[kidx / 32] |= 1 << (kidx % 32);
1803563b5ab1SAlexander V. Chernikov 	}
1804563b5ab1SAlexander V. Chernikov 
1805563b5ab1SAlexander V. Chernikov 	return (count);
1806563b5ab1SAlexander V. Chernikov }
1807563b5ab1SAlexander V. Chernikov 
1808563b5ab1SAlexander V. Chernikov 
1809b074b7bbSAlexander V. Chernikov 
1810b074b7bbSAlexander V. Chernikov /*
1811b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
1812b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
1813b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
1814b074b7bbSAlexander V. Chernikov  *
1815b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
1816b074b7bbSAlexander V. Chernikov  */
1817b074b7bbSAlexander V. Chernikov int
1818b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
1819b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
1820b074b7bbSAlexander V. Chernikov {
1821b074b7bbSAlexander V. Chernikov 	int cmdlen, error, ftype, l;
1822b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
1823b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
1824b074b7bbSAlexander V. Chernikov 	uint8_t type;
1825b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
18269f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
1827b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1828b074b7bbSAlexander V. Chernikov 	struct named_object *no, *no_n, *no_tmp;
18299490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
1830b074b7bbSAlexander V. Chernikov 	struct namedobjects_head nh;
1831b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
1832b074b7bbSAlexander V. Chernikov 
1833b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
1834b074b7bbSAlexander V. Chernikov 
18359490a627SAlexander V. Chernikov 	/* Prepare queue to store configs */
18369490a627SAlexander V. Chernikov 	TAILQ_INIT(&nh);
18379490a627SAlexander V. Chernikov 
1838b074b7bbSAlexander V. Chernikov 	/*
1839b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
1840b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
1841b074b7bbSAlexander V. Chernikov 	 */
1842b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
1843b074b7bbSAlexander V. Chernikov 		/* Stack */
18449490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
1845b074b7bbSAlexander V. Chernikov 	} else
18469490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
1847b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
1848b074b7bbSAlexander V. Chernikov 
18499490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
1850b074b7bbSAlexander V. Chernikov 	error = 0;
1851b074b7bbSAlexander V. Chernikov 
1852b074b7bbSAlexander V. Chernikov 	type = 0;
1853b074b7bbSAlexander V. Chernikov 	ftype = 0;
1854b074b7bbSAlexander V. Chernikov 
1855b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
1856ac35ff17SAlexander V. Chernikov 	ti.set = TABLE_SET(ci->krule->set);
18576c2997ffSAlexander V. Chernikov 	if (ci->ctlv != NULL) {
18586c2997ffSAlexander V. Chernikov 		ti.tlvs = (void *)(ci->ctlv + 1);
18596c2997ffSAlexander V. Chernikov 		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
18606c2997ffSAlexander V. Chernikov 	}
1861b074b7bbSAlexander V. Chernikov 
1862b074b7bbSAlexander V. Chernikov 	/*
18639490a627SAlexander V. Chernikov 	 * Stage 1: reference existing tables, determine number
18649490a627SAlexander V. Chernikov 	 * of tables we need to allocate and allocate indexes for each.
1865b074b7bbSAlexander V. Chernikov 	 */
18669490a627SAlexander V. Chernikov 	error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti);
1867b074b7bbSAlexander V. Chernikov 
1868b074b7bbSAlexander V. Chernikov 	if (error != 0) {
18699490a627SAlexander V. Chernikov 		if (pidx_first != ci->obuf)
18709490a627SAlexander V. Chernikov 			free(pidx_first, M_IPFW);
1871b074b7bbSAlexander V. Chernikov 
1872b074b7bbSAlexander V. Chernikov 		return (error);
1873b074b7bbSAlexander V. Chernikov 	}
1874b074b7bbSAlexander V. Chernikov 
1875b074b7bbSAlexander V. Chernikov 	/*
1876b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate table configs for every non-existent table
1877b074b7bbSAlexander V. Chernikov 	 */
1878b074b7bbSAlexander V. Chernikov 
18799f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
18809490a627SAlexander V. Chernikov 		for (p = pidx_first; p < pidx_last; p++) {
1881b074b7bbSAlexander V. Chernikov 			if (p->new == 0)
1882b074b7bbSAlexander V. Chernikov 				continue;
1883b074b7bbSAlexander V. Chernikov 
1884b074b7bbSAlexander V. Chernikov 			ti.uidx = p->uidx;
1885b074b7bbSAlexander V. Chernikov 			ti.type = p->type;
18869f7d47b0SAlexander V. Chernikov 			ti.atype = 0;
1887b074b7bbSAlexander V. Chernikov 
18889490a627SAlexander V. Chernikov 			ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL);
18899f7d47b0SAlexander V. Chernikov 			if (ta == NULL) {
18909f7d47b0SAlexander V. Chernikov 				error = ENOTSUP;
18919f7d47b0SAlexander V. Chernikov 				goto free;
18929f7d47b0SAlexander V. Chernikov 			}
18939490a627SAlexander V. Chernikov 			tc = alloc_table_config(ni, &ti, ta, NULL);
1894b074b7bbSAlexander V. Chernikov 
1895b074b7bbSAlexander V. Chernikov 			if (tc == NULL) {
1896b074b7bbSAlexander V. Chernikov 				error = ENOMEM;
1897b074b7bbSAlexander V. Chernikov 				goto free;
1898b074b7bbSAlexander V. Chernikov 			}
1899b074b7bbSAlexander V. Chernikov 
1900b074b7bbSAlexander V. Chernikov 			tc->no.kidx = p->kidx;
1901b074b7bbSAlexander V. Chernikov 			tc->no.refcnt = 1;
1902b074b7bbSAlexander V. Chernikov 
1903b074b7bbSAlexander V. Chernikov 			/* Add to list */
1904b074b7bbSAlexander V. Chernikov 			TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next);
1905b074b7bbSAlexander V. Chernikov 		}
1906b074b7bbSAlexander V. Chernikov 
1907b074b7bbSAlexander V. Chernikov 		/*
1908b074b7bbSAlexander V. Chernikov 		 * Stage 2.1: Check if we're going to create 2 tables
1909b074b7bbSAlexander V. Chernikov 		 * with the same name, but different table types.
1910b074b7bbSAlexander V. Chernikov 		 */
1911b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH(no, &nh, nn_next) {
1912b074b7bbSAlexander V. Chernikov 			TAILQ_FOREACH(no_tmp, &nh, nn_next) {
19139490a627SAlexander V. Chernikov 				if (ipfw_objhash_same_name(ni, no, no_tmp) == 0)
1914b074b7bbSAlexander V. Chernikov 					continue;
1915b074b7bbSAlexander V. Chernikov 				if (no->type != no_tmp->type) {
1916b074b7bbSAlexander V. Chernikov 					error = EINVAL;
1917b074b7bbSAlexander V. Chernikov 					goto free;
1918b074b7bbSAlexander V. Chernikov 				}
1919b074b7bbSAlexander V. Chernikov 			}
1920b074b7bbSAlexander V. Chernikov 		}
19219f7d47b0SAlexander V. Chernikov 	}
1922b074b7bbSAlexander V. Chernikov 
19239f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
19249f7d47b0SAlexander V. Chernikov 
19259f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
1926b074b7bbSAlexander V. Chernikov 		/*
1927b074b7bbSAlexander V. Chernikov 		 * Stage 3: link & reference new table configs
1928b074b7bbSAlexander V. Chernikov 		 */
1929b074b7bbSAlexander V. Chernikov 
1930b074b7bbSAlexander V. Chernikov 
1931b074b7bbSAlexander V. Chernikov 		/*
1932b074b7bbSAlexander V. Chernikov 		 * Step 3.1: Check if some tables we need to create have been
1933b074b7bbSAlexander V. Chernikov 		 * already created with different table type.
1934b074b7bbSAlexander V. Chernikov 		 */
1935b074b7bbSAlexander V. Chernikov 
1936b074b7bbSAlexander V. Chernikov 		error = 0;
1937b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1938b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1939b074b7bbSAlexander V. Chernikov 			if (no_n == NULL)
1940b074b7bbSAlexander V. Chernikov 				continue;
1941b074b7bbSAlexander V. Chernikov 
1942b074b7bbSAlexander V. Chernikov 			if (no_n->type != no->type) {
1943b074b7bbSAlexander V. Chernikov 				error = EINVAL;
1944b074b7bbSAlexander V. Chernikov 				break;
1945b074b7bbSAlexander V. Chernikov 			}
1946b074b7bbSAlexander V. Chernikov 
1947b074b7bbSAlexander V. Chernikov 		}
1948b074b7bbSAlexander V. Chernikov 
1949b074b7bbSAlexander V. Chernikov 		if (error != 0) {
1950b074b7bbSAlexander V. Chernikov 			/*
1951b074b7bbSAlexander V. Chernikov 			 * Someone has allocated table with different table type.
1952b074b7bbSAlexander V. Chernikov 			 * We have to rollback everything.
1953b074b7bbSAlexander V. Chernikov 			 */
1954b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(chain);
1955b074b7bbSAlexander V. Chernikov 			goto free;
1956b074b7bbSAlexander V. Chernikov 		}
1957b074b7bbSAlexander V. Chernikov 
1958b074b7bbSAlexander V. Chernikov 		/*
19599f7d47b0SAlexander V. Chernikov 		 * Attach new tables.
19609f7d47b0SAlexander V. Chernikov 		 * We need to set table pointers for each new table,
1961b074b7bbSAlexander V. Chernikov 		 * so we have to acquire main WLOCK.
1962b074b7bbSAlexander V. Chernikov 		 */
1963b074b7bbSAlexander V. Chernikov 		IPFW_WLOCK(chain);
1964b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
1965b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
1966b074b7bbSAlexander V. Chernikov 
19679490a627SAlexander V. Chernikov 			if (no_n == NULL) {
19689490a627SAlexander V. Chernikov 				/* New table. Attach to runtime hash */
19699490a627SAlexander V. Chernikov 				TAILQ_REMOVE(&nh, no, nn_next);
19709490a627SAlexander V. Chernikov 				link_table(chain, (struct table_config *)no);
1971b074b7bbSAlexander V. Chernikov 				continue;
1972b074b7bbSAlexander V. Chernikov 			}
1973b074b7bbSAlexander V. Chernikov 
19749490a627SAlexander V. Chernikov 			/*
19759490a627SAlexander V. Chernikov 			 * Newly-allocated table with the same type.
19769490a627SAlexander V. Chernikov 			 * Reference it and update out @pidx array
19779490a627SAlexander V. Chernikov 			 * rewrite info.
19789490a627SAlexander V. Chernikov 			 */
19799490a627SAlexander V. Chernikov 			no_n->refcnt++;
19809490a627SAlexander V. Chernikov 			/* Keep oib array in sync: update kidx */
19819490a627SAlexander V. Chernikov 			for (p = pidx_first; p < pidx_last; p++) {
19829490a627SAlexander V. Chernikov 				if (p->kidx != no->kidx)
19839490a627SAlexander V. Chernikov 					continue;
19849490a627SAlexander V. Chernikov 				/* Update kidx */
19859490a627SAlexander V. Chernikov 				p->kidx = no_n->kidx;
19869490a627SAlexander V. Chernikov 				break;
19879490a627SAlexander V. Chernikov 			}
1988b074b7bbSAlexander V. Chernikov 		}
1989b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(chain);
19909f7d47b0SAlexander V. Chernikov 	}
1991b074b7bbSAlexander V. Chernikov 
1992b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
1993b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
1994b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
1995b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
19969490a627SAlexander V. Chernikov 	p = pidx_first;
1997b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1998b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1999b074b7bbSAlexander V. Chernikov 
2000b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
2001b074b7bbSAlexander V. Chernikov 			continue;
20029490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
20039490a627SAlexander V. Chernikov 		p++;
2004b074b7bbSAlexander V. Chernikov 	}
2005b074b7bbSAlexander V. Chernikov 
2006b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
2007b074b7bbSAlexander V. Chernikov 
2008b074b7bbSAlexander V. Chernikov 	error = 0;
2009b074b7bbSAlexander V. Chernikov 
2010b074b7bbSAlexander V. Chernikov 	/*
2011b074b7bbSAlexander V. Chernikov 	 * Stage 4: free resources
2012b074b7bbSAlexander V. Chernikov 	 */
2013b074b7bbSAlexander V. Chernikov free:
20149490a627SAlexander V. Chernikov 	if (!TAILQ_EMPTY(&nh)) {
20159490a627SAlexander V. Chernikov 		/* Free indexes first */
20169490a627SAlexander V. Chernikov 		IPFW_UH_WLOCK(chain);
20179490a627SAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
2018ac35ff17SAlexander V. Chernikov 			ipfw_objhash_free_idx(ni, no->kidx);
20199490a627SAlexander V. Chernikov 		}
20209490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
20219490a627SAlexander V. Chernikov 		/* Free configs */
2022b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp)
2023b074b7bbSAlexander V. Chernikov 			free_table_config(ni, tc);
20249490a627SAlexander V. Chernikov 	}
2025b074b7bbSAlexander V. Chernikov 
20269490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
20279490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
2028b074b7bbSAlexander V. Chernikov 
2029b074b7bbSAlexander V. Chernikov 	return (error);
2030b074b7bbSAlexander V. Chernikov }
2031b074b7bbSAlexander V. Chernikov 
2032b074b7bbSAlexander V. Chernikov /*
2033b074b7bbSAlexander V. Chernikov  * Remove references from every table used in @rule.
2034b074b7bbSAlexander V. Chernikov  */
2035b074b7bbSAlexander V. Chernikov void
2036b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
2037b074b7bbSAlexander V. Chernikov {
2038b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
2039b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
2040b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
2041b074b7bbSAlexander V. Chernikov 	struct named_object *no;
2042b074b7bbSAlexander V. Chernikov 	uint32_t set;
2043b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2044b074b7bbSAlexander V. Chernikov 	uint8_t type;
2045b074b7bbSAlexander V. Chernikov 
2046b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
2047b074b7bbSAlexander V. Chernikov 
2048b074b7bbSAlexander V. Chernikov 	set = TABLE_SET(rule->set);
2049b074b7bbSAlexander V. Chernikov 
2050b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
2051b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
2052b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
2053b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2054b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
2055b074b7bbSAlexander V. Chernikov 
2056b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
2057b074b7bbSAlexander V. Chernikov 			continue;
2058b074b7bbSAlexander V. Chernikov 
2059ac35ff17SAlexander V. Chernikov 		no = ipfw_objhash_lookup_kidx(ni, kidx);
2060b074b7bbSAlexander V. Chernikov 
2061b074b7bbSAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
2062b074b7bbSAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
2063b074b7bbSAlexander V. Chernikov 		    no->type, type, kidx));
2064b074b7bbSAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
2065b074b7bbSAlexander V. Chernikov 		    kidx, no->refcnt));
2066b074b7bbSAlexander V. Chernikov 
2067b074b7bbSAlexander V. Chernikov 		no->refcnt--;
2068b074b7bbSAlexander V. Chernikov 	}
2069b074b7bbSAlexander V. Chernikov }
2070b074b7bbSAlexander V. Chernikov 
2071b074b7bbSAlexander V. Chernikov 
2072b074b7bbSAlexander V. Chernikov /*
2073b074b7bbSAlexander V. Chernikov  * Removes table bindings for every rule in rule chain @head.
2074b074b7bbSAlexander V. Chernikov  */
2075b074b7bbSAlexander V. Chernikov void
2076b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head)
2077b074b7bbSAlexander V. Chernikov {
2078b074b7bbSAlexander V. Chernikov 	struct ip_fw *rule;
2079b074b7bbSAlexander V. Chernikov 
2080b074b7bbSAlexander V. Chernikov 	while ((rule = head) != NULL) {
2081b074b7bbSAlexander V. Chernikov 		head = head->x_next;
2082b074b7bbSAlexander V. Chernikov 		ipfw_unbind_table_rule(chain, rule);
2083b074b7bbSAlexander V. Chernikov 	}
2084b074b7bbSAlexander V. Chernikov }
2085b074b7bbSAlexander V. Chernikov 
2086b074b7bbSAlexander V. Chernikov 
20873b3a8eb9SGleb Smirnoff /* end of file */
2088