xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision 4dff4ae0)
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  /*
62b074b7bbSAlexander V. Chernikov  * Table has the following `type` concepts:
63b074b7bbSAlexander V. Chernikov  *
64c21034b7SAlexander V. Chernikov  * `no.type` represents lookup key type (addr, ifp, uid, etc..)
6535e1bbd0SAlexander V. Chernikov  * `vtype` represents table value type (currently U32)
669490a627SAlexander V. Chernikov  * `ftype` (at the moment )is pure userland field helping to properly
679490a627SAlexander V. Chernikov  *     format value data e.g. "value is IPv4 nexthop" or "value is DSCP"
689490a627SAlexander V. Chernikov  *     or "value is port".
69b074b7bbSAlexander V. Chernikov  *
70b074b7bbSAlexander V. Chernikov  */
71b074b7bbSAlexander V. Chernikov struct table_config {
72b074b7bbSAlexander V. Chernikov 	struct named_object	no;
73adf3b2b9SAlexander V. Chernikov 	uint8_t		vtype;		/* value type */
74adf3b2b9SAlexander V. Chernikov 	uint8_t		vftype;		/* value format type */
75914bffb6SAlexander V. Chernikov 	uint8_t		tflags;		/* type flags */
764f43138aSAlexander V. Chernikov 	uint8_t		locked;		/* 1 if locked from changes */
77b074b7bbSAlexander V. Chernikov 	uint32_t	count;		/* Number of records */
784c0c07a5SAlexander V. Chernikov 	uint32_t	limit;		/* Max number of records */
79adf3b2b9SAlexander V. Chernikov 	uint8_t		linked;		/* 1 if already linked */
80adf3b2b9SAlexander V. Chernikov 	uint8_t		ochanged;	/* used by set swapping */
81adf3b2b9SAlexander V. Chernikov 	uint16_t	spare1;
82adf3b2b9SAlexander V. Chernikov 	uint32_t	spare2;
83a73d728dSAlexander V. Chernikov 	uint32_t	ocount;		/* used by set swapping */
84a73d728dSAlexander V. Chernikov 	uint64_t	gencnt;		/* generation count */
85b074b7bbSAlexander V. Chernikov 	char		tablename[64];	/* table name */
869f7d47b0SAlexander V. Chernikov 	struct table_algo	*ta;	/* Callbacks for given algo */
879f7d47b0SAlexander V. Chernikov 	void		*astate;	/* algorithm state */
889f7d47b0SAlexander V. Chernikov 	struct table_info	ti;	/* data to put to table_info */
89b074b7bbSAlexander V. Chernikov };
90b074b7bbSAlexander V. Chernikov 
91b074b7bbSAlexander V. Chernikov struct tables_config {
92b074b7bbSAlexander V. Chernikov 	struct namedobj_instance	*namehash;
939f7d47b0SAlexander V. Chernikov 	int				algo_count;
949f7d47b0SAlexander V. Chernikov 	struct table_algo 		*algo[256];
9557a1cf95SAlexander V. Chernikov 	struct table_algo		*def_algo[IPFW_TABLE_MAXTYPE + 1];
96b074b7bbSAlexander V. Chernikov };
97b074b7bbSAlexander V. Chernikov 
98b074b7bbSAlexander V. Chernikov static struct table_config *find_table(struct namedobj_instance *ni,
99b074b7bbSAlexander V. Chernikov     struct tid_info *ti);
10068394ec8SAlexander V. Chernikov static struct table_config *alloc_table_config(struct ip_fw_chain *ch,
101914bffb6SAlexander V. Chernikov     struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags,
102914bffb6SAlexander V. Chernikov     uint8_t vtype);
103b074b7bbSAlexander V. Chernikov static void free_table_config(struct namedobj_instance *ni,
104b074b7bbSAlexander V. Chernikov     struct table_config *tc);
105db785d31SAlexander V. Chernikov static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
1060468c5baSAlexander V. Chernikov     char *aname, ipfw_xtable_info *i, struct table_config **ptc,
1070468c5baSAlexander V. Chernikov     struct table_algo **pta, uint16_t *pkidx, int ref);
1080468c5baSAlexander V. Chernikov static void link_table(struct ip_fw_chain *ch, struct table_config *tc);
1090468c5baSAlexander V. Chernikov static void unlink_table(struct ip_fw_chain *ch, struct table_config *tc);
1104dff4ae0SAlexander V. Chernikov static int find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti,
1114dff4ae0SAlexander V. Chernikov     struct tentry_info *tei, uint32_t count, int op,
1124dff4ae0SAlexander V. Chernikov     struct table_config **ptc, struct table_algo **pta);
1134dff4ae0SAlexander V. Chernikov #define	OP_ADD	1
1144dff4ae0SAlexander V. Chernikov #define	OP_DEL	0
1152d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
1162d99a349SAlexander V. Chernikov     struct sockopt_data *sd);
117ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
118ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i);
11981d3153dSAlexander V. Chernikov static int dump_table_tentry(void *e, void *arg);
120f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg);
121b074b7bbSAlexander V. Chernikov 
1222d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
1232d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
124db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
125ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
126db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
127ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
128a73d728dSAlexander V. Chernikov static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
12946d52008SAlexander V. Chernikov     struct tid_info *b);
130ac35ff17SAlexander V. Chernikov 
131b6ee846eSAlexander V. Chernikov static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
132b6ee846eSAlexander V. Chernikov     struct table_info *ti, uint32_t count);
133ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
134d3a4f924SAlexander V. Chernikov 
1359f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1369490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
137b074b7bbSAlexander V. Chernikov 
13846d52008SAlexander V. Chernikov static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
13946d52008SAlexander V. Chernikov static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti);
140a73d728dSAlexander V. Chernikov static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
14146d52008SAlexander V. Chernikov 
142b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
143b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1449f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
145b074b7bbSAlexander V. Chernikov 
146b6ee846eSAlexander V. Chernikov #define	TA_BUF_SZ	128	/* On-stack buffer for add/delete state */
147b6ee846eSAlexander V. Chernikov 
1483a845e10SAlexander V. Chernikov /*
1493a845e10SAlexander V. Chernikov  * Checks if we're able to insert/update entry @tei into table
1503a845e10SAlexander V. Chernikov  * w.r.t @tc limits.
1513a845e10SAlexander V. Chernikov  * May alter @tei to indicate insertion error / insert
1523a845e10SAlexander V. Chernikov  * options.
1533a845e10SAlexander V. Chernikov  *
1543a845e10SAlexander V. Chernikov  * Returns 0 if operation can be performed/
1553a845e10SAlexander V. Chernikov  */
1563a845e10SAlexander V. Chernikov static int
1573a845e10SAlexander V. Chernikov check_table_limit(struct table_config *tc, struct tentry_info *tei)
1583a845e10SAlexander V. Chernikov {
159b074b7bbSAlexander V. Chernikov 
1603a845e10SAlexander V. Chernikov 	if (tc->limit == 0 || tc->count < tc->limit)
1613a845e10SAlexander V. Chernikov 		return (0);
1623a845e10SAlexander V. Chernikov 
1633a845e10SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
1643a845e10SAlexander V. Chernikov 		/* Notify userland on error cause */
1653a845e10SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_LIMIT;
1663a845e10SAlexander V. Chernikov 		return (EFBIG);
1673a845e10SAlexander V. Chernikov 	}
1683a845e10SAlexander V. Chernikov 
1693a845e10SAlexander V. Chernikov 	/*
1703a845e10SAlexander V. Chernikov 	 * We have UPDATE flag set.
1713a845e10SAlexander V. Chernikov 	 * Permit updating record (if found),
1723a845e10SAlexander V. Chernikov 	 * but restrict adding new one since we've
1733a845e10SAlexander V. Chernikov 	 * already hit the limit.
1743a845e10SAlexander V. Chernikov 	 */
1753a845e10SAlexander V. Chernikov 	tei->flags |= TEI_FLAGS_DONTADD;
1763a845e10SAlexander V. Chernikov 
1773a845e10SAlexander V. Chernikov 	return (0);
1783a845e10SAlexander V. Chernikov }
1793a845e10SAlexander V. Chernikov 
1803a845e10SAlexander V. Chernikov /*
1811bc0d457SAlexander V. Chernikov  * Convert algorithm callback return code into
1821bc0d457SAlexander V. Chernikov  * one of pre-defined states known by userland.
1833a845e10SAlexander V. Chernikov  */
1841bc0d457SAlexander V. Chernikov static void
1851bc0d457SAlexander V. Chernikov store_tei_result(struct tentry_info *tei, int do_add, int error, uint32_t num)
1863b3a8eb9SGleb Smirnoff {
1871bc0d457SAlexander V. Chernikov 	int flag;
1883b3a8eb9SGleb Smirnoff 
1891bc0d457SAlexander V. Chernikov 	flag = 0;
1901bc0d457SAlexander V. Chernikov 
1911bc0d457SAlexander V. Chernikov 	switch (error) {
1921bc0d457SAlexander V. Chernikov 	case 0:
1931bc0d457SAlexander V. Chernikov 		if (do_add && num != 0)
1941bc0d457SAlexander V. Chernikov 			flag = TEI_FLAGS_ADDED;
1951bc0d457SAlexander V. Chernikov 		if (do_add == 0)
1961bc0d457SAlexander V. Chernikov 			flag = TEI_FLAGS_DELETED;
1971bc0d457SAlexander V. Chernikov 		break;
1981bc0d457SAlexander V. Chernikov 	case ENOENT:
1991bc0d457SAlexander V. Chernikov 		flag = TEI_FLAGS_NOTFOUND;
2001bc0d457SAlexander V. Chernikov 		break;
2011bc0d457SAlexander V. Chernikov 	case EEXIST:
2021bc0d457SAlexander V. Chernikov 		flag = TEI_FLAGS_EXISTS;
2031bc0d457SAlexander V. Chernikov 		break;
2041bc0d457SAlexander V. Chernikov 	default:
2051bc0d457SAlexander V. Chernikov 		flag = TEI_FLAGS_ERROR;
2061bc0d457SAlexander V. Chernikov 	}
2071bc0d457SAlexander V. Chernikov 
2081bc0d457SAlexander V. Chernikov 	tei->flags |= flag;
2091bc0d457SAlexander V. Chernikov }
2109f7d47b0SAlexander V. Chernikov 
211f99fbf96SAlexander V. Chernikov /*
212f99fbf96SAlexander V. Chernikov  * Creates and references table with default parameters.
213f99fbf96SAlexander V. Chernikov  * Saves table config, algo and allocated kidx info @ptc, @pta and
214f99fbf96SAlexander V. Chernikov  * @pkidx if non-zero.
215f99fbf96SAlexander V. Chernikov  * Used for table auto-creation to support old binaries.
216f99fbf96SAlexander V. Chernikov  *
217f99fbf96SAlexander V. Chernikov  * Returns 0 on success.
218f99fbf96SAlexander V. Chernikov  */
2190468c5baSAlexander V. Chernikov static int
2200468c5baSAlexander V. Chernikov create_table_compat(struct ip_fw_chain *ch, struct tid_info *ti,
2210468c5baSAlexander V. Chernikov     struct table_config **ptc, struct table_algo **pta, uint16_t *pkidx)
2220468c5baSAlexander V. Chernikov {
2230468c5baSAlexander V. Chernikov 	ipfw_xtable_info xi;
2240468c5baSAlexander V. Chernikov 	int error;
2250468c5baSAlexander V. Chernikov 
2260468c5baSAlexander V. Chernikov 	memset(&xi, 0, sizeof(xi));
227f99fbf96SAlexander V. Chernikov 	/* Set u32 as default value type for legacy clients */
2280468c5baSAlexander V. Chernikov 	xi.vtype = IPFW_VTYPE_U32;
2290468c5baSAlexander V. Chernikov 
2300468c5baSAlexander V. Chernikov 	error = create_table_internal(ch, ti, NULL, &xi, ptc, pta, pkidx, 1);
2310468c5baSAlexander V. Chernikov 	if (error != 0)
2320468c5baSAlexander V. Chernikov 		return (error);
2330468c5baSAlexander V. Chernikov 
2340468c5baSAlexander V. Chernikov 	return (0);
2350468c5baSAlexander V. Chernikov }
2360468c5baSAlexander V. Chernikov 
2379f7d47b0SAlexander V. Chernikov /*
2381bc0d457SAlexander V. Chernikov  * Find and reference existing table optionally
2391bc0d457SAlexander V. Chernikov  * creating new one.
2401bc0d457SAlexander V. Chernikov  *
2411bc0d457SAlexander V. Chernikov  * Saves found table config/table algo into @ptc / @pta.
2421bc0d457SAlexander V. Chernikov  * Returns 0 if table was found/created and referenced
2431bc0d457SAlexander V. Chernikov  * or non-zero return code.
2449f7d47b0SAlexander V. Chernikov  */
2451bc0d457SAlexander V. Chernikov static int
2461bc0d457SAlexander V. Chernikov find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti,
2474dff4ae0SAlexander V. Chernikov     struct tentry_info *tei, uint32_t count, int op,
2481bc0d457SAlexander V. Chernikov     struct table_config **ptc, struct table_algo **pta)
2491bc0d457SAlexander V. Chernikov {
2501bc0d457SAlexander V. Chernikov 	struct namedobj_instance *ni;
2511bc0d457SAlexander V. Chernikov 	struct table_config *tc;
2521bc0d457SAlexander V. Chernikov 	struct table_algo *ta;
2531bc0d457SAlexander V. Chernikov 	int error;
2541bc0d457SAlexander V. Chernikov 
2551bc0d457SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
2561bc0d457SAlexander V. Chernikov 
2571bc0d457SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2581bc0d457SAlexander V. Chernikov 	tc = NULL;
2599f7d47b0SAlexander V. Chernikov 	ta = NULL;
2609f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
2619f7d47b0SAlexander V. Chernikov 		/* check table type */
2629f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
2639f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
2643b3a8eb9SGleb Smirnoff 			return (EINVAL);
2653b3a8eb9SGleb Smirnoff 		}
2663b3a8eb9SGleb Smirnoff 
2674f43138aSAlexander V. Chernikov 		if (tc->locked != 0) {
2684f43138aSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
2694f43138aSAlexander V. Chernikov 			return (EACCES);
2704f43138aSAlexander V. Chernikov 		}
2714f43138aSAlexander V. Chernikov 
2724c0c07a5SAlexander V. Chernikov 		/* Try to exit early on limit hit */
2734dff4ae0SAlexander V. Chernikov 		if (op == OP_ADD && count == 1 &&
2741bc0d457SAlexander V. Chernikov 		    check_table_limit(tc, tei) != 0) {
2754c0c07a5SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
2764c0c07a5SAlexander V. Chernikov 			return (EFBIG);
2774c0c07a5SAlexander V. Chernikov 		}
2784c0c07a5SAlexander V. Chernikov 
2799f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
2809f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
2819f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
2829f7d47b0SAlexander V. Chernikov 	}
2839f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
2843b3a8eb9SGleb Smirnoff 
285ac35ff17SAlexander V. Chernikov 	if (tc == NULL) {
2864dff4ae0SAlexander V. Chernikov 		if (op == OP_DEL)
2871bc0d457SAlexander V. Chernikov 			return (ESRCH);
2881bc0d457SAlexander V. Chernikov 
289db785d31SAlexander V. Chernikov 		/* Compability mode: create new table for old clients */
290db785d31SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
291db785d31SAlexander V. Chernikov 			return (ESRCH);
2923b3a8eb9SGleb Smirnoff 
2930468c5baSAlexander V. Chernikov 		error = create_table_compat(ch, ti, &tc, &ta, NULL);
294db785d31SAlexander V. Chernikov 		if (error != 0)
295db785d31SAlexander V. Chernikov 			return (error);
296db785d31SAlexander V. Chernikov 
2970468c5baSAlexander V. Chernikov 		/* OK, now we've got referenced table. */
298db785d31SAlexander V. Chernikov 	}
299db785d31SAlexander V. Chernikov 
3001bc0d457SAlexander V. Chernikov 	*ptc = tc;
3011bc0d457SAlexander V. Chernikov 	*pta = ta;
3021bc0d457SAlexander V. Chernikov 	return (0);
3031bc0d457SAlexander V. Chernikov }
3041bc0d457SAlexander V. Chernikov 
3051bc0d457SAlexander V. Chernikov /*
3061bc0d457SAlexander V. Chernikov  * Rolls back already @added to @tc entries using state arrat @ta_buf_m.
3071bc0d457SAlexander V. Chernikov  * Assume the following layout:
3081bc0d457SAlexander V. Chernikov  * 1) ADD state (ta_buf_m[0] ... t_buf_m[added - 1]) for handling update cases
3091bc0d457SAlexander V. Chernikov  * 2) DEL state (ta_buf_m[count[ ... t_buf_m[count + added - 1])
3101bc0d457SAlexander V. Chernikov  *   for storing deleted state
3111bc0d457SAlexander V. Chernikov  */
3121bc0d457SAlexander V. Chernikov static void
3131bc0d457SAlexander V. Chernikov rollback_added_entries(struct ip_fw_chain *ch, struct table_config *tc,
3141bc0d457SAlexander V. Chernikov     struct table_info *tinfo, struct tentry_info *tei, caddr_t ta_buf_m,
3151bc0d457SAlexander V. Chernikov     uint32_t count, uint32_t added)
3161bc0d457SAlexander V. Chernikov {
3171bc0d457SAlexander V. Chernikov 	struct table_algo *ta;
3181bc0d457SAlexander V. Chernikov 	struct tentry_info *ptei;
3191bc0d457SAlexander V. Chernikov 	caddr_t v, vv;
3201bc0d457SAlexander V. Chernikov 	size_t ta_buf_sz;
3211bc0d457SAlexander V. Chernikov 	int error, i;
3221bc0d457SAlexander V. Chernikov 	uint32_t num;
3231bc0d457SAlexander V. Chernikov 
3241bc0d457SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
3251bc0d457SAlexander V. Chernikov 
3261bc0d457SAlexander V. Chernikov 	ta = tc->ta;
3273a845e10SAlexander V. Chernikov 	ta_buf_sz = ta->ta_buf_size;
3281bc0d457SAlexander V. Chernikov 	v = ta_buf_m;
3291bc0d457SAlexander V. Chernikov 	vv = v + count * ta_buf_sz;
3301bc0d457SAlexander V. Chernikov 	for (i = 0; i < added; i++, v += ta_buf_sz, vv += ta_buf_sz) {
3311bc0d457SAlexander V. Chernikov 		ptei = &tei[i];
3321bc0d457SAlexander V. Chernikov 		if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) {
3331bc0d457SAlexander V. Chernikov 
3341bc0d457SAlexander V. Chernikov 			/*
3351bc0d457SAlexander V. Chernikov 			 * We have old value stored by previous
3361bc0d457SAlexander V. Chernikov 			 * call in @ptei->value. Do add once again
3371bc0d457SAlexander V. Chernikov 			 * to restore it.
3381bc0d457SAlexander V. Chernikov 			 */
3391bc0d457SAlexander V. Chernikov 			error = ta->add(tc->astate, tinfo, ptei, v, &num);
3401bc0d457SAlexander V. Chernikov 			KASSERT(error == 0, ("rollback UPDATE fail"));
3411bc0d457SAlexander V. Chernikov 			KASSERT(num == 0, ("rollback UPDATE fail2"));
3421bc0d457SAlexander V. Chernikov 			continue;
3431bc0d457SAlexander V. Chernikov 		}
3441bc0d457SAlexander V. Chernikov 
3451bc0d457SAlexander V. Chernikov 		error = ta->prepare_del(ch, ptei, vv);
3461bc0d457SAlexander V. Chernikov 		KASSERT(error == 0, ("pre-rollback INSERT failed"));
3471bc0d457SAlexander V. Chernikov 		error = ta->del(tc->astate, tinfo, ptei, vv, &num);
3481bc0d457SAlexander V. Chernikov 		KASSERT(error == 0, ("rollback INSERT failed"));
3491bc0d457SAlexander V. Chernikov 		tc->count -= num;
3501bc0d457SAlexander V. Chernikov 	}
3511bc0d457SAlexander V. Chernikov }
3521bc0d457SAlexander V. Chernikov 
3531bc0d457SAlexander V. Chernikov /*
3541bc0d457SAlexander V. Chernikov  * Prepares add/del state for all @count entries in @tei.
3551bc0d457SAlexander V. Chernikov  * Uses either stack buffer (@ta_buf) or allocates a new one.
3561bc0d457SAlexander V. Chernikov  * Stores pointer to allocated buffer back to @ta_buf.
3571bc0d457SAlexander V. Chernikov  *
3581bc0d457SAlexander V. Chernikov  * Returns 0 on success.
3591bc0d457SAlexander V. Chernikov  */
3601bc0d457SAlexander V. Chernikov static int
3611bc0d457SAlexander V. Chernikov prepare_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
3624dff4ae0SAlexander V. Chernikov     struct tentry_info *tei, uint32_t count, int op, caddr_t *ta_buf)
3631bc0d457SAlexander V. Chernikov {
3641bc0d457SAlexander V. Chernikov 	caddr_t ta_buf_m, v;
3651bc0d457SAlexander V. Chernikov 	size_t ta_buf_sz, sz;
3661bc0d457SAlexander V. Chernikov 	struct tentry_info *ptei;
3671bc0d457SAlexander V. Chernikov 	int error, i;
3681bc0d457SAlexander V. Chernikov 
3691bc0d457SAlexander V. Chernikov 	error = 0;
3701bc0d457SAlexander V. Chernikov 	ta_buf_sz = ta->ta_buf_size;
3713a845e10SAlexander V. Chernikov 	if (count == 1) {
3721bc0d457SAlexander V. Chernikov 		/* Sigle add/delete, use on-stack buffer */
3731bc0d457SAlexander V. Chernikov 		memset(*ta_buf, 0, TA_BUF_SZ);
3741bc0d457SAlexander V. Chernikov 		ta_buf_m = *ta_buf;
3753a845e10SAlexander V. Chernikov 	} else {
3763a845e10SAlexander V. Chernikov 
3773a845e10SAlexander V. Chernikov 		/*
3781bc0d457SAlexander V. Chernikov 		 * Multiple adds/deletes, allocate larger buffer
3791bc0d457SAlexander V. Chernikov 		 *
3801bc0d457SAlexander V. Chernikov 		 * Note we need 2xcount buffer for add case:
3811bc0d457SAlexander V. Chernikov 		 * we have hold both ADD state
3823a845e10SAlexander V. Chernikov 		 * and DELETE state (this may be needed
3833a845e10SAlexander V. Chernikov 		 * if we need to rollback all changes)
3843a845e10SAlexander V. Chernikov 		 */
3851bc0d457SAlexander V. Chernikov 		sz = count * ta_buf_sz;
3864dff4ae0SAlexander V. Chernikov 		ta_buf_m = malloc((op == OP_ADD) ? sz * 2 : sz, M_TEMP,
3873a845e10SAlexander V. Chernikov 		    M_WAITOK | M_ZERO);
3883a845e10SAlexander V. Chernikov 	}
3891bc0d457SAlexander V. Chernikov 
3903a845e10SAlexander V. Chernikov 	v = ta_buf_m;
3913a845e10SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta_buf_sz) {
3921bc0d457SAlexander V. Chernikov 		ptei = &tei[i];
3934dff4ae0SAlexander V. Chernikov 		error = (op == OP_ADD) ?
3941bc0d457SAlexander V. Chernikov 		    ta->prepare_add(ch, ptei, v) : ta->prepare_del(ch, ptei, v);
3953a845e10SAlexander V. Chernikov 
3963a845e10SAlexander V. Chernikov 		/*
3973a845e10SAlexander V. Chernikov 		 * Some syntax error (incorrect mask, or address, or
3983a845e10SAlexander V. Chernikov 		 * anything). Return error regardless of atomicity
3993a845e10SAlexander V. Chernikov 		 * settings.
4003a845e10SAlexander V. Chernikov 		 */
401db785d31SAlexander V. Chernikov 		if (error != 0)
4021bc0d457SAlexander V. Chernikov 			break;
4033a845e10SAlexander V. Chernikov 	}
4043b3a8eb9SGleb Smirnoff 
4051bc0d457SAlexander V. Chernikov 	*ta_buf = ta_buf_m;
4061bc0d457SAlexander V. Chernikov 	return (error);
4071bc0d457SAlexander V. Chernikov }
4081bc0d457SAlexander V. Chernikov 
4091bc0d457SAlexander V. Chernikov /*
4101bc0d457SAlexander V. Chernikov  * Flushes allocated state for each @count entries in @tei.
4111bc0d457SAlexander V. Chernikov  * Frees @ta_buf_m if differs from stack buffer @ta_buf.
4121bc0d457SAlexander V. Chernikov  */
4131bc0d457SAlexander V. Chernikov static void
4141bc0d457SAlexander V. Chernikov flush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
4151bc0d457SAlexander V. Chernikov     struct tentry_info *tei, uint32_t count, int do_add, int rollback,
4161bc0d457SAlexander V. Chernikov     caddr_t ta_buf_m, caddr_t ta_buf)
4171bc0d457SAlexander V. Chernikov {
4181bc0d457SAlexander V. Chernikov 	caddr_t v;
4191bc0d457SAlexander V. Chernikov 	size_t ta_buf_sz;
4201bc0d457SAlexander V. Chernikov 	int i;
4211bc0d457SAlexander V. Chernikov 
4221bc0d457SAlexander V. Chernikov 	ta_buf_sz = ta->ta_buf_size;
4231bc0d457SAlexander V. Chernikov 
4241bc0d457SAlexander V. Chernikov 	/* Run cleaning callback anyway */
4251bc0d457SAlexander V. Chernikov 	v = ta_buf_m;
4261bc0d457SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta_buf_sz)
4271bc0d457SAlexander V. Chernikov 		ta->flush_entry(ch, &tei[i], v);
4281bc0d457SAlexander V. Chernikov 
4291bc0d457SAlexander V. Chernikov 	/* Clean up "deleted" state in case of rollback */
4301bc0d457SAlexander V. Chernikov 	if (rollback != 0) {
4311bc0d457SAlexander V. Chernikov 		v = ta_buf_m + count * ta_buf_sz;
4321bc0d457SAlexander V. Chernikov 		for (i = 0; i < count; i++, v += ta_buf_sz)
4331bc0d457SAlexander V. Chernikov 			ta->flush_entry(ch, &tei[i], v);
4341bc0d457SAlexander V. Chernikov 	}
4351bc0d457SAlexander V. Chernikov 
4361bc0d457SAlexander V. Chernikov 	if (ta_buf_m != ta_buf)
4371bc0d457SAlexander V. Chernikov 		free(ta_buf_m, M_TEMP);
4381bc0d457SAlexander V. Chernikov }
4391bc0d457SAlexander V. Chernikov 
4401bc0d457SAlexander V. Chernikov /*
4411bc0d457SAlexander V. Chernikov  * Adds/updates one or more entries in table @ti.
4420468c5baSAlexander V. Chernikov  * Function references @ti first to ensure table won't
4430468c5baSAlexander V. Chernikov  * disappear or change its type.
4440468c5baSAlexander V. Chernikov  * After that, prepare_add callback is called for each @tei entry.
4450468c5baSAlexander V. Chernikov  * Next, we try to add each entry under UH+WHLOCK
4460468c5baSAlexander V. Chernikov  * using add() callback.
4470468c5baSAlexander V. Chernikov  * Finally, we free all state by calling flush_entry callback
4480468c5baSAlexander V. Chernikov  * for each @tei.
4491bc0d457SAlexander V. Chernikov  *
4501bc0d457SAlexander V. Chernikov  * Returns 0 on success.
4511bc0d457SAlexander V. Chernikov  */
4521bc0d457SAlexander V. Chernikov int
4531bc0d457SAlexander V. Chernikov add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
4541bc0d457SAlexander V. Chernikov     struct tentry_info *tei, uint8_t flags, uint32_t count)
4551bc0d457SAlexander V. Chernikov {
4561bc0d457SAlexander V. Chernikov 	struct table_config *tc;
4571bc0d457SAlexander V. Chernikov 	struct table_algo *ta;
4581bc0d457SAlexander V. Chernikov 	uint16_t kidx;
4591bc0d457SAlexander V. Chernikov 	int error, first_error, i, rollback;
4601bc0d457SAlexander V. Chernikov 	uint32_t num, numadd;
4611bc0d457SAlexander V. Chernikov 	struct tentry_info *ptei;
4621bc0d457SAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
4631bc0d457SAlexander V. Chernikov 	caddr_t ta_buf_m, v;
4641bc0d457SAlexander V. Chernikov 
4651bc0d457SAlexander V. Chernikov 	/*
4661bc0d457SAlexander V. Chernikov 	 * Find and reference existing table.
4671bc0d457SAlexander V. Chernikov 	 */
4684dff4ae0SAlexander V. Chernikov 	error = find_ref_table(ch, ti, tei, count, OP_ADD, &tc, &ta);
4694dff4ae0SAlexander V. Chernikov 	if (error != 0)
4701bc0d457SAlexander V. Chernikov 		return (error);
4711bc0d457SAlexander V. Chernikov 
4721bc0d457SAlexander V. Chernikov 	/* Allocate memory and prepare record(s) */
4731bc0d457SAlexander V. Chernikov 	rollback = 0;
4741bc0d457SAlexander V. Chernikov 	/* Pass stack buffer by default */
4751bc0d457SAlexander V. Chernikov 	ta_buf_m = ta_buf;
4764dff4ae0SAlexander V. Chernikov 	error = prepare_batch_buffer(ch, ta, tei, count, OP_ADD, &ta_buf_m);
4771bc0d457SAlexander V. Chernikov 	if (error != 0)
4781bc0d457SAlexander V. Chernikov 		goto cleanup;
4791bc0d457SAlexander V. Chernikov 
480b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
4813b3a8eb9SGleb Smirnoff 
482c8d5d308SAlexander V. Chernikov 	/* Drop reference we've used in first search */
483c8d5d308SAlexander V. Chernikov 	tc->no.refcnt--;
484c8d5d308SAlexander V. Chernikov 
485b6ee846eSAlexander V. Chernikov 	/*
486b6ee846eSAlexander V. Chernikov 	 * Ensure we are able to add all entries without additional
487b6ee846eSAlexander V. Chernikov 	 * memory allocations. May release/reacquire UH_WLOCK.
488c8d5d308SAlexander V. Chernikov 	 * check_table_space() guarantees us @tc won't disappear
489c8d5d308SAlexander V. Chernikov 	 * by referencing it internally.
490b6ee846eSAlexander V. Chernikov 	 */
491b6ee846eSAlexander V. Chernikov 	kidx = tc->no.kidx;
492b6ee846eSAlexander V. Chernikov 	error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count);
493b6ee846eSAlexander V. Chernikov 	if (error != 0) {
494b6ee846eSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
4953a845e10SAlexander V. Chernikov 		goto cleanup;
496b6ee846eSAlexander V. Chernikov 	}
497b6ee846eSAlexander V. Chernikov 
498c8d5d308SAlexander V. Chernikov 	/*
499c8d5d308SAlexander V. Chernikov 	 * Check if table algo is still the same.
500c8d5d308SAlexander V. Chernikov 	 * (changed ta may be the result of table swap).
501c8d5d308SAlexander V. Chernikov 	 */
502c8d5d308SAlexander V. Chernikov 	if (ta != tc->ta) {
503c8d5d308SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
504c8d5d308SAlexander V. Chernikov 		error = EINVAL;
505c8d5d308SAlexander V. Chernikov 		goto cleanup;
506c8d5d308SAlexander V. Chernikov 	}
507c8d5d308SAlexander V. Chernikov 
5083a845e10SAlexander V. Chernikov 	/* We've got valid table in @tc. Let's try to add data */
5099f7d47b0SAlexander V. Chernikov 	kidx = tc->no.kidx;
5109f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
5113a845e10SAlexander V. Chernikov 	numadd = 0;
5123a845e10SAlexander V. Chernikov 	first_error = 0;
5139f7d47b0SAlexander V. Chernikov 
514b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
5153a845e10SAlexander V. Chernikov 
5163a845e10SAlexander V. Chernikov 	v = ta_buf_m;
5171bc0d457SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta->ta_buf_size) {
5183a845e10SAlexander V. Chernikov 		ptei = &tei[i];
5193a845e10SAlexander V. Chernikov 		num = 0;
5203a845e10SAlexander V. Chernikov 		/* check limit before adding */
5213a845e10SAlexander V. Chernikov 		if ((error = check_table_limit(tc, ptei)) == 0) {
5223a845e10SAlexander V. Chernikov 			error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx),
5233a845e10SAlexander V. Chernikov 			    ptei, v, &num);
5243a845e10SAlexander V. Chernikov 			/* Set status flag to inform userland */
5251bc0d457SAlexander V. Chernikov 			store_tei_result(ptei, 1, error, num);
5263a845e10SAlexander V. Chernikov 		}
5273a845e10SAlexander V. Chernikov 		if (error == 0) {
5283a845e10SAlexander V. Chernikov 			/* Update number of records to ease limit checking */
5293a845e10SAlexander V. Chernikov 			tc->count += num;
5303a845e10SAlexander V. Chernikov 			numadd += num;
5313a845e10SAlexander V. Chernikov 			continue;
5323a845e10SAlexander V. Chernikov 		}
5333a845e10SAlexander V. Chernikov 
5343a845e10SAlexander V. Chernikov 		if (first_error == 0)
5353a845e10SAlexander V. Chernikov 			first_error = error;
5363a845e10SAlexander V. Chernikov 
5373a845e10SAlexander V. Chernikov 		/*
5383a845e10SAlexander V. Chernikov 		 * Some error have happened. Check our atomicity
5393a845e10SAlexander V. Chernikov 		 * settings: continue if atomicity is not required,
5403a845e10SAlexander V. Chernikov 		 * rollback changes otherwise.
5413a845e10SAlexander V. Chernikov 		 */
5423a845e10SAlexander V. Chernikov 		if ((flags & IPFW_CTF_ATOMIC) == 0)
5433a845e10SAlexander V. Chernikov 			continue;
5443a845e10SAlexander V. Chernikov 
5451bc0d457SAlexander V. Chernikov 		rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx),
5461bc0d457SAlexander V. Chernikov 		    tei, ta_buf_m, count, i);
5473a845e10SAlexander V. Chernikov 		break;
5483a845e10SAlexander V. Chernikov 	}
5493a845e10SAlexander V. Chernikov 
5503b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
5519f7d47b0SAlexander V. Chernikov 
552b6ee846eSAlexander V. Chernikov 	/* Permit post-add algorithm grow/rehash. */
5533a845e10SAlexander V. Chernikov 	if (numadd != 0)
5543a845e10SAlexander V. Chernikov 		check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
555db785d31SAlexander V. Chernikov 
556b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
557b074b7bbSAlexander V. Chernikov 
5583a845e10SAlexander V. Chernikov 	/* Return first error to user, if any */
5593a845e10SAlexander V. Chernikov 	error = first_error;
5603a845e10SAlexander V. Chernikov 
5613a845e10SAlexander V. Chernikov cleanup:
5621bc0d457SAlexander V. Chernikov 	flush_batch_buffer(ch, ta, tei, count, 1, rollback, ta_buf_m, ta_buf);
563b074b7bbSAlexander V. Chernikov 
5649f7d47b0SAlexander V. Chernikov 	return (error);
5653b3a8eb9SGleb Smirnoff }
5663b3a8eb9SGleb Smirnoff 
5673a845e10SAlexander V. Chernikov /*
5683a845e10SAlexander V. Chernikov  * Deletes one or more entries in table @ti.
5693a845e10SAlexander V. Chernikov  *
5703a845e10SAlexander V. Chernikov  * Returns 0 on success.
5713a845e10SAlexander V. Chernikov  */
5723b3a8eb9SGleb Smirnoff int
5731832a7b3SAlexander V. Chernikov del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
5743a845e10SAlexander V. Chernikov     struct tentry_info *tei, uint8_t flags, uint32_t count)
5753b3a8eb9SGleb Smirnoff {
576b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
5779f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
5783a845e10SAlexander V. Chernikov 	struct tentry_info *ptei;
579b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
5803a845e10SAlexander V. Chernikov 	int error, first_error, i;
5813a845e10SAlexander V. Chernikov 	uint32_t num, numdel;
582b6ee846eSAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
5833a845e10SAlexander V. Chernikov 	caddr_t ta_buf_m, v;
5843b3a8eb9SGleb Smirnoff 
585db785d31SAlexander V. Chernikov 	/*
5861bc0d457SAlexander V. Chernikov 	 * Find and reference existing table.
587db785d31SAlexander V. Chernikov 	 */
5884dff4ae0SAlexander V. Chernikov 	error = find_ref_table(ch, ti, tei, count, OP_DEL, &tc, &ta);
5894dff4ae0SAlexander V. Chernikov 	if (error != 0)
590db785d31SAlexander V. Chernikov 		return (error);
5913a845e10SAlexander V. Chernikov 
59235e1bbd0SAlexander V. Chernikov 	/* Allocate memory and prepare record(s) */
5931bc0d457SAlexander V. Chernikov 	/* Pass stack buffer by default */
5943a845e10SAlexander V. Chernikov 	ta_buf_m = ta_buf;
5954dff4ae0SAlexander V. Chernikov 	error = prepare_batch_buffer(ch, ta, tei, count, OP_DEL, &ta_buf_m);
5963a845e10SAlexander V. Chernikov 	if (error != 0)
5973a845e10SAlexander V. Chernikov 		goto cleanup;
5989f7d47b0SAlexander V. Chernikov 
5993a845e10SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
6003a845e10SAlexander V. Chernikov 
6013a845e10SAlexander V. Chernikov 	/* Drop reference we've used in first search */
6023a845e10SAlexander V. Chernikov 	tc->no.refcnt--;
6033a845e10SAlexander V. Chernikov 
604c8d5d308SAlexander V. Chernikov 	/*
605c8d5d308SAlexander V. Chernikov 	 * Check if table algo is still the same.
606c8d5d308SAlexander V. Chernikov 	 * (changed ta may be the result of table swap).
607c8d5d308SAlexander V. Chernikov 	 */
608c8d5d308SAlexander V. Chernikov 	if (ta != tc->ta) {
609c8d5d308SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
610c8d5d308SAlexander V. Chernikov 		error = EINVAL;
611c8d5d308SAlexander V. Chernikov 		goto cleanup;
612c8d5d308SAlexander V. Chernikov 	}
613c8d5d308SAlexander V. Chernikov 
614b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
6153a845e10SAlexander V. Chernikov 	numdel = 0;
6163a845e10SAlexander V. Chernikov 	first_error = 0;
617b074b7bbSAlexander V. Chernikov 
618b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
6193a845e10SAlexander V. Chernikov 	v = ta_buf_m;
6201bc0d457SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta->ta_buf_size) {
6213a845e10SAlexander V. Chernikov 		ptei = &tei[i];
6223a845e10SAlexander V. Chernikov 		num = 0;
6233a845e10SAlexander V. Chernikov 		error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v,
6243a845e10SAlexander V. Chernikov 		    &num);
6253a845e10SAlexander V. Chernikov 		/* Save state for userland */
6261bc0d457SAlexander V. Chernikov 		store_tei_result(ptei, 0, error, num);
6273a845e10SAlexander V. Chernikov 		if (error != 0 && first_error == 0)
6283a845e10SAlexander V. Chernikov 			first_error = error;
6293a845e10SAlexander V. Chernikov 		tc->count -= num;
6303a845e10SAlexander V. Chernikov 		numdel += num;
6313a845e10SAlexander V. Chernikov 	}
6323b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
6333b3a8eb9SGleb Smirnoff 
6343a845e10SAlexander V. Chernikov 	if (numdel != 0) {
635b6ee846eSAlexander V. Chernikov 		/* Run post-del hook to permit shrinking */
636b6ee846eSAlexander V. Chernikov 		error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
637b6ee846eSAlexander V. Chernikov 	}
638b074b7bbSAlexander V. Chernikov 
6399f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
6403b3a8eb9SGleb Smirnoff 
6413a845e10SAlexander V. Chernikov 	/* Return first error to user, if any */
6423a845e10SAlexander V. Chernikov 	error = first_error;
6433a845e10SAlexander V. Chernikov 
6443a845e10SAlexander V. Chernikov cleanup:
6451bc0d457SAlexander V. Chernikov 	flush_batch_buffer(ch, ta, tei, count, 0, 0, ta_buf_m, ta_buf);
646ac35ff17SAlexander V. Chernikov 
647ac35ff17SAlexander V. Chernikov 	return (error);
648ac35ff17SAlexander V. Chernikov }
649ac35ff17SAlexander V. Chernikov 
650db785d31SAlexander V. Chernikov /*
651b6ee846eSAlexander V. Chernikov  * Ensure that table @tc has enough space to add @count entries without
652b6ee846eSAlexander V. Chernikov  * need for reallocation.
653db785d31SAlexander V. Chernikov  *
654db785d31SAlexander V. Chernikov  * Callbacks order:
655301290bcSAlexander V. Chernikov  * 0) need_modify() (UH_WLOCK) - checks if @count items can be added w/o resize.
656b6ee846eSAlexander V. Chernikov  *
657db785d31SAlexander V. Chernikov  * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags.
658db785d31SAlexander V. Chernikov  * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage
659db785d31SAlexander V. Chernikov  * 3) modify (UH_WLOCK + WLOCK) - switch pointers
660b6ee846eSAlexander V. Chernikov  * 4) flush_modify (UH_WLOCK) - free state, if needed
661b6ee846eSAlexander V. Chernikov  *
662b6ee846eSAlexander V. Chernikov  * Returns 0 on success.
663db785d31SAlexander V. Chernikov  */
664db785d31SAlexander V. Chernikov static int
665b6ee846eSAlexander V. Chernikov check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
666b6ee846eSAlexander V. Chernikov     struct table_info *ti, uint32_t count)
667db785d31SAlexander V. Chernikov {
668b6ee846eSAlexander V. Chernikov 	struct table_algo *ta;
669b6ee846eSAlexander V. Chernikov 	uint64_t pflags;
670b6ee846eSAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
671db785d31SAlexander V. Chernikov 	int error;
672db785d31SAlexander V. Chernikov 
673b6ee846eSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
674db785d31SAlexander V. Chernikov 
675b6ee846eSAlexander V. Chernikov 	error = 0;
676b6ee846eSAlexander V. Chernikov 	ta = tc->ta;
677fd0869d5SAlexander V. Chernikov 	if (ta->need_modify == NULL)
678fd0869d5SAlexander V. Chernikov 		return (0);
679fd0869d5SAlexander V. Chernikov 
680b6ee846eSAlexander V. Chernikov 	/* Acquire reference not to loose @tc between locks/unlocks */
681b6ee846eSAlexander V. Chernikov 	tc->no.refcnt++;
682db785d31SAlexander V. Chernikov 
683db785d31SAlexander V. Chernikov 	/*
684b6ee846eSAlexander V. Chernikov 	 * TODO: think about avoiding race between large add/large delete
685b6ee846eSAlexander V. Chernikov 	 * operation on algorithm which implements shrinking along with
686b6ee846eSAlexander V. Chernikov 	 * growing.
687db785d31SAlexander V. Chernikov 	 */
688b6ee846eSAlexander V. Chernikov 	while (true) {
689b6ee846eSAlexander V. Chernikov 		pflags = 0;
690301290bcSAlexander V. Chernikov 		if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) {
69135e1bbd0SAlexander V. Chernikov 			error = 0;
69235e1bbd0SAlexander V. Chernikov 			break;
693b6ee846eSAlexander V. Chernikov 		}
694db785d31SAlexander V. Chernikov 
695b6ee846eSAlexander V. Chernikov 		/* We have to shrink/grow table */
696b6ee846eSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
697b6ee846eSAlexander V. Chernikov 
698301290bcSAlexander V. Chernikov 		memset(&ta_buf, 0, sizeof(ta_buf));
699b6ee846eSAlexander V. Chernikov 		if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) {
700b6ee846eSAlexander V. Chernikov 			IPFW_UH_WLOCK(ch);
701b6ee846eSAlexander V. Chernikov 			break;
702b6ee846eSAlexander V. Chernikov 		}
703b6ee846eSAlexander V. Chernikov 
704b6ee846eSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
705b6ee846eSAlexander V. Chernikov 
706b6ee846eSAlexander V. Chernikov 		/* Check if we still need to alter table */
707b6ee846eSAlexander V. Chernikov 		ti = KIDX_TO_TI(ch, tc->no.kidx);
708301290bcSAlexander V. Chernikov 		if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) {
709301290bcSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
710b6ee846eSAlexander V. Chernikov 
711b6ee846eSAlexander V. Chernikov 			/*
71235e1bbd0SAlexander V. Chernikov 			 * Other thread has already performed resize.
71335e1bbd0SAlexander V. Chernikov 			 * Flush our state and return.
714b6ee846eSAlexander V. Chernikov 			 */
715b6ee846eSAlexander V. Chernikov 			ta->flush_mod(ta_buf);
716b6ee846eSAlexander V. Chernikov 			break;
717b6ee846eSAlexander V. Chernikov 		}
718b6ee846eSAlexander V. Chernikov 
719b6ee846eSAlexander V. Chernikov 		error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags);
720b6ee846eSAlexander V. Chernikov 		if (error == 0) {
721db785d31SAlexander V. Chernikov 			/* Do actual modification */
722db785d31SAlexander V. Chernikov 			IPFW_WLOCK(ch);
72368394ec8SAlexander V. Chernikov 			ta->modify(tc->astate, ti, ta_buf, pflags);
724db785d31SAlexander V. Chernikov 			IPFW_WUNLOCK(ch);
725db785d31SAlexander V. Chernikov 		}
726db785d31SAlexander V. Chernikov 
727b6ee846eSAlexander V. Chernikov 		/* Anyway, flush data and retry */
728db785d31SAlexander V. Chernikov 		ta->flush_mod(ta_buf);
729b6ee846eSAlexander V. Chernikov 	}
730db785d31SAlexander V. Chernikov 
731b6ee846eSAlexander V. Chernikov 	tc->no.refcnt--;
732db785d31SAlexander V. Chernikov 	return (error);
733db785d31SAlexander V. Chernikov }
734db785d31SAlexander V. Chernikov 
7353a845e10SAlexander V. Chernikov /*
7363a845e10SAlexander V. Chernikov  * Selects appropriate table operation handler
7373a845e10SAlexander V. Chernikov  * depending on opcode version.
7383a845e10SAlexander V. Chernikov  */
739ac35ff17SAlexander V. Chernikov int
740db785d31SAlexander V. Chernikov ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
741ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
742ac35ff17SAlexander V. Chernikov {
743ac35ff17SAlexander V. Chernikov 	int error;
744ac35ff17SAlexander V. Chernikov 
745ac35ff17SAlexander V. Chernikov 	switch (op3->version) {
746ac35ff17SAlexander V. Chernikov 	case 0:
747db785d31SAlexander V. Chernikov 		error = ipfw_manage_table_ent_v0(ch, op3, sd);
748ac35ff17SAlexander V. Chernikov 		break;
749ac35ff17SAlexander V. Chernikov 	case 1:
750db785d31SAlexander V. Chernikov 		error = ipfw_manage_table_ent_v1(ch, op3, sd);
751ac35ff17SAlexander V. Chernikov 		break;
752ac35ff17SAlexander V. Chernikov 	default:
753ac35ff17SAlexander V. Chernikov 		error = ENOTSUP;
754ac35ff17SAlexander V. Chernikov 	}
755ac35ff17SAlexander V. Chernikov 
756ac35ff17SAlexander V. Chernikov 	return (error);
757ac35ff17SAlexander V. Chernikov }
758ac35ff17SAlexander V. Chernikov 
759ac35ff17SAlexander V. Chernikov /*
760ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
761ac35ff17SAlexander V. Chernikov  * Data layout (v0):
762ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ipfw_table_xentry ]
763ac35ff17SAlexander V. Chernikov  *
764ac35ff17SAlexander V. Chernikov  * Returns 0 on success
765ac35ff17SAlexander V. Chernikov  */
766ac35ff17SAlexander V. Chernikov static int
767db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
768ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
769ac35ff17SAlexander V. Chernikov {
770ac35ff17SAlexander V. Chernikov 	ipfw_table_xentry *xent;
771ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
772ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
773ac35ff17SAlexander V. Chernikov 	int error, hdrlen, read;
774ac35ff17SAlexander V. Chernikov 
775ac35ff17SAlexander V. Chernikov 	hdrlen = offsetof(ipfw_table_xentry, k);
776ac35ff17SAlexander V. Chernikov 
777ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
778ac35ff17SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*op3) + hdrlen))
779ac35ff17SAlexander V. Chernikov 		return (EINVAL);
780ac35ff17SAlexander V. Chernikov 
781ac35ff17SAlexander V. Chernikov 	read = sizeof(ip_fw3_opheader);
782ac35ff17SAlexander V. Chernikov 
783ac35ff17SAlexander V. Chernikov 	/* Check if xentry len field is valid */
784ac35ff17SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)(op3 + 1);
785ac35ff17SAlexander V. Chernikov 	if (xent->len < hdrlen || xent->len + read > sd->valsize)
786ac35ff17SAlexander V. Chernikov 		return (EINVAL);
787ac35ff17SAlexander V. Chernikov 
788ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
789ac35ff17SAlexander V. Chernikov 	tei.paddr = &xent->k;
790ac35ff17SAlexander V. Chernikov 	tei.masklen = xent->masklen;
791ac35ff17SAlexander V. Chernikov 	tei.value = xent->value;
792ac35ff17SAlexander V. Chernikov 	/* Old requests compability */
793db785d31SAlexander V. Chernikov 	tei.flags = TEI_FLAGS_COMPAT;
794c21034b7SAlexander V. Chernikov 	if (xent->type == IPFW_TABLE_ADDR) {
795ac35ff17SAlexander V. Chernikov 		if (xent->len - hdrlen == sizeof(in_addr_t))
796ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET;
797ac35ff17SAlexander V. Chernikov 		else
798ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET6;
799ac35ff17SAlexander V. Chernikov 	}
800ac35ff17SAlexander V. Chernikov 
801ac35ff17SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
802ac35ff17SAlexander V. Chernikov 	ti.uidx = xent->tbl;
803ac35ff17SAlexander V. Chernikov 	ti.type = xent->type;
804ac35ff17SAlexander V. Chernikov 
805ac35ff17SAlexander V. Chernikov 	error = (op3->opcode == IP_FW_TABLE_XADD) ?
8063a845e10SAlexander V. Chernikov 	    add_table_entry(ch, &ti, &tei, 0, 1) :
8073a845e10SAlexander V. Chernikov 	    del_table_entry(ch, &ti, &tei, 0, 1);
808ac35ff17SAlexander V. Chernikov 
809ac35ff17SAlexander V. Chernikov 	return (error);
810ac35ff17SAlexander V. Chernikov }
811ac35ff17SAlexander V. Chernikov 
812ac35ff17SAlexander V. Chernikov /*
813ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
814ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
815db785d31SAlexander V. Chernikov  * Request: [ ipfw_obj_header
816db785d31SAlexander V. Chernikov  *   ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ]
817db785d31SAlexander V. Chernikov  * ]
818ac35ff17SAlexander V. Chernikov  *
819ac35ff17SAlexander V. Chernikov  * Returns 0 on success
820ac35ff17SAlexander V. Chernikov  */
821ac35ff17SAlexander V. Chernikov static int
822db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
823ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
824ac35ff17SAlexander V. Chernikov {
8253a845e10SAlexander V. Chernikov 	ipfw_obj_tentry *tent, *ptent;
826db785d31SAlexander V. Chernikov 	ipfw_obj_ctlv *ctlv;
827ac35ff17SAlexander V. Chernikov 	ipfw_obj_header *oh;
8283a845e10SAlexander V. Chernikov 	struct tentry_info *ptei, tei, *tei_buf;
829ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
8303a845e10SAlexander V. Chernikov 	int error, i, kidx, read;
831ac35ff17SAlexander V. Chernikov 
832ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
833db785d31SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv)))
834ac35ff17SAlexander V. Chernikov 		return (EINVAL);
835ac35ff17SAlexander V. Chernikov 
836ac35ff17SAlexander V. Chernikov 	/* Check if passed data is too long */
837ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sd->kavail)
838ac35ff17SAlexander V. Chernikov 		return (EINVAL);
839ac35ff17SAlexander V. Chernikov 
840ac35ff17SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
841ac35ff17SAlexander V. Chernikov 
842ac35ff17SAlexander V. Chernikov 	/* Basic length checks for TLVs */
843ac35ff17SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
844ac35ff17SAlexander V. Chernikov 		return (EINVAL);
845ac35ff17SAlexander V. Chernikov 
846ac35ff17SAlexander V. Chernikov 	read = sizeof(*oh);
847ac35ff17SAlexander V. Chernikov 
848db785d31SAlexander V. Chernikov 	ctlv = (ipfw_obj_ctlv *)(oh + 1);
849db785d31SAlexander V. Chernikov 	if (ctlv->head.length + read != sd->valsize)
850db785d31SAlexander V. Chernikov 		return (EINVAL);
851db785d31SAlexander V. Chernikov 
852db785d31SAlexander V. Chernikov 	read += sizeof(*ctlv);
853db785d31SAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(ctlv + 1);
8543a845e10SAlexander V. Chernikov 	if (ctlv->count * sizeof(*tent) + read != sd->valsize)
855ac35ff17SAlexander V. Chernikov 		return (EINVAL);
856ac35ff17SAlexander V. Chernikov 
8573a845e10SAlexander V. Chernikov 	if (ctlv->count == 0)
8583a845e10SAlexander V. Chernikov 		return (0);
859ac35ff17SAlexander V. Chernikov 
8603a845e10SAlexander V. Chernikov 	/*
8613a845e10SAlexander V. Chernikov 	 * Mark entire buffer as "read".
86235e1bbd0SAlexander V. Chernikov 	 * This instructs sopt api write it back
8633a845e10SAlexander V. Chernikov 	 * after function return.
8643a845e10SAlexander V. Chernikov 	 */
8653a845e10SAlexander V. Chernikov 	ipfw_get_sopt_header(sd, sd->valsize);
8663a845e10SAlexander V. Chernikov 
8673a845e10SAlexander V. Chernikov 	/* Perform basic checks for each entry */
8683a845e10SAlexander V. Chernikov 	ptent = tent;
8693a845e10SAlexander V. Chernikov 	kidx = tent->idx;
8703a845e10SAlexander V. Chernikov 	for (i = 0; i < ctlv->count; i++, ptent++) {
8713a845e10SAlexander V. Chernikov 		if (ptent->head.length != sizeof(*ptent))
8723a845e10SAlexander V. Chernikov 			return (EINVAL);
8733a845e10SAlexander V. Chernikov 		if (ptent->idx != kidx)
8743a845e10SAlexander V. Chernikov 			return (ENOTSUP);
8753a845e10SAlexander V. Chernikov 	}
8763a845e10SAlexander V. Chernikov 
8773a845e10SAlexander V. Chernikov 	/* Convert data into kernel request objects */
87881d3153dSAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
879ac35ff17SAlexander V. Chernikov 	ti.type = oh->ntlv.type;
8803a845e10SAlexander V. Chernikov 	ti.uidx = kidx;
8813a845e10SAlexander V. Chernikov 
8823a845e10SAlexander V. Chernikov 	/* Use on-stack buffer for single add/del */
8833a845e10SAlexander V. Chernikov 	if (ctlv->count == 1) {
8843a845e10SAlexander V. Chernikov 		memset(&tei, 0, sizeof(tei));
8853a845e10SAlexander V. Chernikov 		tei_buf = &tei;
8863a845e10SAlexander V. Chernikov 	} else
8873a845e10SAlexander V. Chernikov 		tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP,
8883a845e10SAlexander V. Chernikov 		    M_WAITOK | M_ZERO);
8893a845e10SAlexander V. Chernikov 
8903a845e10SAlexander V. Chernikov 	ptei = tei_buf;
8913a845e10SAlexander V. Chernikov 	ptent = tent;
8923a845e10SAlexander V. Chernikov 	for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
8933a845e10SAlexander V. Chernikov 		ptei->paddr = &ptent->k;
8943a845e10SAlexander V. Chernikov 		ptei->subtype = ptent->subtype;
8953a845e10SAlexander V. Chernikov 		ptei->masklen = ptent->masklen;
8963a845e10SAlexander V. Chernikov 		if (ptent->head.flags & IPFW_TF_UPDATE)
8973a845e10SAlexander V. Chernikov 			ptei->flags |= TEI_FLAGS_UPDATE;
8984bbd1577SAlexander V. Chernikov 		ptei->value = ptent->v.value;
8993a845e10SAlexander V. Chernikov 	}
900ac35ff17SAlexander V. Chernikov 
901ac35ff17SAlexander V. Chernikov 	error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
9023a845e10SAlexander V. Chernikov 	    add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) :
9033a845e10SAlexander V. Chernikov 	    del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count);
9043a845e10SAlexander V. Chernikov 
9053a845e10SAlexander V. Chernikov 	/* Translate result back to userland */
9063a845e10SAlexander V. Chernikov 	ptei = tei_buf;
9073a845e10SAlexander V. Chernikov 	ptent = tent;
9083a845e10SAlexander V. Chernikov 	for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
9093a845e10SAlexander V. Chernikov 		if (ptei->flags & TEI_FLAGS_ADDED)
9103a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_ADDED;
9113a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_DELETED)
9123a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_DELETED;
9133a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_UPDATED)
9143a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_UPDATED;
9153a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_LIMIT)
9163a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_LIMIT;
9173a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_ERROR)
9183a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_ERROR;
9193a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_NOTFOUND)
9203a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_NOTFOUND;
9213a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_EXISTS)
9223a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_EXISTS;
9233a845e10SAlexander V. Chernikov 	}
9243a845e10SAlexander V. Chernikov 
9253a845e10SAlexander V. Chernikov 	if (tei_buf != &tei)
9263a845e10SAlexander V. Chernikov 		free(tei_buf, M_TEMP);
927ac35ff17SAlexander V. Chernikov 
928ac35ff17SAlexander V. Chernikov 	return (error);
929ac35ff17SAlexander V. Chernikov }
930ac35ff17SAlexander V. Chernikov 
93181d3153dSAlexander V. Chernikov /*
93281d3153dSAlexander V. Chernikov  * Looks up an entry in given table.
93381d3153dSAlexander V. Chernikov  * Data layout (v0)(current):
93481d3153dSAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_tentry ]
93581d3153dSAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_obj_tentry ]
93681d3153dSAlexander V. Chernikov  *
93781d3153dSAlexander V. Chernikov  * Returns 0 on success
93881d3153dSAlexander V. Chernikov  */
93981d3153dSAlexander V. Chernikov int
94081d3153dSAlexander V. Chernikov ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
94181d3153dSAlexander V. Chernikov     struct sockopt_data *sd)
94281d3153dSAlexander V. Chernikov {
94381d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
94481d3153dSAlexander V. Chernikov 	ipfw_obj_header *oh;
94581d3153dSAlexander V. Chernikov 	struct tid_info ti;
94681d3153dSAlexander V. Chernikov 	struct table_config *tc;
94781d3153dSAlexander V. Chernikov 	struct table_algo *ta;
94881d3153dSAlexander V. Chernikov 	struct table_info *kti;
94981d3153dSAlexander V. Chernikov 	struct namedobj_instance *ni;
950914bffb6SAlexander V. Chernikov 	int error;
95181d3153dSAlexander V. Chernikov 	size_t sz;
95281d3153dSAlexander V. Chernikov 
95381d3153dSAlexander V. Chernikov 	/* Check minimum header size */
95481d3153dSAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*tent);
95581d3153dSAlexander V. Chernikov 	if (sd->valsize != sz)
95681d3153dSAlexander V. Chernikov 		return (EINVAL);
95781d3153dSAlexander V. Chernikov 
95881d3153dSAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
95981d3153dSAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(oh + 1);
96081d3153dSAlexander V. Chernikov 
96181d3153dSAlexander V. Chernikov 	/* Basic length checks for TLVs */
96281d3153dSAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
96381d3153dSAlexander V. Chernikov 		return (EINVAL);
96481d3153dSAlexander V. Chernikov 
96581d3153dSAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
96681d3153dSAlexander V. Chernikov 	ti.type = oh->ntlv.type;
96781d3153dSAlexander V. Chernikov 	ti.uidx = tent->idx;
96881d3153dSAlexander V. Chernikov 
96981d3153dSAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
97081d3153dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
97181d3153dSAlexander V. Chernikov 
97281d3153dSAlexander V. Chernikov 	/*
97381d3153dSAlexander V. Chernikov 	 * Find existing table and check its type .
97481d3153dSAlexander V. Chernikov 	 */
97581d3153dSAlexander V. Chernikov 	ta = NULL;
97681d3153dSAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) == NULL) {
97781d3153dSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
97881d3153dSAlexander V. Chernikov 		return (ESRCH);
97981d3153dSAlexander V. Chernikov 	}
98081d3153dSAlexander V. Chernikov 
98181d3153dSAlexander V. Chernikov 	/* check table type */
98281d3153dSAlexander V. Chernikov 	if (tc->no.type != ti.type) {
98381d3153dSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
98481d3153dSAlexander V. Chernikov 		return (EINVAL);
98581d3153dSAlexander V. Chernikov 	}
98681d3153dSAlexander V. Chernikov 
98781d3153dSAlexander V. Chernikov 	kti = KIDX_TO_TI(ch, tc->no.kidx);
98881d3153dSAlexander V. Chernikov 	ta = tc->ta;
98981d3153dSAlexander V. Chernikov 
990914bffb6SAlexander V. Chernikov 	if (ta->find_tentry == NULL)
991914bffb6SAlexander V. Chernikov 		return (ENOTSUP);
992914bffb6SAlexander V. Chernikov 
993914bffb6SAlexander V. Chernikov 	error = ta->find_tentry(tc->astate, kti, tent);
99481d3153dSAlexander V. Chernikov 
99581d3153dSAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
99681d3153dSAlexander V. Chernikov 
99781d3153dSAlexander V. Chernikov 	return (error);
99881d3153dSAlexander V. Chernikov }
99981d3153dSAlexander V. Chernikov 
100046d52008SAlexander V. Chernikov /*
100146d52008SAlexander V. Chernikov  * Flushes all entries or destroys given table.
100246d52008SAlexander V. Chernikov  * Data layout (v0)(current):
100346d52008SAlexander V. Chernikov  * Request: [ ipfw_obj_header ]
100446d52008SAlexander V. Chernikov  *
100546d52008SAlexander V. Chernikov  * Returns 0 on success
100646d52008SAlexander V. Chernikov  */
1007ac35ff17SAlexander V. Chernikov int
1008ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1009ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
1010ac35ff17SAlexander V. Chernikov {
1011ac35ff17SAlexander V. Chernikov 	int error;
1012ac35ff17SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1013ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
1014ac35ff17SAlexander V. Chernikov 
1015ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh))
1016ac35ff17SAlexander V. Chernikov 		return (EINVAL);
1017ac35ff17SAlexander V. Chernikov 
1018ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
1019ac35ff17SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1020ac35ff17SAlexander V. Chernikov 
10211832a7b3SAlexander V. Chernikov 	if (op3->opcode == IP_FW_TABLE_XDESTROY)
1022ac35ff17SAlexander V. Chernikov 		error = destroy_table(ch, &ti);
10231832a7b3SAlexander V. Chernikov 	else if (op3->opcode == IP_FW_TABLE_XFLUSH)
1024ac35ff17SAlexander V. Chernikov 		error = flush_table(ch, &ti);
1025ac35ff17SAlexander V. Chernikov 	else
1026ac35ff17SAlexander V. Chernikov 		return (ENOTSUP);
1027ac35ff17SAlexander V. Chernikov 
1028ac35ff17SAlexander V. Chernikov 	return (error);
10293b3a8eb9SGleb Smirnoff }
10303b3a8eb9SGleb Smirnoff 
103135e1bbd0SAlexander V. Chernikov /*
103235e1bbd0SAlexander V. Chernikov  * Flushes given table.
103335e1bbd0SAlexander V. Chernikov  *
103435e1bbd0SAlexander V. Chernikov  * Function create new table instance with the same
103535e1bbd0SAlexander V. Chernikov  * parameters, swaps it with old one and
103635e1bbd0SAlexander V. Chernikov  * flushes state without holding any locks.
103735e1bbd0SAlexander V. Chernikov  *
103835e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
103935e1bbd0SAlexander V. Chernikov  */
10401832a7b3SAlexander V. Chernikov int
1041ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
10423b3a8eb9SGleb Smirnoff {
1043b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1044b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
10459f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
10469f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
10479f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
1048914bffb6SAlexander V. Chernikov 	char algostate[64], *pstate;
1049b074b7bbSAlexander V. Chernikov 	int error;
1050b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1051914bffb6SAlexander V. Chernikov 	uint8_t tflags;
10523b3a8eb9SGleb Smirnoff 
10533b3a8eb9SGleb Smirnoff 	/*
10549f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
1055b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
10563b3a8eb9SGleb Smirnoff 	 */
1057b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1058b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1059b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
1060b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1061b074b7bbSAlexander V. Chernikov 		return (ESRCH);
1062b074b7bbSAlexander V. Chernikov 	}
10639f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
1064fd0869d5SAlexander V. Chernikov 	/* Do not flush readonly tables */
1065fd0869d5SAlexander V. Chernikov 	if ((ta->flags & TA_FLAG_READONLY) != 0) {
1066fd0869d5SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1067fd0869d5SAlexander V. Chernikov 		return (EACCES);
1068fd0869d5SAlexander V. Chernikov 	}
1069b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
107035e1bbd0SAlexander V. Chernikov 	/* Save startup algo parameters */
1071daabb523SAlexander V. Chernikov 	if (ta->print_config != NULL) {
1072daabb523SAlexander V. Chernikov 		ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx),
1073daabb523SAlexander V. Chernikov 		    algostate, sizeof(algostate));
1074daabb523SAlexander V. Chernikov 		pstate = algostate;
1075daabb523SAlexander V. Chernikov 	} else
1076daabb523SAlexander V. Chernikov 		pstate = NULL;
1077914bffb6SAlexander V. Chernikov 	tflags = tc->tflags;
1078b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
10793b3a8eb9SGleb Smirnoff 
1080b074b7bbSAlexander V. Chernikov 	/*
10819f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
1082b074b7bbSAlexander V. Chernikov 	 */
10839f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
1084914bffb6SAlexander V. Chernikov 	if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) {
1085b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
1086b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
1087b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1088b074b7bbSAlexander V. Chernikov 		return (error);
1089b074b7bbSAlexander V. Chernikov 	}
1090b074b7bbSAlexander V. Chernikov 
1091b074b7bbSAlexander V. Chernikov 	/*
1092b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
1093b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
1094b074b7bbSAlexander V. Chernikov 	 */
1095b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1096b074b7bbSAlexander V. Chernikov 
1097b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1098b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
10999f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
1100b074b7bbSAlexander V. Chernikov 
11019f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
11029f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
11039f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
11049f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
1105b074b7bbSAlexander V. Chernikov 
11069f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
11079f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
11089f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
11099f7d47b0SAlexander V. Chernikov 	tc->count = 0;
1110b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
1111b074b7bbSAlexander V. Chernikov 
1112b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
11133b3a8eb9SGleb Smirnoff 
1114b074b7bbSAlexander V. Chernikov 	/*
1115b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
1116b074b7bbSAlexander V. Chernikov 	 */
11179f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
11183b3a8eb9SGleb Smirnoff 
11193b3a8eb9SGleb Smirnoff 	return (0);
11203b3a8eb9SGleb Smirnoff }
11213b3a8eb9SGleb Smirnoff 
1122b074b7bbSAlexander V. Chernikov /*
112346d52008SAlexander V. Chernikov  * Swaps two tables.
112446d52008SAlexander V. Chernikov  * Data layout (v0)(current):
112546d52008SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_ntlv ]
112646d52008SAlexander V. Chernikov  *
112746d52008SAlexander V. Chernikov  * Returns 0 on success
112846d52008SAlexander V. Chernikov  */
112946d52008SAlexander V. Chernikov int
113046d52008SAlexander V. Chernikov ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
113146d52008SAlexander V. Chernikov     struct sockopt_data *sd)
113246d52008SAlexander V. Chernikov {
113346d52008SAlexander V. Chernikov 	int error;
113446d52008SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
113546d52008SAlexander V. Chernikov 	struct tid_info ti_a, ti_b;
113646d52008SAlexander V. Chernikov 
113746d52008SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv))
113846d52008SAlexander V. Chernikov 		return (EINVAL);
113946d52008SAlexander V. Chernikov 
114046d52008SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
114146d52008SAlexander V. Chernikov 	ntlv_to_ti(&oh->ntlv, &ti_a);
114246d52008SAlexander V. Chernikov 	ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b);
114346d52008SAlexander V. Chernikov 
1144a73d728dSAlexander V. Chernikov 	error = swap_tables(ch, &ti_a, &ti_b);
114546d52008SAlexander V. Chernikov 
114646d52008SAlexander V. Chernikov 	return (error);
114746d52008SAlexander V. Chernikov }
114846d52008SAlexander V. Chernikov 
114935e1bbd0SAlexander V. Chernikov /*
115035e1bbd0SAlexander V. Chernikov  * Swaps two tables of the same type/valtype.
115135e1bbd0SAlexander V. Chernikov  *
115235e1bbd0SAlexander V. Chernikov  * Checks if tables are compatible and limits
1153c8d5d308SAlexander V. Chernikov  * permits swap, than actually perform swap.
115435e1bbd0SAlexander V. Chernikov  *
1155c8d5d308SAlexander V. Chernikov  * Each table consists of 2 different parts:
1156c8d5d308SAlexander V. Chernikov  * config:
1157c8d5d308SAlexander V. Chernikov  *   @tc (with name, set, kidx) and rule bindings, which is "stable".
1158c8d5d308SAlexander V. Chernikov  *   number of items
1159c8d5d308SAlexander V. Chernikov  *   table algo
1160c8d5d308SAlexander V. Chernikov  * runtime:
1161c8d5d308SAlexander V. Chernikov  *   runtime data @ti (ch->tablestate)
1162c8d5d308SAlexander V. Chernikov  *   runtime cache in @tc
1163c8d5d308SAlexander V. Chernikov  *   algo-specific data (@tc->astate)
1164c8d5d308SAlexander V. Chernikov  *
1165c8d5d308SAlexander V. Chernikov  * So we switch:
1166c8d5d308SAlexander V. Chernikov  *  all runtime data
1167c8d5d308SAlexander V. Chernikov  *   number of items
1168c8d5d308SAlexander V. Chernikov  *   table algo
1169c8d5d308SAlexander V. Chernikov  *
1170c8d5d308SAlexander V. Chernikov  * After that we call @ti change handler for each table.
1171c8d5d308SAlexander V. Chernikov  *
1172c8d5d308SAlexander V. Chernikov  * Note that referencing @tc won't protect tc->ta from change.
1173c8d5d308SAlexander V. Chernikov  * XXX: Do we need to restrict swap between locked tables?
1174c8d5d308SAlexander V. Chernikov  * XXX: Do we need to exchange ftype?
117535e1bbd0SAlexander V. Chernikov  *
117635e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
117735e1bbd0SAlexander V. Chernikov  */
117846d52008SAlexander V. Chernikov static int
1179a73d728dSAlexander V. Chernikov swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
118046d52008SAlexander V. Chernikov     struct tid_info *b)
118146d52008SAlexander V. Chernikov {
118246d52008SAlexander V. Chernikov 	struct namedobj_instance *ni;
118346d52008SAlexander V. Chernikov 	struct table_config *tc_a, *tc_b;
118446d52008SAlexander V. Chernikov 	struct table_algo *ta;
118546d52008SAlexander V. Chernikov 	struct table_info ti, *tablestate;
118646d52008SAlexander V. Chernikov 	void *astate;
118746d52008SAlexander V. Chernikov 	uint32_t count;
118846d52008SAlexander V. Chernikov 
118946d52008SAlexander V. Chernikov 	/*
119046d52008SAlexander V. Chernikov 	 * Stage 1: find both tables and ensure they are of
119135e1bbd0SAlexander V. Chernikov 	 * the same type.
119246d52008SAlexander V. Chernikov 	 */
119346d52008SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
119446d52008SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
119546d52008SAlexander V. Chernikov 	if ((tc_a = find_table(ni, a)) == NULL) {
119646d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
119746d52008SAlexander V. Chernikov 		return (ESRCH);
119846d52008SAlexander V. Chernikov 	}
119946d52008SAlexander V. Chernikov 	if ((tc_b = find_table(ni, b)) == NULL) {
120046d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
120146d52008SAlexander V. Chernikov 		return (ESRCH);
120246d52008SAlexander V. Chernikov 	}
120346d52008SAlexander V. Chernikov 
120446d52008SAlexander V. Chernikov 	/* It is very easy to swap between the same table */
120546d52008SAlexander V. Chernikov 	if (tc_a == tc_b) {
120646d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
120746d52008SAlexander V. Chernikov 		return (0);
120846d52008SAlexander V. Chernikov 	}
120946d52008SAlexander V. Chernikov 
121046d52008SAlexander V. Chernikov 	/* Check type and value are the same */
121146d52008SAlexander V. Chernikov 	if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags ||
121246d52008SAlexander V. Chernikov 	    tc_a->vtype != tc_b->vtype) {
121346d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
121446d52008SAlexander V. Chernikov 		return (EINVAL);
121546d52008SAlexander V. Chernikov 	}
121646d52008SAlexander V. Chernikov 
121746d52008SAlexander V. Chernikov 	/* Check limits before swap */
121846d52008SAlexander V. Chernikov 	if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) ||
121946d52008SAlexander V. Chernikov 	    (tc_b->limit != 0 && tc_a->count > tc_b->limit)) {
122046d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
122146d52008SAlexander V. Chernikov 		return (EFBIG);
122246d52008SAlexander V. Chernikov 	}
122346d52008SAlexander V. Chernikov 
1224fd0869d5SAlexander V. Chernikov 	/* Check if one of the tables is readonly */
1225fd0869d5SAlexander V. Chernikov 	if (((tc_a->ta->flags | tc_b->ta->flags) & TA_FLAG_READONLY) != 0) {
1226fd0869d5SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1227fd0869d5SAlexander V. Chernikov 		return (EACCES);
1228fd0869d5SAlexander V. Chernikov 	}
1229fd0869d5SAlexander V. Chernikov 
123046d52008SAlexander V. Chernikov 	/* Everything is fine, prepare to swap */
123146d52008SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
123246d52008SAlexander V. Chernikov 	ti = tablestate[tc_a->no.kidx];
123346d52008SAlexander V. Chernikov 	ta = tc_a->ta;
123446d52008SAlexander V. Chernikov 	astate = tc_a->astate;
123546d52008SAlexander V. Chernikov 	count = tc_a->count;
123646d52008SAlexander V. Chernikov 
123746d52008SAlexander V. Chernikov 	IPFW_WLOCK(ch);
123846d52008SAlexander V. Chernikov 	/* a <- b */
123946d52008SAlexander V. Chernikov 	tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx];
124046d52008SAlexander V. Chernikov 	tc_a->ta = tc_b->ta;
124146d52008SAlexander V. Chernikov 	tc_a->astate = tc_b->astate;
124246d52008SAlexander V. Chernikov 	tc_a->count = tc_b->count;
124346d52008SAlexander V. Chernikov 	/* b <- a */
124446d52008SAlexander V. Chernikov 	tablestate[tc_b->no.kidx] = ti;
124546d52008SAlexander V. Chernikov 	tc_b->ta = ta;
124646d52008SAlexander V. Chernikov 	tc_b->astate = astate;
124746d52008SAlexander V. Chernikov 	tc_b->count = count;
124846d52008SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
124946d52008SAlexander V. Chernikov 
125046d52008SAlexander V. Chernikov 	/* Ensure tc.ti copies are in sync */
125146d52008SAlexander V. Chernikov 	tc_a->ti = tablestate[tc_a->no.kidx];
125246d52008SAlexander V. Chernikov 	tc_b->ti = tablestate[tc_b->no.kidx];
125346d52008SAlexander V. Chernikov 
125446d52008SAlexander V. Chernikov 	/* Notify both tables on @ti change */
125546d52008SAlexander V. Chernikov 	if (tc_a->ta->change_ti != NULL)
125646d52008SAlexander V. Chernikov 		tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]);
125746d52008SAlexander V. Chernikov 	if (tc_b->ta->change_ti != NULL)
125846d52008SAlexander V. Chernikov 		tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]);
125946d52008SAlexander V. Chernikov 
126046d52008SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
126146d52008SAlexander V. Chernikov 
126246d52008SAlexander V. Chernikov 	return (0);
126346d52008SAlexander V. Chernikov }
126446d52008SAlexander V. Chernikov 
126546d52008SAlexander V. Chernikov /*
12669f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
1267ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1268ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ]
1269ac35ff17SAlexander V. Chernikov  *
1270ac35ff17SAlexander V. Chernikov  * Returns 0 on success
1271b074b7bbSAlexander V. Chernikov  */
1272ac35ff17SAlexander V. Chernikov static int
1273ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
1274b074b7bbSAlexander V. Chernikov {
1275b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1276b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1277b074b7bbSAlexander V. Chernikov 
1278b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1279b074b7bbSAlexander V. Chernikov 
1280b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1281b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
1282b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1283b074b7bbSAlexander V. Chernikov 		return (ESRCH);
1284b074b7bbSAlexander V. Chernikov 	}
1285b074b7bbSAlexander V. Chernikov 
12869f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
12879f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
1288b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1289b074b7bbSAlexander V. Chernikov 		return (EBUSY);
1290b074b7bbSAlexander V. Chernikov 	}
1291b074b7bbSAlexander V. Chernikov 
1292b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
1293b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
1294b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
1295b074b7bbSAlexander V. Chernikov 
1296b074b7bbSAlexander V. Chernikov 	/* Free obj index */
1297ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0)
1298b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
1299b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
1300b074b7bbSAlexander V. Chernikov 
1301b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1302b074b7bbSAlexander V. Chernikov 
1303b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
1304b074b7bbSAlexander V. Chernikov 
1305b074b7bbSAlexander V. Chernikov 	return (0);
1306b074b7bbSAlexander V. Chernikov }
1307b074b7bbSAlexander V. Chernikov 
1308b074b7bbSAlexander V. Chernikov static void
1309b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
1310b074b7bbSAlexander V. Chernikov     void *arg)
1311b074b7bbSAlexander V. Chernikov {
1312b074b7bbSAlexander V. Chernikov 
1313b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
1314ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->kidx) != 0)
1315b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
1316b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
1317b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
1318b074b7bbSAlexander V. Chernikov }
1319b074b7bbSAlexander V. Chernikov 
132035e1bbd0SAlexander V. Chernikov /*
132135e1bbd0SAlexander V. Chernikov  * Shuts tables module down.
132235e1bbd0SAlexander V. Chernikov  */
13233b3a8eb9SGleb Smirnoff void
13243b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
13253b3a8eb9SGleb Smirnoff {
13263b3a8eb9SGleb Smirnoff 
1327b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
1328b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1329b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
1330b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
1331b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
1332b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
13333b3a8eb9SGleb Smirnoff 
13343b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
13359f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
13369f7d47b0SAlexander V. Chernikov 
13379f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
1338b074b7bbSAlexander V. Chernikov 
1339b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
1340b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
13413b3a8eb9SGleb Smirnoff }
13423b3a8eb9SGleb Smirnoff 
134335e1bbd0SAlexander V. Chernikov /*
134435e1bbd0SAlexander V. Chernikov  * Starts tables module.
134535e1bbd0SAlexander V. Chernikov  */
13463b3a8eb9SGleb Smirnoff int
13473b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
13483b3a8eb9SGleb Smirnoff {
1349b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
1350b074b7bbSAlexander V. Chernikov 
13513b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
13529f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
13539f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
1354b074b7bbSAlexander V. Chernikov 
1355b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
1356b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
1357b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
1358b074b7bbSAlexander V. Chernikov 
13599f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
13609f7d47b0SAlexander V. Chernikov 
13613b3a8eb9SGleb Smirnoff 	return (0);
13623b3a8eb9SGleb Smirnoff }
13633b3a8eb9SGleb Smirnoff 
136435e1bbd0SAlexander V. Chernikov /*
136535e1bbd0SAlexander V. Chernikov  * Grow tables index.
136635e1bbd0SAlexander V. Chernikov  *
136735e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
136835e1bbd0SAlexander V. Chernikov  */
13693b3a8eb9SGleb Smirnoff int
13703b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
13713b3a8eb9SGleb Smirnoff {
13723b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
1373b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
13749f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
137568394ec8SAlexander V. Chernikov 	struct table_info *ti;
137668394ec8SAlexander V. Chernikov 	struct table_config *tc;
137768394ec8SAlexander V. Chernikov 	int i, new_blocks;
13783b3a8eb9SGleb Smirnoff 
13793b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
13803b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
13813b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
13823b3a8eb9SGleb Smirnoff 
13833b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
13849f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
13859f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
13869f7d47b0SAlexander V. Chernikov 
1387b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
13883b3a8eb9SGleb Smirnoff 
13899f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
13903b3a8eb9SGleb Smirnoff 
13913b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
1392b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1393b074b7bbSAlexander V. Chernikov 
13949f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
13959f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
13969f7d47b0SAlexander V. Chernikov 
13979f7d47b0SAlexander V. Chernikov 		/*
13989f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
13999f7d47b0SAlexander V. Chernikov 		 */
14009f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1401b074b7bbSAlexander V. Chernikov 		return (EINVAL);
1402b074b7bbSAlexander V. Chernikov 	}
14033b3a8eb9SGleb Smirnoff 
14049f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
14059f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
14069f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
14073b3a8eb9SGleb Smirnoff 
14089f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
14099f7d47b0SAlexander V. Chernikov 
14109f7d47b0SAlexander V. Chernikov 	/* Change pointers */
14119f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
14129f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
14139f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
14143b3a8eb9SGleb Smirnoff 
14153b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
14163b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
14173b3a8eb9SGleb Smirnoff 
14183b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
141968394ec8SAlexander V. Chernikov 
142068394ec8SAlexander V. Chernikov 	/* Notify all consumers that their @ti pointer has changed */
142168394ec8SAlexander V. Chernikov 	ti = (struct table_info *)ch->tablestate;
142268394ec8SAlexander V. Chernikov 	for (i = 0; i < tbl; i++, ti++) {
142368394ec8SAlexander V. Chernikov 		if (ti->lookup == NULL)
142468394ec8SAlexander V. Chernikov 			continue;
142568394ec8SAlexander V. Chernikov 		tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i);
142668394ec8SAlexander V. Chernikov 		if (tc == NULL || tc->ta->change_ti == NULL)
142768394ec8SAlexander V. Chernikov 			continue;
142868394ec8SAlexander V. Chernikov 
142968394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, ti);
143068394ec8SAlexander V. Chernikov 	}
143168394ec8SAlexander V. Chernikov 
14329f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
14333b3a8eb9SGleb Smirnoff 
14343b3a8eb9SGleb Smirnoff 	/* Free old pointers */
14359f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
1436b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
14373b3a8eb9SGleb Smirnoff 
14383b3a8eb9SGleb Smirnoff 	return (0);
14393b3a8eb9SGleb Smirnoff }
14403b3a8eb9SGleb Smirnoff 
1441a73d728dSAlexander V. Chernikov /*
144235e1bbd0SAlexander V. Chernikov  * Switch between "set 0" and "rule's set" table binding,
1443a73d728dSAlexander V. Chernikov  * Check all ruleset bindings and permits changing
1444a73d728dSAlexander V. Chernikov  * IFF each binding has both rule AND table in default set (set 0).
1445a73d728dSAlexander V. Chernikov  *
1446a73d728dSAlexander V. Chernikov  * Returns 0 on success.
1447a73d728dSAlexander V. Chernikov  */
1448a73d728dSAlexander V. Chernikov int
1449a73d728dSAlexander V. Chernikov ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
1450a73d728dSAlexander V. Chernikov {
1451a73d728dSAlexander V. Chernikov 	struct namedobj_instance *ni;
1452a73d728dSAlexander V. Chernikov 	struct named_object *no;
1453a73d728dSAlexander V. Chernikov 	struct ip_fw *rule;
1454a73d728dSAlexander V. Chernikov 	ipfw_insn *cmd;
1455a73d728dSAlexander V. Chernikov 	int cmdlen, i, l;
1456a73d728dSAlexander V. Chernikov 	uint16_t kidx;
1457a73d728dSAlexander V. Chernikov 	uint8_t type;
1458a73d728dSAlexander V. Chernikov 
1459a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1460a73d728dSAlexander V. Chernikov 
1461a73d728dSAlexander V. Chernikov 	if (V_fw_tables_sets == sets) {
1462a73d728dSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1463a73d728dSAlexander V. Chernikov 		return (0);
1464a73d728dSAlexander V. Chernikov 	}
1465a73d728dSAlexander V. Chernikov 
1466a73d728dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1467a73d728dSAlexander V. Chernikov 
146835e1bbd0SAlexander V. Chernikov 	/*
146935e1bbd0SAlexander V. Chernikov 	 * Scan all rules and examine tables opcodes.
147035e1bbd0SAlexander V. Chernikov 	 */
1471a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules; i++) {
1472a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
1473a73d728dSAlexander V. Chernikov 
1474a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
1475a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
1476a73d728dSAlexander V. Chernikov 		cmdlen = 0;
1477a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1478a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
1479a73d728dSAlexander V. Chernikov 
1480a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
1481a73d728dSAlexander V. Chernikov 				continue;
1482a73d728dSAlexander V. Chernikov 
1483a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
1484a73d728dSAlexander V. Chernikov 
148535e1bbd0SAlexander V. Chernikov 			/* Check if both table object and rule has the set 0 */
1486a73d728dSAlexander V. Chernikov 			if (no->set != 0 || rule->set != 0) {
1487a73d728dSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
1488a73d728dSAlexander V. Chernikov 				return (EBUSY);
1489a73d728dSAlexander V. Chernikov 			}
1490a73d728dSAlexander V. Chernikov 
1491a73d728dSAlexander V. Chernikov 		}
1492a73d728dSAlexander V. Chernikov 	}
1493a73d728dSAlexander V. Chernikov 	V_fw_tables_sets = sets;
1494a73d728dSAlexander V. Chernikov 
1495a73d728dSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1496a73d728dSAlexander V. Chernikov 
1497a73d728dSAlexander V. Chernikov 	return (0);
1498a73d728dSAlexander V. Chernikov }
1499a73d728dSAlexander V. Chernikov 
1500c8d5d308SAlexander V. Chernikov /*
1501c8d5d308SAlexander V. Chernikov  * Lookup an IP @addr in table @tbl.
1502c8d5d308SAlexander V. Chernikov  * Stores found value in @val.
1503c8d5d308SAlexander V. Chernikov  *
1504c8d5d308SAlexander V. Chernikov  * Returns 1 if @addr was found.
1505c8d5d308SAlexander V. Chernikov  */
15063b3a8eb9SGleb Smirnoff int
15073b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
15083b3a8eb9SGleb Smirnoff     uint32_t *val)
15093b3a8eb9SGleb Smirnoff {
15109f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
15113b3a8eb9SGleb Smirnoff 
1512c8d5d308SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tbl);
15139f7d47b0SAlexander V. Chernikov 
15149f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
15153b3a8eb9SGleb Smirnoff }
15169f7d47b0SAlexander V. Chernikov 
1517c8d5d308SAlexander V. Chernikov /*
1518c8d5d308SAlexander V. Chernikov  * Lookup an arbtrary key @paddr of legth @plen in table @tbl.
1519c8d5d308SAlexander V. Chernikov  * Stores found value in @val.
1520c8d5d308SAlexander V. Chernikov  *
1521c8d5d308SAlexander V. Chernikov  * Returns 1 if key was found.
1522c8d5d308SAlexander V. Chernikov  */
15239f7d47b0SAlexander V. Chernikov int
15249f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
15259f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
15269f7d47b0SAlexander V. Chernikov {
15279f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
15289f7d47b0SAlexander V. Chernikov 
1529c8d5d308SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tbl);
15309f7d47b0SAlexander V. Chernikov 
15319f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
15329f7d47b0SAlexander V. Chernikov }
15339f7d47b0SAlexander V. Chernikov 
15349f7d47b0SAlexander V. Chernikov /*
15359f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
15369f7d47b0SAlexander V. Chernikov  *
15379f7d47b0SAlexander V. Chernikov  */
15389f7d47b0SAlexander V. Chernikov 
1539f1220db8SAlexander V. Chernikov /*
1540d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
1541d3a4f924SAlexander V. Chernikov  */
1542d3a4f924SAlexander V. Chernikov 
1543d3a4f924SAlexander V. Chernikov /*
1544d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
1545ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1546d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
1547d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
1548d3a4f924SAlexander V. Chernikov  *
1549d3a4f924SAlexander V. Chernikov  * Returns 0 on success
1550d3a4f924SAlexander V. Chernikov  */
1551f1220db8SAlexander V. Chernikov int
15522d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
1553f1220db8SAlexander V. Chernikov {
1554f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
1555f1220db8SAlexander V. Chernikov 	int error;
1556f1220db8SAlexander V. Chernikov 
15572d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
15582d99a349SAlexander V. Chernikov 	if (olh == NULL)
1559d3a4f924SAlexander V. Chernikov 		return (EINVAL);
156068394ec8SAlexander V. Chernikov 	if (sd->valsize < olh->size)
156168394ec8SAlexander V. Chernikov 		return (EINVAL);
1562d3a4f924SAlexander V. Chernikov 
1563f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
15642d99a349SAlexander V. Chernikov 	error = export_tables(ch, olh, sd);
1565f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1566f1220db8SAlexander V. Chernikov 
1567f1220db8SAlexander V. Chernikov 	return (error);
1568f1220db8SAlexander V. Chernikov }
1569f1220db8SAlexander V. Chernikov 
1570f1220db8SAlexander V. Chernikov /*
15712d99a349SAlexander V. Chernikov  * Store table info to buffer provided by @sd.
1572ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1573d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
1574d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
1575d3a4f924SAlexander V. Chernikov  *
1576d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
1577d3a4f924SAlexander V. Chernikov  */
1578d3a4f924SAlexander V. Chernikov int
15792d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
1580d3a4f924SAlexander V. Chernikov {
1581d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1582d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
1583d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
1584d3a4f924SAlexander V. Chernikov 	size_t sz;
1585d3a4f924SAlexander V. Chernikov 
1586d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
15872d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
15882d99a349SAlexander V. Chernikov 	if (oh == NULL)
1589d3a4f924SAlexander V. Chernikov 		return (EINVAL);
1590d3a4f924SAlexander V. Chernikov 
1591d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1592d3a4f924SAlexander V. Chernikov 
1593d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
1594d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
1595d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
1596d3a4f924SAlexander V. Chernikov 		return (ESRCH);
1597d3a4f924SAlexander V. Chernikov 	}
1598d3a4f924SAlexander V. Chernikov 
1599ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1));
1600d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1601d3a4f924SAlexander V. Chernikov 
16022d99a349SAlexander V. Chernikov 	return (0);
1603d3a4f924SAlexander V. Chernikov }
1604d3a4f924SAlexander V. Chernikov 
1605d3a4f924SAlexander V. Chernikov /*
1606adf3b2b9SAlexander V. Chernikov  * Modifies existing table.
1607adf3b2b9SAlexander V. Chernikov  * Data layout (v0)(current):
1608adf3b2b9SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
1609adf3b2b9SAlexander V. Chernikov  *
1610adf3b2b9SAlexander V. Chernikov  * Returns 0 on success
1611adf3b2b9SAlexander V. Chernikov  */
1612adf3b2b9SAlexander V. Chernikov int
1613adf3b2b9SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1614adf3b2b9SAlexander V. Chernikov     struct sockopt_data *sd)
1615adf3b2b9SAlexander V. Chernikov {
1616adf3b2b9SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1617adf3b2b9SAlexander V. Chernikov 	ipfw_xtable_info *i;
1618adf3b2b9SAlexander V. Chernikov 	char *tname;
1619adf3b2b9SAlexander V. Chernikov 	struct tid_info ti;
1620adf3b2b9SAlexander V. Chernikov 	struct namedobj_instance *ni;
1621adf3b2b9SAlexander V. Chernikov 	struct table_config *tc;
1622adf3b2b9SAlexander V. Chernikov 
1623adf3b2b9SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
1624adf3b2b9SAlexander V. Chernikov 		return (EINVAL);
1625adf3b2b9SAlexander V. Chernikov 
1626adf3b2b9SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)sd->kbuf;
1627adf3b2b9SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
1628adf3b2b9SAlexander V. Chernikov 
1629adf3b2b9SAlexander V. Chernikov 	/*
1630adf3b2b9SAlexander V. Chernikov 	 * Verify user-supplied strings.
1631adf3b2b9SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
1632adf3b2b9SAlexander V. Chernikov 	 */
1633adf3b2b9SAlexander V. Chernikov 	tname = oh->ntlv.name;
1634adf3b2b9SAlexander V. Chernikov 	if (ipfw_check_table_name(tname) != 0)
1635adf3b2b9SAlexander V. Chernikov 		return (EINVAL);
1636adf3b2b9SAlexander V. Chernikov 
1637adf3b2b9SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1638adf3b2b9SAlexander V. Chernikov 	ti.type = i->type;
1639adf3b2b9SAlexander V. Chernikov 
1640adf3b2b9SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1641adf3b2b9SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1642adf3b2b9SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) == NULL) {
1643adf3b2b9SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1644adf3b2b9SAlexander V. Chernikov 		return (ESRCH);
1645adf3b2b9SAlexander V. Chernikov 	}
1646fd0869d5SAlexander V. Chernikov 
1647fd0869d5SAlexander V. Chernikov 	/* Do not support any modifications for readonly tables */
1648fd0869d5SAlexander V. Chernikov 	if ((tc->ta->flags & TA_FLAG_READONLY) != 0) {
1649fd0869d5SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1650fd0869d5SAlexander V. Chernikov 		return (EACCES);
1651fd0869d5SAlexander V. Chernikov 	}
1652fd0869d5SAlexander V. Chernikov 
1653adf3b2b9SAlexander V. Chernikov 	if ((i->mflags & IPFW_TMFLAGS_FTYPE) != 0)
1654adf3b2b9SAlexander V. Chernikov 		tc->vftype = i->vftype;
1655adf3b2b9SAlexander V. Chernikov 	if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0)
1656adf3b2b9SAlexander V. Chernikov 		tc->limit = i->limit;
16574f43138aSAlexander V. Chernikov 	if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0)
16584f43138aSAlexander V. Chernikov 		tc->locked = ((i->flags & IPFW_TGFLAGS_LOCKED) != 0);
1659adf3b2b9SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1660adf3b2b9SAlexander V. Chernikov 
1661adf3b2b9SAlexander V. Chernikov 	return (0);
1662adf3b2b9SAlexander V. Chernikov }
1663adf3b2b9SAlexander V. Chernikov 
1664adf3b2b9SAlexander V. Chernikov /*
16659490a627SAlexander V. Chernikov  * Creates new table.
1666ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
16679490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
16689490a627SAlexander V. Chernikov  *
16699490a627SAlexander V. Chernikov  * Returns 0 on success
16709490a627SAlexander V. Chernikov  */
16719490a627SAlexander V. Chernikov int
1672ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1673ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
16749490a627SAlexander V. Chernikov {
16759490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
16769490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
16779490a627SAlexander V. Chernikov 	char *tname, *aname;
16789490a627SAlexander V. Chernikov 	struct tid_info ti;
16799490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
16809490a627SAlexander V. Chernikov 	struct table_config *tc;
16819490a627SAlexander V. Chernikov 
1682ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
16839490a627SAlexander V. Chernikov 		return (EINVAL);
16849490a627SAlexander V. Chernikov 
1685ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)sd->kbuf;
16869490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
16879490a627SAlexander V. Chernikov 
16889490a627SAlexander V. Chernikov 	/*
16899490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
16902d99a349SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
16919490a627SAlexander V. Chernikov 	 */
1692ac35ff17SAlexander V. Chernikov 	tname = oh->ntlv.name;
16939490a627SAlexander V. Chernikov 	aname = i->algoname;
1694ac35ff17SAlexander V. Chernikov 	if (ipfw_check_table_name(tname) != 0 ||
16959490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
16969490a627SAlexander V. Chernikov 		return (EINVAL);
16979490a627SAlexander V. Chernikov 
16989490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
16999490a627SAlexander V. Chernikov 		/* Use default algorithm */
17009490a627SAlexander V. Chernikov 		aname = NULL;
17019490a627SAlexander V. Chernikov 	}
17029490a627SAlexander V. Chernikov 
17039490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1704ac35ff17SAlexander V. Chernikov 	ti.type = i->type;
17059490a627SAlexander V. Chernikov 
17069490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
17079490a627SAlexander V. Chernikov 
17089490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
17099490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
17109490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
17119490a627SAlexander V. Chernikov 		return (EEXIST);
17129490a627SAlexander V. Chernikov 	}
17139490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
17149490a627SAlexander V. Chernikov 
17150468c5baSAlexander V. Chernikov 	return (create_table_internal(ch, &ti, aname, i, NULL, NULL, NULL, 0));
1716db785d31SAlexander V. Chernikov }
1717db785d31SAlexander V. Chernikov 
1718db785d31SAlexander V. Chernikov /*
1719db785d31SAlexander V. Chernikov  * Creates new table based on @ti and @aname.
1720db785d31SAlexander V. Chernikov  *
1721db785d31SAlexander V. Chernikov  * Relies on table name checking inside find_name_tlv()
1722db785d31SAlexander V. Chernikov  * Assume @aname to be checked and valid.
17230468c5baSAlexander V. Chernikov  * Stores allocated table config, used algo and kidx
17240468c5baSAlexander V. Chernikov  * inside @ptc, @pta and @pkidx (if non-NULL).
17250468c5baSAlexander V. Chernikov  * Reference created table if @compat is non-zero.
1726db785d31SAlexander V. Chernikov  *
1727db785d31SAlexander V. Chernikov  * Returns 0 on success.
1728db785d31SAlexander V. Chernikov  */
1729db785d31SAlexander V. Chernikov static int
1730db785d31SAlexander V. Chernikov create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
17310468c5baSAlexander V. Chernikov     char *aname, ipfw_xtable_info *i, struct table_config **ptc,
17320468c5baSAlexander V. Chernikov     struct table_algo **pta, uint16_t *pkidx, int compat)
1733db785d31SAlexander V. Chernikov {
1734db785d31SAlexander V. Chernikov 	struct namedobj_instance *ni;
1735c8d5d308SAlexander V. Chernikov 	struct table_config *tc, *tc_new, *tmp;
1736db785d31SAlexander V. Chernikov 	struct table_algo *ta;
1737db785d31SAlexander V. Chernikov 	uint16_t kidx;
1738db785d31SAlexander V. Chernikov 
1739db785d31SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1740db785d31SAlexander V. Chernikov 
1741db785d31SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname);
17429490a627SAlexander V. Chernikov 	if (ta == NULL)
17439490a627SAlexander V. Chernikov 		return (ENOTSUP);
17449490a627SAlexander V. Chernikov 
17454c0c07a5SAlexander V. Chernikov 	tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype);
17464c0c07a5SAlexander V. Chernikov 	if (tc == NULL)
17479490a627SAlexander V. Chernikov 		return (ENOMEM);
17489490a627SAlexander V. Chernikov 
1749adf3b2b9SAlexander V. Chernikov 	tc->vftype = i->vftype;
17504c0c07a5SAlexander V. Chernikov 	tc->limit = i->limit;
1751fd0869d5SAlexander V. Chernikov 	if (ta->flags & TA_FLAG_READONLY)
1752fd0869d5SAlexander V. Chernikov 		tc->locked = 1;
1753fd0869d5SAlexander V. Chernikov 	else
17544f43138aSAlexander V. Chernikov 		tc->locked = (i->flags & IPFW_TGFLAGS_LOCKED) != 0;
17554c0c07a5SAlexander V. Chernikov 
17569490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1757db785d31SAlexander V. Chernikov 
1758db785d31SAlexander V. Chernikov 	/* Check if table has been already created */
17590468c5baSAlexander V. Chernikov 	tc_new = find_table(ni, ti);
17600468c5baSAlexander V. Chernikov 	if (tc_new != NULL) {
17610468c5baSAlexander V. Chernikov 
17620468c5baSAlexander V. Chernikov 		/*
17630468c5baSAlexander V. Chernikov 		 * Compat: do not fail if we're
17640468c5baSAlexander V. Chernikov 		 * requesting to create existing table
17650468c5baSAlexander V. Chernikov 		 * which has the same type / vtype
17660468c5baSAlexander V. Chernikov 		 */
17670468c5baSAlexander V. Chernikov 		if (compat == 0 || tc_new->no.type != tc->no.type ||
17680468c5baSAlexander V. Chernikov 		    tc_new->vtype != tc->vtype) {
1769db785d31SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1770db785d31SAlexander V. Chernikov 			free_table_config(ni, tc);
1771db785d31SAlexander V. Chernikov 			return (EEXIST);
1772db785d31SAlexander V. Chernikov 		}
1773db785d31SAlexander V. Chernikov 
17740468c5baSAlexander V. Chernikov 		/* Exchange tc and tc_new for proper refcounting & freeing */
17750468c5baSAlexander V. Chernikov 		tmp = tc;
17760468c5baSAlexander V. Chernikov 		tc = tc_new;
17770468c5baSAlexander V. Chernikov 		tc_new = tmp;
17780468c5baSAlexander V. Chernikov 	} else {
17790468c5baSAlexander V. Chernikov 		/* New table */
1780ac35ff17SAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
17819490a627SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1782db785d31SAlexander V. Chernikov 			printf("Unable to allocate table index."
1783db785d31SAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max");
17849490a627SAlexander V. Chernikov 			free_table_config(ni, tc);
17859490a627SAlexander V. Chernikov 			return (EBUSY);
17869490a627SAlexander V. Chernikov 		}
17879490a627SAlexander V. Chernikov 		tc->no.kidx = kidx;
17889490a627SAlexander V. Chernikov 
17899490a627SAlexander V. Chernikov 		IPFW_WLOCK(ch);
17909490a627SAlexander V. Chernikov 		link_table(ch, tc);
17919490a627SAlexander V. Chernikov 		IPFW_WUNLOCK(ch);
17920468c5baSAlexander V. Chernikov 	}
17930468c5baSAlexander V. Chernikov 
17940468c5baSAlexander V. Chernikov 	if (compat != 0)
17950468c5baSAlexander V. Chernikov 		tc->no.refcnt++;
17960468c5baSAlexander V. Chernikov 	if (ptc != NULL)
17970468c5baSAlexander V. Chernikov 		*ptc = tc;
17980468c5baSAlexander V. Chernikov 	if (pta != NULL)
17990468c5baSAlexander V. Chernikov 		*pta = ta;
18000468c5baSAlexander V. Chernikov 	if (pkidx != NULL)
18010468c5baSAlexander V. Chernikov 		*pkidx = tc->no.kidx;
18029490a627SAlexander V. Chernikov 
18039490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
18049490a627SAlexander V. Chernikov 
18050468c5baSAlexander V. Chernikov 	if (tc_new != NULL)
18060468c5baSAlexander V. Chernikov 		free_table_config(ni, tc_new);
18070468c5baSAlexander V. Chernikov 
18089490a627SAlexander V. Chernikov 	return (0);
18099490a627SAlexander V. Chernikov }
18109490a627SAlexander V. Chernikov 
181146d52008SAlexander V. Chernikov static void
181246d52008SAlexander V. Chernikov ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti)
1813d3a4f924SAlexander V. Chernikov {
1814d3a4f924SAlexander V. Chernikov 
1815d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
181646d52008SAlexander V. Chernikov 	ti->set = ntlv->set;
181746d52008SAlexander V. Chernikov 	ti->uidx = ntlv->idx;
181846d52008SAlexander V. Chernikov 	ti->tlvs = ntlv;
181946d52008SAlexander V. Chernikov 	ti->tlen = ntlv->head.length;
182046d52008SAlexander V. Chernikov }
182146d52008SAlexander V. Chernikov 
182246d52008SAlexander V. Chernikov static void
182346d52008SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
182446d52008SAlexander V. Chernikov {
182546d52008SAlexander V. Chernikov 
182646d52008SAlexander V. Chernikov 	ntlv_to_ti(&oh->ntlv, ti);
1827d3a4f924SAlexander V. Chernikov }
1828d3a4f924SAlexander V. Chernikov 
182935e1bbd0SAlexander V. Chernikov /*
183035e1bbd0SAlexander V. Chernikov  * Exports basic table info as name TLV.
183135e1bbd0SAlexander V. Chernikov  * Used inside dump_static_rules() to provide info
183235e1bbd0SAlexander V. Chernikov  * about all tables referenced by current ruleset.
183335e1bbd0SAlexander V. Chernikov  *
183435e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
183535e1bbd0SAlexander V. Chernikov  */
1836563b5ab1SAlexander V. Chernikov int
1837563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
1838563b5ab1SAlexander V. Chernikov     struct sockopt_data *sd)
1839563b5ab1SAlexander V. Chernikov {
1840563b5ab1SAlexander V. Chernikov 	struct namedobj_instance *ni;
1841563b5ab1SAlexander V. Chernikov 	struct named_object *no;
1842563b5ab1SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1843563b5ab1SAlexander V. Chernikov 
1844563b5ab1SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1845563b5ab1SAlexander V. Chernikov 
1846ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_kidx(ni, kidx);
1847563b5ab1SAlexander V. Chernikov 	KASSERT(no != NULL, ("invalid table kidx passed"));
1848563b5ab1SAlexander V. Chernikov 
1849563b5ab1SAlexander V. Chernikov 	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
1850563b5ab1SAlexander V. Chernikov 	if (ntlv == NULL)
1851563b5ab1SAlexander V. Chernikov 		return (ENOMEM);
1852563b5ab1SAlexander V. Chernikov 
1853563b5ab1SAlexander V. Chernikov 	ntlv->head.type = IPFW_TLV_TBL_NAME;
1854563b5ab1SAlexander V. Chernikov 	ntlv->head.length = sizeof(*ntlv);
1855563b5ab1SAlexander V. Chernikov 	ntlv->idx = no->kidx;
1856563b5ab1SAlexander V. Chernikov 	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
1857563b5ab1SAlexander V. Chernikov 
1858563b5ab1SAlexander V. Chernikov 	return (0);
1859563b5ab1SAlexander V. Chernikov }
1860563b5ab1SAlexander V. Chernikov 
186135e1bbd0SAlexander V. Chernikov /*
1862f99fbf96SAlexander V. Chernikov  * Marks every table kidx used in @rule with bit in @bmask.
1863f99fbf96SAlexander V. Chernikov  * Used to generate bitmask of referenced tables for given ruleset.
1864f99fbf96SAlexander V. Chernikov  *
1865f99fbf96SAlexander V. Chernikov  * Returns number of newly-referenced tables.
1866f99fbf96SAlexander V. Chernikov  */
1867f99fbf96SAlexander V. Chernikov int
1868f99fbf96SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
1869f99fbf96SAlexander V. Chernikov     uint32_t *bmask)
1870f99fbf96SAlexander V. Chernikov {
1871f99fbf96SAlexander V. Chernikov 	int cmdlen, l, count;
1872f99fbf96SAlexander V. Chernikov 	ipfw_insn *cmd;
1873f99fbf96SAlexander V. Chernikov 	uint16_t kidx;
1874f99fbf96SAlexander V. Chernikov 	uint8_t type;
1875f99fbf96SAlexander V. Chernikov 
1876f99fbf96SAlexander V. Chernikov 	l = rule->cmd_len;
1877f99fbf96SAlexander V. Chernikov 	cmd = rule->cmd;
1878f99fbf96SAlexander V. Chernikov 	cmdlen = 0;
1879f99fbf96SAlexander V. Chernikov 	count = 0;
1880f99fbf96SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1881f99fbf96SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1882f99fbf96SAlexander V. Chernikov 
1883f99fbf96SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1884f99fbf96SAlexander V. Chernikov 			continue;
1885f99fbf96SAlexander V. Chernikov 
1886f99fbf96SAlexander V. Chernikov 		if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
1887f99fbf96SAlexander V. Chernikov 			count++;
1888f99fbf96SAlexander V. Chernikov 
1889f99fbf96SAlexander V. Chernikov 		bmask[kidx / 32] |= 1 << (kidx % 32);
1890f99fbf96SAlexander V. Chernikov 	}
1891f99fbf96SAlexander V. Chernikov 
1892f99fbf96SAlexander V. Chernikov 	return (count);
1893f99fbf96SAlexander V. Chernikov }
1894f99fbf96SAlexander V. Chernikov 
1895d3b00c08SAlexander V. Chernikov struct dump_args {
1896d3b00c08SAlexander V. Chernikov 	struct table_info *ti;
1897d3b00c08SAlexander V. Chernikov 	struct table_config *tc;
1898d3b00c08SAlexander V. Chernikov 	struct sockopt_data *sd;
1899d3b00c08SAlexander V. Chernikov 	uint32_t cnt;
1900d3b00c08SAlexander V. Chernikov 	uint16_t uidx;
1901d3b00c08SAlexander V. Chernikov 	int error;
1902d3b00c08SAlexander V. Chernikov 	uint32_t size;
19034dff4ae0SAlexander V. Chernikov 	ipfw_table_entry *ent;
19044dff4ae0SAlexander V. Chernikov 	ta_foreach_f *f;
19054dff4ae0SAlexander V. Chernikov 	void *farg;
1906d3b00c08SAlexander V. Chernikov 	ipfw_obj_tentry tent;
1907d3b00c08SAlexander V. Chernikov };
1908d3b00c08SAlexander V. Chernikov 
1909d3b00c08SAlexander V. Chernikov static int
1910d3b00c08SAlexander V. Chernikov count_ext_entries(void *e, void *arg)
1911d3b00c08SAlexander V. Chernikov {
1912d3b00c08SAlexander V. Chernikov 	struct dump_args *da;
1913d3b00c08SAlexander V. Chernikov 
1914d3b00c08SAlexander V. Chernikov 	da = (struct dump_args *)arg;
1915d3b00c08SAlexander V. Chernikov 	da->cnt++;
1916d3b00c08SAlexander V. Chernikov 
1917d3b00c08SAlexander V. Chernikov 	return (0);
1918d3b00c08SAlexander V. Chernikov }
1919d3b00c08SAlexander V. Chernikov 
1920d3b00c08SAlexander V. Chernikov /*
1921d3b00c08SAlexander V. Chernikov  * Gets number of items from table either using
1922d3b00c08SAlexander V. Chernikov  * internal counter or calling algo callback for
1923d3b00c08SAlexander V. Chernikov  * externally-managed tables.
1924d3b00c08SAlexander V. Chernikov  *
1925d3b00c08SAlexander V. Chernikov  * Returns number of records.
1926d3b00c08SAlexander V. Chernikov  */
1927d3b00c08SAlexander V. Chernikov static uint32_t
1928d3b00c08SAlexander V. Chernikov table_get_count(struct ip_fw_chain *ch, struct table_config *tc)
1929d3b00c08SAlexander V. Chernikov {
1930d3b00c08SAlexander V. Chernikov 	struct table_info *ti;
1931d3b00c08SAlexander V. Chernikov 	struct table_algo *ta;
1932d3b00c08SAlexander V. Chernikov 	struct dump_args da;
1933d3b00c08SAlexander V. Chernikov 
1934d3b00c08SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tc->no.kidx);
1935d3b00c08SAlexander V. Chernikov 	ta = tc->ta;
1936d3b00c08SAlexander V. Chernikov 
1937d3b00c08SAlexander V. Chernikov 	/* Use internal counter for self-managed tables */
1938d3b00c08SAlexander V. Chernikov 	if ((ta->flags & TA_FLAG_READONLY) == 0)
1939d3b00c08SAlexander V. Chernikov 		return (tc->count);
1940d3b00c08SAlexander V. Chernikov 
1941d3b00c08SAlexander V. Chernikov 	/* Use callback to quickly get number of items */
1942d3b00c08SAlexander V. Chernikov 	if ((ta->flags & TA_FLAG_EXTCOUNTER) != 0)
1943d3b00c08SAlexander V. Chernikov 		return (ta->get_count(tc->astate, ti));
1944d3b00c08SAlexander V. Chernikov 
1945d3b00c08SAlexander V. Chernikov 	/* Count number of iterms ourselves */
1946d3b00c08SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
1947d3b00c08SAlexander V. Chernikov 	ta->foreach(tc->astate, ti, count_ext_entries, &da);
1948d3b00c08SAlexander V. Chernikov 
1949d3b00c08SAlexander V. Chernikov 	return (da.cnt);
1950d3b00c08SAlexander V. Chernikov }
1951f99fbf96SAlexander V. Chernikov 
1952f99fbf96SAlexander V. Chernikov /*
195335e1bbd0SAlexander V. Chernikov  * Exports table @tc info into standard ipfw_xtable_info format.
195435e1bbd0SAlexander V. Chernikov  */
19559f7d47b0SAlexander V. Chernikov static void
1956ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
1957ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i)
19589f7d47b0SAlexander V. Chernikov {
1959ac35ff17SAlexander V. Chernikov 	struct table_info *ti;
19605f379342SAlexander V. Chernikov 	struct table_algo *ta;
19619f7d47b0SAlexander V. Chernikov 
19629f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
1963914bffb6SAlexander V. Chernikov 	i->tflags = tc->tflags;
1964ac35ff17SAlexander V. Chernikov 	i->vtype = tc->vtype;
1965adf3b2b9SAlexander V. Chernikov 	i->vftype = tc->vftype;
19669f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
19679f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
19689f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
1969d3b00c08SAlexander V. Chernikov 	i->count = table_get_count(ch, tc);
19704c0c07a5SAlexander V. Chernikov 	i->limit = tc->limit;
19714f43138aSAlexander V. Chernikov 	i->flags |= (tc->locked != 0) ? IPFW_TGFLAGS_LOCKED : 0;
197281d3153dSAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_obj_tentry);
1973f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
19749f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
1975ac35ff17SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tc->no.kidx);
19765f379342SAlexander V. Chernikov 	ta = tc->ta;
19775f379342SAlexander V. Chernikov 	if (ta->print_config != NULL) {
19785f379342SAlexander V. Chernikov 		/* Use algo function to print table config to string */
19795f379342SAlexander V. Chernikov 		ta->print_config(tc->astate, ti, i->algoname,
1980ac35ff17SAlexander V. Chernikov 		    sizeof(i->algoname));
1981ac35ff17SAlexander V. Chernikov 	} else
19825f379342SAlexander V. Chernikov 		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
19835f379342SAlexander V. Chernikov 	/* Dump algo-specific data, if possible */
19845f379342SAlexander V. Chernikov 	if (ta->dump_tinfo != NULL) {
19855f379342SAlexander V. Chernikov 		ta->dump_tinfo(tc->astate, ti, &i->ta_info);
19865f379342SAlexander V. Chernikov 		i->ta_info.flags |= IPFW_TATFLAGS_DATA;
19875f379342SAlexander V. Chernikov 	}
19889f7d47b0SAlexander V. Chernikov }
19899f7d47b0SAlexander V. Chernikov 
1990ac35ff17SAlexander V. Chernikov struct dump_table_args {
1991ac35ff17SAlexander V. Chernikov 	struct ip_fw_chain *ch;
1992ac35ff17SAlexander V. Chernikov 	struct sockopt_data *sd;
1993ac35ff17SAlexander V. Chernikov };
1994ac35ff17SAlexander V. Chernikov 
19959f7d47b0SAlexander V. Chernikov static void
19969f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
19979f7d47b0SAlexander V. Chernikov     void *arg)
19983b3a8eb9SGleb Smirnoff {
19999f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
2000ac35ff17SAlexander V. Chernikov 	struct dump_table_args *dta;
20013b3a8eb9SGleb Smirnoff 
2002ac35ff17SAlexander V. Chernikov 	dta = (struct dump_table_args *)arg;
2003ac35ff17SAlexander V. Chernikov 
2004ac35ff17SAlexander V. Chernikov 	i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
200568394ec8SAlexander V. Chernikov 	KASSERT(i != 0, ("previously checked buffer is not enough"));
20069f7d47b0SAlexander V. Chernikov 
2007ac35ff17SAlexander V. Chernikov 	export_table_info(dta->ch, (struct table_config *)no, i);
20089f7d47b0SAlexander V. Chernikov }
20099f7d47b0SAlexander V. Chernikov 
2010f1220db8SAlexander V. Chernikov /*
2011f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
20122d99a349SAlexander V. Chernikov  * storage provided by @sd.
201328ea4fa3SAlexander V. Chernikov  *
201428ea4fa3SAlexander V. Chernikov  * If supplied buffer is too small, fills in required size
201528ea4fa3SAlexander V. Chernikov  * and returns ENOMEM.
2016f1220db8SAlexander V. Chernikov  * Returns 0 on success.
2017f1220db8SAlexander V. Chernikov  */
2018f1220db8SAlexander V. Chernikov static int
20192d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
20202d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
20219f7d47b0SAlexander V. Chernikov {
20229f7d47b0SAlexander V. Chernikov 	uint32_t size;
20239f7d47b0SAlexander V. Chernikov 	uint32_t count;
2024ac35ff17SAlexander V. Chernikov 	struct dump_table_args dta;
20259f7d47b0SAlexander V. Chernikov 
20269f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
20279f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
20282d99a349SAlexander V. Chernikov 
20292d99a349SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
2030f1220db8SAlexander V. Chernikov 	olh->count = count;
2031f1220db8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
20322d99a349SAlexander V. Chernikov 
20332d99a349SAlexander V. Chernikov 	if (size > olh->size) {
20342d99a349SAlexander V. Chernikov 		olh->size = size;
20359f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
2036f1220db8SAlexander V. Chernikov 	}
203768394ec8SAlexander V. Chernikov 
20389f7d47b0SAlexander V. Chernikov 	olh->size = size;
20392d99a349SAlexander V. Chernikov 
2040ac35ff17SAlexander V. Chernikov 	dta.ch = ch;
2041ac35ff17SAlexander V. Chernikov 	dta.sd = sd;
2042ac35ff17SAlexander V. Chernikov 
2043ac35ff17SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta);
20449f7d47b0SAlexander V. Chernikov 
20453b3a8eb9SGleb Smirnoff 	return (0);
20463b3a8eb9SGleb Smirnoff }
20473b3a8eb9SGleb Smirnoff 
2048f99fbf96SAlexander V. Chernikov int
2049f99fbf96SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
2050f99fbf96SAlexander V. Chernikov     struct sockopt_data *sd)
2051f99fbf96SAlexander V. Chernikov {
2052f99fbf96SAlexander V. Chernikov 	int error;
2053f99fbf96SAlexander V. Chernikov 
2054f99fbf96SAlexander V. Chernikov 	switch (op3->version) {
2055f99fbf96SAlexander V. Chernikov 	case 0:
2056f99fbf96SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sd);
2057f99fbf96SAlexander V. Chernikov 		break;
2058f99fbf96SAlexander V. Chernikov 	case 1:
2059f99fbf96SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sd);
2060f99fbf96SAlexander V. Chernikov 		break;
2061f99fbf96SAlexander V. Chernikov 	default:
2062f99fbf96SAlexander V. Chernikov 		error = ENOTSUP;
2063f99fbf96SAlexander V. Chernikov 	}
2064f99fbf96SAlexander V. Chernikov 
2065f99fbf96SAlexander V. Chernikov 	return (error);
2066f99fbf96SAlexander V. Chernikov }
2067f99fbf96SAlexander V. Chernikov 
2068f99fbf96SAlexander V. Chernikov /*
2069f99fbf96SAlexander V. Chernikov  * Dumps all table data
2070f99fbf96SAlexander V. Chernikov  * Data layout (v1)(current):
2071f99fbf96SAlexander V. Chernikov  * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
2072f99fbf96SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ]
2073f99fbf96SAlexander V. Chernikov  *
2074f99fbf96SAlexander V. Chernikov  * Returns 0 on success
2075f99fbf96SAlexander V. Chernikov  */
2076f99fbf96SAlexander V. Chernikov static int
2077f99fbf96SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
2078f99fbf96SAlexander V. Chernikov {
2079f99fbf96SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
2080f99fbf96SAlexander V. Chernikov 	ipfw_xtable_info *i;
2081f99fbf96SAlexander V. Chernikov 	struct tid_info ti;
2082f99fbf96SAlexander V. Chernikov 	struct table_config *tc;
2083f99fbf96SAlexander V. Chernikov 	struct table_algo *ta;
2084f99fbf96SAlexander V. Chernikov 	struct dump_args da;
2085f99fbf96SAlexander V. Chernikov 	uint32_t sz;
2086f99fbf96SAlexander V. Chernikov 
2087f99fbf96SAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
2088f99fbf96SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
2089f99fbf96SAlexander V. Chernikov 	if (oh == NULL)
2090f99fbf96SAlexander V. Chernikov 		return (EINVAL);
2091f99fbf96SAlexander V. Chernikov 
2092f99fbf96SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
2093f99fbf96SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
2094f99fbf96SAlexander V. Chernikov 
2095f99fbf96SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
2096f99fbf96SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
2097f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2098f99fbf96SAlexander V. Chernikov 		return (ESRCH);
2099f99fbf96SAlexander V. Chernikov 	}
2100f99fbf96SAlexander V. Chernikov 	export_table_info(ch, tc, i);
2101f99fbf96SAlexander V. Chernikov 
2102f99fbf96SAlexander V. Chernikov 	if (sd->valsize < i->size) {
2103f99fbf96SAlexander V. Chernikov 
2104f99fbf96SAlexander V. Chernikov 		/*
2105f99fbf96SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
2106f99fbf96SAlexander V. Chernikov 		 * WE've already filled in @i structure with
2107f99fbf96SAlexander V. Chernikov 		 * relevant table info including size, so we
2108f99fbf96SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
2109f99fbf96SAlexander V. Chernikov 		 */
2110f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2111f99fbf96SAlexander V. Chernikov 		return (ENOMEM);
2112f99fbf96SAlexander V. Chernikov 	}
2113f99fbf96SAlexander V. Chernikov 
2114f99fbf96SAlexander V. Chernikov 	/*
2115f99fbf96SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
2116f99fbf96SAlexander V. Chernikov 	 */
2117f99fbf96SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
2118f99fbf96SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2119f99fbf96SAlexander V. Chernikov 	da.tc = tc;
2120f99fbf96SAlexander V. Chernikov 	da.sd = sd;
2121f99fbf96SAlexander V. Chernikov 
2122f99fbf96SAlexander V. Chernikov 	ta = tc->ta;
2123f99fbf96SAlexander V. Chernikov 
2124f99fbf96SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_tentry, &da);
2125f99fbf96SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
2126f99fbf96SAlexander V. Chernikov 
2127f99fbf96SAlexander V. Chernikov 	return (da.error);
2128f99fbf96SAlexander V. Chernikov }
2129f99fbf96SAlexander V. Chernikov 
2130f99fbf96SAlexander V. Chernikov /*
2131f99fbf96SAlexander V. Chernikov  * Dumps all table data
2132f99fbf96SAlexander V. Chernikov  * Data layout (version 0)(legacy):
2133f99fbf96SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
2134f99fbf96SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
2135f99fbf96SAlexander V. Chernikov  *
2136f99fbf96SAlexander V. Chernikov  * Returns 0 on success
2137f99fbf96SAlexander V. Chernikov  */
2138f99fbf96SAlexander V. Chernikov static int
2139f99fbf96SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
2140f99fbf96SAlexander V. Chernikov {
2141f99fbf96SAlexander V. Chernikov 	ipfw_xtable *xtbl;
2142f99fbf96SAlexander V. Chernikov 	struct tid_info ti;
2143f99fbf96SAlexander V. Chernikov 	struct table_config *tc;
2144f99fbf96SAlexander V. Chernikov 	struct table_algo *ta;
2145f99fbf96SAlexander V. Chernikov 	struct dump_args da;
2146d3b00c08SAlexander V. Chernikov 	size_t sz, count;
2147f99fbf96SAlexander V. Chernikov 
2148f99fbf96SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
2149f99fbf96SAlexander V. Chernikov 	if (xtbl == NULL)
2150f99fbf96SAlexander V. Chernikov 		return (EINVAL);
2151f99fbf96SAlexander V. Chernikov 
2152f99fbf96SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
2153f99fbf96SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
2154f99fbf96SAlexander V. Chernikov 
2155f99fbf96SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
2156f99fbf96SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
2157f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2158f99fbf96SAlexander V. Chernikov 		return (0);
2159f99fbf96SAlexander V. Chernikov 	}
2160d3b00c08SAlexander V. Chernikov 	count = table_get_count(ch, tc);
2161d3b00c08SAlexander V. Chernikov 	sz = count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
2162f99fbf96SAlexander V. Chernikov 
2163d3b00c08SAlexander V. Chernikov 	xtbl->cnt = count;
2164f99fbf96SAlexander V. Chernikov 	xtbl->size = sz;
2165f99fbf96SAlexander V. Chernikov 	xtbl->type = tc->no.type;
2166f99fbf96SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
2167f99fbf96SAlexander V. Chernikov 
2168f99fbf96SAlexander V. Chernikov 	if (sd->valsize < sz) {
2169f99fbf96SAlexander V. Chernikov 
2170f99fbf96SAlexander V. Chernikov 		/*
2171f99fbf96SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
2172f99fbf96SAlexander V. Chernikov 		 * WE've already filled in @i structure with
2173f99fbf96SAlexander V. Chernikov 		 * relevant table info including size, so we
2174f99fbf96SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
2175f99fbf96SAlexander V. Chernikov 		 */
2176f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2177f99fbf96SAlexander V. Chernikov 		return (ENOMEM);
2178f99fbf96SAlexander V. Chernikov 	}
2179f99fbf96SAlexander V. Chernikov 
2180f99fbf96SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
2181f99fbf96SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
2182f99fbf96SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2183f99fbf96SAlexander V. Chernikov 	da.tc = tc;
2184f99fbf96SAlexander V. Chernikov 	da.sd = sd;
2185f99fbf96SAlexander V. Chernikov 
2186f99fbf96SAlexander V. Chernikov 	ta = tc->ta;
2187f99fbf96SAlexander V. Chernikov 
2188f99fbf96SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
2189f99fbf96SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
2190f99fbf96SAlexander V. Chernikov 
2191f99fbf96SAlexander V. Chernikov 	return (0);
2192f99fbf96SAlexander V. Chernikov }
2193f99fbf96SAlexander V. Chernikov 
219481d3153dSAlexander V. Chernikov /*
219581d3153dSAlexander V. Chernikov  * Legacy IP_FW_TABLE_GETSIZE handler
219681d3153dSAlexander V. Chernikov  */
21973b3a8eb9SGleb Smirnoff int
2198b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
21993b3a8eb9SGleb Smirnoff {
2200b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
22013b3a8eb9SGleb Smirnoff 
2202b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
2203b074b7bbSAlexander V. Chernikov 		return (ESRCH);
2204d3b00c08SAlexander V. Chernikov 	*cnt = table_get_count(ch, tc);
22053b3a8eb9SGleb Smirnoff 	return (0);
22063b3a8eb9SGleb Smirnoff }
22073b3a8eb9SGleb Smirnoff 
220881d3153dSAlexander V. Chernikov /*
220981d3153dSAlexander V. Chernikov  * Legacy IP_FW_TABLE_XGETSIZE handler
221081d3153dSAlexander V. Chernikov  */
22119f7d47b0SAlexander V. Chernikov int
22129f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
22133b3a8eb9SGleb Smirnoff {
22149f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
2215d3b00c08SAlexander V. Chernikov 	uint32_t count;
22169f7d47b0SAlexander V. Chernikov 
2217d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
2218d3a4f924SAlexander V. Chernikov 		*cnt = 0;
22199f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
2220d3a4f924SAlexander V. Chernikov 	}
2221d3b00c08SAlexander V. Chernikov 
2222d3b00c08SAlexander V. Chernikov 	count = table_get_count(ch, tc);
2223d3b00c08SAlexander V. Chernikov 	*cnt = count * sizeof(ipfw_table_xentry);
2224d3b00c08SAlexander V. Chernikov 	if (count > 0)
22259f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
22269f7d47b0SAlexander V. Chernikov 	return (0);
22279f7d47b0SAlexander V. Chernikov }
22289f7d47b0SAlexander V. Chernikov 
22299f7d47b0SAlexander V. Chernikov static int
22309f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
22319f7d47b0SAlexander V. Chernikov {
22329f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
22339f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
22349f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
22353b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
223681d3153dSAlexander V. Chernikov 	int error;
22373b3a8eb9SGleb Smirnoff 
22389f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
22399f7d47b0SAlexander V. Chernikov 
22409f7d47b0SAlexander V. Chernikov 	tc = da->tc;
22419f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
22429f7d47b0SAlexander V. Chernikov 
22439f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
2244f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
22453b3a8eb9SGleb Smirnoff 		return (1);
2246f1220db8SAlexander V. Chernikov 	ent = da->ent++;
2247f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
2248f1220db8SAlexander V. Chernikov 	da->cnt++;
22499f7d47b0SAlexander V. Chernikov 
225081d3153dSAlexander V. Chernikov 	error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent);
225181d3153dSAlexander V. Chernikov 	if (error != 0)
225281d3153dSAlexander V. Chernikov 		return (error);
225381d3153dSAlexander V. Chernikov 
225481d3153dSAlexander V. Chernikov 	ent->addr = da->tent.k.addr.s_addr;
225581d3153dSAlexander V. Chernikov 	ent->masklen = da->tent.masklen;
22564bbd1577SAlexander V. Chernikov 	ent->value = da->tent.v.value;
225781d3153dSAlexander V. Chernikov 
225881d3153dSAlexander V. Chernikov 	return (0);
22593b3a8eb9SGleb Smirnoff }
22603b3a8eb9SGleb Smirnoff 
2261f1220db8SAlexander V. Chernikov /*
2262f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
2263f1220db8SAlexander V. Chernikov  */
22643b3a8eb9SGleb Smirnoff int
2265f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
2266f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
22673b3a8eb9SGleb Smirnoff {
2268b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
22699f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
22709f7d47b0SAlexander V. Chernikov 	struct dump_args da;
22713b3a8eb9SGleb Smirnoff 
22723b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
22733b3a8eb9SGleb Smirnoff 
2274b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
2275b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
22769f7d47b0SAlexander V. Chernikov 
22779f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
22789f7d47b0SAlexander V. Chernikov 
227981d3153dSAlexander V. Chernikov 	/* This dump format supports IPv4 only */
2280c21034b7SAlexander V. Chernikov 	if (tc->no.type != IPFW_TABLE_ADDR)
228181d3153dSAlexander V. Chernikov 		return (0);
22829f7d47b0SAlexander V. Chernikov 
2283d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
22849f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
22859f7d47b0SAlexander V. Chernikov 	da.tc = tc;
2286f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
2287f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
22889f7d47b0SAlexander V. Chernikov 
22899f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
22909f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
2291f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
22929f7d47b0SAlexander V. Chernikov 
22933b3a8eb9SGleb Smirnoff 	return (0);
22943b3a8eb9SGleb Smirnoff }
22953b3a8eb9SGleb Smirnoff 
22969490a627SAlexander V. Chernikov /*
229781d3153dSAlexander V. Chernikov  * Dumps table entry in eXtended format (v1)(current).
229881d3153dSAlexander V. Chernikov  */
229981d3153dSAlexander V. Chernikov static int
230081d3153dSAlexander V. Chernikov dump_table_tentry(void *e, void *arg)
230181d3153dSAlexander V. Chernikov {
230281d3153dSAlexander V. Chernikov 	struct dump_args *da;
230381d3153dSAlexander V. Chernikov 	struct table_config *tc;
230481d3153dSAlexander V. Chernikov 	struct table_algo *ta;
230581d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
230681d3153dSAlexander V. Chernikov 
230781d3153dSAlexander V. Chernikov 	da = (struct dump_args *)arg;
230881d3153dSAlexander V. Chernikov 
230981d3153dSAlexander V. Chernikov 	tc = da->tc;
231081d3153dSAlexander V. Chernikov 	ta = tc->ta;
231181d3153dSAlexander V. Chernikov 
231281d3153dSAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent));
231381d3153dSAlexander V. Chernikov 	/* Out of memory, returning */
231481d3153dSAlexander V. Chernikov 	if (tent == NULL) {
231581d3153dSAlexander V. Chernikov 		da->error = ENOMEM;
231681d3153dSAlexander V. Chernikov 		return (1);
231781d3153dSAlexander V. Chernikov 	}
231881d3153dSAlexander V. Chernikov 	tent->head.length = sizeof(ipfw_obj_tentry);
231981d3153dSAlexander V. Chernikov 	tent->idx = da->uidx;
232081d3153dSAlexander V. Chernikov 
232181d3153dSAlexander V. Chernikov 	return (ta->dump_tentry(tc->astate, da->ti, e, tent));
232281d3153dSAlexander V. Chernikov }
232381d3153dSAlexander V. Chernikov 
232481d3153dSAlexander V. Chernikov /*
232581d3153dSAlexander V. Chernikov  * Dumps table entry in eXtended format (v0).
23269490a627SAlexander V. Chernikov  */
23273b3a8eb9SGleb Smirnoff static int
23289f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
23293b3a8eb9SGleb Smirnoff {
23309f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
23319f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
23329f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
23333b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
233481d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
233581d3153dSAlexander V. Chernikov 	int error;
23363b3a8eb9SGleb Smirnoff 
23379f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
23389f7d47b0SAlexander V. Chernikov 
23399f7d47b0SAlexander V. Chernikov 	tc = da->tc;
23409f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
23419f7d47b0SAlexander V. Chernikov 
23422d99a349SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
23433b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
23442d99a349SAlexander V. Chernikov 	if (xent == NULL)
23453b3a8eb9SGleb Smirnoff 		return (1);
23463b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
2347f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
23489f7d47b0SAlexander V. Chernikov 
234981d3153dSAlexander V. Chernikov 	memset(&da->tent, 0, sizeof(da->tent));
235081d3153dSAlexander V. Chernikov 	tent = &da->tent;
235181d3153dSAlexander V. Chernikov 	error = ta->dump_tentry(tc->astate, da->ti, e, tent);
235281d3153dSAlexander V. Chernikov 	if (error != 0)
235381d3153dSAlexander V. Chernikov 		return (error);
235481d3153dSAlexander V. Chernikov 
235581d3153dSAlexander V. Chernikov 	/* Convert current format to previous one */
235681d3153dSAlexander V. Chernikov 	xent->masklen = tent->masklen;
23574bbd1577SAlexander V. Chernikov 	xent->value = tent->v.value;
235881d3153dSAlexander V. Chernikov 	/* Apply some hacks */
2359c21034b7SAlexander V. Chernikov 	if (tc->no.type == IPFW_TABLE_ADDR && tent->subtype == AF_INET) {
236081d3153dSAlexander V. Chernikov 		xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr;
236181d3153dSAlexander V. Chernikov 		xent->flags = IPFW_TCF_INET;
236281d3153dSAlexander V. Chernikov 	} else
236381d3153dSAlexander V. Chernikov 		memcpy(&xent->k, &tent->k, sizeof(xent->k));
236481d3153dSAlexander V. Chernikov 
236581d3153dSAlexander V. Chernikov 	return (0);
23669f7d47b0SAlexander V. Chernikov }
23679f7d47b0SAlexander V. Chernikov 
23689f7d47b0SAlexander V. Chernikov /*
23694dff4ae0SAlexander V. Chernikov  * Helper function to export table algo data
23704dff4ae0SAlexander V. Chernikov  * to tentry format before calling user function.
23714dff4ae0SAlexander V. Chernikov  *
23724dff4ae0SAlexander V. Chernikov  * Returns 0 on success.
23734dff4ae0SAlexander V. Chernikov  */
23744dff4ae0SAlexander V. Chernikov static int
23754dff4ae0SAlexander V. Chernikov prepare_table_tentry(void *e, void *arg)
23764dff4ae0SAlexander V. Chernikov {
23774dff4ae0SAlexander V. Chernikov 	struct dump_args *da;
23784dff4ae0SAlexander V. Chernikov 	struct table_config *tc;
23794dff4ae0SAlexander V. Chernikov 	struct table_algo *ta;
23804dff4ae0SAlexander V. Chernikov 	int error;
23814dff4ae0SAlexander V. Chernikov 
23824dff4ae0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
23834dff4ae0SAlexander V. Chernikov 
23844dff4ae0SAlexander V. Chernikov 	tc = da->tc;
23854dff4ae0SAlexander V. Chernikov 	ta = tc->ta;
23864dff4ae0SAlexander V. Chernikov 
23874dff4ae0SAlexander V. Chernikov 	error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent);
23884dff4ae0SAlexander V. Chernikov 	if (error != 0)
23894dff4ae0SAlexander V. Chernikov 		return (error);
23904dff4ae0SAlexander V. Chernikov 
23914dff4ae0SAlexander V. Chernikov 	da->f(&da->tent, da->farg);
23924dff4ae0SAlexander V. Chernikov 
23934dff4ae0SAlexander V. Chernikov 	return (0);
23944dff4ae0SAlexander V. Chernikov }
23954dff4ae0SAlexander V. Chernikov 
23964dff4ae0SAlexander V. Chernikov /*
23974dff4ae0SAlexander V. Chernikov  * Allow external consumers to read table entries in standard format.
23984dff4ae0SAlexander V. Chernikov  */
23994dff4ae0SAlexander V. Chernikov int
24004dff4ae0SAlexander V. Chernikov ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx,
24014dff4ae0SAlexander V. Chernikov     ta_foreach_f *f, void *arg)
24024dff4ae0SAlexander V. Chernikov {
24034dff4ae0SAlexander V. Chernikov 	struct namedobj_instance *ni;
24044dff4ae0SAlexander V. Chernikov 	struct table_config *tc;
24054dff4ae0SAlexander V. Chernikov 	struct table_algo *ta;
24064dff4ae0SAlexander V. Chernikov 	struct dump_args da;
24074dff4ae0SAlexander V. Chernikov 
24084dff4ae0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
24094dff4ae0SAlexander V. Chernikov 
24104dff4ae0SAlexander V. Chernikov 	tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx);
24114dff4ae0SAlexander V. Chernikov 	if (tc == NULL)
24124dff4ae0SAlexander V. Chernikov 		return (ESRCH);
24134dff4ae0SAlexander V. Chernikov 
24144dff4ae0SAlexander V. Chernikov 	ta = tc->ta;
24154dff4ae0SAlexander V. Chernikov 
24164dff4ae0SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
24174dff4ae0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
24184dff4ae0SAlexander V. Chernikov 	da.tc = tc;
24194dff4ae0SAlexander V. Chernikov 	da.f = f;
24204dff4ae0SAlexander V. Chernikov 	da.farg = arg;
24214dff4ae0SAlexander V. Chernikov 
24224dff4ae0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, prepare_table_tentry, &da);
24234dff4ae0SAlexander V. Chernikov 
24244dff4ae0SAlexander V. Chernikov 	return (0);
24254dff4ae0SAlexander V. Chernikov }
24264dff4ae0SAlexander V. Chernikov 
24274dff4ae0SAlexander V. Chernikov /*
24289f7d47b0SAlexander V. Chernikov  * Table algorithms
24299f7d47b0SAlexander V. Chernikov  */
24303b3a8eb9SGleb Smirnoff 
24319f7d47b0SAlexander V. Chernikov /*
243235e1bbd0SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name.
243335e1bbd0SAlexander V. Chernikov  *
243435e1bbd0SAlexander V. Chernikov  * Returns pointer to algo or NULL.
24359f7d47b0SAlexander V. Chernikov  */
24369f7d47b0SAlexander V. Chernikov static struct table_algo *
24379490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
24389f7d47b0SAlexander V. Chernikov {
24399490a627SAlexander V. Chernikov 	int i, l;
24409490a627SAlexander V. Chernikov 	struct table_algo *ta;
24419f7d47b0SAlexander V. Chernikov 
244257a1cf95SAlexander V. Chernikov 	if (ti->type > IPFW_TABLE_MAXTYPE)
244357a1cf95SAlexander V. Chernikov 		return (NULL);
244457a1cf95SAlexander V. Chernikov 
24459f7d47b0SAlexander V. Chernikov 	/* Search by index */
24469f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
24479f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
24489f7d47b0SAlexander V. Chernikov 			return (NULL);
24499f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
24509f7d47b0SAlexander V. Chernikov 	}
24519f7d47b0SAlexander V. Chernikov 
2452fd0869d5SAlexander V. Chernikov 	if (name == NULL) {
2453fd0869d5SAlexander V. Chernikov 		/* Return default algorithm for given type if set */
2454fd0869d5SAlexander V. Chernikov 		return (tcfg->def_algo[ti->type]);
2455fd0869d5SAlexander V. Chernikov 	}
2456fd0869d5SAlexander V. Chernikov 
2457fd0869d5SAlexander V. Chernikov 	/* Search by name */
24589490a627SAlexander V. Chernikov 	/* TODO: better search */
24599490a627SAlexander V. Chernikov 	for (i = 1; i <= tcfg->algo_count; i++) {
24609490a627SAlexander V. Chernikov 		ta = tcfg->algo[i];
24619490a627SAlexander V. Chernikov 
24629490a627SAlexander V. Chernikov 		/*
24639490a627SAlexander V. Chernikov 		 * One can supply additional algorithm
24649490a627SAlexander V. Chernikov 		 * parameters so we compare only the first word
24659490a627SAlexander V. Chernikov 		 * of supplied name:
2466c21034b7SAlexander V. Chernikov 		 * 'addr:chash hsize=32'
24679490a627SAlexander V. Chernikov 		 * '^^^^^^^^^'
24689490a627SAlexander V. Chernikov 		 *
24699490a627SAlexander V. Chernikov 		 */
24709490a627SAlexander V. Chernikov 		l = strlen(ta->name);
2471fd0869d5SAlexander V. Chernikov 		if (strncmp(name, ta->name, l) != 0)
2472fd0869d5SAlexander V. Chernikov 			continue;
2473fd0869d5SAlexander V. Chernikov 		if (name[l] != '\0' && name[l] != ' ')
2474fd0869d5SAlexander V. Chernikov 			continue;
2475fd0869d5SAlexander V. Chernikov 		/* Check if we're requesting proper table type */
2476fd0869d5SAlexander V. Chernikov 		if (ti->type != 0 && ti->type != ta->type)
2477fd0869d5SAlexander V. Chernikov 			return (NULL);
24789490a627SAlexander V. Chernikov 		return (ta);
24799490a627SAlexander V. Chernikov 	}
24809490a627SAlexander V. Chernikov 
24819490a627SAlexander V. Chernikov 	return (NULL);
24829490a627SAlexander V. Chernikov }
24839490a627SAlexander V. Chernikov 
2484b6ee846eSAlexander V. Chernikov /*
2485b6ee846eSAlexander V. Chernikov  * Register new table algo @ta.
248635e1bbd0SAlexander V. Chernikov  * Stores algo id inside @idx.
2487b6ee846eSAlexander V. Chernikov  *
2488b6ee846eSAlexander V. Chernikov  * Returns 0 on success.
2489b6ee846eSAlexander V. Chernikov  */
24900b565ac0SAlexander V. Chernikov int
24910b565ac0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size,
24920b565ac0SAlexander V. Chernikov     int *idx)
24933b3a8eb9SGleb Smirnoff {
24949f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
24950b565ac0SAlexander V. Chernikov 	struct table_algo *ta_new;
2496b6ee846eSAlexander V. Chernikov 	size_t sz;
24970b565ac0SAlexander V. Chernikov 
24980b565ac0SAlexander V. Chernikov 	if (size > sizeof(struct table_algo))
24990b565ac0SAlexander V. Chernikov 		return (EINVAL);
25000b565ac0SAlexander V. Chernikov 
2501b6ee846eSAlexander V. Chernikov 	/* Check for the required on-stack size for add/del */
2502b6ee846eSAlexander V. Chernikov 	sz = roundup2(ta->ta_buf_size, sizeof(void *));
2503b6ee846eSAlexander V. Chernikov 	if (sz > TA_BUF_SZ)
2504b6ee846eSAlexander V. Chernikov 		return (EINVAL);
2505b6ee846eSAlexander V. Chernikov 
2506fddbbf75SAlexander V. Chernikov 	KASSERT(ta->type <= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE"));
250757a1cf95SAlexander V. Chernikov 
250835e1bbd0SAlexander V. Chernikov 	/* Copy algorithm data to stable storage. */
25090b565ac0SAlexander V. Chernikov 	ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO);
25100b565ac0SAlexander V. Chernikov 	memcpy(ta_new, ta, size);
25113b3a8eb9SGleb Smirnoff 
25129f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
2513b074b7bbSAlexander V. Chernikov 
25149f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
25159f7d47b0SAlexander V. Chernikov 
25160b565ac0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta_new;
25170b565ac0SAlexander V. Chernikov 	ta_new->idx = tcfg->algo_count;
25180b565ac0SAlexander V. Chernikov 
251957a1cf95SAlexander V. Chernikov 	/* Set algorithm as default one for given type */
252057a1cf95SAlexander V. Chernikov 	if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 &&
252157a1cf95SAlexander V. Chernikov 	    tcfg->def_algo[ta_new->type] == NULL)
252257a1cf95SAlexander V. Chernikov 		tcfg->def_algo[ta_new->type] = ta_new;
252357a1cf95SAlexander V. Chernikov 
25240b565ac0SAlexander V. Chernikov 	*idx = ta_new->idx;
25250b565ac0SAlexander V. Chernikov 
25260b565ac0SAlexander V. Chernikov 	return (0);
25270b565ac0SAlexander V. Chernikov }
25280b565ac0SAlexander V. Chernikov 
2529b6ee846eSAlexander V. Chernikov /*
2530b6ee846eSAlexander V. Chernikov  * Unregisters table algo using @idx as id.
2531c8d5d308SAlexander V. Chernikov  * XXX: It is NOT safe to call this function in any place
2532c8d5d308SAlexander V. Chernikov  * other than ipfw instance destroy handler.
2533b6ee846eSAlexander V. Chernikov  */
25340b565ac0SAlexander V. Chernikov void
25350b565ac0SAlexander V. Chernikov ipfw_del_table_algo(struct ip_fw_chain *ch, int idx)
25360b565ac0SAlexander V. Chernikov {
25370b565ac0SAlexander V. Chernikov 	struct tables_config *tcfg;
25380b565ac0SAlexander V. Chernikov 	struct table_algo *ta;
25390b565ac0SAlexander V. Chernikov 
25400b565ac0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
25410b565ac0SAlexander V. Chernikov 
2542b6ee846eSAlexander V. Chernikov 	KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d",
2543b6ee846eSAlexander V. Chernikov 	    idx, tcfg->algo_count));
25440b565ac0SAlexander V. Chernikov 
25450b565ac0SAlexander V. Chernikov 	ta = tcfg->algo[idx];
25460b565ac0SAlexander V. Chernikov 	KASSERT(ta != NULL, ("algo idx %d is NULL", idx));
254757a1cf95SAlexander V. Chernikov 
254857a1cf95SAlexander V. Chernikov 	if (tcfg->def_algo[ta->type] == ta)
254957a1cf95SAlexander V. Chernikov 		tcfg->def_algo[ta->type] = NULL;
255057a1cf95SAlexander V. Chernikov 
25510b565ac0SAlexander V. Chernikov 	free(ta, M_IPFW);
25523b3a8eb9SGleb Smirnoff }
25533b3a8eb9SGleb Smirnoff 
25549d099b4fSAlexander V. Chernikov /*
25559d099b4fSAlexander V. Chernikov  * Lists all table algorithms currently available.
25569d099b4fSAlexander V. Chernikov  * Data layout (v0)(current):
25579d099b4fSAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
25589d099b4fSAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ]
25599d099b4fSAlexander V. Chernikov  *
25609d099b4fSAlexander V. Chernikov  * Returns 0 on success
25619d099b4fSAlexander V. Chernikov  */
25629d099b4fSAlexander V. Chernikov int
25639d099b4fSAlexander V. Chernikov ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd)
25649d099b4fSAlexander V. Chernikov {
25659d099b4fSAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
25669d099b4fSAlexander V. Chernikov 	struct tables_config *tcfg;
25679d099b4fSAlexander V. Chernikov 	ipfw_ta_info *i;
25689d099b4fSAlexander V. Chernikov 	struct table_algo *ta;
25699d099b4fSAlexander V. Chernikov 	uint32_t count, n, size;
25709d099b4fSAlexander V. Chernikov 
25719d099b4fSAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
25729d099b4fSAlexander V. Chernikov 	if (olh == NULL)
25739d099b4fSAlexander V. Chernikov 		return (EINVAL);
25749d099b4fSAlexander V. Chernikov 	if (sd->valsize < olh->size)
25759d099b4fSAlexander V. Chernikov 		return (EINVAL);
25769d099b4fSAlexander V. Chernikov 
25779d099b4fSAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
25789d099b4fSAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
25799d099b4fSAlexander V. Chernikov 	count = tcfg->algo_count;
25809d099b4fSAlexander V. Chernikov 	size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader);
25819d099b4fSAlexander V. Chernikov 
25829d099b4fSAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
25839d099b4fSAlexander V. Chernikov 	olh->count = count;
25849d099b4fSAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_ta_info);
25859d099b4fSAlexander V. Chernikov 
25869d099b4fSAlexander V. Chernikov 	if (size > olh->size) {
25879d099b4fSAlexander V. Chernikov 		olh->size = size;
25889d099b4fSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
25899d099b4fSAlexander V. Chernikov 		return (ENOMEM);
25909d099b4fSAlexander V. Chernikov 	}
25919d099b4fSAlexander V. Chernikov 	olh->size = size;
25929d099b4fSAlexander V. Chernikov 
25939d099b4fSAlexander V. Chernikov 	for (n = 1; n <= count; n++) {
25949d099b4fSAlexander V. Chernikov 		i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i));
25959d099b4fSAlexander V. Chernikov 		KASSERT(i != 0, ("previously checked buffer is not enough"));
25969d099b4fSAlexander V. Chernikov 		ta = tcfg->algo[n];
25979d099b4fSAlexander V. Chernikov 		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
25989d099b4fSAlexander V. Chernikov 		i->type = ta->type;
25999d099b4fSAlexander V. Chernikov 		i->refcnt = ta->refcnt;
26009d099b4fSAlexander V. Chernikov 	}
26019d099b4fSAlexander V. Chernikov 
26029d099b4fSAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
26039d099b4fSAlexander V. Chernikov 
26049d099b4fSAlexander V. Chernikov 	return (0);
26059d099b4fSAlexander V. Chernikov }
26069d099b4fSAlexander V. Chernikov 
2607b074b7bbSAlexander V. Chernikov /*
2608b074b7bbSAlexander V. Chernikov  * Tables rewriting code
2609b074b7bbSAlexander V. Chernikov  */
2610b074b7bbSAlexander V. Chernikov 
2611b074b7bbSAlexander V. Chernikov /*
2612b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
2613b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
2614b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
2615b074b7bbSAlexander V. Chernikov  */
2616b074b7bbSAlexander V. Chernikov static int
2617b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
2618b074b7bbSAlexander V. Chernikov {
2619b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
2620b074b7bbSAlexander V. Chernikov 	int skip;
2621b074b7bbSAlexander V. Chernikov 	uint16_t v;
2622b074b7bbSAlexander V. Chernikov 
2623b074b7bbSAlexander V. Chernikov 	skip = 1;
2624b074b7bbSAlexander V. Chernikov 
2625b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
2626b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
2627b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
2628b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
2629b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
2630c21034b7SAlexander V. Chernikov 		/* Assume ADDR by default */
2631c21034b7SAlexander V. Chernikov 		*ptype = IPFW_TABLE_ADDR;
2632b074b7bbSAlexander V. Chernikov 		skip = 0;
2633b074b7bbSAlexander V. Chernikov 
2634b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
2635b074b7bbSAlexander V. Chernikov 			/*
2636b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
2637b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
2638b074b7bbSAlexander V. Chernikov 			 */
2639b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
2640b074b7bbSAlexander V. Chernikov 			switch (v) {
2641b074b7bbSAlexander V. Chernikov 			case 0:
2642b074b7bbSAlexander V. Chernikov 			case 1:
2643b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
2644b074b7bbSAlexander V. Chernikov 				break;
2645b074b7bbSAlexander V. Chernikov 			case 2:
2646b074b7bbSAlexander V. Chernikov 			case 3:
2647b074b7bbSAlexander V. Chernikov 				/* src/dst port */
2648b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2649b074b7bbSAlexander V. Chernikov 				break;
2650b074b7bbSAlexander V. Chernikov 			case 4:
2651b074b7bbSAlexander V. Chernikov 				/* uid/gid */
2652b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2653b23d5de9SAlexander V. Chernikov 				break;
2654b074b7bbSAlexander V. Chernikov 			case 5:
2655b074b7bbSAlexander V. Chernikov 				/* jid */
2656b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2657b23d5de9SAlexander V. Chernikov 				break;
2658b074b7bbSAlexander V. Chernikov 			case 6:
2659b074b7bbSAlexander V. Chernikov 				/* dscp */
2660b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2661b074b7bbSAlexander V. Chernikov 				break;
2662b074b7bbSAlexander V. Chernikov 			}
2663b074b7bbSAlexander V. Chernikov 		}
2664b074b7bbSAlexander V. Chernikov 		break;
2665b074b7bbSAlexander V. Chernikov 	case O_XMIT:
2666b074b7bbSAlexander V. Chernikov 	case O_RECV:
2667b074b7bbSAlexander V. Chernikov 	case O_VIA:
2668b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
2669b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
2670b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
2671b074b7bbSAlexander V. Chernikov 			break;
2672b074b7bbSAlexander V. Chernikov 
2673b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
26741940fa77SAlexander V. Chernikov 		*puidx = cmdif->p.kidx;
2675b074b7bbSAlexander V. Chernikov 		skip = 0;
2676b074b7bbSAlexander V. Chernikov 		break;
2677914bffb6SAlexander V. Chernikov 	case O_IP_FLOW_LOOKUP:
2678914bffb6SAlexander V. Chernikov 		*puidx = cmd->arg1;
2679914bffb6SAlexander V. Chernikov 		*ptype = IPFW_TABLE_FLOW;
2680914bffb6SAlexander V. Chernikov 		skip = 0;
2681914bffb6SAlexander V. Chernikov 		break;
2682b074b7bbSAlexander V. Chernikov 	}
2683b074b7bbSAlexander V. Chernikov 
2684b074b7bbSAlexander V. Chernikov 	return (skip);
2685b074b7bbSAlexander V. Chernikov }
2686b074b7bbSAlexander V. Chernikov 
2687b074b7bbSAlexander V. Chernikov /*
2688b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
2689b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
2690b074b7bbSAlexander V. Chernikov  */
2691b074b7bbSAlexander V. Chernikov static void
2692b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
2693b074b7bbSAlexander V. Chernikov {
2694b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
2695b074b7bbSAlexander V. Chernikov 
2696b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
2697b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
2698b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
2699b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
2700b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
2701b074b7bbSAlexander V. Chernikov 		break;
2702b074b7bbSAlexander V. Chernikov 	case O_XMIT:
2703b074b7bbSAlexander V. Chernikov 	case O_RECV:
2704b074b7bbSAlexander V. Chernikov 	case O_VIA:
2705b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
2706b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
27071940fa77SAlexander V. Chernikov 		cmdif->p.kidx = idx;
2708b074b7bbSAlexander V. Chernikov 		break;
2709914bffb6SAlexander V. Chernikov 	case O_IP_FLOW_LOOKUP:
2710914bffb6SAlexander V. Chernikov 		cmd->arg1 = idx;
2711914bffb6SAlexander V. Chernikov 		break;
2712b074b7bbSAlexander V. Chernikov 	}
2713b074b7bbSAlexander V. Chernikov }
2714b074b7bbSAlexander V. Chernikov 
2715ac35ff17SAlexander V. Chernikov /*
2716ac35ff17SAlexander V. Chernikov  * Checks table name for validity.
2717ac35ff17SAlexander V. Chernikov  * Enforce basic length checks, the rest
2718ac35ff17SAlexander V. Chernikov  * should be done in userland.
2719ac35ff17SAlexander V. Chernikov  *
2720ac35ff17SAlexander V. Chernikov  * Returns 0 if name is considered valid.
2721ac35ff17SAlexander V. Chernikov  */
2722ac35ff17SAlexander V. Chernikov int
2723ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name)
2724ac35ff17SAlexander V. Chernikov {
2725ac35ff17SAlexander V. Chernikov 	int nsize;
2726ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv = NULL;
2727ac35ff17SAlexander V. Chernikov 
2728ac35ff17SAlexander V. Chernikov 	nsize = sizeof(ntlv->name);
2729ac35ff17SAlexander V. Chernikov 
2730ac35ff17SAlexander V. Chernikov 	if (strnlen(name, nsize) == nsize)
2731ac35ff17SAlexander V. Chernikov 		return (EINVAL);
2732ac35ff17SAlexander V. Chernikov 
2733ac35ff17SAlexander V. Chernikov 	if (name[0] == '\0')
2734ac35ff17SAlexander V. Chernikov 		return (EINVAL);
2735ac35ff17SAlexander V. Chernikov 
2736ac35ff17SAlexander V. Chernikov 	/*
2737ac35ff17SAlexander V. Chernikov 	 * TODO: do some more complicated checks
2738ac35ff17SAlexander V. Chernikov 	 */
2739ac35ff17SAlexander V. Chernikov 
2740ac35ff17SAlexander V. Chernikov 	return (0);
2741ac35ff17SAlexander V. Chernikov }
2742ac35ff17SAlexander V. Chernikov 
2743ac35ff17SAlexander V. Chernikov /*
2744ac35ff17SAlexander V. Chernikov  * Find tablename TLV by @uid.
2745ac35ff17SAlexander V. Chernikov  * Check @tlvs for valid data inside.
2746ac35ff17SAlexander V. Chernikov  *
2747ac35ff17SAlexander V. Chernikov  * Returns pointer to found TLV or NULL.
2748ac35ff17SAlexander V. Chernikov  */
2749ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv *
2750b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
2751b074b7bbSAlexander V. Chernikov {
27529f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2753b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
2754b074b7bbSAlexander V. Chernikov 	int l;
2755b074b7bbSAlexander V. Chernikov 
2756b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
2757b074b7bbSAlexander V. Chernikov 	pe = pa + len;
2758b074b7bbSAlexander V. Chernikov 	l = 0;
2759b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
27609f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
2761b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
2762ac35ff17SAlexander V. Chernikov 
2763ac35ff17SAlexander V. Chernikov 		if (l != sizeof(*ntlv))
2764ac35ff17SAlexander V. Chernikov 			return (NULL);
2765ac35ff17SAlexander V. Chernikov 
2766563b5ab1SAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_TBL_NAME)
2767b074b7bbSAlexander V. Chernikov 			continue;
2768ac35ff17SAlexander V. Chernikov 
2769b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
2770b074b7bbSAlexander V. Chernikov 			continue;
2771b074b7bbSAlexander V. Chernikov 
2772ac35ff17SAlexander V. Chernikov 		if (ipfw_check_table_name(ntlv->name) != 0)
2773ac35ff17SAlexander V. Chernikov 			return (NULL);
2774ac35ff17SAlexander V. Chernikov 
2775ac35ff17SAlexander V. Chernikov 		return (ntlv);
2776b074b7bbSAlexander V. Chernikov 	}
2777b074b7bbSAlexander V. Chernikov 
2778b074b7bbSAlexander V. Chernikov 	return (NULL);
2779b074b7bbSAlexander V. Chernikov }
2780b074b7bbSAlexander V. Chernikov 
2781ac35ff17SAlexander V. Chernikov /*
2782ac35ff17SAlexander V. Chernikov  * Finds table config based on either legacy index
2783ac35ff17SAlexander V. Chernikov  * or name in ntlv.
2784ac35ff17SAlexander V. Chernikov  * Note @ti structure contains unchecked data from userland.
2785ac35ff17SAlexander V. Chernikov  *
2786ac35ff17SAlexander V. Chernikov  * Returns pointer to table_config or NULL.
2787ac35ff17SAlexander V. Chernikov  */
2788b074b7bbSAlexander V. Chernikov static struct table_config *
2789b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
2790b074b7bbSAlexander V. Chernikov {
2791b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
2792b074b7bbSAlexander V. Chernikov 	struct named_object *no;
2793ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2794ac35ff17SAlexander V. Chernikov 	uint32_t set;
2795b074b7bbSAlexander V. Chernikov 
2796b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
2797ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
2798ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
2799b074b7bbSAlexander V. Chernikov 			return (NULL);
2800ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
280140e5f498SAlexander V. Chernikov 
280240e5f498SAlexander V. Chernikov 		/*
280340e5f498SAlexander V. Chernikov 		 * Use set provided by @ti instead of @ntlv one.
280440e5f498SAlexander V. Chernikov 		 * This is needed due to different sets behavior
280540e5f498SAlexander V. Chernikov 		 * controlled by V_fw_tables_sets.
280640e5f498SAlexander V. Chernikov 		 */
280740e5f498SAlexander V. Chernikov 		set = ti->set;
2808b074b7bbSAlexander V. Chernikov 	} else {
2809b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
2810b074b7bbSAlexander V. Chernikov 		name = bname;
2811ac35ff17SAlexander V. Chernikov 		set = 0;
2812b074b7bbSAlexander V. Chernikov 	}
2813b074b7bbSAlexander V. Chernikov 
2814ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, set, name);
2815b074b7bbSAlexander V. Chernikov 
2816b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
2817b074b7bbSAlexander V. Chernikov }
2818b074b7bbSAlexander V. Chernikov 
281935e1bbd0SAlexander V. Chernikov /*
282035e1bbd0SAlexander V. Chernikov  * Allocate new table config structure using
282135e1bbd0SAlexander V. Chernikov  * specified @algo and @aname.
282235e1bbd0SAlexander V. Chernikov  *
282335e1bbd0SAlexander V. Chernikov  * Returns pointer to config or NULL.
282435e1bbd0SAlexander V. Chernikov  */
2825b074b7bbSAlexander V. Chernikov static struct table_config *
282668394ec8SAlexander V. Chernikov alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti,
2827914bffb6SAlexander V. Chernikov     struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype)
2828b074b7bbSAlexander V. Chernikov {
2829b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
2830b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2831b074b7bbSAlexander V. Chernikov 	int error;
2832ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2833ac35ff17SAlexander V. Chernikov 	uint32_t set;
2834b074b7bbSAlexander V. Chernikov 
2835b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
2836ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
2837ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
2838b074b7bbSAlexander V. Chernikov 			return (NULL);
2839ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
2840ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
2841b074b7bbSAlexander V. Chernikov 	} else {
2842b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
2843b074b7bbSAlexander V. Chernikov 		name = bname;
2844ac35ff17SAlexander V. Chernikov 		set = 0;
2845b074b7bbSAlexander V. Chernikov 	}
2846b074b7bbSAlexander V. Chernikov 
2847b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
2848b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
2849fd0869d5SAlexander V. Chernikov 	tc->no.type = ta->type;
2850ac35ff17SAlexander V. Chernikov 	tc->no.set = set;
2851914bffb6SAlexander V. Chernikov 	tc->tflags = tflags;
28529f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
2853b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
2854db785d31SAlexander V. Chernikov 	tc->vtype = vtype;
2855b074b7bbSAlexander V. Chernikov 
2856b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
2857b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
2858b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
2859b074b7bbSAlexander V. Chernikov 	}
2860b074b7bbSAlexander V. Chernikov 
2861b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
2862914bffb6SAlexander V. Chernikov 	error = ta->init(ch, &tc->astate, &tc->ti, aname, tflags);
2863b074b7bbSAlexander V. Chernikov 	if (error != 0) {
2864b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
2865b074b7bbSAlexander V. Chernikov 		return (NULL);
2866b074b7bbSAlexander V. Chernikov 	}
2867b074b7bbSAlexander V. Chernikov 
2868b074b7bbSAlexander V. Chernikov 	return (tc);
2869b074b7bbSAlexander V. Chernikov }
2870b074b7bbSAlexander V. Chernikov 
287135e1bbd0SAlexander V. Chernikov /*
287235e1bbd0SAlexander V. Chernikov  * Destroys table state and config.
287335e1bbd0SAlexander V. Chernikov  */
2874b074b7bbSAlexander V. Chernikov static void
2875b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
2876b074b7bbSAlexander V. Chernikov {
2877b074b7bbSAlexander V. Chernikov 
287835e1bbd0SAlexander V. Chernikov 	KASSERT(tc->linked == 0, ("free() on linked config"));
2879b074b7bbSAlexander V. Chernikov 
28800468c5baSAlexander V. Chernikov 	/*
28810468c5baSAlexander V. Chernikov 	 * We're using ta without any locking/referencing.
28820468c5baSAlexander V. Chernikov 	 * TODO: fix this if we're going to use unloadable algos.
28830468c5baSAlexander V. Chernikov 	 */
288435e1bbd0SAlexander V. Chernikov 	tc->ta->destroy(tc->astate, &tc->ti);
2885b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
2886b074b7bbSAlexander V. Chernikov }
2887b074b7bbSAlexander V. Chernikov 
2888b074b7bbSAlexander V. Chernikov /*
2889b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
2890b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
2891b074b7bbSAlexander V. Chernikov  */
2892b074b7bbSAlexander V. Chernikov static void
28939f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
2894b074b7bbSAlexander V. Chernikov {
2895b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
28969f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
2897b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2898b074b7bbSAlexander V. Chernikov 
28999f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
29009f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
2901b074b7bbSAlexander V. Chernikov 
29029f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2903b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
2904b074b7bbSAlexander V. Chernikov 
2905b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
29069f7d47b0SAlexander V. Chernikov 
29079f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
29089f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
2909b074b7bbSAlexander V. Chernikov 
291068394ec8SAlexander V. Chernikov 	/* Notify algo on real @ti address */
291168394ec8SAlexander V. Chernikov 	if (tc->ta->change_ti != NULL)
291268394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, ti);
291368394ec8SAlexander V. Chernikov 
2914b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
29159d099b4fSAlexander V. Chernikov 	tc->ta->refcnt++;
2916b074b7bbSAlexander V. Chernikov }
2917b074b7bbSAlexander V. Chernikov 
2918b074b7bbSAlexander V. Chernikov /*
2919b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
2920b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
2921b074b7bbSAlexander V. Chernikov  */
2922b074b7bbSAlexander V. Chernikov static void
29239f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
2924b074b7bbSAlexander V. Chernikov {
2925b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
29269f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
2927b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2928b074b7bbSAlexander V. Chernikov 
29299f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
29309f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
2931b074b7bbSAlexander V. Chernikov 
29329f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2933b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
2934b074b7bbSAlexander V. Chernikov 
29359f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
2936b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
29379f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
29389f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
2939b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
29409d099b4fSAlexander V. Chernikov 	tc->ta->refcnt--;
294168394ec8SAlexander V. Chernikov 
294268394ec8SAlexander V. Chernikov 	/* Notify algo on real @ti address */
294368394ec8SAlexander V. Chernikov 	if (tc->ta->change_ti != NULL)
294468394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, NULL);
2945b074b7bbSAlexander V. Chernikov }
2946b074b7bbSAlexander V. Chernikov 
2947a73d728dSAlexander V. Chernikov struct swap_table_args {
2948a73d728dSAlexander V. Chernikov 	int set;
2949a73d728dSAlexander V. Chernikov 	int new_set;
2950a73d728dSAlexander V. Chernikov 	int mv;
2951a73d728dSAlexander V. Chernikov };
2952a73d728dSAlexander V. Chernikov 
2953a73d728dSAlexander V. Chernikov /*
2954a73d728dSAlexander V. Chernikov  * Change set for each matching table.
2955a73d728dSAlexander V. Chernikov  *
2956a73d728dSAlexander V. Chernikov  * Ensure we dispatch each table once by setting/checking ochange
2957a73d728dSAlexander V. Chernikov  * fields.
2958a73d728dSAlexander V. Chernikov  */
2959a73d728dSAlexander V. Chernikov static void
2960a73d728dSAlexander V. Chernikov swap_table_set(struct namedobj_instance *ni, struct named_object *no,
2961a73d728dSAlexander V. Chernikov     void *arg)
2962a73d728dSAlexander V. Chernikov {
2963a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2964a73d728dSAlexander V. Chernikov 	struct swap_table_args *sta;
2965a73d728dSAlexander V. Chernikov 
2966a73d728dSAlexander V. Chernikov 	tc = (struct table_config *)no;
2967a73d728dSAlexander V. Chernikov 	sta = (struct swap_table_args *)arg;
2968a73d728dSAlexander V. Chernikov 
2969a73d728dSAlexander V. Chernikov 	if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0))
2970a73d728dSAlexander V. Chernikov 		return;
2971a73d728dSAlexander V. Chernikov 
2972a73d728dSAlexander V. Chernikov 	if (tc->ochanged != 0)
2973a73d728dSAlexander V. Chernikov 		return;
2974a73d728dSAlexander V. Chernikov 
2975a73d728dSAlexander V. Chernikov 	tc->ochanged = 1;
2976a73d728dSAlexander V. Chernikov 	ipfw_objhash_del(ni, no);
2977a73d728dSAlexander V. Chernikov 	if (no->set == sta->set)
2978a73d728dSAlexander V. Chernikov 		no->set = sta->new_set;
2979a73d728dSAlexander V. Chernikov 	else
2980a73d728dSAlexander V. Chernikov 		no->set = sta->set;
2981a73d728dSAlexander V. Chernikov 	ipfw_objhash_add(ni, no);
2982a73d728dSAlexander V. Chernikov }
2983a73d728dSAlexander V. Chernikov 
2984a73d728dSAlexander V. Chernikov /*
2985a73d728dSAlexander V. Chernikov  * Cleans up ochange field for all tables.
2986a73d728dSAlexander V. Chernikov  */
2987a73d728dSAlexander V. Chernikov static void
2988a73d728dSAlexander V. Chernikov clean_table_set_data(struct namedobj_instance *ni, struct named_object *no,
2989a73d728dSAlexander V. Chernikov     void *arg)
2990a73d728dSAlexander V. Chernikov {
2991a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2992a73d728dSAlexander V. Chernikov 	struct swap_table_args *sta;
2993a73d728dSAlexander V. Chernikov 
2994a73d728dSAlexander V. Chernikov 	tc = (struct table_config *)no;
2995a73d728dSAlexander V. Chernikov 	sta = (struct swap_table_args *)arg;
2996a73d728dSAlexander V. Chernikov 
2997a73d728dSAlexander V. Chernikov 	tc->ochanged = 0;
2998a73d728dSAlexander V. Chernikov }
2999a73d728dSAlexander V. Chernikov 
3000a73d728dSAlexander V. Chernikov /*
3001a73d728dSAlexander V. Chernikov  * Swaps tables within two sets.
3002a73d728dSAlexander V. Chernikov  */
3003a73d728dSAlexander V. Chernikov void
3004a73d728dSAlexander V. Chernikov ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set,
3005a73d728dSAlexander V. Chernikov     uint32_t new_set, int mv)
3006a73d728dSAlexander V. Chernikov {
3007a73d728dSAlexander V. Chernikov 	struct swap_table_args sta;
3008a73d728dSAlexander V. Chernikov 
3009a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
3010a73d728dSAlexander V. Chernikov 
3011a73d728dSAlexander V. Chernikov 	sta.set = set;
3012a73d728dSAlexander V. Chernikov 	sta.new_set = new_set;
3013a73d728dSAlexander V. Chernikov 	sta.mv = mv;
3014a73d728dSAlexander V. Chernikov 
3015a73d728dSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta);
3016a73d728dSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta);
3017a73d728dSAlexander V. Chernikov }
3018a73d728dSAlexander V. Chernikov 
3019a73d728dSAlexander V. Chernikov /*
3020a73d728dSAlexander V. Chernikov  * Move all tables which are reference by rules in @rr to set @new_set.
3021a73d728dSAlexander V. Chernikov  * Makes sure that all relevant tables are referenced ONLLY by given rules.
3022a73d728dSAlexander V. Chernikov  *
3023a73d728dSAlexander V. Chernikov  * Retuns 0 on success,
3024a73d728dSAlexander V. Chernikov  */
3025a73d728dSAlexander V. Chernikov int
3026a73d728dSAlexander V. Chernikov ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
3027a73d728dSAlexander V. Chernikov     uint32_t new_set)
3028a73d728dSAlexander V. Chernikov {
3029a73d728dSAlexander V. Chernikov 	struct ip_fw *rule;
3030a73d728dSAlexander V. Chernikov 	struct table_config *tc;
3031a73d728dSAlexander V. Chernikov 	struct named_object *no;
3032a73d728dSAlexander V. Chernikov 	struct namedobj_instance *ni;
3033a73d728dSAlexander V. Chernikov 	int bad, i, l, cmdlen;
3034a73d728dSAlexander V. Chernikov 	uint16_t kidx;
3035a73d728dSAlexander V. Chernikov 	uint8_t type;
3036a73d728dSAlexander V. Chernikov 	ipfw_insn *cmd;
3037a73d728dSAlexander V. Chernikov 
3038a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
3039a73d728dSAlexander V. Chernikov 
3040a73d728dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
3041a73d728dSAlexander V. Chernikov 
3042a73d728dSAlexander V. Chernikov 	/* Stage 1: count number of references by given rules */
3043a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
3044a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
3045a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
3046a73d728dSAlexander V. Chernikov 			continue;
3047a73d728dSAlexander V. Chernikov 
3048a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
3049a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
3050a73d728dSAlexander V. Chernikov 		cmdlen = 0;
3051a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3052a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
3053a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3054a73d728dSAlexander V. Chernikov 				continue;
3055a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
3056a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
3057a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
3058a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
3059a73d728dSAlexander V. Chernikov 			tc->ocount++;
3060a73d728dSAlexander V. Chernikov 		}
3061a73d728dSAlexander V. Chernikov 
3062a73d728dSAlexander V. Chernikov 	}
3063a73d728dSAlexander V. Chernikov 
3064a73d728dSAlexander V. Chernikov 	/* Stage 2: verify "ownership" */
3065a73d728dSAlexander V. Chernikov 	bad = 0;
3066a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
3067a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
3068a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
3069a73d728dSAlexander V. Chernikov 			continue;
3070a73d728dSAlexander V. Chernikov 
3071a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
3072a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
3073a73d728dSAlexander V. Chernikov 		cmdlen = 0;
3074a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3075a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
3076a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3077a73d728dSAlexander V. Chernikov 				continue;
3078a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
3079a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
3080a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
3081a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
3082a73d728dSAlexander V. Chernikov 			if (tc->no.refcnt != tc->ocount) {
3083a73d728dSAlexander V. Chernikov 
3084a73d728dSAlexander V. Chernikov 				/*
3085a73d728dSAlexander V. Chernikov 				 * Number of references differ:
3086a73d728dSAlexander V. Chernikov 				 * Other rule(s) are holding reference to given
3087a73d728dSAlexander V. Chernikov 				 * table, so it is not possible to change its set.
3088a73d728dSAlexander V. Chernikov 				 *
3089a73d728dSAlexander V. Chernikov 				 * Note that refcnt may account
3090a73d728dSAlexander V. Chernikov 				 * references to some going-to-be-added rules.
3091a73d728dSAlexander V. Chernikov 				 * Since we don't know their numbers (and event
3092a73d728dSAlexander V. Chernikov 				 * if they will be added) it is perfectly OK
3093a73d728dSAlexander V. Chernikov 				 * to return error here.
3094a73d728dSAlexander V. Chernikov 				 */
3095a73d728dSAlexander V. Chernikov 				bad = 1;
3096a73d728dSAlexander V. Chernikov 				break;
3097a73d728dSAlexander V. Chernikov 			}
3098a73d728dSAlexander V. Chernikov 		}
3099a73d728dSAlexander V. Chernikov 
3100a73d728dSAlexander V. Chernikov 		if (bad != 0)
3101a73d728dSAlexander V. Chernikov 			break;
3102a73d728dSAlexander V. Chernikov 	}
3103a73d728dSAlexander V. Chernikov 
3104a73d728dSAlexander V. Chernikov 	/* Stage 3: change set or cleanup */
3105a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
3106a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
3107a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
3108a73d728dSAlexander V. Chernikov 			continue;
3109a73d728dSAlexander V. Chernikov 
3110a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
3111a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
3112a73d728dSAlexander V. Chernikov 		cmdlen = 0;
3113a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3114a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
3115a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3116a73d728dSAlexander V. Chernikov 				continue;
3117a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
3118a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
3119a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
3120a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
3121a73d728dSAlexander V. Chernikov 
3122a73d728dSAlexander V. Chernikov 			tc->ocount = 0;
3123a73d728dSAlexander V. Chernikov 			if (bad != 0)
3124a73d728dSAlexander V. Chernikov 				continue;
3125a73d728dSAlexander V. Chernikov 
3126a73d728dSAlexander V. Chernikov 			/* Actually change set. */
3127a73d728dSAlexander V. Chernikov 			ipfw_objhash_del(ni, no);
3128a73d728dSAlexander V. Chernikov 			no->set = new_set;
3129a73d728dSAlexander V. Chernikov 			ipfw_objhash_add(ni, no);
3130a73d728dSAlexander V. Chernikov 		}
3131a73d728dSAlexander V. Chernikov 	}
3132a73d728dSAlexander V. Chernikov 
3133a73d728dSAlexander V. Chernikov 	return (bad);
3134a73d728dSAlexander V. Chernikov }
3135a73d728dSAlexander V. Chernikov 
3136b074b7bbSAlexander V. Chernikov /*
3137f99fbf96SAlexander V. Chernikov  * Finds and bumps refcount for tables referenced by given @rule.
3138e5eec6ddSAlexander V. Chernikov  * Auto-creates non-existing tables.
3139f99fbf96SAlexander V. Chernikov  * Fills in @oib array with userland/kernel indexes.
3140f99fbf96SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
3141f99fbf96SAlexander V. Chernikov  *
3142f99fbf96SAlexander V. Chernikov  * Returns 0 on success.
3143f99fbf96SAlexander V. Chernikov  */
3144f99fbf96SAlexander V. Chernikov static int
3145e5eec6ddSAlexander V. Chernikov find_ref_rule_tables(struct ip_fw_chain *ch, struct ip_fw *rule,
3146f99fbf96SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
3147f99fbf96SAlexander V. Chernikov {
3148f99fbf96SAlexander V. Chernikov 	struct table_config *tc;
3149f99fbf96SAlexander V. Chernikov 	struct namedobj_instance *ni;
3150f99fbf96SAlexander V. Chernikov 	struct named_object *no;
3151f99fbf96SAlexander V. Chernikov 	int cmdlen, error, l, numnew;
3152f99fbf96SAlexander V. Chernikov 	uint16_t kidx;
3153f99fbf96SAlexander V. Chernikov 	ipfw_insn *cmd;
3154f99fbf96SAlexander V. Chernikov 	struct obj_idx *pidx, *pidx_first, *p;
3155f99fbf96SAlexander V. Chernikov 
3156f99fbf96SAlexander V. Chernikov 	pidx_first = *oib;
3157f99fbf96SAlexander V. Chernikov 	pidx = pidx_first;
3158f99fbf96SAlexander V. Chernikov 	l = rule->cmd_len;
3159f99fbf96SAlexander V. Chernikov 	cmd = rule->cmd;
3160f99fbf96SAlexander V. Chernikov 	cmdlen = 0;
3161f99fbf96SAlexander V. Chernikov 	error = 0;
3162f99fbf96SAlexander V. Chernikov 	numnew = 0;
3163f99fbf96SAlexander V. Chernikov 
3164f99fbf96SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
3165f99fbf96SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
3166f99fbf96SAlexander V. Chernikov 
3167e5eec6ddSAlexander V. Chernikov 	/* Increase refcount on each existing referenced table. */
3168f99fbf96SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3169f99fbf96SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3170f99fbf96SAlexander V. Chernikov 
3171f99fbf96SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
3172f99fbf96SAlexander V. Chernikov 			continue;
3173f99fbf96SAlexander V. Chernikov 
3174f99fbf96SAlexander V. Chernikov 		pidx->uidx = ti->uidx;
3175f99fbf96SAlexander V. Chernikov 		pidx->type = ti->type;
3176f99fbf96SAlexander V. Chernikov 
3177f99fbf96SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
3178f99fbf96SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
3179f99fbf96SAlexander V. Chernikov 				/* Incompatible types */
3180f99fbf96SAlexander V. Chernikov 				error = EINVAL;
3181f99fbf96SAlexander V. Chernikov 				break;
3182f99fbf96SAlexander V. Chernikov 			}
3183f99fbf96SAlexander V. Chernikov 
3184f99fbf96SAlexander V. Chernikov 			/* Reference found table and save kidx */
3185f99fbf96SAlexander V. Chernikov 			tc->no.refcnt++;
3186f99fbf96SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
3187f99fbf96SAlexander V. Chernikov 			pidx++;
3188f99fbf96SAlexander V. Chernikov 			continue;
3189f99fbf96SAlexander V. Chernikov 		}
3190f99fbf96SAlexander V. Chernikov 
3191f99fbf96SAlexander V. Chernikov 		/*
3192f99fbf96SAlexander V. Chernikov 		 * Compability stuff for old clients:
3193f99fbf96SAlexander V. Chernikov 		 * prepare to manually create non-existing tables.
3194f99fbf96SAlexander V. Chernikov 		 */
3195f99fbf96SAlexander V. Chernikov 		pidx++;
3196f99fbf96SAlexander V. Chernikov 		numnew++;
3197f99fbf96SAlexander V. Chernikov 	}
3198f99fbf96SAlexander V. Chernikov 
3199f99fbf96SAlexander V. Chernikov 	if (error != 0) {
3200f99fbf96SAlexander V. Chernikov 		/* Unref everything we have already done */
3201f99fbf96SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
3202f99fbf96SAlexander V. Chernikov 			if (p->kidx == 0)
3203f99fbf96SAlexander V. Chernikov 				continue;
3204f99fbf96SAlexander V. Chernikov 
3205f99fbf96SAlexander V. Chernikov 			/* Find & unref by existing idx */
3206f99fbf96SAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
3207f99fbf96SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
3208f99fbf96SAlexander V. Chernikov 			    p->kidx));
3209f99fbf96SAlexander V. Chernikov 
3210f99fbf96SAlexander V. Chernikov 			no->refcnt--;
3211f99fbf96SAlexander V. Chernikov 		}
3212f99fbf96SAlexander V. Chernikov 	}
3213f99fbf96SAlexander V. Chernikov 
3214f99fbf96SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3215f99fbf96SAlexander V. Chernikov 
3216f99fbf96SAlexander V. Chernikov 	if (numnew == 0) {
3217f99fbf96SAlexander V. Chernikov 		*oib = pidx;
3218f99fbf96SAlexander V. Chernikov 		return (error);
3219f99fbf96SAlexander V. Chernikov 	}
3220f99fbf96SAlexander V. Chernikov 
3221f99fbf96SAlexander V. Chernikov 	/*
3222f99fbf96SAlexander V. Chernikov 	 * Compatibility stuff: do actual creation for non-existing,
3223f99fbf96SAlexander V. Chernikov 	 * but referenced tables.
3224f99fbf96SAlexander V. Chernikov 	 */
3225f99fbf96SAlexander V. Chernikov 	for (p = pidx_first; p < pidx; p++) {
3226f99fbf96SAlexander V. Chernikov 		if (p->kidx != 0)
3227f99fbf96SAlexander V. Chernikov 			continue;
3228f99fbf96SAlexander V. Chernikov 
3229f99fbf96SAlexander V. Chernikov 		ti->uidx = p->uidx;
3230f99fbf96SAlexander V. Chernikov 		ti->type = p->type;
3231f99fbf96SAlexander V. Chernikov 		ti->atype = 0;
3232f99fbf96SAlexander V. Chernikov 
3233f99fbf96SAlexander V. Chernikov 		error = create_table_compat(ch, ti, NULL, NULL, &kidx);
3234f99fbf96SAlexander V. Chernikov 		if (error == 0) {
3235f99fbf96SAlexander V. Chernikov 			p->kidx = kidx;
3236f99fbf96SAlexander V. Chernikov 			continue;
3237f99fbf96SAlexander V. Chernikov 		}
3238f99fbf96SAlexander V. Chernikov 
3239f99fbf96SAlexander V. Chernikov 		/* Error. We have to drop references */
3240f99fbf96SAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
3241f99fbf96SAlexander V. Chernikov 		for (p = pidx_first; p < pidx; p++) {
3242f99fbf96SAlexander V. Chernikov 			if (p->kidx == 0)
3243f99fbf96SAlexander V. Chernikov 				continue;
3244f99fbf96SAlexander V. Chernikov 
3245f99fbf96SAlexander V. Chernikov 			/* Find & unref by existing idx */
3246f99fbf96SAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
3247f99fbf96SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
3248f99fbf96SAlexander V. Chernikov 			    p->kidx));
3249f99fbf96SAlexander V. Chernikov 
3250f99fbf96SAlexander V. Chernikov 			no->refcnt--;
3251f99fbf96SAlexander V. Chernikov 		}
3252f99fbf96SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
3253f99fbf96SAlexander V. Chernikov 
3254f99fbf96SAlexander V. Chernikov 		return (error);
3255f99fbf96SAlexander V. Chernikov 	}
3256f99fbf96SAlexander V. Chernikov 
3257f99fbf96SAlexander V. Chernikov 	*oib = pidx;
3258f99fbf96SAlexander V. Chernikov 
3259f99fbf96SAlexander V. Chernikov 	return (error);
3260f99fbf96SAlexander V. Chernikov }
3261f99fbf96SAlexander V. Chernikov 
3262f99fbf96SAlexander V. Chernikov /*
3263f99fbf96SAlexander V. Chernikov  * Remove references from every table used in @rule.
3264f99fbf96SAlexander V. Chernikov  */
3265f99fbf96SAlexander V. Chernikov void
3266e5eec6ddSAlexander V. Chernikov ipfw_unref_rule_tables(struct ip_fw_chain *chain, struct ip_fw *rule)
3267f99fbf96SAlexander V. Chernikov {
3268f99fbf96SAlexander V. Chernikov 	int cmdlen, l;
3269f99fbf96SAlexander V. Chernikov 	ipfw_insn *cmd;
3270f99fbf96SAlexander V. Chernikov 	struct namedobj_instance *ni;
3271f99fbf96SAlexander V. Chernikov 	struct named_object *no;
3272f99fbf96SAlexander V. Chernikov 	uint16_t kidx;
3273f99fbf96SAlexander V. Chernikov 	uint8_t type;
3274f99fbf96SAlexander V. Chernikov 
3275f99fbf96SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(chain);
3276f99fbf96SAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
3277f99fbf96SAlexander V. Chernikov 
3278f99fbf96SAlexander V. Chernikov 	l = rule->cmd_len;
3279f99fbf96SAlexander V. Chernikov 	cmd = rule->cmd;
3280f99fbf96SAlexander V. Chernikov 	cmdlen = 0;
3281f99fbf96SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3282f99fbf96SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3283f99fbf96SAlexander V. Chernikov 
3284f99fbf96SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
3285f99fbf96SAlexander V. Chernikov 			continue;
3286f99fbf96SAlexander V. Chernikov 
3287f99fbf96SAlexander V. Chernikov 		no = ipfw_objhash_lookup_kidx(ni, kidx);
3288f99fbf96SAlexander V. Chernikov 
3289f99fbf96SAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
3290f99fbf96SAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
3291f99fbf96SAlexander V. Chernikov 		    no->type, type, kidx));
3292f99fbf96SAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
3293f99fbf96SAlexander V. Chernikov 		    kidx, no->refcnt));
3294f99fbf96SAlexander V. Chernikov 
3295f99fbf96SAlexander V. Chernikov 		no->refcnt--;
3296f99fbf96SAlexander V. Chernikov 	}
3297f99fbf96SAlexander V. Chernikov }
3298f99fbf96SAlexander V. Chernikov 
3299f99fbf96SAlexander V. Chernikov /*
3300b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
3301b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
330235e1bbd0SAlexander V. Chernikov  * Convert tables matching '/^\d+$/' to their atoi() value.
330335e1bbd0SAlexander V. Chernikov  * Use number 65535 for other tables.
3304b074b7bbSAlexander V. Chernikov  *
3305b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
3306b074b7bbSAlexander V. Chernikov  */
3307b074b7bbSAlexander V. Chernikov int
33087e767c79SAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule)
3309b074b7bbSAlexander V. Chernikov {
33101832a7b3SAlexander V. Chernikov 	int cmdlen, error, l;
3311b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
33121832a7b3SAlexander V. Chernikov 	uint16_t kidx, uidx;
3313b074b7bbSAlexander V. Chernikov 	uint8_t type;
3314b074b7bbSAlexander V. Chernikov 	struct named_object *no;
3315b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
3316b074b7bbSAlexander V. Chernikov 
3317b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
33181832a7b3SAlexander V. Chernikov 	error = 0;
3319b074b7bbSAlexander V. Chernikov 
3320b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
3321b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
3322b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
3323b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3324b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3325b074b7bbSAlexander V. Chernikov 
3326b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
3327b074b7bbSAlexander V. Chernikov 			continue;
3328b074b7bbSAlexander V. Chernikov 
3329ac35ff17SAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL)
3330b074b7bbSAlexander V. Chernikov 			return (1);
3331b074b7bbSAlexander V. Chernikov 
33321832a7b3SAlexander V. Chernikov 		uidx = no->uidx;
33331832a7b3SAlexander V. Chernikov 		if (no->compat == 0) {
3334b074b7bbSAlexander V. Chernikov 
33351832a7b3SAlexander V. Chernikov 			/*
33361832a7b3SAlexander V. Chernikov 			 * We are called via legacy opcode.
33371832a7b3SAlexander V. Chernikov 			 * Save error and show table as fake number
33381832a7b3SAlexander V. Chernikov 			 * not to make ipfw(8) hang.
33391832a7b3SAlexander V. Chernikov 			 */
33401832a7b3SAlexander V. Chernikov 			uidx = 65535;
33411832a7b3SAlexander V. Chernikov 			error = 2;
3342b074b7bbSAlexander V. Chernikov 		}
3343b074b7bbSAlexander V. Chernikov 
33441832a7b3SAlexander V. Chernikov 		update_table_opcode(cmd, uidx);
33451832a7b3SAlexander V. Chernikov 	}
33461832a7b3SAlexander V. Chernikov 
33471832a7b3SAlexander V. Chernikov 	return (error);
3348b074b7bbSAlexander V. Chernikov }
3349b074b7bbSAlexander V. Chernikov 
3350563b5ab1SAlexander V. Chernikov /*
3351b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
3352b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
3353b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
3354b074b7bbSAlexander V. Chernikov  *
3355b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
3356b074b7bbSAlexander V. Chernikov  */
3357b074b7bbSAlexander V. Chernikov int
3358b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
3359b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
3360b074b7bbSAlexander V. Chernikov {
33610468c5baSAlexander V. Chernikov 	int cmdlen, error, l;
3362b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
3363b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
3364b074b7bbSAlexander V. Chernikov 	uint8_t type;
3365b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
33669490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
3367b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
3368b074b7bbSAlexander V. Chernikov 
3369b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
3370b074b7bbSAlexander V. Chernikov 
3371b074b7bbSAlexander V. Chernikov 	/*
3372b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
3373b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
3374b074b7bbSAlexander V. Chernikov 	 */
3375b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
3376b074b7bbSAlexander V. Chernikov 		/* Stack */
33779490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
3378b074b7bbSAlexander V. Chernikov 	} else
33799490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
3380b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
3381b074b7bbSAlexander V. Chernikov 
33829490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
3383b074b7bbSAlexander V. Chernikov 	error = 0;
3384b074b7bbSAlexander V. Chernikov 	type = 0;
3385b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
33861832a7b3SAlexander V. Chernikov 
33871832a7b3SAlexander V. Chernikov 	/*
33881832a7b3SAlexander V. Chernikov 	 * Use default set for looking up tables (old way) or
33891832a7b3SAlexander V. Chernikov 	 * use set rule is assigned to (new way).
33901832a7b3SAlexander V. Chernikov 	 */
33911832a7b3SAlexander V. Chernikov 	ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0;
33926c2997ffSAlexander V. Chernikov 	if (ci->ctlv != NULL) {
33936c2997ffSAlexander V. Chernikov 		ti.tlvs = (void *)(ci->ctlv + 1);
33946c2997ffSAlexander V. Chernikov 		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
33956c2997ffSAlexander V. Chernikov 	}
3396b074b7bbSAlexander V. Chernikov 
33970468c5baSAlexander V. Chernikov 	/* Reference all used tables */
3398e5eec6ddSAlexander V. Chernikov 	error = find_ref_rule_tables(chain, ci->krule, ci, &pidx_last, &ti);
33990468c5baSAlexander V. Chernikov 	if (error != 0)
34009f7d47b0SAlexander V. Chernikov 		goto free;
3401b074b7bbSAlexander V. Chernikov 
34029f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
34039f7d47b0SAlexander V. Chernikov 
3404b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
3405b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
3406b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
3407b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
34089490a627SAlexander V. Chernikov 	p = pidx_first;
3409b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3410b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3411b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
3412b074b7bbSAlexander V. Chernikov 			continue;
34139490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
34149490a627SAlexander V. Chernikov 		p++;
3415b074b7bbSAlexander V. Chernikov 	}
3416b074b7bbSAlexander V. Chernikov 
3417b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
3418b074b7bbSAlexander V. Chernikov 
3419b074b7bbSAlexander V. Chernikov free:
34209490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
34219490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
3422b074b7bbSAlexander V. Chernikov 
3423b074b7bbSAlexander V. Chernikov 	return (error);
3424b074b7bbSAlexander V. Chernikov }
3425b074b7bbSAlexander V. Chernikov 
3426