xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision a73d728d)
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 
543b3a8eb9SGleb Smirnoff #include <netinet/in.h>
553b3a8eb9SGleb Smirnoff #include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
563b3a8eb9SGleb Smirnoff #include <netinet/ip_fw.h>
573b3a8eb9SGleb Smirnoff 
583b3a8eb9SGleb Smirnoff #include <netpfil/ipfw/ip_fw_private.h>
59ea761a5dSAlexander V. Chernikov #include <netpfil/ipfw/ip_fw_table.h>
603b3a8eb9SGleb Smirnoff 
613b3a8eb9SGleb Smirnoff 
623b3a8eb9SGleb Smirnoff  /*
63b074b7bbSAlexander V. Chernikov  * Table has the following `type` concepts:
64b074b7bbSAlexander V. Chernikov  *
659f7d47b0SAlexander V. Chernikov  * `no.type` represents lookup key type (cidr, ifp, uid, etc..)
669f7d47b0SAlexander V. Chernikov  * `ta->atype` represents exact lookup algorithm.
67b074b7bbSAlexander V. Chernikov  *     For example, we can use more efficient search schemes if we plan
68b074b7bbSAlexander V. Chernikov  *     to use some specific table for storing host-routes only.
699490a627SAlexander V. Chernikov  * `ftype` (at the moment )is pure userland field helping to properly
709490a627SAlexander V. Chernikov  *     format value data e.g. "value is IPv4 nexthop" or "value is DSCP"
719490a627SAlexander V. Chernikov  *     or "value is port".
72b074b7bbSAlexander V. Chernikov  *
73b074b7bbSAlexander V. Chernikov  */
74b074b7bbSAlexander V. Chernikov struct table_config {
75b074b7bbSAlexander V. Chernikov 	struct named_object	no;
76ac35ff17SAlexander V. Chernikov 	uint8_t		vtype;		/* format table type */
77b074b7bbSAlexander V. Chernikov 	uint8_t		linked;		/* 1 if already linked */
78914bffb6SAlexander V. Chernikov 	uint8_t		tflags;		/* type flags */
79a73d728dSAlexander V. Chernikov 	uint8_t		ochanged;	/* used by set swapping */
80b074b7bbSAlexander V. Chernikov 	uint32_t	count;		/* Number of records */
814c0c07a5SAlexander V. Chernikov 	uint32_t	limit;		/* Max number of records */
82a73d728dSAlexander V. Chernikov 	uint32_t	ocount;		/* used by set swapping */
83a73d728dSAlexander V. Chernikov 	uint64_t	gencnt;		/* generation count */
84b074b7bbSAlexander V. Chernikov 	char		tablename[64];	/* table name */
859f7d47b0SAlexander V. Chernikov 	struct table_algo	*ta;	/* Callbacks for given algo */
869f7d47b0SAlexander V. Chernikov 	void		*astate;	/* algorithm state */
879f7d47b0SAlexander V. Chernikov 	struct table_info	ti;	/* data to put to table_info */
88b074b7bbSAlexander V. Chernikov };
89b074b7bbSAlexander V. Chernikov 
90b074b7bbSAlexander V. Chernikov struct tables_config {
91b074b7bbSAlexander V. Chernikov 	struct namedobj_instance	*namehash;
929f7d47b0SAlexander V. Chernikov 	int				algo_count;
939f7d47b0SAlexander V. Chernikov 	struct table_algo 		*algo[256];
9457a1cf95SAlexander V. Chernikov 	struct table_algo		*def_algo[IPFW_TABLE_MAXTYPE + 1];
95b074b7bbSAlexander V. Chernikov };
96b074b7bbSAlexander V. Chernikov 
97b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni,
98b074b7bbSAlexander V. Chernikov     struct tid_info *ti);
9968394ec8SAlexander V. Chernikov static struct table_config *alloc_table_config(struct ip_fw_chain *ch,
100914bffb6SAlexander V. Chernikov     struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags,
101914bffb6SAlexander V. Chernikov     uint8_t vtype);
102b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni,
103b074b7bbSAlexander V. Chernikov     struct table_config *tc);
104db785d31SAlexander V. Chernikov static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
1054c0c07a5SAlexander V. Chernikov     char *aname, ipfw_xtable_info *i);
106b074b7bbSAlexander V. Chernikov static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
107b074b7bbSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
108b074b7bbSAlexander V. Chernikov static void free_table_state(void **state, void **xstate, uint8_t type);
1092d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
1102d99a349SAlexander V. Chernikov     struct sockopt_data *sd);
111ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
112ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i);
11381d3153dSAlexander V. Chernikov static int dump_table_tentry(void *e, void *arg);
114f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg);
115b074b7bbSAlexander V. Chernikov 
1162d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
1172d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
118db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
119ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
120db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
121ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
122a73d728dSAlexander V. Chernikov static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
12346d52008SAlexander V. Chernikov     struct tid_info *b);
124ac35ff17SAlexander V. Chernikov 
125b6ee846eSAlexander V. Chernikov static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
126b6ee846eSAlexander V. Chernikov     struct table_info *ti, uint32_t count);
127ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
128d3a4f924SAlexander V. Chernikov 
1299f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1309490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
131b074b7bbSAlexander V. Chernikov 
13246d52008SAlexander V. Chernikov static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
13346d52008SAlexander V. Chernikov static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti);
134a73d728dSAlexander V. Chernikov static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
13546d52008SAlexander V. Chernikov 
136b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
137b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1389f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
139b074b7bbSAlexander V. Chernikov 
140b6ee846eSAlexander V. Chernikov #define	TA_BUF_SZ	128	/* On-stack buffer for add/delete state */
141b6ee846eSAlexander V. Chernikov 
142b074b7bbSAlexander V. Chernikov 
1433b3a8eb9SGleb Smirnoff int
1441832a7b3SAlexander V. Chernikov add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
145b6ee846eSAlexander V. Chernikov     struct tentry_info *tei, uint32_t count)
1463b3a8eb9SGleb Smirnoff {
147db785d31SAlexander V. Chernikov 	struct table_config *tc;
1489f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
149b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
150b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1519f7d47b0SAlexander V. Chernikov 	int error;
152adea6201SAlexander V. Chernikov 	uint32_t num;
153b6ee846eSAlexander V. Chernikov 	ipfw_xtable_info *xi;
154b6ee846eSAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
1553b3a8eb9SGleb Smirnoff 
1569f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1579f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1589f7d47b0SAlexander V. Chernikov 
1599f7d47b0SAlexander V. Chernikov 	/*
1609f7d47b0SAlexander V. Chernikov 	 * Find and reference existing table.
1619f7d47b0SAlexander V. Chernikov 	 */
1629f7d47b0SAlexander V. Chernikov 	ta = NULL;
1639f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
1649f7d47b0SAlexander V. Chernikov 		/* check table type */
1659f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
1669f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1673b3a8eb9SGleb Smirnoff 			return (EINVAL);
1683b3a8eb9SGleb Smirnoff 		}
1693b3a8eb9SGleb Smirnoff 
1704c0c07a5SAlexander V. Chernikov 		/* Try to exit early on limit hit */
1714c0c07a5SAlexander V. Chernikov 		if (tc->limit != 0 && tc->count == tc->limit &&
1724c0c07a5SAlexander V. Chernikov 		    (tei->flags & TEI_FLAGS_UPDATE) == 0) {
1734c0c07a5SAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
1744c0c07a5SAlexander V. Chernikov 				return (EFBIG);
1754c0c07a5SAlexander V. Chernikov 		}
1764c0c07a5SAlexander V. Chernikov 
1779f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
1789f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
1799f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
1809f7d47b0SAlexander V. Chernikov 	}
1819f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1823b3a8eb9SGleb Smirnoff 
183ac35ff17SAlexander V. Chernikov 	if (tc == NULL) {
184db785d31SAlexander V. Chernikov 		/* Compability mode: create new table for old clients */
185db785d31SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
186db785d31SAlexander V. Chernikov 			return (ESRCH);
1873b3a8eb9SGleb Smirnoff 
188b6ee846eSAlexander V. Chernikov 		xi = malloc(sizeof(ipfw_xtable_info), M_TEMP, M_WAITOK|M_ZERO);
189b6ee846eSAlexander V. Chernikov 		xi->vtype = IPFW_VTYPE_U32;
1904c0c07a5SAlexander V. Chernikov 
191b6ee846eSAlexander V. Chernikov 		error = create_table_internal(ch, ti, NULL, xi);
192b6ee846eSAlexander V. Chernikov 		free(xi, M_TEMP);
193db785d31SAlexander V. Chernikov 
194db785d31SAlexander V. Chernikov 		if (error != 0)
195db785d31SAlexander V. Chernikov 			return (error);
196db785d31SAlexander V. Chernikov 
197db785d31SAlexander V. Chernikov 		/* Let's try to find & reference another time */
198db785d31SAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
199db785d31SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) == NULL) {
200db785d31SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
201db785d31SAlexander V. Chernikov 			return (EINVAL);
202db785d31SAlexander V. Chernikov 		}
203db785d31SAlexander V. Chernikov 
204db785d31SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
205db785d31SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
206db785d31SAlexander V. Chernikov 			return (EINVAL);
207db785d31SAlexander V. Chernikov 		}
208db785d31SAlexander V. Chernikov 
209db785d31SAlexander V. Chernikov 		/* Reference and unlock */
210db785d31SAlexander V. Chernikov 		tc->no.refcnt++;
211db785d31SAlexander V. Chernikov 		ta = tc->ta;
212db785d31SAlexander V. Chernikov 
213db785d31SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
214db785d31SAlexander V. Chernikov 	}
215db785d31SAlexander V. Chernikov 
2169f7d47b0SAlexander V. Chernikov 	/* Prepare record (allocate memory) */
2179f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
21868394ec8SAlexander V. Chernikov 	error = ta->prepare_add(ch, tei, &ta_buf);
219db785d31SAlexander V. Chernikov 	if (error != 0)
2209f7d47b0SAlexander V. Chernikov 		return (error);
2213b3a8eb9SGleb Smirnoff 
222b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
2233b3a8eb9SGleb Smirnoff 
224b6ee846eSAlexander V. Chernikov 	/*
225b6ee846eSAlexander V. Chernikov 	 * Ensure we are able to add all entries without additional
226b6ee846eSAlexander V. Chernikov 	 * memory allocations. May release/reacquire UH_WLOCK.
227b6ee846eSAlexander V. Chernikov 	 */
228b6ee846eSAlexander V. Chernikov 	kidx = tc->no.kidx;
229b6ee846eSAlexander V. Chernikov 	error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count);
230b6ee846eSAlexander V. Chernikov 	if (error != 0) {
231b6ee846eSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
232b6ee846eSAlexander V. Chernikov 		ta->flush_entry(ch, tei, &ta_buf);
233b6ee846eSAlexander V. Chernikov 		return (error);
234b6ee846eSAlexander V. Chernikov 	}
235b6ee846eSAlexander V. Chernikov 
236b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2373b3a8eb9SGleb Smirnoff 
2389f7d47b0SAlexander V. Chernikov 	/* Drop reference we've used in first search */
2399f7d47b0SAlexander V. Chernikov 	tc->no.refcnt--;
240b074b7bbSAlexander V. Chernikov 
2414c0c07a5SAlexander V. Chernikov 	/* Check limit before adding */
2424c0c07a5SAlexander V. Chernikov 	if (tc->limit != 0 && tc->count == tc->limit) {
2434c0c07a5SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
2444c0c07a5SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
245b6ee846eSAlexander V. Chernikov 			ta->flush_entry(ch, tei, &ta_buf);
2464c0c07a5SAlexander V. Chernikov 			return (EFBIG);
2474c0c07a5SAlexander V. Chernikov 		}
2484c0c07a5SAlexander V. Chernikov 
2494c0c07a5SAlexander V. Chernikov 		/*
2504c0c07a5SAlexander V. Chernikov 		 * We have UPDATE flag set.
2514c0c07a5SAlexander V. Chernikov 		 * Permit updating record (if found),
2524c0c07a5SAlexander V. Chernikov 		 * but restrict adding new one since we've
2534c0c07a5SAlexander V. Chernikov 		 * already hit the limit.
2544c0c07a5SAlexander V. Chernikov 		 */
2554c0c07a5SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_DONTADD;
2564c0c07a5SAlexander V. Chernikov 	}
2574c0c07a5SAlexander V. Chernikov 
258b074b7bbSAlexander V. Chernikov 	/* We've got valid table in @tc. Let's add data */
2599f7d47b0SAlexander V. Chernikov 	kidx = tc->no.kidx;
2609f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
261adea6201SAlexander V. Chernikov 	num = 0;
2629f7d47b0SAlexander V. Chernikov 
263b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
264b6ee846eSAlexander V. Chernikov 	error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num);
2653b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
2669f7d47b0SAlexander V. Chernikov 
267ac35ff17SAlexander V. Chernikov 	/* Update number of records. */
268b6ee846eSAlexander V. Chernikov 	if (error == 0) {
269adea6201SAlexander V. Chernikov 		tc->count += num;
270b6ee846eSAlexander V. Chernikov 		/* Permit post-add algorithm grow/rehash. */
271b6ee846eSAlexander V. Chernikov 		error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
272b6ee846eSAlexander V. Chernikov 	}
273db785d31SAlexander V. Chernikov 
274b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
275b074b7bbSAlexander V. Chernikov 
276ac35ff17SAlexander V. Chernikov 	/* Run cleaning callback anyway */
27768394ec8SAlexander V. Chernikov 	ta->flush_entry(ch, tei, &ta_buf);
278b074b7bbSAlexander V. Chernikov 
2799f7d47b0SAlexander V. Chernikov 	return (error);
2803b3a8eb9SGleb Smirnoff }
2813b3a8eb9SGleb Smirnoff 
2823b3a8eb9SGleb Smirnoff int
2831832a7b3SAlexander V. Chernikov del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
284b6ee846eSAlexander V. Chernikov     struct tentry_info *tei, uint32_t count)
2853b3a8eb9SGleb Smirnoff {
286b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2879f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
288b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
289b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2909f7d47b0SAlexander V. Chernikov 	int error;
291adea6201SAlexander V. Chernikov 	uint32_t num;
292b6ee846eSAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
2933b3a8eb9SGleb Smirnoff 
2949f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
295b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
296b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
2979f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
2983b3a8eb9SGleb Smirnoff 		return (ESRCH);
2993b3a8eb9SGleb Smirnoff 	}
3003b3a8eb9SGleb Smirnoff 
301b074b7bbSAlexander V. Chernikov 	if (tc->no.type != ti->type) {
3029f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
3033b3a8eb9SGleb Smirnoff 		return (EINVAL);
3043b3a8eb9SGleb Smirnoff 	}
3059f7d47b0SAlexander V. Chernikov 
3069f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
3079f7d47b0SAlexander V. Chernikov 
308db785d31SAlexander V. Chernikov 	/*
309b6ee846eSAlexander V. Chernikov 	 * Give a chance for algorithm to shrink.
310b6ee846eSAlexander V. Chernikov 	 * May release/reacquire UH_WLOCK.
311db785d31SAlexander V. Chernikov 	 */
312b6ee846eSAlexander V. Chernikov 	kidx = tc->no.kidx;
313b6ee846eSAlexander V. Chernikov 	error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
314db785d31SAlexander V. Chernikov 	if (error != 0) {
315db785d31SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
316b6ee846eSAlexander V. Chernikov 		ta->flush_entry(ch, tei, &ta_buf);
317db785d31SAlexander V. Chernikov 		return (error);
318db785d31SAlexander V. Chernikov 	}
319db785d31SAlexander V. Chernikov 
320db785d31SAlexander V. Chernikov 	/*
321db785d31SAlexander V. Chernikov 	 * We assume ta_buf size is enough for storing
322b6ee846eSAlexander V. Chernikov 	 * prepare_del() key, so we're running under UH_WLOCK here.
323db785d31SAlexander V. Chernikov 	 */
3249f7d47b0SAlexander V. Chernikov 	memset(&ta_buf, 0, sizeof(ta_buf));
32568394ec8SAlexander V. Chernikov 	if ((error = ta->prepare_del(ch, tei, &ta_buf)) != 0) {
3269f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
3279f7d47b0SAlexander V. Chernikov 		return (error);
3289f7d47b0SAlexander V. Chernikov 	}
3299f7d47b0SAlexander V. Chernikov 
330b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
331adea6201SAlexander V. Chernikov 	num = 0;
332b074b7bbSAlexander V. Chernikov 
333b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
334b6ee846eSAlexander V. Chernikov 	error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf, &num);
3353b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
3363b3a8eb9SGleb Smirnoff 
337b6ee846eSAlexander V. Chernikov 	if (error == 0) {
338adea6201SAlexander V. Chernikov 		tc->count -= num;
339b6ee846eSAlexander V. Chernikov 		/* Run post-del hook to permit shrinking */
340b6ee846eSAlexander V. Chernikov 		error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
341b6ee846eSAlexander V. Chernikov 	}
342b074b7bbSAlexander V. Chernikov 
3439f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3443b3a8eb9SGleb Smirnoff 
34568394ec8SAlexander V. Chernikov 	ta->flush_entry(ch, tei, &ta_buf);
346ac35ff17SAlexander V. Chernikov 
347ac35ff17SAlexander V. Chernikov 	return (error);
348ac35ff17SAlexander V. Chernikov }
349ac35ff17SAlexander V. Chernikov 
350db785d31SAlexander V. Chernikov /*
351b6ee846eSAlexander V. Chernikov  * Ensure that table @tc has enough space to add @count entries without
352b6ee846eSAlexander V. Chernikov  * need for reallocation.
353db785d31SAlexander V. Chernikov  *
354db785d31SAlexander V. Chernikov  * Callbacks order:
355b6ee846eSAlexander V. Chernikov  * 0) has_space() (UH_WLOCK) - checks if @count items can be added w/o resize.
356b6ee846eSAlexander V. Chernikov  *
357db785d31SAlexander V. Chernikov  * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags.
358db785d31SAlexander V. Chernikov  * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage
359db785d31SAlexander V. Chernikov  * 3) modify (UH_WLOCK + WLOCK) - switch pointers
360b6ee846eSAlexander V. Chernikov  * 4) flush_modify (UH_WLOCK) - free state, if needed
361b6ee846eSAlexander V. Chernikov  *
362b6ee846eSAlexander V. Chernikov  * Returns 0 on success.
363db785d31SAlexander V. Chernikov  */
364db785d31SAlexander V. Chernikov static int
365b6ee846eSAlexander V. Chernikov check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
366b6ee846eSAlexander V. Chernikov     struct table_info *ti, uint32_t count)
367db785d31SAlexander V. Chernikov {
368b6ee846eSAlexander V. Chernikov 	struct table_algo *ta;
369b6ee846eSAlexander V. Chernikov 	uint64_t pflags;
370b6ee846eSAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
371db785d31SAlexander V. Chernikov 	int error;
372db785d31SAlexander V. Chernikov 
373b6ee846eSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
374db785d31SAlexander V. Chernikov 
375b6ee846eSAlexander V. Chernikov 	error = 0;
376b6ee846eSAlexander V. Chernikov 	ta = tc->ta;
377b6ee846eSAlexander V. Chernikov 	/* Acquire reference not to loose @tc between locks/unlocks */
378b6ee846eSAlexander V. Chernikov 	tc->no.refcnt++;
379db785d31SAlexander V. Chernikov 
380db785d31SAlexander V. Chernikov 	/*
381b6ee846eSAlexander V. Chernikov 	 * TODO: think about avoiding race between large add/large delete
382b6ee846eSAlexander V. Chernikov 	 * operation on algorithm which implements shrinking along with
383b6ee846eSAlexander V. Chernikov 	 * growing.
384db785d31SAlexander V. Chernikov 	 */
385b6ee846eSAlexander V. Chernikov 	while (true) {
386b6ee846eSAlexander V. Chernikov 		pflags = 0;
387b6ee846eSAlexander V. Chernikov 		if (ta->has_space(tc->astate, ti, count, &pflags) != 0) {
388b6ee846eSAlexander V. Chernikov 			tc->no.refcnt--;
389b6ee846eSAlexander V. Chernikov 			return (0);
390b6ee846eSAlexander V. Chernikov 		}
391db785d31SAlexander V. Chernikov 
392b6ee846eSAlexander V. Chernikov 		/* We have to shrink/grow table */
393b6ee846eSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
394b6ee846eSAlexander V. Chernikov 		memset(&ta_buf, 0, sizeof(ta_buf));
395b6ee846eSAlexander V. Chernikov 
396b6ee846eSAlexander V. Chernikov 		if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) {
397b6ee846eSAlexander V. Chernikov 			IPFW_UH_WLOCK(ch);
398b6ee846eSAlexander V. Chernikov 			break;
399b6ee846eSAlexander V. Chernikov 		}
400b6ee846eSAlexander V. Chernikov 
401b6ee846eSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
402b6ee846eSAlexander V. Chernikov 
403b6ee846eSAlexander V. Chernikov 		/* Check if we still need to alter table */
404b6ee846eSAlexander V. Chernikov 		ti = KIDX_TO_TI(ch, tc->no.kidx);
405b6ee846eSAlexander V. Chernikov 		if (ta->has_space(tc->astate, ti, count, &pflags) != 0) {
406b6ee846eSAlexander V. Chernikov 
407b6ee846eSAlexander V. Chernikov 			/*
408b6ee846eSAlexander V. Chernikov 			 * Other threads has already performed resize.
409b6ee846eSAlexander V. Chernikov 			 * Flush our state and return/
410b6ee846eSAlexander V. Chernikov 			 */
411b6ee846eSAlexander V. Chernikov 			ta->flush_mod(ta_buf);
412b6ee846eSAlexander V. Chernikov 			break;
413b6ee846eSAlexander V. Chernikov 		}
414b6ee846eSAlexander V. Chernikov 
415b6ee846eSAlexander V. Chernikov 		error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags);
416b6ee846eSAlexander V. Chernikov 		if (error == 0) {
417db785d31SAlexander V. Chernikov 			/* Do actual modification */
418db785d31SAlexander V. Chernikov 			IPFW_WLOCK(ch);
41968394ec8SAlexander V. Chernikov 			ta->modify(tc->astate, ti, ta_buf, pflags);
420db785d31SAlexander V. Chernikov 			IPFW_WUNLOCK(ch);
421db785d31SAlexander V. Chernikov 		}
422db785d31SAlexander V. Chernikov 
423b6ee846eSAlexander V. Chernikov 		/* Anyway, flush data and retry */
424db785d31SAlexander V. Chernikov 		ta->flush_mod(ta_buf);
425b6ee846eSAlexander V. Chernikov 	}
426db785d31SAlexander V. Chernikov 
427b6ee846eSAlexander V. Chernikov 	tc->no.refcnt--;
428db785d31SAlexander V. Chernikov 	return (error);
429db785d31SAlexander V. Chernikov }
430db785d31SAlexander V. Chernikov 
431b6ee846eSAlexander V. Chernikov 
432b6ee846eSAlexander V. Chernikov 
433ac35ff17SAlexander V. Chernikov int
434db785d31SAlexander V. Chernikov ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
435ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
436ac35ff17SAlexander V. Chernikov {
437ac35ff17SAlexander V. Chernikov 	int error;
438ac35ff17SAlexander V. Chernikov 
439ac35ff17SAlexander V. Chernikov 	switch (op3->version) {
440ac35ff17SAlexander V. Chernikov 	case 0:
441db785d31SAlexander V. Chernikov 		error = ipfw_manage_table_ent_v0(ch, op3, sd);
442ac35ff17SAlexander V. Chernikov 		break;
443ac35ff17SAlexander V. Chernikov 	case 1:
444db785d31SAlexander V. Chernikov 		error = ipfw_manage_table_ent_v1(ch, op3, sd);
445ac35ff17SAlexander V. Chernikov 		break;
446ac35ff17SAlexander V. Chernikov 	default:
447ac35ff17SAlexander V. Chernikov 		error = ENOTSUP;
448ac35ff17SAlexander V. Chernikov 	}
449ac35ff17SAlexander V. Chernikov 
450ac35ff17SAlexander V. Chernikov 	return (error);
451ac35ff17SAlexander V. Chernikov }
452ac35ff17SAlexander V. Chernikov 
453ac35ff17SAlexander V. Chernikov /*
454ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
455ac35ff17SAlexander V. Chernikov  * Data layout (v0):
456ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ipfw_table_xentry ]
457ac35ff17SAlexander V. Chernikov  *
458ac35ff17SAlexander V. Chernikov  * Returns 0 on success
459ac35ff17SAlexander V. Chernikov  */
460ac35ff17SAlexander V. Chernikov static int
461db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
462ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
463ac35ff17SAlexander V. Chernikov {
464ac35ff17SAlexander V. Chernikov 	ipfw_table_xentry *xent;
465ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
466ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
467ac35ff17SAlexander V. Chernikov 	int error, hdrlen, read;
468ac35ff17SAlexander V. Chernikov 
469ac35ff17SAlexander V. Chernikov 	hdrlen = offsetof(ipfw_table_xentry, k);
470ac35ff17SAlexander V. Chernikov 
471ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
472ac35ff17SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*op3) + hdrlen))
473ac35ff17SAlexander V. Chernikov 		return (EINVAL);
474ac35ff17SAlexander V. Chernikov 
475ac35ff17SAlexander V. Chernikov 	read = sizeof(ip_fw3_opheader);
476ac35ff17SAlexander V. Chernikov 
477ac35ff17SAlexander V. Chernikov 	/* Check if xentry len field is valid */
478ac35ff17SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)(op3 + 1);
479ac35ff17SAlexander V. Chernikov 	if (xent->len < hdrlen || xent->len + read > sd->valsize)
480ac35ff17SAlexander V. Chernikov 		return (EINVAL);
481ac35ff17SAlexander V. Chernikov 
482ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
483ac35ff17SAlexander V. Chernikov 	tei.paddr = &xent->k;
484ac35ff17SAlexander V. Chernikov 	tei.masklen = xent->masklen;
485ac35ff17SAlexander V. Chernikov 	tei.value = xent->value;
486ac35ff17SAlexander V. Chernikov 	/* Old requests compability */
487db785d31SAlexander V. Chernikov 	tei.flags = TEI_FLAGS_COMPAT;
488ac35ff17SAlexander V. Chernikov 	if (xent->type == IPFW_TABLE_CIDR) {
489ac35ff17SAlexander V. Chernikov 		if (xent->len - hdrlen == sizeof(in_addr_t))
490ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET;
491ac35ff17SAlexander V. Chernikov 		else
492ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET6;
493ac35ff17SAlexander V. Chernikov 	}
494ac35ff17SAlexander V. Chernikov 
495ac35ff17SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
496ac35ff17SAlexander V. Chernikov 	ti.uidx = xent->tbl;
497ac35ff17SAlexander V. Chernikov 	ti.type = xent->type;
498ac35ff17SAlexander V. Chernikov 
499ac35ff17SAlexander V. Chernikov 	error = (op3->opcode == IP_FW_TABLE_XADD) ?
500b6ee846eSAlexander V. Chernikov 	    add_table_entry(ch, &ti, &tei, 1) :
501b6ee846eSAlexander V. Chernikov 	    del_table_entry(ch, &ti, &tei, 1);
502ac35ff17SAlexander V. Chernikov 
503ac35ff17SAlexander V. Chernikov 	return (error);
504ac35ff17SAlexander V. Chernikov }
505ac35ff17SAlexander V. Chernikov 
506ac35ff17SAlexander V. Chernikov /*
507ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
508ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
509db785d31SAlexander V. Chernikov  * Request: [ ipfw_obj_header
510db785d31SAlexander V. Chernikov  *   ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ]
511db785d31SAlexander V. Chernikov  * ]
512ac35ff17SAlexander V. Chernikov  *
513ac35ff17SAlexander V. Chernikov  * Returns 0 on success
514ac35ff17SAlexander V. Chernikov  */
515ac35ff17SAlexander V. Chernikov static int
516db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
517ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
518ac35ff17SAlexander V. Chernikov {
519ac35ff17SAlexander V. Chernikov 	ipfw_obj_tentry *tent;
520db785d31SAlexander V. Chernikov 	ipfw_obj_ctlv *ctlv;
521ac35ff17SAlexander V. Chernikov 	ipfw_obj_header *oh;
522ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
523ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
524ac35ff17SAlexander V. Chernikov 	int error, read;
525ac35ff17SAlexander V. Chernikov 
526ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
527db785d31SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv)))
528ac35ff17SAlexander V. Chernikov 		return (EINVAL);
529ac35ff17SAlexander V. Chernikov 
530ac35ff17SAlexander V. Chernikov 	/* Check if passed data is too long */
531ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sd->kavail)
532ac35ff17SAlexander V. Chernikov 		return (EINVAL);
533ac35ff17SAlexander V. Chernikov 
534ac35ff17SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
535ac35ff17SAlexander V. Chernikov 
536ac35ff17SAlexander V. Chernikov 	/* Basic length checks for TLVs */
537ac35ff17SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
538ac35ff17SAlexander V. Chernikov 		return (EINVAL);
539ac35ff17SAlexander V. Chernikov 
540ac35ff17SAlexander V. Chernikov 	read = sizeof(*oh);
541ac35ff17SAlexander V. Chernikov 
542db785d31SAlexander V. Chernikov 	ctlv = (ipfw_obj_ctlv *)(oh + 1);
543db785d31SAlexander V. Chernikov 	if (ctlv->head.length + read != sd->valsize)
544db785d31SAlexander V. Chernikov 		return (EINVAL);
545db785d31SAlexander V. Chernikov 
546db785d31SAlexander V. Chernikov 	/*
547db785d31SAlexander V. Chernikov 	 * TODO: permit adding multiple entries for given table
548db785d31SAlexander V. Chernikov 	 * at once
549db785d31SAlexander V. Chernikov 	 */
550db785d31SAlexander V. Chernikov 	if (ctlv->count != 1)
551db785d31SAlexander V. Chernikov 		return (EOPNOTSUPP);
552db785d31SAlexander V. Chernikov 
553db785d31SAlexander V. Chernikov 	read += sizeof(*ctlv);
554db785d31SAlexander V. Chernikov 
555ac35ff17SAlexander V. Chernikov 	/* Assume tentry may grow to support larger keys */
556db785d31SAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(ctlv + 1);
557ac35ff17SAlexander V. Chernikov 	if (tent->head.length < sizeof(*tent) ||
558ac35ff17SAlexander V. Chernikov 	    tent->head.length + read > sd->valsize)
559ac35ff17SAlexander V. Chernikov 		return (EINVAL);
560ac35ff17SAlexander V. Chernikov 
56181d3153dSAlexander V. Chernikov 	/* Convert data into kernel request objects */
562ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
563ac35ff17SAlexander V. Chernikov 	tei.paddr = &tent->k;
564ac35ff17SAlexander V. Chernikov 	tei.subtype = tent->subtype;
565ac35ff17SAlexander V. Chernikov 	tei.masklen = tent->masklen;
56681d3153dSAlexander V. Chernikov 	if (tent->head.flags & IPFW_TF_UPDATE)
567ac35ff17SAlexander V. Chernikov 		tei.flags |= TEI_FLAGS_UPDATE;
568ac35ff17SAlexander V. Chernikov 	tei.value = tent->value;
569ac35ff17SAlexander V. Chernikov 
57081d3153dSAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
571ac35ff17SAlexander V. Chernikov 	ti.type = oh->ntlv.type;
57281d3153dSAlexander V. Chernikov 	ti.uidx = tent->idx;
573ac35ff17SAlexander V. Chernikov 
574ac35ff17SAlexander V. Chernikov 	error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
575b6ee846eSAlexander V. Chernikov 	    add_table_entry(ch, &ti, &tei, 1) :
576b6ee846eSAlexander V. Chernikov 	    del_table_entry(ch, &ti, &tei, 1);
577ac35ff17SAlexander V. Chernikov 
578ac35ff17SAlexander V. Chernikov 	return (error);
579ac35ff17SAlexander V. Chernikov }
580ac35ff17SAlexander V. Chernikov 
58181d3153dSAlexander V. Chernikov /*
58281d3153dSAlexander V. Chernikov  * Looks up an entry in given table.
58381d3153dSAlexander V. Chernikov  * Data layout (v0)(current):
58481d3153dSAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_tentry ]
58581d3153dSAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_obj_tentry ]
58681d3153dSAlexander V. Chernikov  *
58781d3153dSAlexander V. Chernikov  * Returns 0 on success
58881d3153dSAlexander V. Chernikov  */
58981d3153dSAlexander V. Chernikov int
59081d3153dSAlexander V. Chernikov ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
59181d3153dSAlexander V. Chernikov     struct sockopt_data *sd)
59281d3153dSAlexander V. Chernikov {
59381d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
59481d3153dSAlexander V. Chernikov 	ipfw_obj_header *oh;
59581d3153dSAlexander V. Chernikov 	struct tid_info ti;
59681d3153dSAlexander V. Chernikov 	struct table_config *tc;
59781d3153dSAlexander V. Chernikov 	struct table_algo *ta;
59881d3153dSAlexander V. Chernikov 	struct table_info *kti;
59981d3153dSAlexander V. Chernikov 	struct namedobj_instance *ni;
600914bffb6SAlexander V. Chernikov 	int error;
60181d3153dSAlexander V. Chernikov 	size_t sz;
60281d3153dSAlexander V. Chernikov 
60381d3153dSAlexander V. Chernikov 	/* Check minimum header size */
60481d3153dSAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*tent);
60581d3153dSAlexander V. Chernikov 	if (sd->valsize != sz)
60681d3153dSAlexander V. Chernikov 		return (EINVAL);
60781d3153dSAlexander V. Chernikov 
60881d3153dSAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
60981d3153dSAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(oh + 1);
61081d3153dSAlexander V. Chernikov 
61181d3153dSAlexander V. Chernikov 	/* Basic length checks for TLVs */
61281d3153dSAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
61381d3153dSAlexander V. Chernikov 		return (EINVAL);
61481d3153dSAlexander V. Chernikov 
61581d3153dSAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
61681d3153dSAlexander V. Chernikov 	ti.type = oh->ntlv.type;
61781d3153dSAlexander V. Chernikov 	ti.uidx = tent->idx;
61881d3153dSAlexander V. Chernikov 
61981d3153dSAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
62081d3153dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
62181d3153dSAlexander V. Chernikov 
62281d3153dSAlexander V. Chernikov 	/*
62381d3153dSAlexander V. Chernikov 	 * Find existing table and check its type .
62481d3153dSAlexander V. Chernikov 	 */
62581d3153dSAlexander V. Chernikov 	ta = NULL;
62681d3153dSAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) == NULL) {
62781d3153dSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
62881d3153dSAlexander V. Chernikov 		return (ESRCH);
62981d3153dSAlexander V. Chernikov 	}
63081d3153dSAlexander V. Chernikov 
63181d3153dSAlexander V. Chernikov 	/* check table type */
63281d3153dSAlexander V. Chernikov 	if (tc->no.type != ti.type) {
63381d3153dSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
63481d3153dSAlexander V. Chernikov 		return (EINVAL);
63581d3153dSAlexander V. Chernikov 	}
63681d3153dSAlexander V. Chernikov 
63781d3153dSAlexander V. Chernikov 	kti = KIDX_TO_TI(ch, tc->no.kidx);
63881d3153dSAlexander V. Chernikov 	ta = tc->ta;
63981d3153dSAlexander V. Chernikov 
640914bffb6SAlexander V. Chernikov 	if (ta->find_tentry == NULL)
641914bffb6SAlexander V. Chernikov 		return (ENOTSUP);
642914bffb6SAlexander V. Chernikov 
643914bffb6SAlexander V. Chernikov 	error = ta->find_tentry(tc->astate, kti, tent);
64481d3153dSAlexander V. Chernikov 
64581d3153dSAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
64681d3153dSAlexander V. Chernikov 
64781d3153dSAlexander V. Chernikov 	return (error);
64881d3153dSAlexander V. Chernikov }
64981d3153dSAlexander V. Chernikov 
65046d52008SAlexander V. Chernikov /*
65146d52008SAlexander V. Chernikov  * Flushes all entries or destroys given table.
65246d52008SAlexander V. Chernikov  * Data layout (v0)(current):
65346d52008SAlexander V. Chernikov  * Request: [ ipfw_obj_header ]
65446d52008SAlexander V. Chernikov  *
65546d52008SAlexander V. Chernikov  * Returns 0 on success
65646d52008SAlexander V. Chernikov  */
657ac35ff17SAlexander V. Chernikov int
658ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
659ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
660ac35ff17SAlexander V. Chernikov {
661ac35ff17SAlexander V. Chernikov 	int error;
662ac35ff17SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
663ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
664ac35ff17SAlexander V. Chernikov 
665ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh))
666ac35ff17SAlexander V. Chernikov 		return (EINVAL);
667ac35ff17SAlexander V. Chernikov 
668ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
669ac35ff17SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
670ac35ff17SAlexander V. Chernikov 
6711832a7b3SAlexander V. Chernikov 	if (op3->opcode == IP_FW_TABLE_XDESTROY)
672ac35ff17SAlexander V. Chernikov 		error = destroy_table(ch, &ti);
6731832a7b3SAlexander V. Chernikov 	else if (op3->opcode == IP_FW_TABLE_XFLUSH)
674ac35ff17SAlexander V. Chernikov 		error = flush_table(ch, &ti);
675ac35ff17SAlexander V. Chernikov 	else
676ac35ff17SAlexander V. Chernikov 		return (ENOTSUP);
677ac35ff17SAlexander V. Chernikov 
678ac35ff17SAlexander V. Chernikov 	return (error);
6793b3a8eb9SGleb Smirnoff }
6803b3a8eb9SGleb Smirnoff 
6811832a7b3SAlexander V. Chernikov int
682ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
6833b3a8eb9SGleb Smirnoff {
684b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
685b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
6869f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
6879f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
6889f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
689914bffb6SAlexander V. Chernikov 	char algostate[64], *pstate;
690b074b7bbSAlexander V. Chernikov 	int error;
691b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
692914bffb6SAlexander V. Chernikov 	uint8_t tflags;
6933b3a8eb9SGleb Smirnoff 
6943b3a8eb9SGleb Smirnoff 	/*
6959f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
696b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
6973b3a8eb9SGleb Smirnoff 	 */
698b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
699b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
700b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
701b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
702b074b7bbSAlexander V. Chernikov 		return (ESRCH);
703b074b7bbSAlexander V. Chernikov 	}
7049f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
705b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
706daabb523SAlexander V. Chernikov 	/* Save statup algo parameters */
707daabb523SAlexander V. Chernikov 	if (ta->print_config != NULL) {
708daabb523SAlexander V. Chernikov 		ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx),
709daabb523SAlexander V. Chernikov 		    algostate, sizeof(algostate));
710daabb523SAlexander V. Chernikov 		pstate = algostate;
711daabb523SAlexander V. Chernikov 	} else
712daabb523SAlexander V. Chernikov 		pstate = NULL;
713914bffb6SAlexander V. Chernikov 	tflags = tc->tflags;
714b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
7153b3a8eb9SGleb Smirnoff 
716b074b7bbSAlexander V. Chernikov 	/*
7179f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
718b074b7bbSAlexander V. Chernikov 	 */
7199f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
720914bffb6SAlexander V. Chernikov 	if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) {
721b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
722b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
723b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
724b074b7bbSAlexander V. Chernikov 		return (error);
725b074b7bbSAlexander V. Chernikov 	}
726b074b7bbSAlexander V. Chernikov 
727b074b7bbSAlexander V. Chernikov 	/*
728b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
729b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
730b074b7bbSAlexander V. Chernikov 	 */
731b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
732b074b7bbSAlexander V. Chernikov 
733b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
734b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
7359f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
736b074b7bbSAlexander V. Chernikov 
7379f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
7389f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
7399f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
7409f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
741b074b7bbSAlexander V. Chernikov 
7429f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
7439f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
7449f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
7459f7d47b0SAlexander V. Chernikov 	tc->count = 0;
746b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
747b074b7bbSAlexander V. Chernikov 
748b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
7493b3a8eb9SGleb Smirnoff 
750b074b7bbSAlexander V. Chernikov 	/*
751b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
752b074b7bbSAlexander V. Chernikov 	 */
7539f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
7543b3a8eb9SGleb Smirnoff 
7553b3a8eb9SGleb Smirnoff 	return (0);
7563b3a8eb9SGleb Smirnoff }
7573b3a8eb9SGleb Smirnoff 
758b074b7bbSAlexander V. Chernikov /*
75946d52008SAlexander V. Chernikov  * Swaps two tables.
76046d52008SAlexander V. Chernikov  * Data layout (v0)(current):
76146d52008SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_ntlv ]
76246d52008SAlexander V. Chernikov  *
76346d52008SAlexander V. Chernikov  * Returns 0 on success
76446d52008SAlexander V. Chernikov  */
76546d52008SAlexander V. Chernikov int
76646d52008SAlexander V. Chernikov ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
76746d52008SAlexander V. Chernikov     struct sockopt_data *sd)
76846d52008SAlexander V. Chernikov {
76946d52008SAlexander V. Chernikov 	int error;
77046d52008SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
77146d52008SAlexander V. Chernikov 	struct tid_info ti_a, ti_b;
77246d52008SAlexander V. Chernikov 
77346d52008SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv))
77446d52008SAlexander V. Chernikov 		return (EINVAL);
77546d52008SAlexander V. Chernikov 
77646d52008SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
77746d52008SAlexander V. Chernikov 	ntlv_to_ti(&oh->ntlv, &ti_a);
77846d52008SAlexander V. Chernikov 	ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b);
77946d52008SAlexander V. Chernikov 
780a73d728dSAlexander V. Chernikov 	error = swap_tables(ch, &ti_a, &ti_b);
78146d52008SAlexander V. Chernikov 
78246d52008SAlexander V. Chernikov 	return (error);
78346d52008SAlexander V. Chernikov }
78446d52008SAlexander V. Chernikov 
78546d52008SAlexander V. Chernikov static int
786a73d728dSAlexander V. Chernikov swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
78746d52008SAlexander V. Chernikov     struct tid_info *b)
78846d52008SAlexander V. Chernikov {
78946d52008SAlexander V. Chernikov 	struct namedobj_instance *ni;
79046d52008SAlexander V. Chernikov 	struct table_config *tc_a, *tc_b;
79146d52008SAlexander V. Chernikov 	struct table_algo *ta;
79246d52008SAlexander V. Chernikov 	struct table_info ti, *tablestate;
79346d52008SAlexander V. Chernikov 	void *astate;
79446d52008SAlexander V. Chernikov 	uint32_t count;
79546d52008SAlexander V. Chernikov 
79646d52008SAlexander V. Chernikov 	/*
79746d52008SAlexander V. Chernikov 	 * Stage 1: find both tables and ensure they are of
79846d52008SAlexander V. Chernikov 	 * the same type and algo.
79946d52008SAlexander V. Chernikov 	 */
80046d52008SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
80146d52008SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
80246d52008SAlexander V. Chernikov 	if ((tc_a = find_table(ni, a)) == NULL) {
80346d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
80446d52008SAlexander V. Chernikov 		return (ESRCH);
80546d52008SAlexander V. Chernikov 	}
80646d52008SAlexander V. Chernikov 	if ((tc_b = find_table(ni, b)) == NULL) {
80746d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
80846d52008SAlexander V. Chernikov 		return (ESRCH);
80946d52008SAlexander V. Chernikov 	}
81046d52008SAlexander V. Chernikov 
81146d52008SAlexander V. Chernikov 	/* It is very easy to swap between the same table */
81246d52008SAlexander V. Chernikov 	if (tc_a == tc_b) {
81346d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
81446d52008SAlexander V. Chernikov 		return (0);
81546d52008SAlexander V. Chernikov 	}
81646d52008SAlexander V. Chernikov 
81746d52008SAlexander V. Chernikov 	/* Check type and value are the same */
81846d52008SAlexander V. Chernikov 	if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags ||
81946d52008SAlexander V. Chernikov 	    tc_a->vtype != tc_b->vtype) {
82046d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
82146d52008SAlexander V. Chernikov 		return (EINVAL);
82246d52008SAlexander V. Chernikov 	}
82346d52008SAlexander V. Chernikov 
82446d52008SAlexander V. Chernikov 	/* Check limits before swap */
82546d52008SAlexander V. Chernikov 	if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) ||
82646d52008SAlexander V. Chernikov 	    (tc_b->limit != 0 && tc_a->count > tc_b->limit)) {
82746d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
82846d52008SAlexander V. Chernikov 		return (EFBIG);
82946d52008SAlexander V. Chernikov 	}
83046d52008SAlexander V. Chernikov 
83146d52008SAlexander V. Chernikov 	/* Everything is fine, prepare to swap */
83246d52008SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
83346d52008SAlexander V. Chernikov 	ti = tablestate[tc_a->no.kidx];
83446d52008SAlexander V. Chernikov 	ta = tc_a->ta;
83546d52008SAlexander V. Chernikov 	astate = tc_a->astate;
83646d52008SAlexander V. Chernikov 	count = tc_a->count;
83746d52008SAlexander V. Chernikov 
83846d52008SAlexander V. Chernikov 	IPFW_WLOCK(ch);
83946d52008SAlexander V. Chernikov 	/* a <- b */
84046d52008SAlexander V. Chernikov 	tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx];
84146d52008SAlexander V. Chernikov 	tc_a->ta = tc_b->ta;
84246d52008SAlexander V. Chernikov 	tc_a->astate = tc_b->astate;
84346d52008SAlexander V. Chernikov 	tc_a->count = tc_b->count;
84446d52008SAlexander V. Chernikov 	/* b <- a */
84546d52008SAlexander V. Chernikov 	tablestate[tc_b->no.kidx] = ti;
84646d52008SAlexander V. Chernikov 	tc_b->ta = ta;
84746d52008SAlexander V. Chernikov 	tc_b->astate = astate;
84846d52008SAlexander V. Chernikov 	tc_b->count = count;
84946d52008SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
85046d52008SAlexander V. Chernikov 
85146d52008SAlexander V. Chernikov 	/* Ensure tc.ti copies are in sync */
85246d52008SAlexander V. Chernikov 	tc_a->ti = tablestate[tc_a->no.kidx];
85346d52008SAlexander V. Chernikov 	tc_b->ti = tablestate[tc_b->no.kidx];
85446d52008SAlexander V. Chernikov 
85546d52008SAlexander V. Chernikov 	/* Notify both tables on @ti change */
85646d52008SAlexander V. Chernikov 	if (tc_a->ta->change_ti != NULL)
85746d52008SAlexander V. Chernikov 		tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]);
85846d52008SAlexander V. Chernikov 	if (tc_b->ta->change_ti != NULL)
85946d52008SAlexander V. Chernikov 		tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]);
86046d52008SAlexander V. Chernikov 
86146d52008SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
86246d52008SAlexander V. Chernikov 
86346d52008SAlexander V. Chernikov 	return (0);
86446d52008SAlexander V. Chernikov }
86546d52008SAlexander V. Chernikov 
86646d52008SAlexander V. Chernikov /*
8679f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
868ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
869ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ]
870ac35ff17SAlexander V. Chernikov  *
871ac35ff17SAlexander V. Chernikov  * Returns 0 on success
872b074b7bbSAlexander V. Chernikov  */
873ac35ff17SAlexander V. Chernikov static int
874ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
875b074b7bbSAlexander V. Chernikov {
876b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
877b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
878b074b7bbSAlexander V. Chernikov 
879b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
880b074b7bbSAlexander V. Chernikov 
881b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
882b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
883b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
884b074b7bbSAlexander V. Chernikov 		return (ESRCH);
885b074b7bbSAlexander V. Chernikov 	}
886b074b7bbSAlexander V. Chernikov 
8879f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
8889f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
889b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
890b074b7bbSAlexander V. Chernikov 		return (EBUSY);
891b074b7bbSAlexander V. Chernikov 	}
892b074b7bbSAlexander V. Chernikov 
893b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
894b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
895b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
896b074b7bbSAlexander V. Chernikov 
897b074b7bbSAlexander V. Chernikov 	/* Free obj index */
898ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0)
899b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
900b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
901b074b7bbSAlexander V. Chernikov 
902b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
903b074b7bbSAlexander V. Chernikov 
904b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
905b074b7bbSAlexander V. Chernikov 
906b074b7bbSAlexander V. Chernikov 	return (0);
907b074b7bbSAlexander V. Chernikov }
908b074b7bbSAlexander V. Chernikov 
909b074b7bbSAlexander V. Chernikov static void
910b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
911b074b7bbSAlexander V. Chernikov     void *arg)
912b074b7bbSAlexander V. Chernikov {
913b074b7bbSAlexander V. Chernikov 
914b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
915ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->kidx) != 0)
916b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
917b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
918b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
919b074b7bbSAlexander V. Chernikov }
920b074b7bbSAlexander V. Chernikov 
9213b3a8eb9SGleb Smirnoff void
9223b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
9233b3a8eb9SGleb Smirnoff {
9243b3a8eb9SGleb Smirnoff 
925b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
926b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
927b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
928b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
929b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
930b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
9313b3a8eb9SGleb Smirnoff 
9323b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
9339f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
9349f7d47b0SAlexander V. Chernikov 
9359f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
936b074b7bbSAlexander V. Chernikov 
937b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
938b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
9393b3a8eb9SGleb Smirnoff }
9403b3a8eb9SGleb Smirnoff 
9413b3a8eb9SGleb Smirnoff int
9423b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
9433b3a8eb9SGleb Smirnoff {
944b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
945b074b7bbSAlexander V. Chernikov 
9463b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
9479f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
9489f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
949b074b7bbSAlexander V. Chernikov 
950b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
951b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
952b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
953b074b7bbSAlexander V. Chernikov 
9549f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
9559f7d47b0SAlexander V. Chernikov 
9563b3a8eb9SGleb Smirnoff 	return (0);
9573b3a8eb9SGleb Smirnoff }
9583b3a8eb9SGleb Smirnoff 
9593b3a8eb9SGleb Smirnoff int
9603b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
9613b3a8eb9SGleb Smirnoff {
9623b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
963b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
9649f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
96568394ec8SAlexander V. Chernikov 	struct table_info *ti;
96668394ec8SAlexander V. Chernikov 	struct table_config *tc;
96768394ec8SAlexander V. Chernikov 	int i, new_blocks;
9683b3a8eb9SGleb Smirnoff 
9693b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
9703b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
9713b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
9723b3a8eb9SGleb Smirnoff 
9733b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
9749f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
9759f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
9769f7d47b0SAlexander V. Chernikov 
977b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
9783b3a8eb9SGleb Smirnoff 
9799f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
9803b3a8eb9SGleb Smirnoff 
9813b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
982b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
983b074b7bbSAlexander V. Chernikov 
9849f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
9859f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
9869f7d47b0SAlexander V. Chernikov 
9879f7d47b0SAlexander V. Chernikov 		/*
9889f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
9899f7d47b0SAlexander V. Chernikov 		 */
9909f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
991b074b7bbSAlexander V. Chernikov 		return (EINVAL);
992b074b7bbSAlexander V. Chernikov 	}
9933b3a8eb9SGleb Smirnoff 
9949f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
9959f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
9969f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
9973b3a8eb9SGleb Smirnoff 
9989f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
9999f7d47b0SAlexander V. Chernikov 
10009f7d47b0SAlexander V. Chernikov 	/* Change pointers */
10019f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
10029f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
10039f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
10043b3a8eb9SGleb Smirnoff 
10053b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
10063b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
10073b3a8eb9SGleb Smirnoff 
10083b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
100968394ec8SAlexander V. Chernikov 
101068394ec8SAlexander V. Chernikov 	/* Notify all consumers that their @ti pointer has changed */
101168394ec8SAlexander V. Chernikov 	ti = (struct table_info *)ch->tablestate;
101268394ec8SAlexander V. Chernikov 	for (i = 0; i < tbl; i++, ti++) {
101368394ec8SAlexander V. Chernikov 		if (ti->lookup == NULL)
101468394ec8SAlexander V. Chernikov 			continue;
101568394ec8SAlexander V. Chernikov 		tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i);
101668394ec8SAlexander V. Chernikov 		if (tc == NULL || tc->ta->change_ti == NULL)
101768394ec8SAlexander V. Chernikov 			continue;
101868394ec8SAlexander V. Chernikov 
101968394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, ti);
102068394ec8SAlexander V. Chernikov 	}
102168394ec8SAlexander V. Chernikov 
10229f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
10233b3a8eb9SGleb Smirnoff 
10243b3a8eb9SGleb Smirnoff 	/* Free old pointers */
10259f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
1026b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
10273b3a8eb9SGleb Smirnoff 
10283b3a8eb9SGleb Smirnoff 	return (0);
10293b3a8eb9SGleb Smirnoff }
10303b3a8eb9SGleb Smirnoff 
1031a73d728dSAlexander V. Chernikov /*
1032a73d728dSAlexander V. Chernikov  * Switch between "set 0" and "rule set" table binding,
1033a73d728dSAlexander V. Chernikov  * Check all ruleset bindings and permits changing
1034a73d728dSAlexander V. Chernikov  * IFF each binding has both rule AND table in default set (set 0).
1035a73d728dSAlexander V. Chernikov  *
1036a73d728dSAlexander V. Chernikov  * Returns 0 on success.
1037a73d728dSAlexander V. Chernikov  */
1038a73d728dSAlexander V. Chernikov int
1039a73d728dSAlexander V. Chernikov ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
1040a73d728dSAlexander V. Chernikov {
1041a73d728dSAlexander V. Chernikov 	struct namedobj_instance *ni;
1042a73d728dSAlexander V. Chernikov 	struct named_object *no;
1043a73d728dSAlexander V. Chernikov 	struct ip_fw *rule;
1044a73d728dSAlexander V. Chernikov 	ipfw_insn *cmd;
1045a73d728dSAlexander V. Chernikov 	int cmdlen, i, l;
1046a73d728dSAlexander V. Chernikov 	uint16_t kidx;
1047a73d728dSAlexander V. Chernikov 	uint8_t type;
1048a73d728dSAlexander V. Chernikov 
1049a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1050a73d728dSAlexander V. Chernikov 
1051a73d728dSAlexander V. Chernikov 	if (V_fw_tables_sets == sets) {
1052a73d728dSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1053a73d728dSAlexander V. Chernikov 		return (0);
1054a73d728dSAlexander V. Chernikov 	}
1055a73d728dSAlexander V. Chernikov 
1056a73d728dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1057a73d728dSAlexander V. Chernikov 
1058a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules; i++) {
1059a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
1060a73d728dSAlexander V. Chernikov 
1061a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
1062a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
1063a73d728dSAlexander V. Chernikov 		cmdlen = 0;
1064a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1065a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
1066a73d728dSAlexander V. Chernikov 
1067a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
1068a73d728dSAlexander V. Chernikov 				continue;
1069a73d728dSAlexander V. Chernikov 
1070a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
1071a73d728dSAlexander V. Chernikov 
1072a73d728dSAlexander V. Chernikov 			if (no->set != 0 || rule->set != 0) {
1073a73d728dSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
1074a73d728dSAlexander V. Chernikov 				return (EBUSY);
1075a73d728dSAlexander V. Chernikov 			}
1076a73d728dSAlexander V. Chernikov 
1077a73d728dSAlexander V. Chernikov 		}
1078a73d728dSAlexander V. Chernikov 	}
1079a73d728dSAlexander V. Chernikov 	V_fw_tables_sets = sets;
1080a73d728dSAlexander V. Chernikov 
1081a73d728dSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1082a73d728dSAlexander V. Chernikov 
1083a73d728dSAlexander V. Chernikov 	return (0);
1084a73d728dSAlexander V. Chernikov }
1085a73d728dSAlexander V. Chernikov 
10863b3a8eb9SGleb Smirnoff int
10873b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
10883b3a8eb9SGleb Smirnoff     uint32_t *val)
10893b3a8eb9SGleb Smirnoff {
10909f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
10913b3a8eb9SGleb Smirnoff 
10929f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
10939f7d47b0SAlexander V. Chernikov 
10949f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
10953b3a8eb9SGleb Smirnoff }
10969f7d47b0SAlexander V. Chernikov 
10979f7d47b0SAlexander V. Chernikov int
10989f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
10999f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
11009f7d47b0SAlexander V. Chernikov {
11019f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
11029f7d47b0SAlexander V. Chernikov 
11039f7d47b0SAlexander V. Chernikov 	ti = &(((struct table_info *)ch->tablestate)[tbl]);
11049f7d47b0SAlexander V. Chernikov 
11059f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
11069f7d47b0SAlexander V. Chernikov }
11079f7d47b0SAlexander V. Chernikov 
11089f7d47b0SAlexander V. Chernikov /*
11099f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
11109f7d47b0SAlexander V. Chernikov  *
11119f7d47b0SAlexander V. Chernikov  */
11129f7d47b0SAlexander V. Chernikov 
1113f1220db8SAlexander V. Chernikov /*
1114d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
1115d3a4f924SAlexander V. Chernikov  */
1116d3a4f924SAlexander V. Chernikov 
1117d3a4f924SAlexander V. Chernikov /*
1118d3a4f924SAlexander V. Chernikov  * Get buffer size needed to list info for all tables.
1119ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1120d3a4f924SAlexander V. Chernikov  * Request: [ empty ], size = sizeof(ipfw_obj_lheader)
1121d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ]
1122d3a4f924SAlexander V. Chernikov  *
1123d3a4f924SAlexander V. Chernikov  * Returns 0 on success
1124f1220db8SAlexander V. Chernikov  */
1125f1220db8SAlexander V. Chernikov int
11262d99a349SAlexander V. Chernikov ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
1127f1220db8SAlexander V. Chernikov {
1128f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
1129f1220db8SAlexander V. Chernikov 
11302d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
11312d99a349SAlexander V. Chernikov 	if (olh == NULL)
1132d3a4f924SAlexander V. Chernikov 		return (EINVAL);
1133d3a4f924SAlexander V. Chernikov 
1134f1220db8SAlexander V. Chernikov 	olh->size = sizeof(*olh); /* Make export_table store needed size */
1135f1220db8SAlexander V. Chernikov 
1136f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
11372d99a349SAlexander V. Chernikov 	export_tables(ch, olh, sd);
1138f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1139f1220db8SAlexander V. Chernikov 
11402d99a349SAlexander V. Chernikov 	return (0);
1141f1220db8SAlexander V. Chernikov }
1142f1220db8SAlexander V. Chernikov 
1143d3a4f924SAlexander V. Chernikov /*
1144d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
1145ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1146d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
1147d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
1148d3a4f924SAlexander V. Chernikov  *
1149d3a4f924SAlexander V. Chernikov  * Returns 0 on success
1150d3a4f924SAlexander V. Chernikov  */
1151f1220db8SAlexander V. Chernikov int
11522d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
1153f1220db8SAlexander V. Chernikov {
1154f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
1155f1220db8SAlexander V. Chernikov 	int error;
1156f1220db8SAlexander V. Chernikov 
11572d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
11582d99a349SAlexander V. Chernikov 	if (olh == NULL)
1159d3a4f924SAlexander V. Chernikov 		return (EINVAL);
116068394ec8SAlexander V. Chernikov 	if (sd->valsize < olh->size)
116168394ec8SAlexander V. Chernikov 		return (EINVAL);
1162d3a4f924SAlexander V. Chernikov 
1163f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
11642d99a349SAlexander V. Chernikov 	error = export_tables(ch, olh, sd);
1165f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1166f1220db8SAlexander V. Chernikov 
1167f1220db8SAlexander V. Chernikov 	return (error);
1168f1220db8SAlexander V. Chernikov }
1169f1220db8SAlexander V. Chernikov 
1170f1220db8SAlexander V. Chernikov /*
11712d99a349SAlexander V. Chernikov  * Store table info to buffer provided by @sd.
1172ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1173d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
1174d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
1175d3a4f924SAlexander V. Chernikov  *
1176d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
1177d3a4f924SAlexander V. Chernikov  */
1178d3a4f924SAlexander V. Chernikov int
11792d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
1180d3a4f924SAlexander V. Chernikov {
1181d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1182d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
1183d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
1184d3a4f924SAlexander V. Chernikov 	size_t sz;
1185d3a4f924SAlexander V. Chernikov 
1186d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
11872d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
11882d99a349SAlexander V. Chernikov 	if (oh == NULL)
1189d3a4f924SAlexander V. Chernikov 		return (EINVAL);
1190d3a4f924SAlexander V. Chernikov 
1191d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1192d3a4f924SAlexander V. Chernikov 
1193d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
1194d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
1195d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
1196d3a4f924SAlexander V. Chernikov 		return (ESRCH);
1197d3a4f924SAlexander V. Chernikov 	}
1198d3a4f924SAlexander V. Chernikov 
1199ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1));
1200d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1201d3a4f924SAlexander V. Chernikov 
12022d99a349SAlexander V. Chernikov 	return (0);
1203d3a4f924SAlexander V. Chernikov }
1204d3a4f924SAlexander V. Chernikov 
1205f1220db8SAlexander V. Chernikov struct dump_args {
1206f1220db8SAlexander V. Chernikov 	struct table_info *ti;
1207f1220db8SAlexander V. Chernikov 	struct table_config *tc;
12082d99a349SAlexander V. Chernikov 	struct sockopt_data *sd;
1209f1220db8SAlexander V. Chernikov 	uint32_t cnt;
1210f1220db8SAlexander V. Chernikov 	uint16_t uidx;
121181d3153dSAlexander V. Chernikov 	int error;
12122d99a349SAlexander V. Chernikov 	ipfw_table_entry *ent;
12132d99a349SAlexander V. Chernikov 	uint32_t size;
121481d3153dSAlexander V. Chernikov 	ipfw_obj_tentry tent;
1215f1220db8SAlexander V. Chernikov };
1216f1220db8SAlexander V. Chernikov 
1217f1220db8SAlexander V. Chernikov int
12182d99a349SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
12192d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
1220f1220db8SAlexander V. Chernikov {
1221d3a4f924SAlexander V. Chernikov 	int error;
1222d3a4f924SAlexander V. Chernikov 
1223d3a4f924SAlexander V. Chernikov 	switch (op3->version) {
1224d3a4f924SAlexander V. Chernikov 	case 0:
12252d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sd);
1226d3a4f924SAlexander V. Chernikov 		break;
1227d3a4f924SAlexander V. Chernikov 	case 1:
12282d99a349SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sd);
1229d3a4f924SAlexander V. Chernikov 		break;
1230d3a4f924SAlexander V. Chernikov 	default:
1231d3a4f924SAlexander V. Chernikov 		error = ENOTSUP;
1232d3a4f924SAlexander V. Chernikov 	}
1233d3a4f924SAlexander V. Chernikov 
1234d3a4f924SAlexander V. Chernikov 	return (error);
1235d3a4f924SAlexander V. Chernikov }
1236d3a4f924SAlexander V. Chernikov 
1237d3a4f924SAlexander V. Chernikov /*
1238d3a4f924SAlexander V. Chernikov  * Dumps all table data
1239ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
12402d99a349SAlexander V. Chernikov  * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
124181d3153dSAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ]
1242d3a4f924SAlexander V. Chernikov  *
1243d3a4f924SAlexander V. Chernikov  * Returns 0 on success
1244d3a4f924SAlexander V. Chernikov  */
1245d3a4f924SAlexander V. Chernikov static int
12462d99a349SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
1247d3a4f924SAlexander V. Chernikov {
1248f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1249f1220db8SAlexander V. Chernikov 	ipfw_xtable_info *i;
1250f1220db8SAlexander V. Chernikov 	struct tid_info ti;
1251f1220db8SAlexander V. Chernikov 	struct table_config *tc;
1252f1220db8SAlexander V. Chernikov 	struct table_algo *ta;
1253f1220db8SAlexander V. Chernikov 	struct dump_args da;
1254d3a4f924SAlexander V. Chernikov 	uint32_t sz;
1255f1220db8SAlexander V. Chernikov 
12562d99a349SAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
12572d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
12582d99a349SAlexander V. Chernikov 	if (oh == NULL)
1259d3a4f924SAlexander V. Chernikov 		return (EINVAL);
1260d3a4f924SAlexander V. Chernikov 
12612d99a349SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
1262d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1263f1220db8SAlexander V. Chernikov 
1264f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
1265f1220db8SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
1266f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
1267f1220db8SAlexander V. Chernikov 		return (ESRCH);
1268f1220db8SAlexander V. Chernikov 	}
1269ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, i);
12702d99a349SAlexander V. Chernikov 	sz = tc->count;
12712d99a349SAlexander V. Chernikov 
127281d3153dSAlexander V. Chernikov 	if (sd->valsize < sz + tc->count * sizeof(ipfw_obj_tentry)) {
12732d99a349SAlexander V. Chernikov 
12742d99a349SAlexander V. Chernikov 		/*
12752d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
12762d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
12772d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
12782d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
12792d99a349SAlexander V. Chernikov 		 */
1280f1220db8SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
12812d99a349SAlexander V. Chernikov 		return (ENOMEM);
1282f1220db8SAlexander V. Chernikov 	}
1283f1220db8SAlexander V. Chernikov 
1284f1220db8SAlexander V. Chernikov 	/*
1285f1220db8SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
1286f1220db8SAlexander V. Chernikov 	 */
1287d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
1288f1220db8SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
1289f1220db8SAlexander V. Chernikov 	da.tc = tc;
12902d99a349SAlexander V. Chernikov 	da.sd = sd;
1291f1220db8SAlexander V. Chernikov 
1292f1220db8SAlexander V. Chernikov 	ta = tc->ta;
1293f1220db8SAlexander V. Chernikov 
129481d3153dSAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_tentry, &da);
1295f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1296f1220db8SAlexander V. Chernikov 
129781d3153dSAlexander V. Chernikov 	return (da.error);
1298f1220db8SAlexander V. Chernikov }
1299f1220db8SAlexander V. Chernikov 
1300d3a4f924SAlexander V. Chernikov /*
1301d3a4f924SAlexander V. Chernikov  * Dumps all table data
13022d99a349SAlexander V. Chernikov  * Data layout (version 0)(legacy):
1303d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
1304d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
1305d3a4f924SAlexander V. Chernikov  *
1306d3a4f924SAlexander V. Chernikov  * Returns 0 on success
1307d3a4f924SAlexander V. Chernikov  */
1308d3a4f924SAlexander V. Chernikov static int
13092d99a349SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
1310d3a4f924SAlexander V. Chernikov {
1311d3a4f924SAlexander V. Chernikov 	ipfw_xtable *xtbl;
1312d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
1313d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
1314d3a4f924SAlexander V. Chernikov 	struct table_algo *ta;
1315d3a4f924SAlexander V. Chernikov 	struct dump_args da;
1316d3a4f924SAlexander V. Chernikov 	size_t sz;
1317d3a4f924SAlexander V. Chernikov 
13182d99a349SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
13192d99a349SAlexander V. Chernikov 	if (xtbl == NULL)
1320d3a4f924SAlexander V. Chernikov 		return (EINVAL);
1321d3a4f924SAlexander V. Chernikov 
1322d3a4f924SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
1323d3a4f924SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
1324d3a4f924SAlexander V. Chernikov 
1325d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
1326d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
1327d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
1328d3a4f924SAlexander V. Chernikov 		return (0);
1329d3a4f924SAlexander V. Chernikov 	}
1330d3a4f924SAlexander V. Chernikov 	sz = tc->count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
13312d99a349SAlexander V. Chernikov 
13322d99a349SAlexander V. Chernikov 	xtbl->cnt = tc->count;
13332d99a349SAlexander V. Chernikov 	xtbl->size = sz;
13342d99a349SAlexander V. Chernikov 	xtbl->type = tc->no.type;
13352d99a349SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
13362d99a349SAlexander V. Chernikov 
13372d99a349SAlexander V. Chernikov 	if (sd->valsize < sz) {
13382d99a349SAlexander V. Chernikov 
13392d99a349SAlexander V. Chernikov 		/*
13402d99a349SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
13412d99a349SAlexander V. Chernikov 		 * WE've already filled in @i structure with
13422d99a349SAlexander V. Chernikov 		 * relevant table info including size, so we
13432d99a349SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
13442d99a349SAlexander V. Chernikov 		 */
1345d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
13462d99a349SAlexander V. Chernikov 		return (ENOMEM);
1347d3a4f924SAlexander V. Chernikov 	}
1348d3a4f924SAlexander V. Chernikov 
1349d3a4f924SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
1350d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
1351d3a4f924SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
1352d3a4f924SAlexander V. Chernikov 	da.tc = tc;
13532d99a349SAlexander V. Chernikov 	da.sd = sd;
13542d99a349SAlexander V. Chernikov 
1355d3a4f924SAlexander V. Chernikov 	ta = tc->ta;
1356d3a4f924SAlexander V. Chernikov 
1357d3a4f924SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
1358d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1359d3a4f924SAlexander V. Chernikov 
13602d99a349SAlexander V. Chernikov 	return (0);
1361d3a4f924SAlexander V. Chernikov }
1362d3a4f924SAlexander V. Chernikov 
1363d3a4f924SAlexander V. Chernikov /*
13649490a627SAlexander V. Chernikov  * Creates new table.
1365ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
13669490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
13679490a627SAlexander V. Chernikov  *
13689490a627SAlexander V. Chernikov  * Returns 0 on success
13699490a627SAlexander V. Chernikov  */
13709490a627SAlexander V. Chernikov int
1371ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1372ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
13739490a627SAlexander V. Chernikov {
13749490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
13759490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
13769490a627SAlexander V. Chernikov 	char *tname, *aname;
13779490a627SAlexander V. Chernikov 	struct tid_info ti;
13789490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
13799490a627SAlexander V. Chernikov 	struct table_config *tc;
13809490a627SAlexander V. Chernikov 
1381ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
13829490a627SAlexander V. Chernikov 		return (EINVAL);
13839490a627SAlexander V. Chernikov 
1384ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)sd->kbuf;
13859490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
13869490a627SAlexander V. Chernikov 
13879490a627SAlexander V. Chernikov 	/*
13889490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
13892d99a349SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
13909490a627SAlexander V. Chernikov 	 */
1391ac35ff17SAlexander V. Chernikov 	tname = oh->ntlv.name;
13929490a627SAlexander V. Chernikov 	aname = i->algoname;
1393ac35ff17SAlexander V. Chernikov 	if (ipfw_check_table_name(tname) != 0 ||
13949490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
13959490a627SAlexander V. Chernikov 		return (EINVAL);
13969490a627SAlexander V. Chernikov 
13979490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
13989490a627SAlexander V. Chernikov 		/* Use default algorithm */
13999490a627SAlexander V. Chernikov 		aname = NULL;
14009490a627SAlexander V. Chernikov 	}
14019490a627SAlexander V. Chernikov 
14029490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1403ac35ff17SAlexander V. Chernikov 	ti.type = i->type;
14049490a627SAlexander V. Chernikov 
14059490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
14069490a627SAlexander V. Chernikov 
14079490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
14089490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
14099490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
14109490a627SAlexander V. Chernikov 		return (EEXIST);
14119490a627SAlexander V. Chernikov 	}
14129490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
14139490a627SAlexander V. Chernikov 
14144c0c07a5SAlexander V. Chernikov 	return (create_table_internal(ch, &ti, aname, i));
1415db785d31SAlexander V. Chernikov }
1416db785d31SAlexander V. Chernikov 
1417db785d31SAlexander V. Chernikov /*
1418db785d31SAlexander V. Chernikov  * Creates new table based on @ti and @aname.
1419db785d31SAlexander V. Chernikov  *
1420db785d31SAlexander V. Chernikov  * Relies on table name checking inside find_name_tlv()
1421db785d31SAlexander V. Chernikov  * Assume @aname to be checked and valid.
1422db785d31SAlexander V. Chernikov  *
1423db785d31SAlexander V. Chernikov  * Returns 0 on success.
1424db785d31SAlexander V. Chernikov  */
1425db785d31SAlexander V. Chernikov static int
1426db785d31SAlexander V. Chernikov create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
14274c0c07a5SAlexander V. Chernikov     char *aname, ipfw_xtable_info *i)
1428db785d31SAlexander V. Chernikov {
1429db785d31SAlexander V. Chernikov 	struct namedobj_instance *ni;
1430db785d31SAlexander V. Chernikov 	struct table_config *tc;
1431db785d31SAlexander V. Chernikov 	struct table_algo *ta;
1432db785d31SAlexander V. Chernikov 	uint16_t kidx;
1433db785d31SAlexander V. Chernikov 
1434db785d31SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1435db785d31SAlexander V. Chernikov 
1436db785d31SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname);
14379490a627SAlexander V. Chernikov 	if (ta == NULL)
14389490a627SAlexander V. Chernikov 		return (ENOTSUP);
14399490a627SAlexander V. Chernikov 
14404c0c07a5SAlexander V. Chernikov 	tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype);
14414c0c07a5SAlexander V. Chernikov 	if (tc == NULL)
14429490a627SAlexander V. Chernikov 		return (ENOMEM);
14439490a627SAlexander V. Chernikov 
14444c0c07a5SAlexander V. Chernikov 	tc->limit = i->limit;
14454c0c07a5SAlexander V. Chernikov 
14469490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1447db785d31SAlexander V. Chernikov 
1448db785d31SAlexander V. Chernikov 	/* Check if table has been already created */
1449db785d31SAlexander V. Chernikov 	if (find_table(ni, ti) != NULL) {
1450db785d31SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1451db785d31SAlexander V. Chernikov 		free_table_config(ni, tc);
1452db785d31SAlexander V. Chernikov 		return (EEXIST);
1453db785d31SAlexander V. Chernikov 	}
1454db785d31SAlexander V. Chernikov 
1455ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
14569490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1457db785d31SAlexander V. Chernikov 		printf("Unable to allocate table index."
1458db785d31SAlexander V. Chernikov 		    " Consider increasing net.inet.ip.fw.tables_max");
14599490a627SAlexander V. Chernikov 		free_table_config(ni, tc);
14609490a627SAlexander V. Chernikov 		return (EBUSY);
14619490a627SAlexander V. Chernikov 	}
14629490a627SAlexander V. Chernikov 
14639490a627SAlexander V. Chernikov 	tc->no.kidx = kidx;
14649490a627SAlexander V. Chernikov 
14659490a627SAlexander V. Chernikov 	IPFW_WLOCK(ch);
14669490a627SAlexander V. Chernikov 	link_table(ch, tc);
14679490a627SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
14689490a627SAlexander V. Chernikov 
14699490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
14709490a627SAlexander V. Chernikov 
14719490a627SAlexander V. Chernikov 	return (0);
14729490a627SAlexander V. Chernikov }
14739490a627SAlexander V. Chernikov 
147446d52008SAlexander V. Chernikov static void
147546d52008SAlexander V. Chernikov ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti)
1476d3a4f924SAlexander V. Chernikov {
1477d3a4f924SAlexander V. Chernikov 
1478d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
147946d52008SAlexander V. Chernikov 	ti->set = ntlv->set;
148046d52008SAlexander V. Chernikov 	ti->uidx = ntlv->idx;
148146d52008SAlexander V. Chernikov 	ti->tlvs = ntlv;
148246d52008SAlexander V. Chernikov 	ti->tlen = ntlv->head.length;
148346d52008SAlexander V. Chernikov }
148446d52008SAlexander V. Chernikov 
148546d52008SAlexander V. Chernikov static void
148646d52008SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
148746d52008SAlexander V. Chernikov {
148846d52008SAlexander V. Chernikov 
148946d52008SAlexander V. Chernikov 	ntlv_to_ti(&oh->ntlv, ti);
1490d3a4f924SAlexander V. Chernikov }
1491d3a4f924SAlexander V. Chernikov 
1492563b5ab1SAlexander V. Chernikov int
1493563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
1494563b5ab1SAlexander V. Chernikov     struct sockopt_data *sd)
1495563b5ab1SAlexander V. Chernikov {
1496563b5ab1SAlexander V. Chernikov 	struct namedobj_instance *ni;
1497563b5ab1SAlexander V. Chernikov 	struct named_object *no;
1498563b5ab1SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1499563b5ab1SAlexander V. Chernikov 
1500563b5ab1SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1501563b5ab1SAlexander V. Chernikov 
1502ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_kidx(ni, kidx);
1503563b5ab1SAlexander V. Chernikov 	KASSERT(no != NULL, ("invalid table kidx passed"));
1504563b5ab1SAlexander V. Chernikov 
1505563b5ab1SAlexander V. Chernikov 	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
1506563b5ab1SAlexander V. Chernikov 	if (ntlv == NULL)
1507563b5ab1SAlexander V. Chernikov 		return (ENOMEM);
1508563b5ab1SAlexander V. Chernikov 
1509563b5ab1SAlexander V. Chernikov 	ntlv->head.type = IPFW_TLV_TBL_NAME;
1510563b5ab1SAlexander V. Chernikov 	ntlv->head.length = sizeof(*ntlv);
1511563b5ab1SAlexander V. Chernikov 	ntlv->idx = no->kidx;
1512563b5ab1SAlexander V. Chernikov 	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
1513563b5ab1SAlexander V. Chernikov 
1514563b5ab1SAlexander V. Chernikov 	return (0);
1515563b5ab1SAlexander V. Chernikov }
1516563b5ab1SAlexander V. Chernikov 
15179f7d47b0SAlexander V. Chernikov static void
1518ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
1519ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i)
15209f7d47b0SAlexander V. Chernikov {
1521ac35ff17SAlexander V. Chernikov 	struct table_info *ti;
15225f379342SAlexander V. Chernikov 	struct table_algo *ta;
15239f7d47b0SAlexander V. Chernikov 
15249f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
1525914bffb6SAlexander V. Chernikov 	i->tflags = tc->tflags;
1526ac35ff17SAlexander V. Chernikov 	i->vtype = tc->vtype;
15279f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
15289f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
15299f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
15309f7d47b0SAlexander V. Chernikov 	i->count = tc->count;
15314c0c07a5SAlexander V. Chernikov 	i->limit = tc->limit;
153281d3153dSAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_obj_tentry);
1533f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
15349f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
1535ac35ff17SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tc->no.kidx);
15365f379342SAlexander V. Chernikov 	ta = tc->ta;
15375f379342SAlexander V. Chernikov 	if (ta->print_config != NULL) {
15385f379342SAlexander V. Chernikov 		/* Use algo function to print table config to string */
15395f379342SAlexander V. Chernikov 		ta->print_config(tc->astate, ti, i->algoname,
1540ac35ff17SAlexander V. Chernikov 		    sizeof(i->algoname));
1541ac35ff17SAlexander V. Chernikov 	} else
15425f379342SAlexander V. Chernikov 		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
15435f379342SAlexander V. Chernikov 	/* Dump algo-specific data, if possible */
15445f379342SAlexander V. Chernikov 	if (ta->dump_tinfo != NULL) {
15455f379342SAlexander V. Chernikov 		ta->dump_tinfo(tc->astate, ti, &i->ta_info);
15465f379342SAlexander V. Chernikov 		i->ta_info.flags |= IPFW_TATFLAGS_DATA;
15475f379342SAlexander V. Chernikov 	}
15489f7d47b0SAlexander V. Chernikov }
15499f7d47b0SAlexander V. Chernikov 
1550ac35ff17SAlexander V. Chernikov struct dump_table_args {
1551ac35ff17SAlexander V. Chernikov 	struct ip_fw_chain *ch;
1552ac35ff17SAlexander V. Chernikov 	struct sockopt_data *sd;
1553ac35ff17SAlexander V. Chernikov };
1554ac35ff17SAlexander V. Chernikov 
15559f7d47b0SAlexander V. Chernikov static void
15569f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
15579f7d47b0SAlexander V. Chernikov     void *arg)
15583b3a8eb9SGleb Smirnoff {
15599f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
1560ac35ff17SAlexander V. Chernikov 	struct dump_table_args *dta;
15613b3a8eb9SGleb Smirnoff 
1562ac35ff17SAlexander V. Chernikov 	dta = (struct dump_table_args *)arg;
1563ac35ff17SAlexander V. Chernikov 
1564ac35ff17SAlexander V. Chernikov 	i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
156568394ec8SAlexander V. Chernikov 	KASSERT(i != 0, ("previously checked buffer is not enough"));
15669f7d47b0SAlexander V. Chernikov 
1567ac35ff17SAlexander V. Chernikov 	export_table_info(dta->ch, (struct table_config *)no, i);
15689f7d47b0SAlexander V. Chernikov }
15699f7d47b0SAlexander V. Chernikov 
1570f1220db8SAlexander V. Chernikov /*
1571f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
15722d99a349SAlexander V. Chernikov  * storage provided by @sd.
1573f1220db8SAlexander V. Chernikov  * Returns 0 on success.
1574f1220db8SAlexander V. Chernikov  */
1575f1220db8SAlexander V. Chernikov static int
15762d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
15772d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
15789f7d47b0SAlexander V. Chernikov {
15799f7d47b0SAlexander V. Chernikov 	uint32_t size;
15809f7d47b0SAlexander V. Chernikov 	uint32_t count;
1581ac35ff17SAlexander V. Chernikov 	struct dump_table_args dta;
15829f7d47b0SAlexander V. Chernikov 
15839f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
15849f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
15852d99a349SAlexander V. Chernikov 
15862d99a349SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
1587f1220db8SAlexander V. Chernikov 	olh->count = count;
1588f1220db8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
15892d99a349SAlexander V. Chernikov 
15902d99a349SAlexander V. Chernikov 	if (size > olh->size) {
15912d99a349SAlexander V. Chernikov 		olh->size = size;
15929f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
1593f1220db8SAlexander V. Chernikov 	}
159468394ec8SAlexander V. Chernikov 
15959f7d47b0SAlexander V. Chernikov 	olh->size = size;
15962d99a349SAlexander V. Chernikov 
1597ac35ff17SAlexander V. Chernikov 	dta.ch = ch;
1598ac35ff17SAlexander V. Chernikov 	dta.sd = sd;
1599ac35ff17SAlexander V. Chernikov 
1600ac35ff17SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta);
16019f7d47b0SAlexander V. Chernikov 
16023b3a8eb9SGleb Smirnoff 	return (0);
16033b3a8eb9SGleb Smirnoff }
16043b3a8eb9SGleb Smirnoff 
160581d3153dSAlexander V. Chernikov /*
160681d3153dSAlexander V. Chernikov  * Legacy IP_FW_TABLE_GETSIZE handler
160781d3153dSAlexander V. Chernikov  */
16083b3a8eb9SGleb Smirnoff int
1609b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
16103b3a8eb9SGleb Smirnoff {
1611b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
16123b3a8eb9SGleb Smirnoff 
1613b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1614b074b7bbSAlexander V. Chernikov 		return (ESRCH);
16159f7d47b0SAlexander V. Chernikov 	*cnt = tc->count;
16163b3a8eb9SGleb Smirnoff 	return (0);
16173b3a8eb9SGleb Smirnoff }
16183b3a8eb9SGleb Smirnoff 
16199f7d47b0SAlexander V. Chernikov 
162081d3153dSAlexander V. Chernikov /*
162181d3153dSAlexander V. Chernikov  * Legacy IP_FW_TABLE_XGETSIZE handler
162281d3153dSAlexander V. Chernikov  */
16239f7d47b0SAlexander V. Chernikov int
16249f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
16253b3a8eb9SGleb Smirnoff {
16269f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
16279f7d47b0SAlexander V. Chernikov 
1628d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
1629d3a4f924SAlexander V. Chernikov 		*cnt = 0;
16309f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
1631d3a4f924SAlexander V. Chernikov 	}
16329f7d47b0SAlexander V. Chernikov 	*cnt = tc->count * sizeof(ipfw_table_xentry);
16339f7d47b0SAlexander V. Chernikov 	if (tc->count > 0)
16349f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
16359f7d47b0SAlexander V. Chernikov 	return (0);
16369f7d47b0SAlexander V. Chernikov }
16379f7d47b0SAlexander V. Chernikov 
16389f7d47b0SAlexander V. Chernikov static int
16399f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
16409f7d47b0SAlexander V. Chernikov {
16419f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
16429f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
16439f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
16443b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
164581d3153dSAlexander V. Chernikov 	int error;
16463b3a8eb9SGleb Smirnoff 
16479f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
16489f7d47b0SAlexander V. Chernikov 
16499f7d47b0SAlexander V. Chernikov 	tc = da->tc;
16509f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
16519f7d47b0SAlexander V. Chernikov 
16529f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
1653f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
16543b3a8eb9SGleb Smirnoff 		return (1);
1655f1220db8SAlexander V. Chernikov 	ent = da->ent++;
1656f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
1657f1220db8SAlexander V. Chernikov 	da->cnt++;
16589f7d47b0SAlexander V. Chernikov 
165981d3153dSAlexander V. Chernikov 	error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent);
166081d3153dSAlexander V. Chernikov 	if (error != 0)
166181d3153dSAlexander V. Chernikov 		return (error);
166281d3153dSAlexander V. Chernikov 
166381d3153dSAlexander V. Chernikov 	ent->addr = da->tent.k.addr.s_addr;
166481d3153dSAlexander V. Chernikov 	ent->masklen = da->tent.masklen;
166581d3153dSAlexander V. Chernikov 	ent->value = da->tent.value;
166681d3153dSAlexander V. Chernikov 
166781d3153dSAlexander V. Chernikov 	return (0);
16683b3a8eb9SGleb Smirnoff }
16693b3a8eb9SGleb Smirnoff 
1670f1220db8SAlexander V. Chernikov /*
1671f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
1672f1220db8SAlexander V. Chernikov  */
16733b3a8eb9SGleb Smirnoff int
1674f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
1675f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
16763b3a8eb9SGleb Smirnoff {
1677b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
16789f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
16799f7d47b0SAlexander V. Chernikov 	struct dump_args da;
16803b3a8eb9SGleb Smirnoff 
16813b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
16823b3a8eb9SGleb Smirnoff 
1683b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
1684b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
16859f7d47b0SAlexander V. Chernikov 
16869f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
16879f7d47b0SAlexander V. Chernikov 
168881d3153dSAlexander V. Chernikov 	/* This dump format supports IPv4 only */
168981d3153dSAlexander V. Chernikov 	if (tc->no.type != IPFW_TABLE_CIDR)
169081d3153dSAlexander V. Chernikov 		return (0);
16919f7d47b0SAlexander V. Chernikov 
1692d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
16939f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
16949f7d47b0SAlexander V. Chernikov 	da.tc = tc;
1695f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
1696f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
16979f7d47b0SAlexander V. Chernikov 
16989f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
16999f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
1700f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
17019f7d47b0SAlexander V. Chernikov 
17023b3a8eb9SGleb Smirnoff 	return (0);
17033b3a8eb9SGleb Smirnoff }
17043b3a8eb9SGleb Smirnoff 
17059490a627SAlexander V. Chernikov /*
170681d3153dSAlexander V. Chernikov  * Dumps table entry in eXtended format (v1)(current).
170781d3153dSAlexander V. Chernikov  */
170881d3153dSAlexander V. Chernikov static int
170981d3153dSAlexander V. Chernikov dump_table_tentry(void *e, void *arg)
171081d3153dSAlexander V. Chernikov {
171181d3153dSAlexander V. Chernikov 	struct dump_args *da;
171281d3153dSAlexander V. Chernikov 	struct table_config *tc;
171381d3153dSAlexander V. Chernikov 	struct table_algo *ta;
171481d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
171581d3153dSAlexander V. Chernikov 
171681d3153dSAlexander V. Chernikov 	da = (struct dump_args *)arg;
171781d3153dSAlexander V. Chernikov 
171881d3153dSAlexander V. Chernikov 	tc = da->tc;
171981d3153dSAlexander V. Chernikov 	ta = tc->ta;
172081d3153dSAlexander V. Chernikov 
172181d3153dSAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent));
172281d3153dSAlexander V. Chernikov 	/* Out of memory, returning */
172381d3153dSAlexander V. Chernikov 	if (tent == NULL) {
172481d3153dSAlexander V. Chernikov 		da->error = ENOMEM;
172581d3153dSAlexander V. Chernikov 		return (1);
172681d3153dSAlexander V. Chernikov 	}
172781d3153dSAlexander V. Chernikov 	tent->head.length = sizeof(ipfw_obj_tentry);
172881d3153dSAlexander V. Chernikov 	tent->idx = da->uidx;
172981d3153dSAlexander V. Chernikov 
173081d3153dSAlexander V. Chernikov 	return (ta->dump_tentry(tc->astate, da->ti, e, tent));
173181d3153dSAlexander V. Chernikov }
173281d3153dSAlexander V. Chernikov 
173381d3153dSAlexander V. Chernikov /*
173481d3153dSAlexander V. Chernikov  * Dumps table entry in eXtended format (v0).
17359490a627SAlexander V. Chernikov  */
17363b3a8eb9SGleb Smirnoff static int
17379f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
17383b3a8eb9SGleb Smirnoff {
17399f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
17409f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
17419f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
17423b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
174381d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
174481d3153dSAlexander V. Chernikov 	int error;
17453b3a8eb9SGleb Smirnoff 
17469f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
17479f7d47b0SAlexander V. Chernikov 
17489f7d47b0SAlexander V. Chernikov 	tc = da->tc;
17499f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
17509f7d47b0SAlexander V. Chernikov 
17512d99a349SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
17523b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
17532d99a349SAlexander V. Chernikov 	if (xent == NULL)
17543b3a8eb9SGleb Smirnoff 		return (1);
17553b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
1756f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
17579f7d47b0SAlexander V. Chernikov 
175881d3153dSAlexander V. Chernikov 	memset(&da->tent, 0, sizeof(da->tent));
175981d3153dSAlexander V. Chernikov 	tent = &da->tent;
176081d3153dSAlexander V. Chernikov 	error = ta->dump_tentry(tc->astate, da->ti, e, tent);
176181d3153dSAlexander V. Chernikov 	if (error != 0)
176281d3153dSAlexander V. Chernikov 		return (error);
176381d3153dSAlexander V. Chernikov 
176481d3153dSAlexander V. Chernikov 	/* Convert current format to previous one */
176581d3153dSAlexander V. Chernikov 	xent->masklen = tent->masklen;
176681d3153dSAlexander V. Chernikov 	xent->value = tent->value;
176781d3153dSAlexander V. Chernikov 	/* Apply some hacks */
176881d3153dSAlexander V. Chernikov 	if (tc->no.type == IPFW_TABLE_CIDR && tent->subtype == AF_INET) {
176981d3153dSAlexander V. Chernikov 		xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr;
177081d3153dSAlexander V. Chernikov 		xent->flags = IPFW_TCF_INET;
177181d3153dSAlexander V. Chernikov 	} else
177281d3153dSAlexander V. Chernikov 		memcpy(&xent->k, &tent->k, sizeof(xent->k));
177381d3153dSAlexander V. Chernikov 
177481d3153dSAlexander V. Chernikov 	return (0);
17759f7d47b0SAlexander V. Chernikov }
17769f7d47b0SAlexander V. Chernikov 
17779f7d47b0SAlexander V. Chernikov /*
17789f7d47b0SAlexander V. Chernikov  * Table algorithms
17799f7d47b0SAlexander V. Chernikov  */
17803b3a8eb9SGleb Smirnoff 
17819f7d47b0SAlexander V. Chernikov /*
17829490a627SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name
17839f7d47b0SAlexander V. Chernikov  */
17849f7d47b0SAlexander V. Chernikov static struct table_algo *
17859490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
17869f7d47b0SAlexander V. Chernikov {
17879490a627SAlexander V. Chernikov 	int i, l;
17889490a627SAlexander V. Chernikov 	struct table_algo *ta;
17899f7d47b0SAlexander V. Chernikov 
179057a1cf95SAlexander V. Chernikov 	if (ti->type > IPFW_TABLE_MAXTYPE)
179157a1cf95SAlexander V. Chernikov 		return (NULL);
179257a1cf95SAlexander V. Chernikov 
17939f7d47b0SAlexander V. Chernikov 	/* Search by index */
17949f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
17959f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
17969f7d47b0SAlexander V. Chernikov 			return (NULL);
17979f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
17989f7d47b0SAlexander V. Chernikov 	}
17999f7d47b0SAlexander V. Chernikov 
18009490a627SAlexander V. Chernikov 	/* Search by name if supplied */
18019490a627SAlexander V. Chernikov 	if (name != NULL) {
18029490a627SAlexander V. Chernikov 		/* TODO: better search */
18039490a627SAlexander V. Chernikov 		for (i = 1; i <= tcfg->algo_count; i++) {
18049490a627SAlexander V. Chernikov 			ta = tcfg->algo[i];
18059490a627SAlexander V. Chernikov 
18069490a627SAlexander V. Chernikov 			/*
18079490a627SAlexander V. Chernikov 			 * One can supply additional algorithm
18089490a627SAlexander V. Chernikov 			 * parameters so we compare only the first word
18099490a627SAlexander V. Chernikov 			 * of supplied name:
18109490a627SAlexander V. Chernikov 			 * 'hash_cidr hsize=32'
18119490a627SAlexander V. Chernikov 			 * '^^^^^^^^^'
18129490a627SAlexander V. Chernikov 			 *
18139490a627SAlexander V. Chernikov 			 */
18149490a627SAlexander V. Chernikov 			l = strlen(ta->name);
18159490a627SAlexander V. Chernikov 			if (strncmp(name, ta->name, l) == 0) {
18169490a627SAlexander V. Chernikov 				if (name[l] == '\0' || name[l] == ' ')
18179490a627SAlexander V. Chernikov 					return (ta);
18189490a627SAlexander V. Chernikov 			}
18199490a627SAlexander V. Chernikov 		}
18209490a627SAlexander V. Chernikov 
18219490a627SAlexander V. Chernikov 		return (NULL);
18229490a627SAlexander V. Chernikov 	}
18239490a627SAlexander V. Chernikov 
182457a1cf95SAlexander V. Chernikov 	/* Return default algorithm for given type if set */
182557a1cf95SAlexander V. Chernikov 	return (tcfg->def_algo[ti->type]);
18263b3a8eb9SGleb Smirnoff }
18273b3a8eb9SGleb Smirnoff 
1828b6ee846eSAlexander V. Chernikov /*
1829b6ee846eSAlexander V. Chernikov  * Register new table algo @ta.
1830b6ee846eSAlexander V. Chernikov  * Stores algo id iside @idx.<F2>
1831b6ee846eSAlexander V. Chernikov  *
1832b6ee846eSAlexander V. Chernikov  * Returns 0 on success.
1833b6ee846eSAlexander V. Chernikov  */
18340b565ac0SAlexander V. Chernikov int
18350b565ac0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size,
18360b565ac0SAlexander V. Chernikov     int *idx)
18373b3a8eb9SGleb Smirnoff {
18389f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
18390b565ac0SAlexander V. Chernikov 	struct table_algo *ta_new;
1840b6ee846eSAlexander V. Chernikov 	size_t sz;
18410b565ac0SAlexander V. Chernikov 
18420b565ac0SAlexander V. Chernikov 	if (size > sizeof(struct table_algo))
18430b565ac0SAlexander V. Chernikov 		return (EINVAL);
18440b565ac0SAlexander V. Chernikov 
1845b6ee846eSAlexander V. Chernikov 	/* Check for the required on-stack size for add/del */
1846b6ee846eSAlexander V. Chernikov 	sz = roundup2(ta->ta_buf_size, sizeof(void *));
1847b6ee846eSAlexander V. Chernikov 	if (sz > TA_BUF_SZ)
1848b6ee846eSAlexander V. Chernikov 		return (EINVAL);
1849b6ee846eSAlexander V. Chernikov 
185057a1cf95SAlexander V. Chernikov 	KASSERT(ta->type >= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE"));
185157a1cf95SAlexander V. Chernikov 
18520b565ac0SAlexander V. Chernikov 	ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO);
18530b565ac0SAlexander V. Chernikov 	memcpy(ta_new, ta, size);
18543b3a8eb9SGleb Smirnoff 
18559f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
1856b074b7bbSAlexander V. Chernikov 
18579f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
18589f7d47b0SAlexander V. Chernikov 
18590b565ac0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta_new;
18600b565ac0SAlexander V. Chernikov 	ta_new->idx = tcfg->algo_count;
18610b565ac0SAlexander V. Chernikov 
186257a1cf95SAlexander V. Chernikov 	/* Set algorithm as default one for given type */
186357a1cf95SAlexander V. Chernikov 	if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 &&
186457a1cf95SAlexander V. Chernikov 	    tcfg->def_algo[ta_new->type] == NULL)
186557a1cf95SAlexander V. Chernikov 		tcfg->def_algo[ta_new->type] = ta_new;
186657a1cf95SAlexander V. Chernikov 
18670b565ac0SAlexander V. Chernikov 	*idx = ta_new->idx;
18680b565ac0SAlexander V. Chernikov 
18690b565ac0SAlexander V. Chernikov 	return (0);
18700b565ac0SAlexander V. Chernikov }
18710b565ac0SAlexander V. Chernikov 
1872b6ee846eSAlexander V. Chernikov /*
1873b6ee846eSAlexander V. Chernikov  * Unregisters table algo using @idx as id.
1874b6ee846eSAlexander V. Chernikov  */
18750b565ac0SAlexander V. Chernikov void
18760b565ac0SAlexander V. Chernikov ipfw_del_table_algo(struct ip_fw_chain *ch, int idx)
18770b565ac0SAlexander V. Chernikov {
18780b565ac0SAlexander V. Chernikov 	struct tables_config *tcfg;
18790b565ac0SAlexander V. Chernikov 	struct table_algo *ta;
18800b565ac0SAlexander V. Chernikov 
18810b565ac0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
18820b565ac0SAlexander V. Chernikov 
1883b6ee846eSAlexander V. Chernikov 	KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d",
1884b6ee846eSAlexander V. Chernikov 	    idx, tcfg->algo_count));
18850b565ac0SAlexander V. Chernikov 
18860b565ac0SAlexander V. Chernikov 	ta = tcfg->algo[idx];
18870b565ac0SAlexander V. Chernikov 	KASSERT(ta != NULL, ("algo idx %d is NULL", idx));
188857a1cf95SAlexander V. Chernikov 
188957a1cf95SAlexander V. Chernikov 	if (tcfg->def_algo[ta->type] == ta)
189057a1cf95SAlexander V. Chernikov 		tcfg->def_algo[ta->type] = NULL;
189157a1cf95SAlexander V. Chernikov 
18920b565ac0SAlexander V. Chernikov 	free(ta, M_IPFW);
18933b3a8eb9SGleb Smirnoff }
18943b3a8eb9SGleb Smirnoff 
18959d099b4fSAlexander V. Chernikov /*
18969d099b4fSAlexander V. Chernikov  * Lists all table algorithms currently available.
18979d099b4fSAlexander V. Chernikov  * Data layout (v0)(current):
18989d099b4fSAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
18999d099b4fSAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ]
19009d099b4fSAlexander V. Chernikov  *
19019d099b4fSAlexander V. Chernikov  * Returns 0 on success
19029d099b4fSAlexander V. Chernikov  */
19039d099b4fSAlexander V. Chernikov int
19049d099b4fSAlexander V. Chernikov ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd)
19059d099b4fSAlexander V. Chernikov {
19069d099b4fSAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
19079d099b4fSAlexander V. Chernikov 	struct tables_config *tcfg;
19089d099b4fSAlexander V. Chernikov 	ipfw_ta_info *i;
19099d099b4fSAlexander V. Chernikov 	struct table_algo *ta;
19109d099b4fSAlexander V. Chernikov 	uint32_t count, n, size;
19119d099b4fSAlexander V. Chernikov 
19129d099b4fSAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
19139d099b4fSAlexander V. Chernikov 	if (olh == NULL)
19149d099b4fSAlexander V. Chernikov 		return (EINVAL);
19159d099b4fSAlexander V. Chernikov 	if (sd->valsize < olh->size)
19169d099b4fSAlexander V. Chernikov 		return (EINVAL);
19179d099b4fSAlexander V. Chernikov 
19189d099b4fSAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
19199d099b4fSAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
19209d099b4fSAlexander V. Chernikov 	count = tcfg->algo_count;
19219d099b4fSAlexander V. Chernikov 	size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader);
19229d099b4fSAlexander V. Chernikov 
19239d099b4fSAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
19249d099b4fSAlexander V. Chernikov 	olh->count = count;
19259d099b4fSAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_ta_info);
19269d099b4fSAlexander V. Chernikov 
19279d099b4fSAlexander V. Chernikov 	if (size > olh->size) {
19289d099b4fSAlexander V. Chernikov 		olh->size = size;
19299d099b4fSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
19309d099b4fSAlexander V. Chernikov 		return (ENOMEM);
19319d099b4fSAlexander V. Chernikov 	}
19329d099b4fSAlexander V. Chernikov 	olh->size = size;
19339d099b4fSAlexander V. Chernikov 
19349d099b4fSAlexander V. Chernikov 	for (n = 1; n <= count; n++) {
19359d099b4fSAlexander V. Chernikov 		i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i));
19369d099b4fSAlexander V. Chernikov 		KASSERT(i != 0, ("previously checked buffer is not enough"));
19379d099b4fSAlexander V. Chernikov 		ta = tcfg->algo[n];
19389d099b4fSAlexander V. Chernikov 		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
19399d099b4fSAlexander V. Chernikov 		i->type = ta->type;
19409d099b4fSAlexander V. Chernikov 		i->refcnt = ta->refcnt;
19419d099b4fSAlexander V. Chernikov 	}
19429d099b4fSAlexander V. Chernikov 
19439d099b4fSAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
19449d099b4fSAlexander V. Chernikov 
19459d099b4fSAlexander V. Chernikov 	return (0);
19469d099b4fSAlexander V. Chernikov }
19479d099b4fSAlexander V. Chernikov 
19489f7d47b0SAlexander V. Chernikov 
1949b074b7bbSAlexander V. Chernikov /*
1950b074b7bbSAlexander V. Chernikov  * Tables rewriting code
1951b074b7bbSAlexander V. Chernikov  *
1952b074b7bbSAlexander V. Chernikov  */
1953b074b7bbSAlexander V. Chernikov 
1954b074b7bbSAlexander V. Chernikov /*
1955b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
1956b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
1957b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
1958b074b7bbSAlexander V. Chernikov  */
1959b074b7bbSAlexander V. Chernikov static int
1960b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
1961b074b7bbSAlexander V. Chernikov {
1962b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
1963b074b7bbSAlexander V. Chernikov 	int skip;
1964b074b7bbSAlexander V. Chernikov 	uint16_t v;
1965b074b7bbSAlexander V. Chernikov 
1966b074b7bbSAlexander V. Chernikov 	skip = 1;
1967b074b7bbSAlexander V. Chernikov 
1968b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
1969b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
1970b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
1971b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
1972b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
1973b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
1974b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
1975b074b7bbSAlexander V. Chernikov 		skip = 0;
1976b074b7bbSAlexander V. Chernikov 
1977b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
1978b074b7bbSAlexander V. Chernikov 			/*
1979b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
1980b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
1981b074b7bbSAlexander V. Chernikov 			 */
1982b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
1983b074b7bbSAlexander V. Chernikov 			switch (v) {
1984b074b7bbSAlexander V. Chernikov 			case 0:
1985b074b7bbSAlexander V. Chernikov 			case 1:
1986b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
1987b074b7bbSAlexander V. Chernikov 				break;
1988b074b7bbSAlexander V. Chernikov 			case 2:
1989b074b7bbSAlexander V. Chernikov 			case 3:
1990b074b7bbSAlexander V. Chernikov 				/* src/dst port */
1991b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
1992b074b7bbSAlexander V. Chernikov 				break;
1993b074b7bbSAlexander V. Chernikov 			case 4:
1994b074b7bbSAlexander V. Chernikov 				/* uid/gid */
1995b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
1996b23d5de9SAlexander V. Chernikov 				break;
1997b074b7bbSAlexander V. Chernikov 			case 5:
1998b074b7bbSAlexander V. Chernikov 				/* jid */
1999b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2000b23d5de9SAlexander V. Chernikov 				break;
2001b074b7bbSAlexander V. Chernikov 			case 6:
2002b074b7bbSAlexander V. Chernikov 				/* dscp */
2003b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2004b074b7bbSAlexander V. Chernikov 				break;
2005b074b7bbSAlexander V. Chernikov 			}
2006b074b7bbSAlexander V. Chernikov 		}
2007b074b7bbSAlexander V. Chernikov 		break;
2008b074b7bbSAlexander V. Chernikov 	case O_XMIT:
2009b074b7bbSAlexander V. Chernikov 	case O_RECV:
2010b074b7bbSAlexander V. Chernikov 	case O_VIA:
2011b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
2012b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
2013b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
2014b074b7bbSAlexander V. Chernikov 			break;
2015b074b7bbSAlexander V. Chernikov 
2016b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
2017b074b7bbSAlexander V. Chernikov 		*puidx = cmdif->p.glob;
2018b074b7bbSAlexander V. Chernikov 		skip = 0;
2019b074b7bbSAlexander V. Chernikov 		break;
2020914bffb6SAlexander V. Chernikov 	case O_IP_FLOW_LOOKUP:
2021914bffb6SAlexander V. Chernikov 		*puidx = cmd->arg1;
2022914bffb6SAlexander V. Chernikov 		*ptype = IPFW_TABLE_FLOW;
2023914bffb6SAlexander V. Chernikov 		skip = 0;
2024914bffb6SAlexander V. Chernikov 		break;
2025b074b7bbSAlexander V. Chernikov 	}
2026b074b7bbSAlexander V. Chernikov 
2027b074b7bbSAlexander V. Chernikov 	return (skip);
2028b074b7bbSAlexander V. Chernikov }
2029b074b7bbSAlexander V. Chernikov 
2030b074b7bbSAlexander V. Chernikov /*
2031b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
2032b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
2033b074b7bbSAlexander V. Chernikov  */
2034b074b7bbSAlexander V. Chernikov static void
2035b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
2036b074b7bbSAlexander V. Chernikov {
2037b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
2038b074b7bbSAlexander V. Chernikov 
2039b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
2040b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
2041b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
2042b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
2043b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
2044b074b7bbSAlexander V. Chernikov 		break;
2045b074b7bbSAlexander V. Chernikov 	case O_XMIT:
2046b074b7bbSAlexander V. Chernikov 	case O_RECV:
2047b074b7bbSAlexander V. Chernikov 	case O_VIA:
2048b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
2049b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
2050b074b7bbSAlexander V. Chernikov 		cmdif->p.glob = idx;
2051b074b7bbSAlexander V. Chernikov 		break;
2052914bffb6SAlexander V. Chernikov 	case O_IP_FLOW_LOOKUP:
2053914bffb6SAlexander V. Chernikov 		cmd->arg1 = idx;
2054914bffb6SAlexander V. Chernikov 		break;
2055b074b7bbSAlexander V. Chernikov 	}
2056b074b7bbSAlexander V. Chernikov }
2057b074b7bbSAlexander V. Chernikov 
2058ac35ff17SAlexander V. Chernikov /*
2059ac35ff17SAlexander V. Chernikov  * Checks table name for validity.
2060ac35ff17SAlexander V. Chernikov  * Enforce basic length checks, the rest
2061ac35ff17SAlexander V. Chernikov  * should be done in userland.
2062ac35ff17SAlexander V. Chernikov  *
2063ac35ff17SAlexander V. Chernikov  * Returns 0 if name is considered valid.
2064ac35ff17SAlexander V. Chernikov  */
2065ac35ff17SAlexander V. Chernikov int
2066ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name)
2067ac35ff17SAlexander V. Chernikov {
2068ac35ff17SAlexander V. Chernikov 	int nsize;
2069ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv = NULL;
2070ac35ff17SAlexander V. Chernikov 
2071ac35ff17SAlexander V. Chernikov 	nsize = sizeof(ntlv->name);
2072ac35ff17SAlexander V. Chernikov 
2073ac35ff17SAlexander V. Chernikov 	if (strnlen(name, nsize) == nsize)
2074ac35ff17SAlexander V. Chernikov 		return (EINVAL);
2075ac35ff17SAlexander V. Chernikov 
2076ac35ff17SAlexander V. Chernikov 	if (name[0] == '\0')
2077ac35ff17SAlexander V. Chernikov 		return (EINVAL);
2078ac35ff17SAlexander V. Chernikov 
2079ac35ff17SAlexander V. Chernikov 	/*
2080ac35ff17SAlexander V. Chernikov 	 * TODO: do some more complicated checks
2081ac35ff17SAlexander V. Chernikov 	 */
2082ac35ff17SAlexander V. Chernikov 
2083ac35ff17SAlexander V. Chernikov 	return (0);
2084ac35ff17SAlexander V. Chernikov }
2085ac35ff17SAlexander V. Chernikov 
2086ac35ff17SAlexander V. Chernikov /*
2087ac35ff17SAlexander V. Chernikov  * Find tablename TLV by @uid.
2088ac35ff17SAlexander V. Chernikov  * Check @tlvs for valid data inside.
2089ac35ff17SAlexander V. Chernikov  *
2090ac35ff17SAlexander V. Chernikov  * Returns pointer to found TLV or NULL.
2091ac35ff17SAlexander V. Chernikov  */
2092ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv *
2093b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
2094b074b7bbSAlexander V. Chernikov {
20959f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2096b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
2097b074b7bbSAlexander V. Chernikov 	int l;
2098b074b7bbSAlexander V. Chernikov 
2099b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
2100b074b7bbSAlexander V. Chernikov 	pe = pa + len;
2101b074b7bbSAlexander V. Chernikov 	l = 0;
2102b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
21039f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
2104b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
2105ac35ff17SAlexander V. Chernikov 
2106ac35ff17SAlexander V. Chernikov 		if (l != sizeof(*ntlv))
2107ac35ff17SAlexander V. Chernikov 			return (NULL);
2108ac35ff17SAlexander V. Chernikov 
2109563b5ab1SAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_TBL_NAME)
2110b074b7bbSAlexander V. Chernikov 			continue;
2111ac35ff17SAlexander V. Chernikov 
2112b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
2113b074b7bbSAlexander V. Chernikov 			continue;
2114b074b7bbSAlexander V. Chernikov 
2115ac35ff17SAlexander V. Chernikov 		if (ipfw_check_table_name(ntlv->name) != 0)
2116ac35ff17SAlexander V. Chernikov 			return (NULL);
2117ac35ff17SAlexander V. Chernikov 
2118ac35ff17SAlexander V. Chernikov 		return (ntlv);
2119b074b7bbSAlexander V. Chernikov 	}
2120b074b7bbSAlexander V. Chernikov 
2121b074b7bbSAlexander V. Chernikov 	return (NULL);
2122b074b7bbSAlexander V. Chernikov }
2123b074b7bbSAlexander V. Chernikov 
2124ac35ff17SAlexander V. Chernikov /*
2125ac35ff17SAlexander V. Chernikov  * Finds table config based on either legacy index
2126ac35ff17SAlexander V. Chernikov  * or name in ntlv.
2127ac35ff17SAlexander V. Chernikov  * Note @ti structure contains unchecked data from userland.
2128ac35ff17SAlexander V. Chernikov  *
2129ac35ff17SAlexander V. Chernikov  * Returns pointer to table_config or NULL.
2130ac35ff17SAlexander V. Chernikov  */
2131b074b7bbSAlexander V. Chernikov static struct table_config *
2132b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
2133b074b7bbSAlexander V. Chernikov {
2134b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
2135b074b7bbSAlexander V. Chernikov 	struct named_object *no;
2136ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2137ac35ff17SAlexander V. Chernikov 	uint32_t set;
2138b074b7bbSAlexander V. Chernikov 
2139b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
2140ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
2141ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
2142b074b7bbSAlexander V. Chernikov 			return (NULL);
2143ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
2144ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
2145b074b7bbSAlexander V. Chernikov 	} else {
2146b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
2147b074b7bbSAlexander V. Chernikov 		name = bname;
2148ac35ff17SAlexander V. Chernikov 		set = 0;
2149b074b7bbSAlexander V. Chernikov 	}
2150b074b7bbSAlexander V. Chernikov 
2151ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, set, name);
2152b074b7bbSAlexander V. Chernikov 
2153b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
2154b074b7bbSAlexander V. Chernikov }
2155b074b7bbSAlexander V. Chernikov 
2156b074b7bbSAlexander V. Chernikov static struct table_config *
215768394ec8SAlexander V. Chernikov alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti,
2158914bffb6SAlexander V. Chernikov     struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype)
2159b074b7bbSAlexander V. Chernikov {
2160b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
2161b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2162b074b7bbSAlexander V. Chernikov 	int error;
2163ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2164ac35ff17SAlexander V. Chernikov 	uint32_t set;
2165b074b7bbSAlexander V. Chernikov 
2166b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
2167ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
2168ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
2169b074b7bbSAlexander V. Chernikov 			return (NULL);
2170ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
2171ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
2172b074b7bbSAlexander V. Chernikov 	} else {
2173b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
2174b074b7bbSAlexander V. Chernikov 		name = bname;
2175ac35ff17SAlexander V. Chernikov 		set = 0;
2176b074b7bbSAlexander V. Chernikov 	}
2177b074b7bbSAlexander V. Chernikov 
2178b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
2179b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
2180b074b7bbSAlexander V. Chernikov 	tc->no.type = ti->type;
2181ac35ff17SAlexander V. Chernikov 	tc->no.set = set;
2182914bffb6SAlexander V. Chernikov 	tc->tflags = tflags;
21839f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
2184b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
2185ac35ff17SAlexander V. Chernikov 	/* Set default value type to u32 for compability reasons */
2186db785d31SAlexander V. Chernikov 	if (vtype == 0)
2187ac35ff17SAlexander V. Chernikov 		tc->vtype = IPFW_VTYPE_U32;
2188db785d31SAlexander V. Chernikov 	else
2189db785d31SAlexander V. Chernikov 		tc->vtype = vtype;
2190b074b7bbSAlexander V. Chernikov 
2191b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
2192b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
2193b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
2194b074b7bbSAlexander V. Chernikov 	}
2195b074b7bbSAlexander V. Chernikov 
2196b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
2197914bffb6SAlexander V. Chernikov 	error = ta->init(ch, &tc->astate, &tc->ti, aname, tflags);
2198b074b7bbSAlexander V. Chernikov 	if (error != 0) {
2199b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
2200b074b7bbSAlexander V. Chernikov 		return (NULL);
2201b074b7bbSAlexander V. Chernikov 	}
2202b074b7bbSAlexander V. Chernikov 
2203b074b7bbSAlexander V. Chernikov 	return (tc);
2204b074b7bbSAlexander V. Chernikov }
2205b074b7bbSAlexander V. Chernikov 
2206b074b7bbSAlexander V. Chernikov static void
2207b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
2208b074b7bbSAlexander V. Chernikov {
2209b074b7bbSAlexander V. Chernikov 
2210b074b7bbSAlexander V. Chernikov 	if (tc->linked == 0)
221168394ec8SAlexander V. Chernikov 		tc->ta->destroy(tc->astate, &tc->ti);
2212b074b7bbSAlexander V. Chernikov 
2213b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
2214b074b7bbSAlexander V. Chernikov }
2215b074b7bbSAlexander V. Chernikov 
2216b074b7bbSAlexander V. Chernikov /*
2217b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
2218b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
2219b074b7bbSAlexander V. Chernikov  */
2220b074b7bbSAlexander V. Chernikov static void
22219f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
2222b074b7bbSAlexander V. Chernikov {
2223b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
22249f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
2225b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2226b074b7bbSAlexander V. Chernikov 
22279f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
22289f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
2229b074b7bbSAlexander V. Chernikov 
22309f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2231b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
2232b074b7bbSAlexander V. Chernikov 
2233b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
22349f7d47b0SAlexander V. Chernikov 
22359f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
22369f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
2237b074b7bbSAlexander V. Chernikov 
223868394ec8SAlexander V. Chernikov 	/* Notify algo on real @ti address */
223968394ec8SAlexander V. Chernikov 	if (tc->ta->change_ti != NULL)
224068394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, ti);
224168394ec8SAlexander V. Chernikov 
2242b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
22439d099b4fSAlexander V. Chernikov 	tc->ta->refcnt++;
2244b074b7bbSAlexander V. Chernikov }
2245b074b7bbSAlexander V. Chernikov 
2246b074b7bbSAlexander V. Chernikov /*
2247b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
2248b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
2249b074b7bbSAlexander V. Chernikov  */
2250b074b7bbSAlexander V. Chernikov static void
22519f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
2252b074b7bbSAlexander V. Chernikov {
2253b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
22549f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
2255b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2256b074b7bbSAlexander V. Chernikov 
22579f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
22589f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
2259b074b7bbSAlexander V. Chernikov 
22609f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2261b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
2262b074b7bbSAlexander V. Chernikov 
22639f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
2264b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
22659f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
22669f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
2267b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
22689d099b4fSAlexander V. Chernikov 	tc->ta->refcnt--;
226968394ec8SAlexander V. Chernikov 
227068394ec8SAlexander V. Chernikov 	/* Notify algo on real @ti address */
227168394ec8SAlexander V. Chernikov 	if (tc->ta->change_ti != NULL)
227268394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, NULL);
2273b074b7bbSAlexander V. Chernikov }
2274b074b7bbSAlexander V. Chernikov 
2275b074b7bbSAlexander V. Chernikov /*
2276b074b7bbSAlexander V. Chernikov  * Finds named object by @uidx number.
2277b074b7bbSAlexander V. Chernikov  * Refs found object, allocate new index for non-existing object.
22789490a627SAlexander V. Chernikov  * Fills in @oib with userland/kernel indexes.
22799490a627SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
2280b074b7bbSAlexander V. Chernikov  *
2281b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
2282b074b7bbSAlexander V. Chernikov  */
2283b074b7bbSAlexander V. Chernikov static int
22849490a627SAlexander V. Chernikov bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
22859490a627SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
2286b074b7bbSAlexander V. Chernikov {
2287b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
22889490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
22899490a627SAlexander V. Chernikov 	struct named_object *no;
22909490a627SAlexander V. Chernikov 	int error, l, cmdlen;
22919490a627SAlexander V. Chernikov 	ipfw_insn *cmd;
22929490a627SAlexander V. Chernikov 	struct obj_idx *pidx, *p;
22939490a627SAlexander V. Chernikov 
22949490a627SAlexander V. Chernikov 	pidx = *oib;
22959490a627SAlexander V. Chernikov 	l = rule->cmd_len;
22969490a627SAlexander V. Chernikov 	cmd = rule->cmd;
22979490a627SAlexander V. Chernikov 	cmdlen = 0;
22989490a627SAlexander V. Chernikov 	error = 0;
22999490a627SAlexander V. Chernikov 
23009490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
23019490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
23029490a627SAlexander V. Chernikov 
23039490a627SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
23049490a627SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
23059490a627SAlexander V. Chernikov 
23069490a627SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
23079490a627SAlexander V. Chernikov 			continue;
2308b074b7bbSAlexander V. Chernikov 
2309b074b7bbSAlexander V. Chernikov 		pidx->uidx = ti->uidx;
2310b074b7bbSAlexander V. Chernikov 		pidx->type = ti->type;
2311b074b7bbSAlexander V. Chernikov 
23129490a627SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
23139490a627SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
23149490a627SAlexander V. Chernikov 				/* Incompatible types */
23159490a627SAlexander V. Chernikov 				error = EINVAL;
23169490a627SAlexander V. Chernikov 				break;
23179490a627SAlexander V. Chernikov 			}
23189f7d47b0SAlexander V. Chernikov 
23199490a627SAlexander V. Chernikov 			/* Reference found table and save kidx */
23209490a627SAlexander V. Chernikov 			tc->no.refcnt++;
23219490a627SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
23229490a627SAlexander V. Chernikov 			pidx++;
23239490a627SAlexander V. Chernikov 			continue;
23249490a627SAlexander V. Chernikov 		}
23259490a627SAlexander V. Chernikov 
23269490a627SAlexander V. Chernikov 		/* Table not found. Allocate new index and save for later */
2327ac35ff17SAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) {
2328ac35ff17SAlexander V. Chernikov 			printf("Unable to allocate table %s index in set %u."
2329b074b7bbSAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max",
2330ac35ff17SAlexander V. Chernikov 			    "", ti->set);
23319490a627SAlexander V. Chernikov 			error = EBUSY;
23329490a627SAlexander V. Chernikov 			break;
2333b074b7bbSAlexander V. Chernikov 		}
2334b074b7bbSAlexander V. Chernikov 
2335b074b7bbSAlexander V. Chernikov 		ci->new_tables++;
23369490a627SAlexander V. Chernikov 		pidx->new = 1;
23379490a627SAlexander V. Chernikov 		pidx++;
2338b074b7bbSAlexander V. Chernikov 	}
2339b074b7bbSAlexander V. Chernikov 
23409490a627SAlexander V. Chernikov 	if (error != 0) {
23419490a627SAlexander V. Chernikov 		/* Unref everything we have already done */
23429490a627SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
23439490a627SAlexander V. Chernikov 			if (p->new != 0) {
2344ac35ff17SAlexander V. Chernikov 				ipfw_objhash_free_idx(ni, p->kidx);
23459490a627SAlexander V. Chernikov 				continue;
23469490a627SAlexander V. Chernikov 			}
2347b074b7bbSAlexander V. Chernikov 
23489490a627SAlexander V. Chernikov 			/* Find & unref by existing idx */
2349ac35ff17SAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
23509490a627SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
23519490a627SAlexander V. Chernikov 			    p->kidx));
2352b074b7bbSAlexander V. Chernikov 
23539490a627SAlexander V. Chernikov 			no->refcnt--;
23549490a627SAlexander V. Chernikov 		}
23559490a627SAlexander V. Chernikov 	}
23569490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
2357b074b7bbSAlexander V. Chernikov 
23589490a627SAlexander V. Chernikov 	*oib = pidx;
23599490a627SAlexander V. Chernikov 
23609490a627SAlexander V. Chernikov 	return (error);
2361b074b7bbSAlexander V. Chernikov }
2362b074b7bbSAlexander V. Chernikov 
2363a73d728dSAlexander V. Chernikov struct swap_table_args {
2364a73d728dSAlexander V. Chernikov 	int set;
2365a73d728dSAlexander V. Chernikov 	int new_set;
2366a73d728dSAlexander V. Chernikov 	int mv;
2367a73d728dSAlexander V. Chernikov };
2368a73d728dSAlexander V. Chernikov 
2369a73d728dSAlexander V. Chernikov /*
2370a73d728dSAlexander V. Chernikov  * Change set for each matching table.
2371a73d728dSAlexander V. Chernikov  *
2372a73d728dSAlexander V. Chernikov  * Ensure we dispatch each table once by setting/checking ochange
2373a73d728dSAlexander V. Chernikov  * fields.
2374a73d728dSAlexander V. Chernikov  */
2375a73d728dSAlexander V. Chernikov static void
2376a73d728dSAlexander V. Chernikov swap_table_set(struct namedobj_instance *ni, struct named_object *no,
2377a73d728dSAlexander V. Chernikov     void *arg)
2378a73d728dSAlexander V. Chernikov {
2379a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2380a73d728dSAlexander V. Chernikov 	struct swap_table_args *sta;
2381a73d728dSAlexander V. Chernikov 
2382a73d728dSAlexander V. Chernikov 	tc = (struct table_config *)no;
2383a73d728dSAlexander V. Chernikov 	sta = (struct swap_table_args *)arg;
2384a73d728dSAlexander V. Chernikov 
2385a73d728dSAlexander V. Chernikov 	if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0))
2386a73d728dSAlexander V. Chernikov 		return;
2387a73d728dSAlexander V. Chernikov 
2388a73d728dSAlexander V. Chernikov 	if (tc->ochanged != 0)
2389a73d728dSAlexander V. Chernikov 		return;
2390a73d728dSAlexander V. Chernikov 
2391a73d728dSAlexander V. Chernikov 	tc->ochanged = 1;
2392a73d728dSAlexander V. Chernikov 	ipfw_objhash_del(ni, no);
2393a73d728dSAlexander V. Chernikov 	if (no->set == sta->set)
2394a73d728dSAlexander V. Chernikov 		no->set = sta->new_set;
2395a73d728dSAlexander V. Chernikov 	else
2396a73d728dSAlexander V. Chernikov 		no->set = sta->set;
2397a73d728dSAlexander V. Chernikov 	ipfw_objhash_add(ni, no);
2398a73d728dSAlexander V. Chernikov }
2399a73d728dSAlexander V. Chernikov 
2400a73d728dSAlexander V. Chernikov /*
2401a73d728dSAlexander V. Chernikov  * Cleans up ochange field for all tables.
2402a73d728dSAlexander V. Chernikov  */
2403a73d728dSAlexander V. Chernikov static void
2404a73d728dSAlexander V. Chernikov clean_table_set_data(struct namedobj_instance *ni, struct named_object *no,
2405a73d728dSAlexander V. Chernikov     void *arg)
2406a73d728dSAlexander V. Chernikov {
2407a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2408a73d728dSAlexander V. Chernikov 	struct swap_table_args *sta;
2409a73d728dSAlexander V. Chernikov 
2410a73d728dSAlexander V. Chernikov 	tc = (struct table_config *)no;
2411a73d728dSAlexander V. Chernikov 	sta = (struct swap_table_args *)arg;
2412a73d728dSAlexander V. Chernikov 
2413a73d728dSAlexander V. Chernikov 	tc->ochanged = 0;
2414a73d728dSAlexander V. Chernikov }
2415a73d728dSAlexander V. Chernikov 
2416a73d728dSAlexander V. Chernikov /*
2417a73d728dSAlexander V. Chernikov  * Swaps tables within two sets.
2418a73d728dSAlexander V. Chernikov  */
2419a73d728dSAlexander V. Chernikov void
2420a73d728dSAlexander V. Chernikov ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set,
2421a73d728dSAlexander V. Chernikov     uint32_t new_set, int mv)
2422a73d728dSAlexander V. Chernikov {
2423a73d728dSAlexander V. Chernikov 	struct swap_table_args sta;
2424a73d728dSAlexander V. Chernikov 
2425a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
2426a73d728dSAlexander V. Chernikov 
2427a73d728dSAlexander V. Chernikov 	sta.set = set;
2428a73d728dSAlexander V. Chernikov 	sta.new_set = new_set;
2429a73d728dSAlexander V. Chernikov 	sta.mv = mv;
2430a73d728dSAlexander V. Chernikov 
2431a73d728dSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta);
2432a73d728dSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta);
2433a73d728dSAlexander V. Chernikov }
2434a73d728dSAlexander V. Chernikov 
2435a73d728dSAlexander V. Chernikov /*
2436a73d728dSAlexander V. Chernikov  * Move all tables which are reference by rules in @rr to set @new_set.
2437a73d728dSAlexander V. Chernikov  * Makes sure that all relevant tables are referenced ONLLY by given rules.
2438a73d728dSAlexander V. Chernikov  *
2439a73d728dSAlexander V. Chernikov  * Retuns 0 on success,
2440a73d728dSAlexander V. Chernikov  */
2441a73d728dSAlexander V. Chernikov int
2442a73d728dSAlexander V. Chernikov ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
2443a73d728dSAlexander V. Chernikov     uint32_t new_set)
2444a73d728dSAlexander V. Chernikov {
2445a73d728dSAlexander V. Chernikov 	struct ip_fw *rule;
2446a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2447a73d728dSAlexander V. Chernikov 	struct named_object *no;
2448a73d728dSAlexander V. Chernikov 	struct namedobj_instance *ni;
2449a73d728dSAlexander V. Chernikov 	int bad, i, l, cmdlen;
2450a73d728dSAlexander V. Chernikov 	uint16_t kidx;
2451a73d728dSAlexander V. Chernikov 	uint8_t type;
2452a73d728dSAlexander V. Chernikov 	ipfw_insn *cmd;
2453a73d728dSAlexander V. Chernikov 
2454a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
2455a73d728dSAlexander V. Chernikov 
2456a73d728dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2457a73d728dSAlexander V. Chernikov 
2458a73d728dSAlexander V. Chernikov 	/* Stage 1: count number of references by given rules */
2459a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
2460a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
2461a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
2462a73d728dSAlexander V. Chernikov 			continue;
2463a73d728dSAlexander V. Chernikov 
2464a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
2465a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
2466a73d728dSAlexander V. Chernikov 		cmdlen = 0;
2467a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2468a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
2469a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
2470a73d728dSAlexander V. Chernikov 				continue;
2471a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
2472a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
2473a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
2474a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
2475a73d728dSAlexander V. Chernikov 			tc->ocount++;
2476a73d728dSAlexander V. Chernikov 		}
2477a73d728dSAlexander V. Chernikov 
2478a73d728dSAlexander V. Chernikov 	}
2479a73d728dSAlexander V. Chernikov 
2480a73d728dSAlexander V. Chernikov 	/* Stage 2: verify "ownership" */
2481a73d728dSAlexander V. Chernikov 	bad = 0;
2482a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
2483a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
2484a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
2485a73d728dSAlexander V. Chernikov 			continue;
2486a73d728dSAlexander V. Chernikov 
2487a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
2488a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
2489a73d728dSAlexander V. Chernikov 		cmdlen = 0;
2490a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2491a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
2492a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
2493a73d728dSAlexander V. Chernikov 				continue;
2494a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
2495a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
2496a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
2497a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
2498a73d728dSAlexander V. Chernikov 			if (tc->no.refcnt != tc->ocount) {
2499a73d728dSAlexander V. Chernikov 
2500a73d728dSAlexander V. Chernikov 				/*
2501a73d728dSAlexander V. Chernikov 				 * Number of references differ:
2502a73d728dSAlexander V. Chernikov 				 * Other rule(s) are holding reference to given
2503a73d728dSAlexander V. Chernikov 				 * table, so it is not possible to change its set.
2504a73d728dSAlexander V. Chernikov 				 *
2505a73d728dSAlexander V. Chernikov 				 * Note that refcnt may account
2506a73d728dSAlexander V. Chernikov 				 * references to some going-to-be-added rules.
2507a73d728dSAlexander V. Chernikov 				 * Since we don't know their numbers (and event
2508a73d728dSAlexander V. Chernikov 				 * if they will be added) it is perfectly OK
2509a73d728dSAlexander V. Chernikov 				 * to return error here.
2510a73d728dSAlexander V. Chernikov 				 */
2511a73d728dSAlexander V. Chernikov 				bad = 1;
2512a73d728dSAlexander V. Chernikov 				break;
2513a73d728dSAlexander V. Chernikov 			}
2514a73d728dSAlexander V. Chernikov 		}
2515a73d728dSAlexander V. Chernikov 
2516a73d728dSAlexander V. Chernikov 		if (bad != 0)
2517a73d728dSAlexander V. Chernikov 			break;
2518a73d728dSAlexander V. Chernikov 	}
2519a73d728dSAlexander V. Chernikov 
2520a73d728dSAlexander V. Chernikov 	/* Stage 3: change set or cleanup */
2521a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
2522a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
2523a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
2524a73d728dSAlexander V. Chernikov 			continue;
2525a73d728dSAlexander V. Chernikov 
2526a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
2527a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
2528a73d728dSAlexander V. Chernikov 		cmdlen = 0;
2529a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2530a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
2531a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
2532a73d728dSAlexander V. Chernikov 				continue;
2533a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
2534a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
2535a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
2536a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
2537a73d728dSAlexander V. Chernikov 
2538a73d728dSAlexander V. Chernikov 			tc->ocount = 0;
2539a73d728dSAlexander V. Chernikov 			if (bad != 0)
2540a73d728dSAlexander V. Chernikov 				continue;
2541a73d728dSAlexander V. Chernikov 
2542a73d728dSAlexander V. Chernikov 			/* Actually change set. */
2543a73d728dSAlexander V. Chernikov 			ipfw_objhash_del(ni, no);
2544a73d728dSAlexander V. Chernikov 			no->set = new_set;
2545a73d728dSAlexander V. Chernikov 			ipfw_objhash_add(ni, no);
2546a73d728dSAlexander V. Chernikov 		}
2547a73d728dSAlexander V. Chernikov 	}
2548a73d728dSAlexander V. Chernikov 
2549a73d728dSAlexander V. Chernikov 	return (bad);
2550a73d728dSAlexander V. Chernikov }
2551a73d728dSAlexander V. Chernikov 
2552b074b7bbSAlexander V. Chernikov /*
2553b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
2554b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
2555b074b7bbSAlexander V. Chernikov  * Works for \d+ talbes only (e.g. for tables, converted
2556b074b7bbSAlexander V. Chernikov  * from old numbered system calls).
2557b074b7bbSAlexander V. Chernikov  *
2558b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
2559b074b7bbSAlexander V. Chernikov  * Raises error on any other tables.
2560b074b7bbSAlexander V. Chernikov  */
2561b074b7bbSAlexander V. Chernikov int
25627e767c79SAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule)
2563b074b7bbSAlexander V. Chernikov {
25641832a7b3SAlexander V. Chernikov 	int cmdlen, error, l;
2565b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
25661832a7b3SAlexander V. Chernikov 	uint16_t kidx, uidx;
2567b074b7bbSAlexander V. Chernikov 	uint8_t type;
2568b074b7bbSAlexander V. Chernikov 	struct named_object *no;
2569b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
2570b074b7bbSAlexander V. Chernikov 
2571b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
25721832a7b3SAlexander V. Chernikov 	error = 0;
2573b074b7bbSAlexander V. Chernikov 
2574b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
2575b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
2576b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
2577b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2578b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
2579b074b7bbSAlexander V. Chernikov 
2580b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
2581b074b7bbSAlexander V. Chernikov 			continue;
2582b074b7bbSAlexander V. Chernikov 
2583ac35ff17SAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL)
2584b074b7bbSAlexander V. Chernikov 			return (1);
2585b074b7bbSAlexander V. Chernikov 
25861832a7b3SAlexander V. Chernikov 		uidx = no->uidx;
25871832a7b3SAlexander V. Chernikov 		if (no->compat == 0) {
2588b074b7bbSAlexander V. Chernikov 
25891832a7b3SAlexander V. Chernikov 			/*
25901832a7b3SAlexander V. Chernikov 			 * We are called via legacy opcode.
25911832a7b3SAlexander V. Chernikov 			 * Save error and show table as fake number
25921832a7b3SAlexander V. Chernikov 			 * not to make ipfw(8) hang.
25931832a7b3SAlexander V. Chernikov 			 */
25941832a7b3SAlexander V. Chernikov 			uidx = 65535;
25951832a7b3SAlexander V. Chernikov 			error = 2;
2596b074b7bbSAlexander V. Chernikov 		}
2597b074b7bbSAlexander V. Chernikov 
25981832a7b3SAlexander V. Chernikov 		update_table_opcode(cmd, uidx);
25991832a7b3SAlexander V. Chernikov 	}
26001832a7b3SAlexander V. Chernikov 
26011832a7b3SAlexander V. Chernikov 	return (error);
2602b074b7bbSAlexander V. Chernikov }
2603b074b7bbSAlexander V. Chernikov 
2604563b5ab1SAlexander V. Chernikov /*
2605563b5ab1SAlexander V. Chernikov  * Sets every table kidx in @bmask which is used in rule @rule.
2606563b5ab1SAlexander V. Chernikov  *
2607563b5ab1SAlexander V. Chernikov  * Returns number of newly-referenced tables.
2608563b5ab1SAlexander V. Chernikov  */
2609563b5ab1SAlexander V. Chernikov int
2610563b5ab1SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
2611563b5ab1SAlexander V. Chernikov     uint32_t *bmask)
2612563b5ab1SAlexander V. Chernikov {
2613563b5ab1SAlexander V. Chernikov 	int cmdlen, l, count;
2614563b5ab1SAlexander V. Chernikov 	ipfw_insn *cmd;
2615563b5ab1SAlexander V. Chernikov 	uint16_t kidx;
2616563b5ab1SAlexander V. Chernikov 	uint8_t type;
2617563b5ab1SAlexander V. Chernikov 
2618563b5ab1SAlexander V. Chernikov 	l = rule->cmd_len;
2619563b5ab1SAlexander V. Chernikov 	cmd = rule->cmd;
2620563b5ab1SAlexander V. Chernikov 	cmdlen = 0;
2621563b5ab1SAlexander V. Chernikov 	count = 0;
2622563b5ab1SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2623563b5ab1SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
2624563b5ab1SAlexander V. Chernikov 
2625563b5ab1SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
2626563b5ab1SAlexander V. Chernikov 			continue;
2627563b5ab1SAlexander V. Chernikov 
2628563b5ab1SAlexander V. Chernikov 		if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
2629563b5ab1SAlexander V. Chernikov 			count++;
2630563b5ab1SAlexander V. Chernikov 
2631563b5ab1SAlexander V. Chernikov 		bmask[kidx / 32] |= 1 << (kidx % 32);
2632563b5ab1SAlexander V. Chernikov 	}
2633563b5ab1SAlexander V. Chernikov 
2634563b5ab1SAlexander V. Chernikov 	return (count);
2635563b5ab1SAlexander V. Chernikov }
2636563b5ab1SAlexander V. Chernikov 
2637563b5ab1SAlexander V. Chernikov 
2638b074b7bbSAlexander V. Chernikov 
2639b074b7bbSAlexander V. Chernikov /*
2640b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
2641b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
2642b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
2643b074b7bbSAlexander V. Chernikov  *
2644b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
2645b074b7bbSAlexander V. Chernikov  */
2646b074b7bbSAlexander V. Chernikov int
2647b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
2648b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
2649b074b7bbSAlexander V. Chernikov {
2650b074b7bbSAlexander V. Chernikov 	int cmdlen, error, ftype, l;
2651b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
2652b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
2653b074b7bbSAlexander V. Chernikov 	uint8_t type;
2654b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
26559f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
2656b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
2657b074b7bbSAlexander V. Chernikov 	struct named_object *no, *no_n, *no_tmp;
26589490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
2659b074b7bbSAlexander V. Chernikov 	struct namedobjects_head nh;
2660b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
2661b074b7bbSAlexander V. Chernikov 
2662b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
2663b074b7bbSAlexander V. Chernikov 
26649490a627SAlexander V. Chernikov 	/* Prepare queue to store configs */
26659490a627SAlexander V. Chernikov 	TAILQ_INIT(&nh);
26669490a627SAlexander V. Chernikov 
2667b074b7bbSAlexander V. Chernikov 	/*
2668b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
2669b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
2670b074b7bbSAlexander V. Chernikov 	 */
2671b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
2672b074b7bbSAlexander V. Chernikov 		/* Stack */
26739490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
2674b074b7bbSAlexander V. Chernikov 	} else
26759490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
2676b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
2677b074b7bbSAlexander V. Chernikov 
26789490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
2679b074b7bbSAlexander V. Chernikov 	error = 0;
2680b074b7bbSAlexander V. Chernikov 
2681b074b7bbSAlexander V. Chernikov 	type = 0;
2682b074b7bbSAlexander V. Chernikov 	ftype = 0;
2683b074b7bbSAlexander V. Chernikov 
2684b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
26851832a7b3SAlexander V. Chernikov 
26861832a7b3SAlexander V. Chernikov 	/*
26871832a7b3SAlexander V. Chernikov 	 * Use default set for looking up tables (old way) or
26881832a7b3SAlexander V. Chernikov 	 * use set rule is assigned to (new way).
26891832a7b3SAlexander V. Chernikov 	 */
26901832a7b3SAlexander V. Chernikov 	ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0;
26916c2997ffSAlexander V. Chernikov 	if (ci->ctlv != NULL) {
26926c2997ffSAlexander V. Chernikov 		ti.tlvs = (void *)(ci->ctlv + 1);
26936c2997ffSAlexander V. Chernikov 		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
26946c2997ffSAlexander V. Chernikov 	}
2695b074b7bbSAlexander V. Chernikov 
2696b074b7bbSAlexander V. Chernikov 	/*
26979490a627SAlexander V. Chernikov 	 * Stage 1: reference existing tables, determine number
26989490a627SAlexander V. Chernikov 	 * of tables we need to allocate and allocate indexes for each.
2699b074b7bbSAlexander V. Chernikov 	 */
27009490a627SAlexander V. Chernikov 	error = bind_table_rule(chain, ci->krule, ci, &pidx_last, &ti);
2701b074b7bbSAlexander V. Chernikov 
2702b074b7bbSAlexander V. Chernikov 	if (error != 0) {
27039490a627SAlexander V. Chernikov 		if (pidx_first != ci->obuf)
27049490a627SAlexander V. Chernikov 			free(pidx_first, M_IPFW);
2705b074b7bbSAlexander V. Chernikov 
2706b074b7bbSAlexander V. Chernikov 		return (error);
2707b074b7bbSAlexander V. Chernikov 	}
2708b074b7bbSAlexander V. Chernikov 
2709b074b7bbSAlexander V. Chernikov 	/*
2710b074b7bbSAlexander V. Chernikov 	 * Stage 2: allocate table configs for every non-existent table
2711b074b7bbSAlexander V. Chernikov 	 */
2712b074b7bbSAlexander V. Chernikov 
27139f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
27149490a627SAlexander V. Chernikov 		for (p = pidx_first; p < pidx_last; p++) {
2715b074b7bbSAlexander V. Chernikov 			if (p->new == 0)
2716b074b7bbSAlexander V. Chernikov 				continue;
2717b074b7bbSAlexander V. Chernikov 
2718b074b7bbSAlexander V. Chernikov 			ti.uidx = p->uidx;
2719b074b7bbSAlexander V. Chernikov 			ti.type = p->type;
27209f7d47b0SAlexander V. Chernikov 			ti.atype = 0;
2721b074b7bbSAlexander V. Chernikov 
27229490a627SAlexander V. Chernikov 			ta = find_table_algo(CHAIN_TO_TCFG(chain), &ti, NULL);
27239f7d47b0SAlexander V. Chernikov 			if (ta == NULL) {
27249f7d47b0SAlexander V. Chernikov 				error = ENOTSUP;
27259f7d47b0SAlexander V. Chernikov 				goto free;
27269f7d47b0SAlexander V. Chernikov 			}
2727914bffb6SAlexander V. Chernikov 			tc = alloc_table_config(chain, &ti, ta, NULL, 0,
272868394ec8SAlexander V. Chernikov 			    IPFW_VTYPE_U32);
2729b074b7bbSAlexander V. Chernikov 
2730b074b7bbSAlexander V. Chernikov 			if (tc == NULL) {
2731b074b7bbSAlexander V. Chernikov 				error = ENOMEM;
2732b074b7bbSAlexander V. Chernikov 				goto free;
2733b074b7bbSAlexander V. Chernikov 			}
2734b074b7bbSAlexander V. Chernikov 
2735b074b7bbSAlexander V. Chernikov 			tc->no.kidx = p->kidx;
2736b074b7bbSAlexander V. Chernikov 			tc->no.refcnt = 1;
2737b074b7bbSAlexander V. Chernikov 
2738b074b7bbSAlexander V. Chernikov 			/* Add to list */
2739b074b7bbSAlexander V. Chernikov 			TAILQ_INSERT_TAIL(&nh, &tc->no, nn_next);
2740b074b7bbSAlexander V. Chernikov 		}
2741b074b7bbSAlexander V. Chernikov 
2742b074b7bbSAlexander V. Chernikov 		/*
2743b074b7bbSAlexander V. Chernikov 		 * Stage 2.1: Check if we're going to create 2 tables
2744b074b7bbSAlexander V. Chernikov 		 * with the same name, but different table types.
2745b074b7bbSAlexander V. Chernikov 		 */
2746b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH(no, &nh, nn_next) {
2747b074b7bbSAlexander V. Chernikov 			TAILQ_FOREACH(no_tmp, &nh, nn_next) {
27489490a627SAlexander V. Chernikov 				if (ipfw_objhash_same_name(ni, no, no_tmp) == 0)
2749b074b7bbSAlexander V. Chernikov 					continue;
2750b074b7bbSAlexander V. Chernikov 				if (no->type != no_tmp->type) {
2751b074b7bbSAlexander V. Chernikov 					error = EINVAL;
2752b074b7bbSAlexander V. Chernikov 					goto free;
2753b074b7bbSAlexander V. Chernikov 				}
2754b074b7bbSAlexander V. Chernikov 			}
2755b074b7bbSAlexander V. Chernikov 		}
27569f7d47b0SAlexander V. Chernikov 	}
2757b074b7bbSAlexander V. Chernikov 
27589f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
27599f7d47b0SAlexander V. Chernikov 
27609f7d47b0SAlexander V. Chernikov 	if (ci->new_tables > 0) {
2761b074b7bbSAlexander V. Chernikov 		/*
2762b074b7bbSAlexander V. Chernikov 		 * Stage 3: link & reference new table configs
2763b074b7bbSAlexander V. Chernikov 		 */
2764b074b7bbSAlexander V. Chernikov 
2765b074b7bbSAlexander V. Chernikov 
2766b074b7bbSAlexander V. Chernikov 		/*
2767b074b7bbSAlexander V. Chernikov 		 * Step 3.1: Check if some tables we need to create have been
2768b074b7bbSAlexander V. Chernikov 		 * already created with different table type.
2769b074b7bbSAlexander V. Chernikov 		 */
2770b074b7bbSAlexander V. Chernikov 
2771b074b7bbSAlexander V. Chernikov 		error = 0;
2772b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
2773b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
2774b074b7bbSAlexander V. Chernikov 			if (no_n == NULL)
2775b074b7bbSAlexander V. Chernikov 				continue;
2776b074b7bbSAlexander V. Chernikov 
2777b074b7bbSAlexander V. Chernikov 			if (no_n->type != no->type) {
2778b074b7bbSAlexander V. Chernikov 				error = EINVAL;
2779b074b7bbSAlexander V. Chernikov 				break;
2780b074b7bbSAlexander V. Chernikov 			}
2781b074b7bbSAlexander V. Chernikov 
2782b074b7bbSAlexander V. Chernikov 		}
2783b074b7bbSAlexander V. Chernikov 
2784b074b7bbSAlexander V. Chernikov 		if (error != 0) {
2785b074b7bbSAlexander V. Chernikov 			/*
2786b074b7bbSAlexander V. Chernikov 			 * Someone has allocated table with different table type.
2787b074b7bbSAlexander V. Chernikov 			 * We have to rollback everything.
2788b074b7bbSAlexander V. Chernikov 			 */
2789b074b7bbSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(chain);
2790b074b7bbSAlexander V. Chernikov 			goto free;
2791b074b7bbSAlexander V. Chernikov 		}
2792b074b7bbSAlexander V. Chernikov 
2793b074b7bbSAlexander V. Chernikov 		/*
27949f7d47b0SAlexander V. Chernikov 		 * Attach new tables.
27959f7d47b0SAlexander V. Chernikov 		 * We need to set table pointers for each new table,
2796b074b7bbSAlexander V. Chernikov 		 * so we have to acquire main WLOCK.
2797b074b7bbSAlexander V. Chernikov 		 */
2798b074b7bbSAlexander V. Chernikov 		IPFW_WLOCK(chain);
2799b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
2800b074b7bbSAlexander V. Chernikov 			no_n = ipfw_objhash_lookup_name(ni, no->set, no->name);
2801b074b7bbSAlexander V. Chernikov 
28029490a627SAlexander V. Chernikov 			if (no_n == NULL) {
28039490a627SAlexander V. Chernikov 				/* New table. Attach to runtime hash */
28049490a627SAlexander V. Chernikov 				TAILQ_REMOVE(&nh, no, nn_next);
28059490a627SAlexander V. Chernikov 				link_table(chain, (struct table_config *)no);
2806b074b7bbSAlexander V. Chernikov 				continue;
2807b074b7bbSAlexander V. Chernikov 			}
2808b074b7bbSAlexander V. Chernikov 
28099490a627SAlexander V. Chernikov 			/*
28109490a627SAlexander V. Chernikov 			 * Newly-allocated table with the same type.
28119490a627SAlexander V. Chernikov 			 * Reference it and update out @pidx array
28129490a627SAlexander V. Chernikov 			 * rewrite info.
28139490a627SAlexander V. Chernikov 			 */
28149490a627SAlexander V. Chernikov 			no_n->refcnt++;
28159490a627SAlexander V. Chernikov 			/* Keep oib array in sync: update kidx */
28169490a627SAlexander V. Chernikov 			for (p = pidx_first; p < pidx_last; p++) {
28179490a627SAlexander V. Chernikov 				if (p->kidx != no->kidx)
28189490a627SAlexander V. Chernikov 					continue;
28199490a627SAlexander V. Chernikov 				/* Update kidx */
28209490a627SAlexander V. Chernikov 				p->kidx = no_n->kidx;
28219490a627SAlexander V. Chernikov 				break;
28229490a627SAlexander V. Chernikov 			}
2823b074b7bbSAlexander V. Chernikov 		}
2824b074b7bbSAlexander V. Chernikov 		IPFW_WUNLOCK(chain);
28259f7d47b0SAlexander V. Chernikov 	}
2826b074b7bbSAlexander V. Chernikov 
2827b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
2828b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
2829b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
2830b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
28319490a627SAlexander V. Chernikov 	p = pidx_first;
2832b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2833b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
2834b074b7bbSAlexander V. Chernikov 
2835b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
2836b074b7bbSAlexander V. Chernikov 			continue;
28379490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
28389490a627SAlexander V. Chernikov 		p++;
2839b074b7bbSAlexander V. Chernikov 	}
2840b074b7bbSAlexander V. Chernikov 
2841b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
2842b074b7bbSAlexander V. Chernikov 
2843b074b7bbSAlexander V. Chernikov 	error = 0;
2844b074b7bbSAlexander V. Chernikov 
2845b074b7bbSAlexander V. Chernikov 	/*
2846b074b7bbSAlexander V. Chernikov 	 * Stage 4: free resources
2847b074b7bbSAlexander V. Chernikov 	 */
2848b074b7bbSAlexander V. Chernikov free:
28499490a627SAlexander V. Chernikov 	if (!TAILQ_EMPTY(&nh)) {
28509490a627SAlexander V. Chernikov 		/* Free indexes first */
28519490a627SAlexander V. Chernikov 		IPFW_UH_WLOCK(chain);
28529490a627SAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
2853ac35ff17SAlexander V. Chernikov 			ipfw_objhash_free_idx(ni, no->kidx);
28549490a627SAlexander V. Chernikov 		}
28559490a627SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(chain);
28569490a627SAlexander V. Chernikov 		/* Free configs */
2857b074b7bbSAlexander V. Chernikov 		TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp)
2858b074b7bbSAlexander V. Chernikov 			free_table_config(ni, tc);
28599490a627SAlexander V. Chernikov 	}
2860b074b7bbSAlexander V. Chernikov 
28619490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
28629490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
2863b074b7bbSAlexander V. Chernikov 
2864b074b7bbSAlexander V. Chernikov 	return (error);
2865b074b7bbSAlexander V. Chernikov }
2866b074b7bbSAlexander V. Chernikov 
2867b074b7bbSAlexander V. Chernikov /*
2868b074b7bbSAlexander V. Chernikov  * Remove references from every table used in @rule.
2869b074b7bbSAlexander V. Chernikov  */
2870b074b7bbSAlexander V. Chernikov void
2871b074b7bbSAlexander V. Chernikov ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
2872b074b7bbSAlexander V. Chernikov {
2873b074b7bbSAlexander V. Chernikov 	int cmdlen, l;
2874b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
2875b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
2876b074b7bbSAlexander V. Chernikov 	struct named_object *no;
2877b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2878b074b7bbSAlexander V. Chernikov 	uint8_t type;
2879b074b7bbSAlexander V. Chernikov 
2880b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
2881b074b7bbSAlexander V. Chernikov 
2882b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
2883b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
2884b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
2885b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2886b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
2887b074b7bbSAlexander V. Chernikov 
2888b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
2889b074b7bbSAlexander V. Chernikov 			continue;
2890b074b7bbSAlexander V. Chernikov 
2891ac35ff17SAlexander V. Chernikov 		no = ipfw_objhash_lookup_kidx(ni, kidx);
2892b074b7bbSAlexander V. Chernikov 
2893b074b7bbSAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
2894b074b7bbSAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
2895b074b7bbSAlexander V. Chernikov 		    no->type, type, kidx));
2896b074b7bbSAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
2897b074b7bbSAlexander V. Chernikov 		    kidx, no->refcnt));
2898b074b7bbSAlexander V. Chernikov 
2899b074b7bbSAlexander V. Chernikov 		no->refcnt--;
2900b074b7bbSAlexander V. Chernikov 	}
2901b074b7bbSAlexander V. Chernikov }
2902b074b7bbSAlexander V. Chernikov 
2903b074b7bbSAlexander V. Chernikov 
2904b074b7bbSAlexander V. Chernikov /*
2905b074b7bbSAlexander V. Chernikov  * Removes table bindings for every rule in rule chain @head.
2906b074b7bbSAlexander V. Chernikov  */
2907b074b7bbSAlexander V. Chernikov void
2908b074b7bbSAlexander V. Chernikov ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head)
2909b074b7bbSAlexander V. Chernikov {
2910b074b7bbSAlexander V. Chernikov 	struct ip_fw *rule;
2911b074b7bbSAlexander V. Chernikov 
2912b074b7bbSAlexander V. Chernikov 	while ((rule = head) != NULL) {
2913b074b7bbSAlexander V. Chernikov 		head = head->x_next;
2914b074b7bbSAlexander V. Chernikov 		ipfw_unbind_table_rule(chain, rule);
2915b074b7bbSAlexander V. Chernikov 	}
2916b074b7bbSAlexander V. Chernikov }
2917b074b7bbSAlexander V. Chernikov 
2918b074b7bbSAlexander V. Chernikov 
29193b3a8eb9SGleb Smirnoff /* end of file */
2920