xref: /freebsd/sys/netpfil/ipfw/ip_fw_table.c (revision d3b00c08)
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  *
649f7d47b0SAlexander V. Chernikov  * `no.type` represents lookup key type (cidr, 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);
1102d99a349SAlexander V. Chernikov static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
1112d99a349SAlexander V. Chernikov     struct sockopt_data *sd);
112ac35ff17SAlexander V. Chernikov static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
113ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i);
11481d3153dSAlexander V. Chernikov static int dump_table_tentry(void *e, void *arg);
115f1220db8SAlexander V. Chernikov static int dump_table_xentry(void *e, void *arg);
116b074b7bbSAlexander V. Chernikov 
1172d99a349SAlexander V. Chernikov static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
1182d99a349SAlexander V. Chernikov static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
119db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
120ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
121db785d31SAlexander V. Chernikov static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
122ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd);
123a73d728dSAlexander V. Chernikov static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
12446d52008SAlexander V. Chernikov     struct tid_info *b);
125ac35ff17SAlexander V. Chernikov 
126b6ee846eSAlexander V. Chernikov static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
127b6ee846eSAlexander V. Chernikov     struct table_info *ti, uint32_t count);
128ac35ff17SAlexander V. Chernikov static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
129d3a4f924SAlexander V. Chernikov 
1309f7d47b0SAlexander V. Chernikov static struct table_algo *find_table_algo(struct tables_config *tableconf,
1319490a627SAlexander V. Chernikov     struct tid_info *ti, char *name);
132b074b7bbSAlexander V. Chernikov 
13346d52008SAlexander V. Chernikov static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
13446d52008SAlexander V. Chernikov static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti);
135a73d728dSAlexander V. Chernikov static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
13646d52008SAlexander V. Chernikov 
137b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_TCFG(chain)	((struct tables_config *)(chain)->tblcfg)
138b074b7bbSAlexander V. Chernikov #define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
1399f7d47b0SAlexander V. Chernikov #define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
140b074b7bbSAlexander V. Chernikov 
141b6ee846eSAlexander V. Chernikov #define	TA_BUF_SZ	128	/* On-stack buffer for add/delete state */
142b6ee846eSAlexander V. Chernikov 
1433a845e10SAlexander V. Chernikov /*
1443a845e10SAlexander V. Chernikov  * Checks if we're able to insert/update entry @tei into table
1453a845e10SAlexander V. Chernikov  * w.r.t @tc limits.
1463a845e10SAlexander V. Chernikov  * May alter @tei to indicate insertion error / insert
1473a845e10SAlexander V. Chernikov  * options.
1483a845e10SAlexander V. Chernikov  *
1493a845e10SAlexander V. Chernikov  * Returns 0 if operation can be performed/
1503a845e10SAlexander V. Chernikov  */
1513a845e10SAlexander V. Chernikov static int
1523a845e10SAlexander V. Chernikov check_table_limit(struct table_config *tc, struct tentry_info *tei)
1533a845e10SAlexander V. Chernikov {
154b074b7bbSAlexander V. Chernikov 
1553a845e10SAlexander V. Chernikov 	if (tc->limit == 0 || tc->count < tc->limit)
1563a845e10SAlexander V. Chernikov 		return (0);
1573a845e10SAlexander V. Chernikov 
1583a845e10SAlexander V. Chernikov 	if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
1593a845e10SAlexander V. Chernikov 		/* Notify userland on error cause */
1603a845e10SAlexander V. Chernikov 		tei->flags |= TEI_FLAGS_LIMIT;
1613a845e10SAlexander V. Chernikov 		return (EFBIG);
1623a845e10SAlexander V. Chernikov 	}
1633a845e10SAlexander V. Chernikov 
1643a845e10SAlexander V. Chernikov 	/*
1653a845e10SAlexander V. Chernikov 	 * We have UPDATE flag set.
1663a845e10SAlexander V. Chernikov 	 * Permit updating record (if found),
1673a845e10SAlexander V. Chernikov 	 * but restrict adding new one since we've
1683a845e10SAlexander V. Chernikov 	 * already hit the limit.
1693a845e10SAlexander V. Chernikov 	 */
1703a845e10SAlexander V. Chernikov 	tei->flags |= TEI_FLAGS_DONTADD;
1713a845e10SAlexander V. Chernikov 
1723a845e10SAlexander V. Chernikov 	return (0);
1733a845e10SAlexander V. Chernikov }
1743a845e10SAlexander V. Chernikov 
1753a845e10SAlexander V. Chernikov /*
1761bc0d457SAlexander V. Chernikov  * Convert algorithm callback return code into
1771bc0d457SAlexander V. Chernikov  * one of pre-defined states known by userland.
1783a845e10SAlexander V. Chernikov  */
1791bc0d457SAlexander V. Chernikov static void
1801bc0d457SAlexander V. Chernikov store_tei_result(struct tentry_info *tei, int do_add, int error, uint32_t num)
1813b3a8eb9SGleb Smirnoff {
1821bc0d457SAlexander V. Chernikov 	int flag;
1833b3a8eb9SGleb Smirnoff 
1841bc0d457SAlexander V. Chernikov 	flag = 0;
1851bc0d457SAlexander V. Chernikov 
1861bc0d457SAlexander V. Chernikov 	switch (error) {
1871bc0d457SAlexander V. Chernikov 	case 0:
1881bc0d457SAlexander V. Chernikov 		if (do_add && num != 0)
1891bc0d457SAlexander V. Chernikov 			flag = TEI_FLAGS_ADDED;
1901bc0d457SAlexander V. Chernikov 		if (do_add == 0)
1911bc0d457SAlexander V. Chernikov 			flag = TEI_FLAGS_DELETED;
1921bc0d457SAlexander V. Chernikov 		break;
1931bc0d457SAlexander V. Chernikov 	case ENOENT:
1941bc0d457SAlexander V. Chernikov 		flag = TEI_FLAGS_NOTFOUND;
1951bc0d457SAlexander V. Chernikov 		break;
1961bc0d457SAlexander V. Chernikov 	case EEXIST:
1971bc0d457SAlexander V. Chernikov 		flag = TEI_FLAGS_EXISTS;
1981bc0d457SAlexander V. Chernikov 		break;
1991bc0d457SAlexander V. Chernikov 	default:
2001bc0d457SAlexander V. Chernikov 		flag = TEI_FLAGS_ERROR;
2011bc0d457SAlexander V. Chernikov 	}
2021bc0d457SAlexander V. Chernikov 
2031bc0d457SAlexander V. Chernikov 	tei->flags |= flag;
2041bc0d457SAlexander V. Chernikov }
2059f7d47b0SAlexander V. Chernikov 
206f99fbf96SAlexander V. Chernikov /*
207f99fbf96SAlexander V. Chernikov  * Creates and references table with default parameters.
208f99fbf96SAlexander V. Chernikov  * Saves table config, algo and allocated kidx info @ptc, @pta and
209f99fbf96SAlexander V. Chernikov  * @pkidx if non-zero.
210f99fbf96SAlexander V. Chernikov  * Used for table auto-creation to support old binaries.
211f99fbf96SAlexander V. Chernikov  *
212f99fbf96SAlexander V. Chernikov  * Returns 0 on success.
213f99fbf96SAlexander V. Chernikov  */
2140468c5baSAlexander V. Chernikov static int
2150468c5baSAlexander V. Chernikov create_table_compat(struct ip_fw_chain *ch, struct tid_info *ti,
2160468c5baSAlexander V. Chernikov     struct table_config **ptc, struct table_algo **pta, uint16_t *pkidx)
2170468c5baSAlexander V. Chernikov {
2180468c5baSAlexander V. Chernikov 	ipfw_xtable_info xi;
2190468c5baSAlexander V. Chernikov 	int error;
2200468c5baSAlexander V. Chernikov 
2210468c5baSAlexander V. Chernikov 	memset(&xi, 0, sizeof(xi));
222f99fbf96SAlexander V. Chernikov 	/* Set u32 as default value type for legacy clients */
2230468c5baSAlexander V. Chernikov 	xi.vtype = IPFW_VTYPE_U32;
2240468c5baSAlexander V. Chernikov 
2250468c5baSAlexander V. Chernikov 	error = create_table_internal(ch, ti, NULL, &xi, ptc, pta, pkidx, 1);
2260468c5baSAlexander V. Chernikov 	if (error != 0)
2270468c5baSAlexander V. Chernikov 		return (error);
2280468c5baSAlexander V. Chernikov 
2290468c5baSAlexander V. Chernikov 	return (0);
2300468c5baSAlexander V. Chernikov }
2310468c5baSAlexander V. Chernikov 
2329f7d47b0SAlexander V. Chernikov /*
2331bc0d457SAlexander V. Chernikov  * Find and reference existing table optionally
2341bc0d457SAlexander V. Chernikov  * creating new one.
2351bc0d457SAlexander V. Chernikov  *
2361bc0d457SAlexander V. Chernikov  * Saves found table config/table algo into @ptc / @pta.
2371bc0d457SAlexander V. Chernikov  * Returns 0 if table was found/created and referenced
2381bc0d457SAlexander V. Chernikov  * or non-zero return code.
2399f7d47b0SAlexander V. Chernikov  */
2401bc0d457SAlexander V. Chernikov static int
2411bc0d457SAlexander V. Chernikov find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti,
2421bc0d457SAlexander V. Chernikov     struct tentry_info *tei, uint32_t count, int do_add,
2431bc0d457SAlexander V. Chernikov     struct table_config **ptc, struct table_algo **pta)
2441bc0d457SAlexander V. Chernikov {
2451bc0d457SAlexander V. Chernikov 	struct namedobj_instance *ni;
2461bc0d457SAlexander V. Chernikov 	struct table_config *tc;
2471bc0d457SAlexander V. Chernikov 	struct table_algo *ta;
2481bc0d457SAlexander V. Chernikov 	int error;
2491bc0d457SAlexander V. Chernikov 
2501bc0d457SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
2511bc0d457SAlexander V. Chernikov 
2521bc0d457SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2531bc0d457SAlexander V. Chernikov 	tc = NULL;
2549f7d47b0SAlexander V. Chernikov 	ta = NULL;
2559f7d47b0SAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) != NULL) {
2569f7d47b0SAlexander V. Chernikov 		/* check table type */
2579f7d47b0SAlexander V. Chernikov 		if (tc->no.type != ti->type) {
2589f7d47b0SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
2593b3a8eb9SGleb Smirnoff 			return (EINVAL);
2603b3a8eb9SGleb Smirnoff 		}
2613b3a8eb9SGleb Smirnoff 
2624f43138aSAlexander V. Chernikov 		if (tc->locked != 0) {
2634f43138aSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
2644f43138aSAlexander V. Chernikov 			return (EACCES);
2654f43138aSAlexander V. Chernikov 		}
2664f43138aSAlexander V. Chernikov 
2674c0c07a5SAlexander V. Chernikov 		/* Try to exit early on limit hit */
2681bc0d457SAlexander V. Chernikov 		if (do_add != 0 && count == 1 &&
2691bc0d457SAlexander V. Chernikov 		    check_table_limit(tc, tei) != 0) {
2704c0c07a5SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
2714c0c07a5SAlexander V. Chernikov 			return (EFBIG);
2724c0c07a5SAlexander V. Chernikov 		}
2734c0c07a5SAlexander V. Chernikov 
2749f7d47b0SAlexander V. Chernikov 		/* Reference and unlock */
2759f7d47b0SAlexander V. Chernikov 		tc->no.refcnt++;
2769f7d47b0SAlexander V. Chernikov 		ta = tc->ta;
2779f7d47b0SAlexander V. Chernikov 	}
2789f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
2793b3a8eb9SGleb Smirnoff 
280ac35ff17SAlexander V. Chernikov 	if (tc == NULL) {
2811bc0d457SAlexander V. Chernikov 		if (do_add == 0)
2821bc0d457SAlexander V. Chernikov 			return (ESRCH);
2831bc0d457SAlexander V. Chernikov 
284db785d31SAlexander V. Chernikov 		/* Compability mode: create new table for old clients */
285db785d31SAlexander V. Chernikov 		if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
286db785d31SAlexander V. Chernikov 			return (ESRCH);
2873b3a8eb9SGleb Smirnoff 
2880468c5baSAlexander V. Chernikov 		error = create_table_compat(ch, ti, &tc, &ta, NULL);
289db785d31SAlexander V. Chernikov 		if (error != 0)
290db785d31SAlexander V. Chernikov 			return (error);
291db785d31SAlexander V. Chernikov 
2920468c5baSAlexander V. Chernikov 		/* OK, now we've got referenced table. */
293db785d31SAlexander V. Chernikov 	}
294db785d31SAlexander V. Chernikov 
2951bc0d457SAlexander V. Chernikov 	*ptc = tc;
2961bc0d457SAlexander V. Chernikov 	*pta = ta;
2971bc0d457SAlexander V. Chernikov 	return (0);
2981bc0d457SAlexander V. Chernikov }
2991bc0d457SAlexander V. Chernikov 
3001bc0d457SAlexander V. Chernikov /*
3011bc0d457SAlexander V. Chernikov  * Rolls back already @added to @tc entries using state arrat @ta_buf_m.
3021bc0d457SAlexander V. Chernikov  * Assume the following layout:
3031bc0d457SAlexander V. Chernikov  * 1) ADD state (ta_buf_m[0] ... t_buf_m[added - 1]) for handling update cases
3041bc0d457SAlexander V. Chernikov  * 2) DEL state (ta_buf_m[count[ ... t_buf_m[count + added - 1])
3051bc0d457SAlexander V. Chernikov  *   for storing deleted state
3061bc0d457SAlexander V. Chernikov  */
3071bc0d457SAlexander V. Chernikov static void
3081bc0d457SAlexander V. Chernikov rollback_added_entries(struct ip_fw_chain *ch, struct table_config *tc,
3091bc0d457SAlexander V. Chernikov     struct table_info *tinfo, struct tentry_info *tei, caddr_t ta_buf_m,
3101bc0d457SAlexander V. Chernikov     uint32_t count, uint32_t added)
3111bc0d457SAlexander V. Chernikov {
3121bc0d457SAlexander V. Chernikov 	struct table_algo *ta;
3131bc0d457SAlexander V. Chernikov 	struct tentry_info *ptei;
3141bc0d457SAlexander V. Chernikov 	caddr_t v, vv;
3151bc0d457SAlexander V. Chernikov 	size_t ta_buf_sz;
3161bc0d457SAlexander V. Chernikov 	int error, i;
3171bc0d457SAlexander V. Chernikov 	uint32_t num;
3181bc0d457SAlexander V. Chernikov 
3191bc0d457SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
3201bc0d457SAlexander V. Chernikov 
3211bc0d457SAlexander V. Chernikov 	ta = tc->ta;
3223a845e10SAlexander V. Chernikov 	ta_buf_sz = ta->ta_buf_size;
3231bc0d457SAlexander V. Chernikov 	v = ta_buf_m;
3241bc0d457SAlexander V. Chernikov 	vv = v + count * ta_buf_sz;
3251bc0d457SAlexander V. Chernikov 	for (i = 0; i < added; i++, v += ta_buf_sz, vv += ta_buf_sz) {
3261bc0d457SAlexander V. Chernikov 		ptei = &tei[i];
3271bc0d457SAlexander V. Chernikov 		if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) {
3281bc0d457SAlexander V. Chernikov 
3291bc0d457SAlexander V. Chernikov 			/*
3301bc0d457SAlexander V. Chernikov 			 * We have old value stored by previous
3311bc0d457SAlexander V. Chernikov 			 * call in @ptei->value. Do add once again
3321bc0d457SAlexander V. Chernikov 			 * to restore it.
3331bc0d457SAlexander V. Chernikov 			 */
3341bc0d457SAlexander V. Chernikov 			error = ta->add(tc->astate, tinfo, ptei, v, &num);
3351bc0d457SAlexander V. Chernikov 			KASSERT(error == 0, ("rollback UPDATE fail"));
3361bc0d457SAlexander V. Chernikov 			KASSERT(num == 0, ("rollback UPDATE fail2"));
3371bc0d457SAlexander V. Chernikov 			continue;
3381bc0d457SAlexander V. Chernikov 		}
3391bc0d457SAlexander V. Chernikov 
3401bc0d457SAlexander V. Chernikov 		error = ta->prepare_del(ch, ptei, vv);
3411bc0d457SAlexander V. Chernikov 		KASSERT(error == 0, ("pre-rollback INSERT failed"));
3421bc0d457SAlexander V. Chernikov 		error = ta->del(tc->astate, tinfo, ptei, vv, &num);
3431bc0d457SAlexander V. Chernikov 		KASSERT(error == 0, ("rollback INSERT failed"));
3441bc0d457SAlexander V. Chernikov 		tc->count -= num;
3451bc0d457SAlexander V. Chernikov 	}
3461bc0d457SAlexander V. Chernikov }
3471bc0d457SAlexander V. Chernikov 
3481bc0d457SAlexander V. Chernikov /*
3491bc0d457SAlexander V. Chernikov  * Prepares add/del state for all @count entries in @tei.
3501bc0d457SAlexander V. Chernikov  * Uses either stack buffer (@ta_buf) or allocates a new one.
3511bc0d457SAlexander V. Chernikov  * Stores pointer to allocated buffer back to @ta_buf.
3521bc0d457SAlexander V. Chernikov  *
3531bc0d457SAlexander V. Chernikov  * Returns 0 on success.
3541bc0d457SAlexander V. Chernikov  */
3551bc0d457SAlexander V. Chernikov static int
3561bc0d457SAlexander V. Chernikov prepare_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
3571bc0d457SAlexander V. Chernikov     struct tentry_info *tei, uint32_t count, int do_add, caddr_t *ta_buf)
3581bc0d457SAlexander V. Chernikov {
3591bc0d457SAlexander V. Chernikov 	caddr_t ta_buf_m, v;
3601bc0d457SAlexander V. Chernikov 	size_t ta_buf_sz, sz;
3611bc0d457SAlexander V. Chernikov 	struct tentry_info *ptei;
3621bc0d457SAlexander V. Chernikov 	int error, i;
3631bc0d457SAlexander V. Chernikov 
3641bc0d457SAlexander V. Chernikov 	error = 0;
3651bc0d457SAlexander V. Chernikov 	ta_buf_sz = ta->ta_buf_size;
3663a845e10SAlexander V. Chernikov 	if (count == 1) {
3671bc0d457SAlexander V. Chernikov 		/* Sigle add/delete, use on-stack buffer */
3681bc0d457SAlexander V. Chernikov 		memset(*ta_buf, 0, TA_BUF_SZ);
3691bc0d457SAlexander V. Chernikov 		ta_buf_m = *ta_buf;
3703a845e10SAlexander V. Chernikov 	} else {
3713a845e10SAlexander V. Chernikov 
3723a845e10SAlexander V. Chernikov 		/*
3731bc0d457SAlexander V. Chernikov 		 * Multiple adds/deletes, allocate larger buffer
3741bc0d457SAlexander V. Chernikov 		 *
3751bc0d457SAlexander V. Chernikov 		 * Note we need 2xcount buffer for add case:
3761bc0d457SAlexander V. Chernikov 		 * we have hold both ADD state
3773a845e10SAlexander V. Chernikov 		 * and DELETE state (this may be needed
3783a845e10SAlexander V. Chernikov 		 * if we need to rollback all changes)
3793a845e10SAlexander V. Chernikov 		 */
3801bc0d457SAlexander V. Chernikov 		sz = count * ta_buf_sz;
3811bc0d457SAlexander V. Chernikov 		ta_buf_m = malloc((do_add != 0) ? sz * 2 : sz, M_TEMP,
3823a845e10SAlexander V. Chernikov 		    M_WAITOK | M_ZERO);
3833a845e10SAlexander V. Chernikov 	}
3841bc0d457SAlexander V. Chernikov 
3853a845e10SAlexander V. Chernikov 	v = ta_buf_m;
3863a845e10SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta_buf_sz) {
3871bc0d457SAlexander V. Chernikov 		ptei = &tei[i];
3881bc0d457SAlexander V. Chernikov 		error = (do_add != 0) ?
3891bc0d457SAlexander V. Chernikov 		    ta->prepare_add(ch, ptei, v) : ta->prepare_del(ch, ptei, v);
3903a845e10SAlexander V. Chernikov 
3913a845e10SAlexander V. Chernikov 		/*
3923a845e10SAlexander V. Chernikov 		 * Some syntax error (incorrect mask, or address, or
3933a845e10SAlexander V. Chernikov 		 * anything). Return error regardless of atomicity
3943a845e10SAlexander V. Chernikov 		 * settings.
3953a845e10SAlexander V. Chernikov 		 */
396db785d31SAlexander V. Chernikov 		if (error != 0)
3971bc0d457SAlexander V. Chernikov 			break;
3983a845e10SAlexander V. Chernikov 	}
3993b3a8eb9SGleb Smirnoff 
4001bc0d457SAlexander V. Chernikov 	*ta_buf = ta_buf_m;
4011bc0d457SAlexander V. Chernikov 	return (error);
4021bc0d457SAlexander V. Chernikov }
4031bc0d457SAlexander V. Chernikov 
4041bc0d457SAlexander V. Chernikov /*
4051bc0d457SAlexander V. Chernikov  * Flushes allocated state for each @count entries in @tei.
4061bc0d457SAlexander V. Chernikov  * Frees @ta_buf_m if differs from stack buffer @ta_buf.
4071bc0d457SAlexander V. Chernikov  */
4081bc0d457SAlexander V. Chernikov static void
4091bc0d457SAlexander V. Chernikov flush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
4101bc0d457SAlexander V. Chernikov     struct tentry_info *tei, uint32_t count, int do_add, int rollback,
4111bc0d457SAlexander V. Chernikov     caddr_t ta_buf_m, caddr_t ta_buf)
4121bc0d457SAlexander V. Chernikov {
4131bc0d457SAlexander V. Chernikov 	caddr_t v;
4141bc0d457SAlexander V. Chernikov 	size_t ta_buf_sz;
4151bc0d457SAlexander V. Chernikov 	int i;
4161bc0d457SAlexander V. Chernikov 
4171bc0d457SAlexander V. Chernikov 	ta_buf_sz = ta->ta_buf_size;
4181bc0d457SAlexander V. Chernikov 
4191bc0d457SAlexander V. Chernikov 	/* Run cleaning callback anyway */
4201bc0d457SAlexander V. Chernikov 	v = ta_buf_m;
4211bc0d457SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta_buf_sz)
4221bc0d457SAlexander V. Chernikov 		ta->flush_entry(ch, &tei[i], v);
4231bc0d457SAlexander V. Chernikov 
4241bc0d457SAlexander V. Chernikov 	/* Clean up "deleted" state in case of rollback */
4251bc0d457SAlexander V. Chernikov 	if (rollback != 0) {
4261bc0d457SAlexander V. Chernikov 		v = ta_buf_m + count * ta_buf_sz;
4271bc0d457SAlexander V. Chernikov 		for (i = 0; i < count; i++, v += ta_buf_sz)
4281bc0d457SAlexander V. Chernikov 			ta->flush_entry(ch, &tei[i], v);
4291bc0d457SAlexander V. Chernikov 	}
4301bc0d457SAlexander V. Chernikov 
4311bc0d457SAlexander V. Chernikov 	if (ta_buf_m != ta_buf)
4321bc0d457SAlexander V. Chernikov 		free(ta_buf_m, M_TEMP);
4331bc0d457SAlexander V. Chernikov }
4341bc0d457SAlexander V. Chernikov 
4351bc0d457SAlexander V. Chernikov /*
4361bc0d457SAlexander V. Chernikov  * Adds/updates one or more entries in table @ti.
4370468c5baSAlexander V. Chernikov  * Function references @ti first to ensure table won't
4380468c5baSAlexander V. Chernikov  * disappear or change its type.
4390468c5baSAlexander V. Chernikov  * After that, prepare_add callback is called for each @tei entry.
4400468c5baSAlexander V. Chernikov  * Next, we try to add each entry under UH+WHLOCK
4410468c5baSAlexander V. Chernikov  * using add() callback.
4420468c5baSAlexander V. Chernikov  * Finally, we free all state by calling flush_entry callback
4430468c5baSAlexander V. Chernikov  * for each @tei.
4441bc0d457SAlexander V. Chernikov  *
4451bc0d457SAlexander V. Chernikov  * Returns 0 on success.
4461bc0d457SAlexander V. Chernikov  */
4471bc0d457SAlexander V. Chernikov int
4481bc0d457SAlexander V. Chernikov add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
4491bc0d457SAlexander V. Chernikov     struct tentry_info *tei, uint8_t flags, uint32_t count)
4501bc0d457SAlexander V. Chernikov {
4511bc0d457SAlexander V. Chernikov 	struct table_config *tc;
4521bc0d457SAlexander V. Chernikov 	struct table_algo *ta;
4531bc0d457SAlexander V. Chernikov 	uint16_t kidx;
4541bc0d457SAlexander V. Chernikov 	int error, first_error, i, rollback;
4551bc0d457SAlexander V. Chernikov 	uint32_t num, numadd;
4561bc0d457SAlexander V. Chernikov 	struct tentry_info *ptei;
4571bc0d457SAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
4581bc0d457SAlexander V. Chernikov 	caddr_t ta_buf_m, v;
4591bc0d457SAlexander V. Chernikov 
4601bc0d457SAlexander V. Chernikov 	/*
4611bc0d457SAlexander V. Chernikov 	 * Find and reference existing table.
4621bc0d457SAlexander V. Chernikov 	 */
4631bc0d457SAlexander V. Chernikov 	if ((error = find_ref_table(ch, ti, tei, count, 1, &tc, &ta)) != 0)
4641bc0d457SAlexander V. Chernikov 		return (error);
4651bc0d457SAlexander V. Chernikov 
4661bc0d457SAlexander V. Chernikov 	/* Allocate memory and prepare record(s) */
4671bc0d457SAlexander V. Chernikov 	rollback = 0;
4681bc0d457SAlexander V. Chernikov 	/* Pass stack buffer by default */
4691bc0d457SAlexander V. Chernikov 	ta_buf_m = ta_buf;
4701bc0d457SAlexander V. Chernikov 	error = prepare_batch_buffer(ch, ta, tei, count, 1, &ta_buf_m);
4711bc0d457SAlexander V. Chernikov 	if (error != 0)
4721bc0d457SAlexander V. Chernikov 		goto cleanup;
4731bc0d457SAlexander V. Chernikov 
474b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
4753b3a8eb9SGleb Smirnoff 
476c8d5d308SAlexander V. Chernikov 	/* Drop reference we've used in first search */
477c8d5d308SAlexander V. Chernikov 	tc->no.refcnt--;
478c8d5d308SAlexander V. Chernikov 
479b6ee846eSAlexander V. Chernikov 	/*
480b6ee846eSAlexander V. Chernikov 	 * Ensure we are able to add all entries without additional
481b6ee846eSAlexander V. Chernikov 	 * memory allocations. May release/reacquire UH_WLOCK.
482c8d5d308SAlexander V. Chernikov 	 * check_table_space() guarantees us @tc won't disappear
483c8d5d308SAlexander V. Chernikov 	 * by referencing it internally.
484b6ee846eSAlexander V. Chernikov 	 */
485b6ee846eSAlexander V. Chernikov 	kidx = tc->no.kidx;
486b6ee846eSAlexander V. Chernikov 	error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count);
487b6ee846eSAlexander V. Chernikov 	if (error != 0) {
488b6ee846eSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
4893a845e10SAlexander V. Chernikov 		goto cleanup;
490b6ee846eSAlexander V. Chernikov 	}
491b6ee846eSAlexander V. Chernikov 
492c8d5d308SAlexander V. Chernikov 	/*
493c8d5d308SAlexander V. Chernikov 	 * Check if table algo is still the same.
494c8d5d308SAlexander V. Chernikov 	 * (changed ta may be the result of table swap).
495c8d5d308SAlexander V. Chernikov 	 */
496c8d5d308SAlexander V. Chernikov 	if (ta != tc->ta) {
497c8d5d308SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
498c8d5d308SAlexander V. Chernikov 		error = EINVAL;
499c8d5d308SAlexander V. Chernikov 		goto cleanup;
500c8d5d308SAlexander V. Chernikov 	}
501c8d5d308SAlexander V. Chernikov 
5023a845e10SAlexander V. Chernikov 	/* We've got valid table in @tc. Let's try to add data */
5039f7d47b0SAlexander V. Chernikov 	kidx = tc->no.kidx;
5049f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
5053a845e10SAlexander V. Chernikov 	numadd = 0;
5063a845e10SAlexander V. Chernikov 	first_error = 0;
5079f7d47b0SAlexander V. Chernikov 
508b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
5093a845e10SAlexander V. Chernikov 
5103a845e10SAlexander V. Chernikov 	v = ta_buf_m;
5111bc0d457SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta->ta_buf_size) {
5123a845e10SAlexander V. Chernikov 		ptei = &tei[i];
5133a845e10SAlexander V. Chernikov 		num = 0;
5143a845e10SAlexander V. Chernikov 		/* check limit before adding */
5153a845e10SAlexander V. Chernikov 		if ((error = check_table_limit(tc, ptei)) == 0) {
5163a845e10SAlexander V. Chernikov 			error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx),
5173a845e10SAlexander V. Chernikov 			    ptei, v, &num);
5183a845e10SAlexander V. Chernikov 			/* Set status flag to inform userland */
5191bc0d457SAlexander V. Chernikov 			store_tei_result(ptei, 1, error, num);
5203a845e10SAlexander V. Chernikov 		}
5213a845e10SAlexander V. Chernikov 		if (error == 0) {
5223a845e10SAlexander V. Chernikov 			/* Update number of records to ease limit checking */
5233a845e10SAlexander V. Chernikov 			tc->count += num;
5243a845e10SAlexander V. Chernikov 			numadd += num;
5253a845e10SAlexander V. Chernikov 			continue;
5263a845e10SAlexander V. Chernikov 		}
5273a845e10SAlexander V. Chernikov 
5283a845e10SAlexander V. Chernikov 		if (first_error == 0)
5293a845e10SAlexander V. Chernikov 			first_error = error;
5303a845e10SAlexander V. Chernikov 
5313a845e10SAlexander V. Chernikov 		/*
5323a845e10SAlexander V. Chernikov 		 * Some error have happened. Check our atomicity
5333a845e10SAlexander V. Chernikov 		 * settings: continue if atomicity is not required,
5343a845e10SAlexander V. Chernikov 		 * rollback changes otherwise.
5353a845e10SAlexander V. Chernikov 		 */
5363a845e10SAlexander V. Chernikov 		if ((flags & IPFW_CTF_ATOMIC) == 0)
5373a845e10SAlexander V. Chernikov 			continue;
5383a845e10SAlexander V. Chernikov 
5391bc0d457SAlexander V. Chernikov 		rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx),
5401bc0d457SAlexander V. Chernikov 		    tei, ta_buf_m, count, i);
5413a845e10SAlexander V. Chernikov 		break;
5423a845e10SAlexander V. Chernikov 	}
5433a845e10SAlexander V. Chernikov 
5443b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
5459f7d47b0SAlexander V. Chernikov 
546b6ee846eSAlexander V. Chernikov 	/* Permit post-add algorithm grow/rehash. */
5473a845e10SAlexander V. Chernikov 	if (numadd != 0)
5483a845e10SAlexander V. Chernikov 		check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
549db785d31SAlexander V. Chernikov 
550b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
551b074b7bbSAlexander V. Chernikov 
5523a845e10SAlexander V. Chernikov 	/* Return first error to user, if any */
5533a845e10SAlexander V. Chernikov 	error = first_error;
5543a845e10SAlexander V. Chernikov 
5553a845e10SAlexander V. Chernikov cleanup:
5561bc0d457SAlexander V. Chernikov 	flush_batch_buffer(ch, ta, tei, count, 1, rollback, ta_buf_m, ta_buf);
557b074b7bbSAlexander V. Chernikov 
5589f7d47b0SAlexander V. Chernikov 	return (error);
5593b3a8eb9SGleb Smirnoff }
5603b3a8eb9SGleb Smirnoff 
5613a845e10SAlexander V. Chernikov /*
5623a845e10SAlexander V. Chernikov  * Deletes one or more entries in table @ti.
5633a845e10SAlexander V. Chernikov  *
5643a845e10SAlexander V. Chernikov  * Returns 0 on success.
5653a845e10SAlexander V. Chernikov  */
5663b3a8eb9SGleb Smirnoff int
5671832a7b3SAlexander V. Chernikov del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
5683a845e10SAlexander V. Chernikov     struct tentry_info *tei, uint8_t flags, uint32_t count)
5693b3a8eb9SGleb Smirnoff {
570b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
5719f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
5723a845e10SAlexander V. Chernikov 	struct tentry_info *ptei;
573b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
5743a845e10SAlexander V. Chernikov 	int error, first_error, i;
5753a845e10SAlexander V. Chernikov 	uint32_t num, numdel;
576b6ee846eSAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
5773a845e10SAlexander V. Chernikov 	caddr_t ta_buf_m, v;
5783b3a8eb9SGleb Smirnoff 
579db785d31SAlexander V. Chernikov 	/*
5801bc0d457SAlexander V. Chernikov 	 * Find and reference existing table.
581db785d31SAlexander V. Chernikov 	 */
5821bc0d457SAlexander V. Chernikov 	if ((error = find_ref_table(ch, ti, tei, count, 0, &tc, &ta)) != 0)
583db785d31SAlexander V. Chernikov 		return (error);
5843a845e10SAlexander V. Chernikov 
58535e1bbd0SAlexander V. Chernikov 	/* Allocate memory and prepare record(s) */
5861bc0d457SAlexander V. Chernikov 	/* Pass stack buffer by default */
5873a845e10SAlexander V. Chernikov 	ta_buf_m = ta_buf;
5881bc0d457SAlexander V. Chernikov 	error = prepare_batch_buffer(ch, ta, tei, count, 0, &ta_buf_m);
5893a845e10SAlexander V. Chernikov 	if (error != 0)
5903a845e10SAlexander V. Chernikov 		goto cleanup;
5919f7d47b0SAlexander V. Chernikov 
5923a845e10SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
5933a845e10SAlexander V. Chernikov 
5943a845e10SAlexander V. Chernikov 	/* Drop reference we've used in first search */
5953a845e10SAlexander V. Chernikov 	tc->no.refcnt--;
5963a845e10SAlexander V. Chernikov 
597c8d5d308SAlexander V. Chernikov 	/*
598c8d5d308SAlexander V. Chernikov 	 * Check if table algo is still the same.
599c8d5d308SAlexander V. Chernikov 	 * (changed ta may be the result of table swap).
600c8d5d308SAlexander V. Chernikov 	 */
601c8d5d308SAlexander V. Chernikov 	if (ta != tc->ta) {
602c8d5d308SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
603c8d5d308SAlexander V. Chernikov 		error = EINVAL;
604c8d5d308SAlexander V. Chernikov 		goto cleanup;
605c8d5d308SAlexander V. Chernikov 	}
606c8d5d308SAlexander V. Chernikov 
607b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
6083a845e10SAlexander V. Chernikov 	numdel = 0;
6093a845e10SAlexander V. Chernikov 	first_error = 0;
610b074b7bbSAlexander V. Chernikov 
611b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
6123a845e10SAlexander V. Chernikov 	v = ta_buf_m;
6131bc0d457SAlexander V. Chernikov 	for (i = 0; i < count; i++, v += ta->ta_buf_size) {
6143a845e10SAlexander V. Chernikov 		ptei = &tei[i];
6153a845e10SAlexander V. Chernikov 		num = 0;
6163a845e10SAlexander V. Chernikov 		error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v,
6173a845e10SAlexander V. Chernikov 		    &num);
6183a845e10SAlexander V. Chernikov 		/* Save state for userland */
6191bc0d457SAlexander V. Chernikov 		store_tei_result(ptei, 0, error, num);
6203a845e10SAlexander V. Chernikov 		if (error != 0 && first_error == 0)
6213a845e10SAlexander V. Chernikov 			first_error = error;
6223a845e10SAlexander V. Chernikov 		tc->count -= num;
6233a845e10SAlexander V. Chernikov 		numdel += num;
6243a845e10SAlexander V. Chernikov 	}
6253b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
6263b3a8eb9SGleb Smirnoff 
6273a845e10SAlexander V. Chernikov 	if (numdel != 0) {
628b6ee846eSAlexander V. Chernikov 		/* Run post-del hook to permit shrinking */
629b6ee846eSAlexander V. Chernikov 		error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
630b6ee846eSAlexander V. Chernikov 	}
631b074b7bbSAlexander V. Chernikov 
6329f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
6333b3a8eb9SGleb Smirnoff 
6343a845e10SAlexander V. Chernikov 	/* Return first error to user, if any */
6353a845e10SAlexander V. Chernikov 	error = first_error;
6363a845e10SAlexander V. Chernikov 
6373a845e10SAlexander V. Chernikov cleanup:
6381bc0d457SAlexander V. Chernikov 	flush_batch_buffer(ch, ta, tei, count, 0, 0, ta_buf_m, ta_buf);
639ac35ff17SAlexander V. Chernikov 
640ac35ff17SAlexander V. Chernikov 	return (error);
641ac35ff17SAlexander V. Chernikov }
642ac35ff17SAlexander V. Chernikov 
643db785d31SAlexander V. Chernikov /*
644b6ee846eSAlexander V. Chernikov  * Ensure that table @tc has enough space to add @count entries without
645b6ee846eSAlexander V. Chernikov  * need for reallocation.
646db785d31SAlexander V. Chernikov  *
647db785d31SAlexander V. Chernikov  * Callbacks order:
648301290bcSAlexander V. Chernikov  * 0) need_modify() (UH_WLOCK) - checks if @count items can be added w/o resize.
649b6ee846eSAlexander V. Chernikov  *
650db785d31SAlexander V. Chernikov  * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags.
651db785d31SAlexander V. Chernikov  * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage
652db785d31SAlexander V. Chernikov  * 3) modify (UH_WLOCK + WLOCK) - switch pointers
653b6ee846eSAlexander V. Chernikov  * 4) flush_modify (UH_WLOCK) - free state, if needed
654b6ee846eSAlexander V. Chernikov  *
655b6ee846eSAlexander V. Chernikov  * Returns 0 on success.
656db785d31SAlexander V. Chernikov  */
657db785d31SAlexander V. Chernikov static int
658b6ee846eSAlexander V. Chernikov check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
659b6ee846eSAlexander V. Chernikov     struct table_info *ti, uint32_t count)
660db785d31SAlexander V. Chernikov {
661b6ee846eSAlexander V. Chernikov 	struct table_algo *ta;
662b6ee846eSAlexander V. Chernikov 	uint64_t pflags;
663b6ee846eSAlexander V. Chernikov 	char ta_buf[TA_BUF_SZ];
664db785d31SAlexander V. Chernikov 	int error;
665db785d31SAlexander V. Chernikov 
666b6ee846eSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
667db785d31SAlexander V. Chernikov 
668b6ee846eSAlexander V. Chernikov 	error = 0;
669b6ee846eSAlexander V. Chernikov 	ta = tc->ta;
670fd0869d5SAlexander V. Chernikov 	if (ta->need_modify == NULL)
671fd0869d5SAlexander V. Chernikov 		return (0);
672fd0869d5SAlexander V. Chernikov 
673b6ee846eSAlexander V. Chernikov 	/* Acquire reference not to loose @tc between locks/unlocks */
674b6ee846eSAlexander V. Chernikov 	tc->no.refcnt++;
675db785d31SAlexander V. Chernikov 
676db785d31SAlexander V. Chernikov 	/*
677b6ee846eSAlexander V. Chernikov 	 * TODO: think about avoiding race between large add/large delete
678b6ee846eSAlexander V. Chernikov 	 * operation on algorithm which implements shrinking along with
679b6ee846eSAlexander V. Chernikov 	 * growing.
680db785d31SAlexander V. Chernikov 	 */
681b6ee846eSAlexander V. Chernikov 	while (true) {
682b6ee846eSAlexander V. Chernikov 		pflags = 0;
683301290bcSAlexander V. Chernikov 		if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) {
68435e1bbd0SAlexander V. Chernikov 			error = 0;
68535e1bbd0SAlexander V. Chernikov 			break;
686b6ee846eSAlexander V. Chernikov 		}
687db785d31SAlexander V. Chernikov 
688b6ee846eSAlexander V. Chernikov 		/* We have to shrink/grow table */
689b6ee846eSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
690b6ee846eSAlexander V. Chernikov 
691301290bcSAlexander V. Chernikov 		memset(&ta_buf, 0, sizeof(ta_buf));
692b6ee846eSAlexander V. Chernikov 		if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) {
693b6ee846eSAlexander V. Chernikov 			IPFW_UH_WLOCK(ch);
694b6ee846eSAlexander V. Chernikov 			break;
695b6ee846eSAlexander V. Chernikov 		}
696b6ee846eSAlexander V. Chernikov 
697b6ee846eSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
698b6ee846eSAlexander V. Chernikov 
699b6ee846eSAlexander V. Chernikov 		/* Check if we still need to alter table */
700b6ee846eSAlexander V. Chernikov 		ti = KIDX_TO_TI(ch, tc->no.kidx);
701301290bcSAlexander V. Chernikov 		if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) {
702301290bcSAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
703b6ee846eSAlexander V. Chernikov 
704b6ee846eSAlexander V. Chernikov 			/*
70535e1bbd0SAlexander V. Chernikov 			 * Other thread has already performed resize.
70635e1bbd0SAlexander V. Chernikov 			 * Flush our state and return.
707b6ee846eSAlexander V. Chernikov 			 */
708b6ee846eSAlexander V. Chernikov 			ta->flush_mod(ta_buf);
709b6ee846eSAlexander V. Chernikov 			break;
710b6ee846eSAlexander V. Chernikov 		}
711b6ee846eSAlexander V. Chernikov 
712b6ee846eSAlexander V. Chernikov 		error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags);
713b6ee846eSAlexander V. Chernikov 		if (error == 0) {
714db785d31SAlexander V. Chernikov 			/* Do actual modification */
715db785d31SAlexander V. Chernikov 			IPFW_WLOCK(ch);
71668394ec8SAlexander V. Chernikov 			ta->modify(tc->astate, ti, ta_buf, pflags);
717db785d31SAlexander V. Chernikov 			IPFW_WUNLOCK(ch);
718db785d31SAlexander V. Chernikov 		}
719db785d31SAlexander V. Chernikov 
720b6ee846eSAlexander V. Chernikov 		/* Anyway, flush data and retry */
721db785d31SAlexander V. Chernikov 		ta->flush_mod(ta_buf);
722b6ee846eSAlexander V. Chernikov 	}
723db785d31SAlexander V. Chernikov 
724b6ee846eSAlexander V. Chernikov 	tc->no.refcnt--;
725db785d31SAlexander V. Chernikov 	return (error);
726db785d31SAlexander V. Chernikov }
727db785d31SAlexander V. Chernikov 
7283a845e10SAlexander V. Chernikov /*
7293a845e10SAlexander V. Chernikov  * Selects appropriate table operation handler
7303a845e10SAlexander V. Chernikov  * depending on opcode version.
7313a845e10SAlexander V. Chernikov  */
732ac35ff17SAlexander V. Chernikov int
733db785d31SAlexander V. Chernikov ipfw_manage_table_ent(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
734ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
735ac35ff17SAlexander V. Chernikov {
736ac35ff17SAlexander V. Chernikov 	int error;
737ac35ff17SAlexander V. Chernikov 
738ac35ff17SAlexander V. Chernikov 	switch (op3->version) {
739ac35ff17SAlexander V. Chernikov 	case 0:
740db785d31SAlexander V. Chernikov 		error = ipfw_manage_table_ent_v0(ch, op3, sd);
741ac35ff17SAlexander V. Chernikov 		break;
742ac35ff17SAlexander V. Chernikov 	case 1:
743db785d31SAlexander V. Chernikov 		error = ipfw_manage_table_ent_v1(ch, op3, sd);
744ac35ff17SAlexander V. Chernikov 		break;
745ac35ff17SAlexander V. Chernikov 	default:
746ac35ff17SAlexander V. Chernikov 		error = ENOTSUP;
747ac35ff17SAlexander V. Chernikov 	}
748ac35ff17SAlexander V. Chernikov 
749ac35ff17SAlexander V. Chernikov 	return (error);
750ac35ff17SAlexander V. Chernikov }
751ac35ff17SAlexander V. Chernikov 
752ac35ff17SAlexander V. Chernikov /*
753ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
754ac35ff17SAlexander V. Chernikov  * Data layout (v0):
755ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ipfw_table_xentry ]
756ac35ff17SAlexander V. Chernikov  *
757ac35ff17SAlexander V. Chernikov  * Returns 0 on success
758ac35ff17SAlexander V. Chernikov  */
759ac35ff17SAlexander V. Chernikov static int
760db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
761ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
762ac35ff17SAlexander V. Chernikov {
763ac35ff17SAlexander V. Chernikov 	ipfw_table_xentry *xent;
764ac35ff17SAlexander V. Chernikov 	struct tentry_info tei;
765ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
766ac35ff17SAlexander V. Chernikov 	int error, hdrlen, read;
767ac35ff17SAlexander V. Chernikov 
768ac35ff17SAlexander V. Chernikov 	hdrlen = offsetof(ipfw_table_xentry, k);
769ac35ff17SAlexander V. Chernikov 
770ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
771ac35ff17SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*op3) + hdrlen))
772ac35ff17SAlexander V. Chernikov 		return (EINVAL);
773ac35ff17SAlexander V. Chernikov 
774ac35ff17SAlexander V. Chernikov 	read = sizeof(ip_fw3_opheader);
775ac35ff17SAlexander V. Chernikov 
776ac35ff17SAlexander V. Chernikov 	/* Check if xentry len field is valid */
777ac35ff17SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)(op3 + 1);
778ac35ff17SAlexander V. Chernikov 	if (xent->len < hdrlen || xent->len + read > sd->valsize)
779ac35ff17SAlexander V. Chernikov 		return (EINVAL);
780ac35ff17SAlexander V. Chernikov 
781ac35ff17SAlexander V. Chernikov 	memset(&tei, 0, sizeof(tei));
782ac35ff17SAlexander V. Chernikov 	tei.paddr = &xent->k;
783ac35ff17SAlexander V. Chernikov 	tei.masklen = xent->masklen;
784ac35ff17SAlexander V. Chernikov 	tei.value = xent->value;
785ac35ff17SAlexander V. Chernikov 	/* Old requests compability */
786db785d31SAlexander V. Chernikov 	tei.flags = TEI_FLAGS_COMPAT;
787ac35ff17SAlexander V. Chernikov 	if (xent->type == IPFW_TABLE_CIDR) {
788ac35ff17SAlexander V. Chernikov 		if (xent->len - hdrlen == sizeof(in_addr_t))
789ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET;
790ac35ff17SAlexander V. Chernikov 		else
791ac35ff17SAlexander V. Chernikov 			tei.subtype = AF_INET6;
792ac35ff17SAlexander V. Chernikov 	}
793ac35ff17SAlexander V. Chernikov 
794ac35ff17SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
795ac35ff17SAlexander V. Chernikov 	ti.uidx = xent->tbl;
796ac35ff17SAlexander V. Chernikov 	ti.type = xent->type;
797ac35ff17SAlexander V. Chernikov 
798ac35ff17SAlexander V. Chernikov 	error = (op3->opcode == IP_FW_TABLE_XADD) ?
7993a845e10SAlexander V. Chernikov 	    add_table_entry(ch, &ti, &tei, 0, 1) :
8003a845e10SAlexander V. Chernikov 	    del_table_entry(ch, &ti, &tei, 0, 1);
801ac35ff17SAlexander V. Chernikov 
802ac35ff17SAlexander V. Chernikov 	return (error);
803ac35ff17SAlexander V. Chernikov }
804ac35ff17SAlexander V. Chernikov 
805ac35ff17SAlexander V. Chernikov /*
806ac35ff17SAlexander V. Chernikov  * Adds or deletes record in table.
807ac35ff17SAlexander V. Chernikov  * Data layout (v1)(current):
808db785d31SAlexander V. Chernikov  * Request: [ ipfw_obj_header
809db785d31SAlexander V. Chernikov  *   ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ]
810db785d31SAlexander V. Chernikov  * ]
811ac35ff17SAlexander V. Chernikov  *
812ac35ff17SAlexander V. Chernikov  * Returns 0 on success
813ac35ff17SAlexander V. Chernikov  */
814ac35ff17SAlexander V. Chernikov static int
815db785d31SAlexander V. Chernikov ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
816ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
817ac35ff17SAlexander V. Chernikov {
8183a845e10SAlexander V. Chernikov 	ipfw_obj_tentry *tent, *ptent;
819db785d31SAlexander V. Chernikov 	ipfw_obj_ctlv *ctlv;
820ac35ff17SAlexander V. Chernikov 	ipfw_obj_header *oh;
8213a845e10SAlexander V. Chernikov 	struct tentry_info *ptei, tei, *tei_buf;
822ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
8233a845e10SAlexander V. Chernikov 	int error, i, kidx, read;
824ac35ff17SAlexander V. Chernikov 
825ac35ff17SAlexander V. Chernikov 	/* Check minimum header size */
826db785d31SAlexander V. Chernikov 	if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv)))
827ac35ff17SAlexander V. Chernikov 		return (EINVAL);
828ac35ff17SAlexander V. Chernikov 
829ac35ff17SAlexander V. Chernikov 	/* Check if passed data is too long */
830ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sd->kavail)
831ac35ff17SAlexander V. Chernikov 		return (EINVAL);
832ac35ff17SAlexander V. Chernikov 
833ac35ff17SAlexander V. Chernikov 	oh = (ipfw_obj_header *)sd->kbuf;
834ac35ff17SAlexander V. Chernikov 
835ac35ff17SAlexander V. Chernikov 	/* Basic length checks for TLVs */
836ac35ff17SAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
837ac35ff17SAlexander V. Chernikov 		return (EINVAL);
838ac35ff17SAlexander V. Chernikov 
839ac35ff17SAlexander V. Chernikov 	read = sizeof(*oh);
840ac35ff17SAlexander V. Chernikov 
841db785d31SAlexander V. Chernikov 	ctlv = (ipfw_obj_ctlv *)(oh + 1);
842db785d31SAlexander V. Chernikov 	if (ctlv->head.length + read != sd->valsize)
843db785d31SAlexander V. Chernikov 		return (EINVAL);
844db785d31SAlexander V. Chernikov 
845db785d31SAlexander V. Chernikov 	read += sizeof(*ctlv);
846db785d31SAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(ctlv + 1);
8473a845e10SAlexander V. Chernikov 	if (ctlv->count * sizeof(*tent) + read != sd->valsize)
848ac35ff17SAlexander V. Chernikov 		return (EINVAL);
849ac35ff17SAlexander V. Chernikov 
8503a845e10SAlexander V. Chernikov 	if (ctlv->count == 0)
8513a845e10SAlexander V. Chernikov 		return (0);
852ac35ff17SAlexander V. Chernikov 
8533a845e10SAlexander V. Chernikov 	/*
8543a845e10SAlexander V. Chernikov 	 * Mark entire buffer as "read".
85535e1bbd0SAlexander V. Chernikov 	 * This instructs sopt api write it back
8563a845e10SAlexander V. Chernikov 	 * after function return.
8573a845e10SAlexander V. Chernikov 	 */
8583a845e10SAlexander V. Chernikov 	ipfw_get_sopt_header(sd, sd->valsize);
8593a845e10SAlexander V. Chernikov 
8603a845e10SAlexander V. Chernikov 	/* Perform basic checks for each entry */
8613a845e10SAlexander V. Chernikov 	ptent = tent;
8623a845e10SAlexander V. Chernikov 	kidx = tent->idx;
8633a845e10SAlexander V. Chernikov 	for (i = 0; i < ctlv->count; i++, ptent++) {
8643a845e10SAlexander V. Chernikov 		if (ptent->head.length != sizeof(*ptent))
8653a845e10SAlexander V. Chernikov 			return (EINVAL);
8663a845e10SAlexander V. Chernikov 		if (ptent->idx != kidx)
8673a845e10SAlexander V. Chernikov 			return (ENOTSUP);
8683a845e10SAlexander V. Chernikov 	}
8693a845e10SAlexander V. Chernikov 
8703a845e10SAlexander V. Chernikov 	/* Convert data into kernel request objects */
87181d3153dSAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
872ac35ff17SAlexander V. Chernikov 	ti.type = oh->ntlv.type;
8733a845e10SAlexander V. Chernikov 	ti.uidx = kidx;
8743a845e10SAlexander V. Chernikov 
8753a845e10SAlexander V. Chernikov 	/* Use on-stack buffer for single add/del */
8763a845e10SAlexander V. Chernikov 	if (ctlv->count == 1) {
8773a845e10SAlexander V. Chernikov 		memset(&tei, 0, sizeof(tei));
8783a845e10SAlexander V. Chernikov 		tei_buf = &tei;
8793a845e10SAlexander V. Chernikov 	} else
8803a845e10SAlexander V. Chernikov 		tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP,
8813a845e10SAlexander V. Chernikov 		    M_WAITOK | M_ZERO);
8823a845e10SAlexander V. Chernikov 
8833a845e10SAlexander V. Chernikov 	ptei = tei_buf;
8843a845e10SAlexander V. Chernikov 	ptent = tent;
8853a845e10SAlexander V. Chernikov 	for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
8863a845e10SAlexander V. Chernikov 		ptei->paddr = &ptent->k;
8873a845e10SAlexander V. Chernikov 		ptei->subtype = ptent->subtype;
8883a845e10SAlexander V. Chernikov 		ptei->masklen = ptent->masklen;
8893a845e10SAlexander V. Chernikov 		if (ptent->head.flags & IPFW_TF_UPDATE)
8903a845e10SAlexander V. Chernikov 			ptei->flags |= TEI_FLAGS_UPDATE;
8913a845e10SAlexander V. Chernikov 		ptei->value = ptent->value;
8923a845e10SAlexander V. Chernikov 	}
893ac35ff17SAlexander V. Chernikov 
894ac35ff17SAlexander V. Chernikov 	error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
8953a845e10SAlexander V. Chernikov 	    add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) :
8963a845e10SAlexander V. Chernikov 	    del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count);
8973a845e10SAlexander V. Chernikov 
8983a845e10SAlexander V. Chernikov 	/* Translate result back to userland */
8993a845e10SAlexander V. Chernikov 	ptei = tei_buf;
9003a845e10SAlexander V. Chernikov 	ptent = tent;
9013a845e10SAlexander V. Chernikov 	for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
9023a845e10SAlexander V. Chernikov 		if (ptei->flags & TEI_FLAGS_ADDED)
9033a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_ADDED;
9043a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_DELETED)
9053a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_DELETED;
9063a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_UPDATED)
9073a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_UPDATED;
9083a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_LIMIT)
9093a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_LIMIT;
9103a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_ERROR)
9113a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_ERROR;
9123a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_NOTFOUND)
9133a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_NOTFOUND;
9143a845e10SAlexander V. Chernikov 		else if (ptei->flags & TEI_FLAGS_EXISTS)
9153a845e10SAlexander V. Chernikov 			ptent->result = IPFW_TR_EXISTS;
9163a845e10SAlexander V. Chernikov 	}
9173a845e10SAlexander V. Chernikov 
9183a845e10SAlexander V. Chernikov 	if (tei_buf != &tei)
9193a845e10SAlexander V. Chernikov 		free(tei_buf, M_TEMP);
920ac35ff17SAlexander V. Chernikov 
921ac35ff17SAlexander V. Chernikov 	return (error);
922ac35ff17SAlexander V. Chernikov }
923ac35ff17SAlexander V. Chernikov 
92481d3153dSAlexander V. Chernikov /*
92581d3153dSAlexander V. Chernikov  * Looks up an entry in given table.
92681d3153dSAlexander V. Chernikov  * Data layout (v0)(current):
92781d3153dSAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_tentry ]
92881d3153dSAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_obj_tentry ]
92981d3153dSAlexander V. Chernikov  *
93081d3153dSAlexander V. Chernikov  * Returns 0 on success
93181d3153dSAlexander V. Chernikov  */
93281d3153dSAlexander V. Chernikov int
93381d3153dSAlexander V. Chernikov ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
93481d3153dSAlexander V. Chernikov     struct sockopt_data *sd)
93581d3153dSAlexander V. Chernikov {
93681d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
93781d3153dSAlexander V. Chernikov 	ipfw_obj_header *oh;
93881d3153dSAlexander V. Chernikov 	struct tid_info ti;
93981d3153dSAlexander V. Chernikov 	struct table_config *tc;
94081d3153dSAlexander V. Chernikov 	struct table_algo *ta;
94181d3153dSAlexander V. Chernikov 	struct table_info *kti;
94281d3153dSAlexander V. Chernikov 	struct namedobj_instance *ni;
943914bffb6SAlexander V. Chernikov 	int error;
94481d3153dSAlexander V. Chernikov 	size_t sz;
94581d3153dSAlexander V. Chernikov 
94681d3153dSAlexander V. Chernikov 	/* Check minimum header size */
94781d3153dSAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(*tent);
94881d3153dSAlexander V. Chernikov 	if (sd->valsize != sz)
94981d3153dSAlexander V. Chernikov 		return (EINVAL);
95081d3153dSAlexander V. Chernikov 
95181d3153dSAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
95281d3153dSAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)(oh + 1);
95381d3153dSAlexander V. Chernikov 
95481d3153dSAlexander V. Chernikov 	/* Basic length checks for TLVs */
95581d3153dSAlexander V. Chernikov 	if (oh->ntlv.head.length != sizeof(oh->ntlv))
95681d3153dSAlexander V. Chernikov 		return (EINVAL);
95781d3153dSAlexander V. Chernikov 
95881d3153dSAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
95981d3153dSAlexander V. Chernikov 	ti.type = oh->ntlv.type;
96081d3153dSAlexander V. Chernikov 	ti.uidx = tent->idx;
96181d3153dSAlexander V. Chernikov 
96281d3153dSAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
96381d3153dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
96481d3153dSAlexander V. Chernikov 
96581d3153dSAlexander V. Chernikov 	/*
96681d3153dSAlexander V. Chernikov 	 * Find existing table and check its type .
96781d3153dSAlexander V. Chernikov 	 */
96881d3153dSAlexander V. Chernikov 	ta = NULL;
96981d3153dSAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) == NULL) {
97081d3153dSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
97181d3153dSAlexander V. Chernikov 		return (ESRCH);
97281d3153dSAlexander V. Chernikov 	}
97381d3153dSAlexander V. Chernikov 
97481d3153dSAlexander V. Chernikov 	/* check table type */
97581d3153dSAlexander V. Chernikov 	if (tc->no.type != ti.type) {
97681d3153dSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
97781d3153dSAlexander V. Chernikov 		return (EINVAL);
97881d3153dSAlexander V. Chernikov 	}
97981d3153dSAlexander V. Chernikov 
98081d3153dSAlexander V. Chernikov 	kti = KIDX_TO_TI(ch, tc->no.kidx);
98181d3153dSAlexander V. Chernikov 	ta = tc->ta;
98281d3153dSAlexander V. Chernikov 
983914bffb6SAlexander V. Chernikov 	if (ta->find_tentry == NULL)
984914bffb6SAlexander V. Chernikov 		return (ENOTSUP);
985914bffb6SAlexander V. Chernikov 
986914bffb6SAlexander V. Chernikov 	error = ta->find_tentry(tc->astate, kti, tent);
98781d3153dSAlexander V. Chernikov 
98881d3153dSAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
98981d3153dSAlexander V. Chernikov 
99081d3153dSAlexander V. Chernikov 	return (error);
99181d3153dSAlexander V. Chernikov }
99281d3153dSAlexander V. Chernikov 
99346d52008SAlexander V. Chernikov /*
99446d52008SAlexander V. Chernikov  * Flushes all entries or destroys given table.
99546d52008SAlexander V. Chernikov  * Data layout (v0)(current):
99646d52008SAlexander V. Chernikov  * Request: [ ipfw_obj_header ]
99746d52008SAlexander V. Chernikov  *
99846d52008SAlexander V. Chernikov  * Returns 0 on success
99946d52008SAlexander V. Chernikov  */
1000ac35ff17SAlexander V. Chernikov int
1001ac35ff17SAlexander V. Chernikov ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1002ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
1003ac35ff17SAlexander V. Chernikov {
1004ac35ff17SAlexander V. Chernikov 	int error;
1005ac35ff17SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1006ac35ff17SAlexander V. Chernikov 	struct tid_info ti;
1007ac35ff17SAlexander V. Chernikov 
1008ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh))
1009ac35ff17SAlexander V. Chernikov 		return (EINVAL);
1010ac35ff17SAlexander V. Chernikov 
1011ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
1012ac35ff17SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1013ac35ff17SAlexander V. Chernikov 
10141832a7b3SAlexander V. Chernikov 	if (op3->opcode == IP_FW_TABLE_XDESTROY)
1015ac35ff17SAlexander V. Chernikov 		error = destroy_table(ch, &ti);
10161832a7b3SAlexander V. Chernikov 	else if (op3->opcode == IP_FW_TABLE_XFLUSH)
1017ac35ff17SAlexander V. Chernikov 		error = flush_table(ch, &ti);
1018ac35ff17SAlexander V. Chernikov 	else
1019ac35ff17SAlexander V. Chernikov 		return (ENOTSUP);
1020ac35ff17SAlexander V. Chernikov 
1021ac35ff17SAlexander V. Chernikov 	return (error);
10223b3a8eb9SGleb Smirnoff }
10233b3a8eb9SGleb Smirnoff 
102435e1bbd0SAlexander V. Chernikov /*
102535e1bbd0SAlexander V. Chernikov  * Flushes given table.
102635e1bbd0SAlexander V. Chernikov  *
102735e1bbd0SAlexander V. Chernikov  * Function create new table instance with the same
102835e1bbd0SAlexander V. Chernikov  * parameters, swaps it with old one and
102935e1bbd0SAlexander V. Chernikov  * flushes state without holding any locks.
103035e1bbd0SAlexander V. Chernikov  *
103135e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
103235e1bbd0SAlexander V. Chernikov  */
10331832a7b3SAlexander V. Chernikov int
1034ac35ff17SAlexander V. Chernikov flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
10353b3a8eb9SGleb Smirnoff {
1036b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1037b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
10389f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
10399f7d47b0SAlexander V. Chernikov 	struct table_info ti_old, ti_new, *tablestate;
10409f7d47b0SAlexander V. Chernikov 	void *astate_old, *astate_new;
1041914bffb6SAlexander V. Chernikov 	char algostate[64], *pstate;
1042b074b7bbSAlexander V. Chernikov 	int error;
1043b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
1044914bffb6SAlexander V. Chernikov 	uint8_t tflags;
10453b3a8eb9SGleb Smirnoff 
10463b3a8eb9SGleb Smirnoff 	/*
10479f7d47b0SAlexander V. Chernikov 	 * Stage 1: save table algoritm.
1048b074b7bbSAlexander V. Chernikov 	 * Reference found table to ensure it won't disappear.
10493b3a8eb9SGleb Smirnoff 	 */
1050b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1051b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1052b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
1053b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1054b074b7bbSAlexander V. Chernikov 		return (ESRCH);
1055b074b7bbSAlexander V. Chernikov 	}
10569f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
1057fd0869d5SAlexander V. Chernikov 	/* Do not flush readonly tables */
1058fd0869d5SAlexander V. Chernikov 	if ((ta->flags & TA_FLAG_READONLY) != 0) {
1059fd0869d5SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1060fd0869d5SAlexander V. Chernikov 		return (EACCES);
1061fd0869d5SAlexander V. Chernikov 	}
1062b074b7bbSAlexander V. Chernikov 	tc->no.refcnt++;
106335e1bbd0SAlexander V. Chernikov 	/* Save startup algo parameters */
1064daabb523SAlexander V. Chernikov 	if (ta->print_config != NULL) {
1065daabb523SAlexander V. Chernikov 		ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx),
1066daabb523SAlexander V. Chernikov 		    algostate, sizeof(algostate));
1067daabb523SAlexander V. Chernikov 		pstate = algostate;
1068daabb523SAlexander V. Chernikov 	} else
1069daabb523SAlexander V. Chernikov 		pstate = NULL;
1070914bffb6SAlexander V. Chernikov 	tflags = tc->tflags;
1071b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
10723b3a8eb9SGleb Smirnoff 
1073b074b7bbSAlexander V. Chernikov 	/*
10749f7d47b0SAlexander V. Chernikov 	 * Stage 2: allocate new table instance using same algo.
1075b074b7bbSAlexander V. Chernikov 	 */
10769f7d47b0SAlexander V. Chernikov 	memset(&ti_new, 0, sizeof(struct table_info));
1077914bffb6SAlexander V. Chernikov 	if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) {
1078b074b7bbSAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
1079b074b7bbSAlexander V. Chernikov 		tc->no.refcnt--;
1080b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1081b074b7bbSAlexander V. Chernikov 		return (error);
1082b074b7bbSAlexander V. Chernikov 	}
1083b074b7bbSAlexander V. Chernikov 
1084b074b7bbSAlexander V. Chernikov 	/*
1085b074b7bbSAlexander V. Chernikov 	 * Stage 3: swap old state pointers with newly-allocated ones.
1086b074b7bbSAlexander V. Chernikov 	 * Decrease refcount.
1087b074b7bbSAlexander V. Chernikov 	 */
1088b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1089b074b7bbSAlexander V. Chernikov 
1090b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1091b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
10929f7d47b0SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
1093b074b7bbSAlexander V. Chernikov 
10949f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
10959f7d47b0SAlexander V. Chernikov 	ti_old = tablestate[kidx];
10969f7d47b0SAlexander V. Chernikov 	tablestate[kidx] = ti_new;
10979f7d47b0SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
1098b074b7bbSAlexander V. Chernikov 
10999f7d47b0SAlexander V. Chernikov 	astate_old = tc->astate;
11009f7d47b0SAlexander V. Chernikov 	tc->astate = astate_new;
11019f7d47b0SAlexander V. Chernikov 	tc->ti = ti_new;
11029f7d47b0SAlexander V. Chernikov 	tc->count = 0;
1103b074b7bbSAlexander V. Chernikov 	tc->no.refcnt--;
1104b074b7bbSAlexander V. Chernikov 
1105b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
11063b3a8eb9SGleb Smirnoff 
1107b074b7bbSAlexander V. Chernikov 	/*
1108b074b7bbSAlexander V. Chernikov 	 * Stage 4: perform real flush.
1109b074b7bbSAlexander V. Chernikov 	 */
11109f7d47b0SAlexander V. Chernikov 	ta->destroy(astate_old, &ti_old);
11113b3a8eb9SGleb Smirnoff 
11123b3a8eb9SGleb Smirnoff 	return (0);
11133b3a8eb9SGleb Smirnoff }
11143b3a8eb9SGleb Smirnoff 
1115b074b7bbSAlexander V. Chernikov /*
111646d52008SAlexander V. Chernikov  * Swaps two tables.
111746d52008SAlexander V. Chernikov  * Data layout (v0)(current):
111846d52008SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_obj_ntlv ]
111946d52008SAlexander V. Chernikov  *
112046d52008SAlexander V. Chernikov  * Returns 0 on success
112146d52008SAlexander V. Chernikov  */
112246d52008SAlexander V. Chernikov int
112346d52008SAlexander V. Chernikov ipfw_swap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
112446d52008SAlexander V. Chernikov     struct sockopt_data *sd)
112546d52008SAlexander V. Chernikov {
112646d52008SAlexander V. Chernikov 	int error;
112746d52008SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
112846d52008SAlexander V. Chernikov 	struct tid_info ti_a, ti_b;
112946d52008SAlexander V. Chernikov 
113046d52008SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv))
113146d52008SAlexander V. Chernikov 		return (EINVAL);
113246d52008SAlexander V. Chernikov 
113346d52008SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)op3;
113446d52008SAlexander V. Chernikov 	ntlv_to_ti(&oh->ntlv, &ti_a);
113546d52008SAlexander V. Chernikov 	ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b);
113646d52008SAlexander V. Chernikov 
1137a73d728dSAlexander V. Chernikov 	error = swap_tables(ch, &ti_a, &ti_b);
113846d52008SAlexander V. Chernikov 
113946d52008SAlexander V. Chernikov 	return (error);
114046d52008SAlexander V. Chernikov }
114146d52008SAlexander V. Chernikov 
114235e1bbd0SAlexander V. Chernikov /*
114335e1bbd0SAlexander V. Chernikov  * Swaps two tables of the same type/valtype.
114435e1bbd0SAlexander V. Chernikov  *
114535e1bbd0SAlexander V. Chernikov  * Checks if tables are compatible and limits
1146c8d5d308SAlexander V. Chernikov  * permits swap, than actually perform swap.
114735e1bbd0SAlexander V. Chernikov  *
1148c8d5d308SAlexander V. Chernikov  * Each table consists of 2 different parts:
1149c8d5d308SAlexander V. Chernikov  * config:
1150c8d5d308SAlexander V. Chernikov  *   @tc (with name, set, kidx) and rule bindings, which is "stable".
1151c8d5d308SAlexander V. Chernikov  *   number of items
1152c8d5d308SAlexander V. Chernikov  *   table algo
1153c8d5d308SAlexander V. Chernikov  * runtime:
1154c8d5d308SAlexander V. Chernikov  *   runtime data @ti (ch->tablestate)
1155c8d5d308SAlexander V. Chernikov  *   runtime cache in @tc
1156c8d5d308SAlexander V. Chernikov  *   algo-specific data (@tc->astate)
1157c8d5d308SAlexander V. Chernikov  *
1158c8d5d308SAlexander V. Chernikov  * So we switch:
1159c8d5d308SAlexander V. Chernikov  *  all runtime data
1160c8d5d308SAlexander V. Chernikov  *   number of items
1161c8d5d308SAlexander V. Chernikov  *   table algo
1162c8d5d308SAlexander V. Chernikov  *
1163c8d5d308SAlexander V. Chernikov  * After that we call @ti change handler for each table.
1164c8d5d308SAlexander V. Chernikov  *
1165c8d5d308SAlexander V. Chernikov  * Note that referencing @tc won't protect tc->ta from change.
1166c8d5d308SAlexander V. Chernikov  * XXX: Do we need to restrict swap between locked tables?
1167c8d5d308SAlexander V. Chernikov  * XXX: Do we need to exchange ftype?
116835e1bbd0SAlexander V. Chernikov  *
116935e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
117035e1bbd0SAlexander V. Chernikov  */
117146d52008SAlexander V. Chernikov static int
1172a73d728dSAlexander V. Chernikov swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
117346d52008SAlexander V. Chernikov     struct tid_info *b)
117446d52008SAlexander V. Chernikov {
117546d52008SAlexander V. Chernikov 	struct namedobj_instance *ni;
117646d52008SAlexander V. Chernikov 	struct table_config *tc_a, *tc_b;
117746d52008SAlexander V. Chernikov 	struct table_algo *ta;
117846d52008SAlexander V. Chernikov 	struct table_info ti, *tablestate;
117946d52008SAlexander V. Chernikov 	void *astate;
118046d52008SAlexander V. Chernikov 	uint32_t count;
118146d52008SAlexander V. Chernikov 
118246d52008SAlexander V. Chernikov 	/*
118346d52008SAlexander V. Chernikov 	 * Stage 1: find both tables and ensure they are of
118435e1bbd0SAlexander V. Chernikov 	 * the same type.
118546d52008SAlexander V. Chernikov 	 */
118646d52008SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
118746d52008SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
118846d52008SAlexander V. Chernikov 	if ((tc_a = find_table(ni, a)) == NULL) {
118946d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
119046d52008SAlexander V. Chernikov 		return (ESRCH);
119146d52008SAlexander V. Chernikov 	}
119246d52008SAlexander V. Chernikov 	if ((tc_b = find_table(ni, b)) == NULL) {
119346d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
119446d52008SAlexander V. Chernikov 		return (ESRCH);
119546d52008SAlexander V. Chernikov 	}
119646d52008SAlexander V. Chernikov 
119746d52008SAlexander V. Chernikov 	/* It is very easy to swap between the same table */
119846d52008SAlexander V. Chernikov 	if (tc_a == tc_b) {
119946d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
120046d52008SAlexander V. Chernikov 		return (0);
120146d52008SAlexander V. Chernikov 	}
120246d52008SAlexander V. Chernikov 
120346d52008SAlexander V. Chernikov 	/* Check type and value are the same */
120446d52008SAlexander V. Chernikov 	if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags ||
120546d52008SAlexander V. Chernikov 	    tc_a->vtype != tc_b->vtype) {
120646d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
120746d52008SAlexander V. Chernikov 		return (EINVAL);
120846d52008SAlexander V. Chernikov 	}
120946d52008SAlexander V. Chernikov 
121046d52008SAlexander V. Chernikov 	/* Check limits before swap */
121146d52008SAlexander V. Chernikov 	if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) ||
121246d52008SAlexander V. Chernikov 	    (tc_b->limit != 0 && tc_a->count > tc_b->limit)) {
121346d52008SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
121446d52008SAlexander V. Chernikov 		return (EFBIG);
121546d52008SAlexander V. Chernikov 	}
121646d52008SAlexander V. Chernikov 
1217fd0869d5SAlexander V. Chernikov 	/* Check if one of the tables is readonly */
1218fd0869d5SAlexander V. Chernikov 	if (((tc_a->ta->flags | tc_b->ta->flags) & TA_FLAG_READONLY) != 0) {
1219fd0869d5SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1220fd0869d5SAlexander V. Chernikov 		return (EACCES);
1221fd0869d5SAlexander V. Chernikov 	}
1222fd0869d5SAlexander V. Chernikov 
122346d52008SAlexander V. Chernikov 	/* Everything is fine, prepare to swap */
122446d52008SAlexander V. Chernikov 	tablestate = (struct table_info *)ch->tablestate;
122546d52008SAlexander V. Chernikov 	ti = tablestate[tc_a->no.kidx];
122646d52008SAlexander V. Chernikov 	ta = tc_a->ta;
122746d52008SAlexander V. Chernikov 	astate = tc_a->astate;
122846d52008SAlexander V. Chernikov 	count = tc_a->count;
122946d52008SAlexander V. Chernikov 
123046d52008SAlexander V. Chernikov 	IPFW_WLOCK(ch);
123146d52008SAlexander V. Chernikov 	/* a <- b */
123246d52008SAlexander V. Chernikov 	tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx];
123346d52008SAlexander V. Chernikov 	tc_a->ta = tc_b->ta;
123446d52008SAlexander V. Chernikov 	tc_a->astate = tc_b->astate;
123546d52008SAlexander V. Chernikov 	tc_a->count = tc_b->count;
123646d52008SAlexander V. Chernikov 	/* b <- a */
123746d52008SAlexander V. Chernikov 	tablestate[tc_b->no.kidx] = ti;
123846d52008SAlexander V. Chernikov 	tc_b->ta = ta;
123946d52008SAlexander V. Chernikov 	tc_b->astate = astate;
124046d52008SAlexander V. Chernikov 	tc_b->count = count;
124146d52008SAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
124246d52008SAlexander V. Chernikov 
124346d52008SAlexander V. Chernikov 	/* Ensure tc.ti copies are in sync */
124446d52008SAlexander V. Chernikov 	tc_a->ti = tablestate[tc_a->no.kidx];
124546d52008SAlexander V. Chernikov 	tc_b->ti = tablestate[tc_b->no.kidx];
124646d52008SAlexander V. Chernikov 
124746d52008SAlexander V. Chernikov 	/* Notify both tables on @ti change */
124846d52008SAlexander V. Chernikov 	if (tc_a->ta->change_ti != NULL)
124946d52008SAlexander V. Chernikov 		tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]);
125046d52008SAlexander V. Chernikov 	if (tc_b->ta->change_ti != NULL)
125146d52008SAlexander V. Chernikov 		tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]);
125246d52008SAlexander V. Chernikov 
125346d52008SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
125446d52008SAlexander V. Chernikov 
125546d52008SAlexander V. Chernikov 	return (0);
125646d52008SAlexander V. Chernikov }
125746d52008SAlexander V. Chernikov 
125846d52008SAlexander V. Chernikov /*
12599f7d47b0SAlexander V. Chernikov  * Destroys table specified by @ti.
1260ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1261ac35ff17SAlexander V. Chernikov  * Request: [ ip_fw3_opheader ]
1262ac35ff17SAlexander V. Chernikov  *
1263ac35ff17SAlexander V. Chernikov  * Returns 0 on success
1264b074b7bbSAlexander V. Chernikov  */
1265ac35ff17SAlexander V. Chernikov static int
1266ac35ff17SAlexander V. Chernikov destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
1267b074b7bbSAlexander V. Chernikov {
1268b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
1269b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
1270b074b7bbSAlexander V. Chernikov 
1271b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1272b074b7bbSAlexander V. Chernikov 
1273b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1274b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(ni, ti)) == NULL) {
1275b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1276b074b7bbSAlexander V. Chernikov 		return (ESRCH);
1277b074b7bbSAlexander V. Chernikov 	}
1278b074b7bbSAlexander V. Chernikov 
12799f7d47b0SAlexander V. Chernikov 	/* Do not permit destroying referenced tables */
12809f7d47b0SAlexander V. Chernikov 	if (tc->no.refcnt > 0) {
1281b074b7bbSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1282b074b7bbSAlexander V. Chernikov 		return (EBUSY);
1283b074b7bbSAlexander V. Chernikov 	}
1284b074b7bbSAlexander V. Chernikov 
1285b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
1286b074b7bbSAlexander V. Chernikov 	unlink_table(ch, tc);
1287b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
1288b074b7bbSAlexander V. Chernikov 
1289b074b7bbSAlexander V. Chernikov 	/* Free obj index */
1290ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0)
1291b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
1292b074b7bbSAlexander V. Chernikov 		    tc->no.kidx, tc->tablename);
1293b074b7bbSAlexander V. Chernikov 
1294b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1295b074b7bbSAlexander V. Chernikov 
1296b074b7bbSAlexander V. Chernikov 	free_table_config(ni, tc);
1297b074b7bbSAlexander V. Chernikov 
1298b074b7bbSAlexander V. Chernikov 	return (0);
1299b074b7bbSAlexander V. Chernikov }
1300b074b7bbSAlexander V. Chernikov 
1301b074b7bbSAlexander V. Chernikov static void
1302b074b7bbSAlexander V. Chernikov destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
1303b074b7bbSAlexander V. Chernikov     void *arg)
1304b074b7bbSAlexander V. Chernikov {
1305b074b7bbSAlexander V. Chernikov 
1306b074b7bbSAlexander V. Chernikov 	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
1307ac35ff17SAlexander V. Chernikov 	if (ipfw_objhash_free_idx(ni, no->kidx) != 0)
1308b074b7bbSAlexander V. Chernikov 		printf("Error unlinking kidx %d from table %s\n",
1309b074b7bbSAlexander V. Chernikov 		    no->kidx, no->name);
1310b074b7bbSAlexander V. Chernikov 	free_table_config(ni, (struct table_config *)no);
1311b074b7bbSAlexander V. Chernikov }
1312b074b7bbSAlexander V. Chernikov 
131335e1bbd0SAlexander V. Chernikov /*
131435e1bbd0SAlexander V. Chernikov  * Shuts tables module down.
131535e1bbd0SAlexander V. Chernikov  */
13163b3a8eb9SGleb Smirnoff void
13173b3a8eb9SGleb Smirnoff ipfw_destroy_tables(struct ip_fw_chain *ch)
13183b3a8eb9SGleb Smirnoff {
13193b3a8eb9SGleb Smirnoff 
1320b074b7bbSAlexander V. Chernikov 	/* Remove all tables from working set */
1321b074b7bbSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1322b074b7bbSAlexander V. Chernikov 	IPFW_WLOCK(ch);
1323b074b7bbSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
1324b074b7bbSAlexander V. Chernikov 	IPFW_WUNLOCK(ch);
1325b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
13263b3a8eb9SGleb Smirnoff 
13273b3a8eb9SGleb Smirnoff 	/* Free pointers itself */
13289f7d47b0SAlexander V. Chernikov 	free(ch->tablestate, M_IPFW);
13299f7d47b0SAlexander V. Chernikov 
13309f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_destroy(ch);
1331b074b7bbSAlexander V. Chernikov 
1332b074b7bbSAlexander V. Chernikov 	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
1333b074b7bbSAlexander V. Chernikov 	free(CHAIN_TO_TCFG(ch), M_IPFW);
13343b3a8eb9SGleb Smirnoff }
13353b3a8eb9SGleb Smirnoff 
133635e1bbd0SAlexander V. Chernikov /*
133735e1bbd0SAlexander V. Chernikov  * Starts tables module.
133835e1bbd0SAlexander V. Chernikov  */
13393b3a8eb9SGleb Smirnoff int
13403b3a8eb9SGleb Smirnoff ipfw_init_tables(struct ip_fw_chain *ch)
13413b3a8eb9SGleb Smirnoff {
1342b074b7bbSAlexander V. Chernikov 	struct tables_config *tcfg;
1343b074b7bbSAlexander V. Chernikov 
13443b3a8eb9SGleb Smirnoff 	/* Allocate pointers */
13459f7d47b0SAlexander V. Chernikov 	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
13469f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
1347b074b7bbSAlexander V. Chernikov 
1348b074b7bbSAlexander V. Chernikov 	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
1349b074b7bbSAlexander V. Chernikov 	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
1350b074b7bbSAlexander V. Chernikov 	ch->tblcfg = tcfg;
1351b074b7bbSAlexander V. Chernikov 
13529f7d47b0SAlexander V. Chernikov 	ipfw_table_algo_init(ch);
13539f7d47b0SAlexander V. Chernikov 
13543b3a8eb9SGleb Smirnoff 	return (0);
13553b3a8eb9SGleb Smirnoff }
13563b3a8eb9SGleb Smirnoff 
135735e1bbd0SAlexander V. Chernikov /*
135835e1bbd0SAlexander V. Chernikov  * Grow tables index.
135935e1bbd0SAlexander V. Chernikov  *
136035e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
136135e1bbd0SAlexander V. Chernikov  */
13623b3a8eb9SGleb Smirnoff int
13633b3a8eb9SGleb Smirnoff ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
13643b3a8eb9SGleb Smirnoff {
13653b3a8eb9SGleb Smirnoff 	unsigned int ntables_old, tbl;
1366b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
13679f7d47b0SAlexander V. Chernikov 	void *new_idx, *old_tablestate, *tablestate;
136868394ec8SAlexander V. Chernikov 	struct table_info *ti;
136968394ec8SAlexander V. Chernikov 	struct table_config *tc;
137068394ec8SAlexander V. Chernikov 	int i, new_blocks;
13713b3a8eb9SGleb Smirnoff 
13723b3a8eb9SGleb Smirnoff 	/* Check new value for validity */
13733b3a8eb9SGleb Smirnoff 	if (ntables > IPFW_TABLES_MAX)
13743b3a8eb9SGleb Smirnoff 		ntables = IPFW_TABLES_MAX;
13753b3a8eb9SGleb Smirnoff 
13763b3a8eb9SGleb Smirnoff 	/* Allocate new pointers */
13779f7d47b0SAlexander V. Chernikov 	tablestate = malloc(ntables * sizeof(struct table_info),
13789f7d47b0SAlexander V. Chernikov 	    M_IPFW, M_WAITOK | M_ZERO);
13799f7d47b0SAlexander V. Chernikov 
1380b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
13813b3a8eb9SGleb Smirnoff 
13829f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
13833b3a8eb9SGleb Smirnoff 
13843b3a8eb9SGleb Smirnoff 	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
1385b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1386b074b7bbSAlexander V. Chernikov 
13879f7d47b0SAlexander V. Chernikov 	/* Temporary restrict decreasing max_tables */
13889f7d47b0SAlexander V. Chernikov 	if (ntables < V_fw_tables_max) {
13899f7d47b0SAlexander V. Chernikov 
13909f7d47b0SAlexander V. Chernikov 		/*
13919f7d47b0SAlexander V. Chernikov 		 * FIXME: Check if we really can shrink
13929f7d47b0SAlexander V. Chernikov 		 */
13939f7d47b0SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1394b074b7bbSAlexander V. Chernikov 		return (EINVAL);
1395b074b7bbSAlexander V. Chernikov 	}
13963b3a8eb9SGleb Smirnoff 
13979f7d47b0SAlexander V. Chernikov 	/* Copy table info/indices */
13989f7d47b0SAlexander V. Chernikov 	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
13999f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
14003b3a8eb9SGleb Smirnoff 
14019f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK(ch);
14029f7d47b0SAlexander V. Chernikov 
14039f7d47b0SAlexander V. Chernikov 	/* Change pointers */
14049f7d47b0SAlexander V. Chernikov 	old_tablestate = ch->tablestate;
14059f7d47b0SAlexander V. Chernikov 	ch->tablestate = tablestate;
14069f7d47b0SAlexander V. Chernikov 	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
14073b3a8eb9SGleb Smirnoff 
14083b3a8eb9SGleb Smirnoff 	ntables_old = V_fw_tables_max;
14093b3a8eb9SGleb Smirnoff 	V_fw_tables_max = ntables;
14103b3a8eb9SGleb Smirnoff 
14113b3a8eb9SGleb Smirnoff 	IPFW_WUNLOCK(ch);
141268394ec8SAlexander V. Chernikov 
141368394ec8SAlexander V. Chernikov 	/* Notify all consumers that their @ti pointer has changed */
141468394ec8SAlexander V. Chernikov 	ti = (struct table_info *)ch->tablestate;
141568394ec8SAlexander V. Chernikov 	for (i = 0; i < tbl; i++, ti++) {
141668394ec8SAlexander V. Chernikov 		if (ti->lookup == NULL)
141768394ec8SAlexander V. Chernikov 			continue;
141868394ec8SAlexander V. Chernikov 		tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i);
141968394ec8SAlexander V. Chernikov 		if (tc == NULL || tc->ta->change_ti == NULL)
142068394ec8SAlexander V. Chernikov 			continue;
142168394ec8SAlexander V. Chernikov 
142268394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, ti);
142368394ec8SAlexander V. Chernikov 	}
142468394ec8SAlexander V. Chernikov 
14259f7d47b0SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
14263b3a8eb9SGleb Smirnoff 
14273b3a8eb9SGleb Smirnoff 	/* Free old pointers */
14289f7d47b0SAlexander V. Chernikov 	free(old_tablestate, M_IPFW);
1429b074b7bbSAlexander V. Chernikov 	ipfw_objhash_bitmap_free(new_idx, new_blocks);
14303b3a8eb9SGleb Smirnoff 
14313b3a8eb9SGleb Smirnoff 	return (0);
14323b3a8eb9SGleb Smirnoff }
14333b3a8eb9SGleb Smirnoff 
1434a73d728dSAlexander V. Chernikov /*
143535e1bbd0SAlexander V. Chernikov  * Switch between "set 0" and "rule's set" table binding,
1436a73d728dSAlexander V. Chernikov  * Check all ruleset bindings and permits changing
1437a73d728dSAlexander V. Chernikov  * IFF each binding has both rule AND table in default set (set 0).
1438a73d728dSAlexander V. Chernikov  *
1439a73d728dSAlexander V. Chernikov  * Returns 0 on success.
1440a73d728dSAlexander V. Chernikov  */
1441a73d728dSAlexander V. Chernikov int
1442a73d728dSAlexander V. Chernikov ipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
1443a73d728dSAlexander V. Chernikov {
1444a73d728dSAlexander V. Chernikov 	struct namedobj_instance *ni;
1445a73d728dSAlexander V. Chernikov 	struct named_object *no;
1446a73d728dSAlexander V. Chernikov 	struct ip_fw *rule;
1447a73d728dSAlexander V. Chernikov 	ipfw_insn *cmd;
1448a73d728dSAlexander V. Chernikov 	int cmdlen, i, l;
1449a73d728dSAlexander V. Chernikov 	uint16_t kidx;
1450a73d728dSAlexander V. Chernikov 	uint8_t type;
1451a73d728dSAlexander V. Chernikov 
1452a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1453a73d728dSAlexander V. Chernikov 
1454a73d728dSAlexander V. Chernikov 	if (V_fw_tables_sets == sets) {
1455a73d728dSAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1456a73d728dSAlexander V. Chernikov 		return (0);
1457a73d728dSAlexander V. Chernikov 	}
1458a73d728dSAlexander V. Chernikov 
1459a73d728dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1460a73d728dSAlexander V. Chernikov 
146135e1bbd0SAlexander V. Chernikov 	/*
146235e1bbd0SAlexander V. Chernikov 	 * Scan all rules and examine tables opcodes.
146335e1bbd0SAlexander V. Chernikov 	 */
1464a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules; i++) {
1465a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
1466a73d728dSAlexander V. Chernikov 
1467a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
1468a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
1469a73d728dSAlexander V. Chernikov 		cmdlen = 0;
1470a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1471a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
1472a73d728dSAlexander V. Chernikov 
1473a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
1474a73d728dSAlexander V. Chernikov 				continue;
1475a73d728dSAlexander V. Chernikov 
1476a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
1477a73d728dSAlexander V. Chernikov 
147835e1bbd0SAlexander V. Chernikov 			/* Check if both table object and rule has the set 0 */
1479a73d728dSAlexander V. Chernikov 			if (no->set != 0 || rule->set != 0) {
1480a73d728dSAlexander V. Chernikov 				IPFW_UH_WUNLOCK(ch);
1481a73d728dSAlexander V. Chernikov 				return (EBUSY);
1482a73d728dSAlexander V. Chernikov 			}
1483a73d728dSAlexander V. Chernikov 
1484a73d728dSAlexander V. Chernikov 		}
1485a73d728dSAlexander V. Chernikov 	}
1486a73d728dSAlexander V. Chernikov 	V_fw_tables_sets = sets;
1487a73d728dSAlexander V. Chernikov 
1488a73d728dSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1489a73d728dSAlexander V. Chernikov 
1490a73d728dSAlexander V. Chernikov 	return (0);
1491a73d728dSAlexander V. Chernikov }
1492a73d728dSAlexander V. Chernikov 
1493c8d5d308SAlexander V. Chernikov /*
1494c8d5d308SAlexander V. Chernikov  * Lookup an IP @addr in table @tbl.
1495c8d5d308SAlexander V. Chernikov  * Stores found value in @val.
1496c8d5d308SAlexander V. Chernikov  *
1497c8d5d308SAlexander V. Chernikov  * Returns 1 if @addr was found.
1498c8d5d308SAlexander V. Chernikov  */
14993b3a8eb9SGleb Smirnoff int
15003b3a8eb9SGleb Smirnoff ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
15013b3a8eb9SGleb Smirnoff     uint32_t *val)
15023b3a8eb9SGleb Smirnoff {
15039f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
15043b3a8eb9SGleb Smirnoff 
1505c8d5d308SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tbl);
15069f7d47b0SAlexander V. Chernikov 
15079f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
15083b3a8eb9SGleb Smirnoff }
15099f7d47b0SAlexander V. Chernikov 
1510c8d5d308SAlexander V. Chernikov /*
1511c8d5d308SAlexander V. Chernikov  * Lookup an arbtrary key @paddr of legth @plen in table @tbl.
1512c8d5d308SAlexander V. Chernikov  * Stores found value in @val.
1513c8d5d308SAlexander V. Chernikov  *
1514c8d5d308SAlexander V. Chernikov  * Returns 1 if key was found.
1515c8d5d308SAlexander V. Chernikov  */
15169f7d47b0SAlexander V. Chernikov int
15179f7d47b0SAlexander V. Chernikov ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
15189f7d47b0SAlexander V. Chernikov     void *paddr, uint32_t *val)
15199f7d47b0SAlexander V. Chernikov {
15209f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
15219f7d47b0SAlexander V. Chernikov 
1522c8d5d308SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tbl);
15239f7d47b0SAlexander V. Chernikov 
15249f7d47b0SAlexander V. Chernikov 	return (ti->lookup(ti, paddr, plen, val));
15259f7d47b0SAlexander V. Chernikov }
15269f7d47b0SAlexander V. Chernikov 
15279f7d47b0SAlexander V. Chernikov /*
15289f7d47b0SAlexander V. Chernikov  * Info/List/dump support for tables.
15299f7d47b0SAlexander V. Chernikov  *
15309f7d47b0SAlexander V. Chernikov  */
15319f7d47b0SAlexander V. Chernikov 
1532f1220db8SAlexander V. Chernikov /*
1533d3a4f924SAlexander V. Chernikov  * High-level 'get' cmds sysctl handlers
1534d3a4f924SAlexander V. Chernikov  */
1535d3a4f924SAlexander V. Chernikov 
1536d3a4f924SAlexander V. Chernikov /*
1537d3a4f924SAlexander V. Chernikov  * Lists all tables currently available in kernel.
1538ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1539d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
1540d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
1541d3a4f924SAlexander V. Chernikov  *
1542d3a4f924SAlexander V. Chernikov  * Returns 0 on success
1543d3a4f924SAlexander V. Chernikov  */
1544f1220db8SAlexander V. Chernikov int
15452d99a349SAlexander V. Chernikov ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
1546f1220db8SAlexander V. Chernikov {
1547f1220db8SAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
1548f1220db8SAlexander V. Chernikov 	int error;
1549f1220db8SAlexander V. Chernikov 
15502d99a349SAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
15512d99a349SAlexander V. Chernikov 	if (olh == NULL)
1552d3a4f924SAlexander V. Chernikov 		return (EINVAL);
155368394ec8SAlexander V. Chernikov 	if (sd->valsize < olh->size)
155468394ec8SAlexander V. Chernikov 		return (EINVAL);
1555d3a4f924SAlexander V. Chernikov 
1556f1220db8SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
15572d99a349SAlexander V. Chernikov 	error = export_tables(ch, olh, sd);
1558f1220db8SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1559f1220db8SAlexander V. Chernikov 
1560f1220db8SAlexander V. Chernikov 	return (error);
1561f1220db8SAlexander V. Chernikov }
1562f1220db8SAlexander V. Chernikov 
1563f1220db8SAlexander V. Chernikov /*
15642d99a349SAlexander V. Chernikov  * Store table info to buffer provided by @sd.
1565ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
1566d3a4f924SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
1567d3a4f924SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ]
1568d3a4f924SAlexander V. Chernikov  *
1569d3a4f924SAlexander V. Chernikov  * Returns 0 on success.
1570d3a4f924SAlexander V. Chernikov  */
1571d3a4f924SAlexander V. Chernikov int
15722d99a349SAlexander V. Chernikov ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
1573d3a4f924SAlexander V. Chernikov {
1574d3a4f924SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1575d3a4f924SAlexander V. Chernikov 	struct table_config *tc;
1576d3a4f924SAlexander V. Chernikov 	struct tid_info ti;
1577d3a4f924SAlexander V. Chernikov 	size_t sz;
1578d3a4f924SAlexander V. Chernikov 
1579d3a4f924SAlexander V. Chernikov 	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
15802d99a349SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
15812d99a349SAlexander V. Chernikov 	if (oh == NULL)
1582d3a4f924SAlexander V. Chernikov 		return (EINVAL);
1583d3a4f924SAlexander V. Chernikov 
1584d3a4f924SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1585d3a4f924SAlexander V. Chernikov 
1586d3a4f924SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
1587d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
1588d3a4f924SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
1589d3a4f924SAlexander V. Chernikov 		return (ESRCH);
1590d3a4f924SAlexander V. Chernikov 	}
1591d3a4f924SAlexander V. Chernikov 
1592ac35ff17SAlexander V. Chernikov 	export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1));
1593d3a4f924SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
1594d3a4f924SAlexander V. Chernikov 
15952d99a349SAlexander V. Chernikov 	return (0);
1596d3a4f924SAlexander V. Chernikov }
1597d3a4f924SAlexander V. Chernikov 
1598d3a4f924SAlexander V. Chernikov /*
1599adf3b2b9SAlexander V. Chernikov  * Modifies existing table.
1600adf3b2b9SAlexander V. Chernikov  * Data layout (v0)(current):
1601adf3b2b9SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
1602adf3b2b9SAlexander V. Chernikov  *
1603adf3b2b9SAlexander V. Chernikov  * Returns 0 on success
1604adf3b2b9SAlexander V. Chernikov  */
1605adf3b2b9SAlexander V. Chernikov int
1606adf3b2b9SAlexander V. Chernikov ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1607adf3b2b9SAlexander V. Chernikov     struct sockopt_data *sd)
1608adf3b2b9SAlexander V. Chernikov {
1609adf3b2b9SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
1610adf3b2b9SAlexander V. Chernikov 	ipfw_xtable_info *i;
1611adf3b2b9SAlexander V. Chernikov 	char *tname;
1612adf3b2b9SAlexander V. Chernikov 	struct tid_info ti;
1613adf3b2b9SAlexander V. Chernikov 	struct namedobj_instance *ni;
1614adf3b2b9SAlexander V. Chernikov 	struct table_config *tc;
1615adf3b2b9SAlexander V. Chernikov 
1616adf3b2b9SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
1617adf3b2b9SAlexander V. Chernikov 		return (EINVAL);
1618adf3b2b9SAlexander V. Chernikov 
1619adf3b2b9SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)sd->kbuf;
1620adf3b2b9SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
1621adf3b2b9SAlexander V. Chernikov 
1622adf3b2b9SAlexander V. Chernikov 	/*
1623adf3b2b9SAlexander V. Chernikov 	 * Verify user-supplied strings.
1624adf3b2b9SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
1625adf3b2b9SAlexander V. Chernikov 	 */
1626adf3b2b9SAlexander V. Chernikov 	tname = oh->ntlv.name;
1627adf3b2b9SAlexander V. Chernikov 	if (ipfw_check_table_name(tname) != 0)
1628adf3b2b9SAlexander V. Chernikov 		return (EINVAL);
1629adf3b2b9SAlexander V. Chernikov 
1630adf3b2b9SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1631adf3b2b9SAlexander V. Chernikov 	ti.type = i->type;
1632adf3b2b9SAlexander V. Chernikov 
1633adf3b2b9SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1634adf3b2b9SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1635adf3b2b9SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) == NULL) {
1636adf3b2b9SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1637adf3b2b9SAlexander V. Chernikov 		return (ESRCH);
1638adf3b2b9SAlexander V. Chernikov 	}
1639fd0869d5SAlexander V. Chernikov 
1640fd0869d5SAlexander V. Chernikov 	/* Do not support any modifications for readonly tables */
1641fd0869d5SAlexander V. Chernikov 	if ((tc->ta->flags & TA_FLAG_READONLY) != 0) {
1642fd0869d5SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
1643fd0869d5SAlexander V. Chernikov 		return (EACCES);
1644fd0869d5SAlexander V. Chernikov 	}
1645fd0869d5SAlexander V. Chernikov 
1646adf3b2b9SAlexander V. Chernikov 	if ((i->mflags & IPFW_TMFLAGS_FTYPE) != 0)
1647adf3b2b9SAlexander V. Chernikov 		tc->vftype = i->vftype;
1648adf3b2b9SAlexander V. Chernikov 	if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0)
1649adf3b2b9SAlexander V. Chernikov 		tc->limit = i->limit;
16504f43138aSAlexander V. Chernikov 	if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0)
16514f43138aSAlexander V. Chernikov 		tc->locked = ((i->flags & IPFW_TGFLAGS_LOCKED) != 0);
1652adf3b2b9SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
1653adf3b2b9SAlexander V. Chernikov 
1654adf3b2b9SAlexander V. Chernikov 	return (0);
1655adf3b2b9SAlexander V. Chernikov }
1656adf3b2b9SAlexander V. Chernikov 
1657adf3b2b9SAlexander V. Chernikov /*
16589490a627SAlexander V. Chernikov  * Creates new table.
1659ac35ff17SAlexander V. Chernikov  * Data layout (v0)(current):
16609490a627SAlexander V. Chernikov  * Request: [ ipfw_obj_header ipfw_xtable_info ]
16619490a627SAlexander V. Chernikov  *
16629490a627SAlexander V. Chernikov  * Returns 0 on success
16639490a627SAlexander V. Chernikov  */
16649490a627SAlexander V. Chernikov int
1665ac35ff17SAlexander V. Chernikov ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1666ac35ff17SAlexander V. Chernikov     struct sockopt_data *sd)
16679490a627SAlexander V. Chernikov {
16689490a627SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
16699490a627SAlexander V. Chernikov 	ipfw_xtable_info *i;
16709490a627SAlexander V. Chernikov 	char *tname, *aname;
16719490a627SAlexander V. Chernikov 	struct tid_info ti;
16729490a627SAlexander V. Chernikov 	struct namedobj_instance *ni;
16739490a627SAlexander V. Chernikov 	struct table_config *tc;
16749490a627SAlexander V. Chernikov 
1675ac35ff17SAlexander V. Chernikov 	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
16769490a627SAlexander V. Chernikov 		return (EINVAL);
16779490a627SAlexander V. Chernikov 
1678ac35ff17SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)sd->kbuf;
16799490a627SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
16809490a627SAlexander V. Chernikov 
16819490a627SAlexander V. Chernikov 	/*
16829490a627SAlexander V. Chernikov 	 * Verify user-supplied strings.
16832d99a349SAlexander V. Chernikov 	 * Check for null-terminated/zero-length strings/
16849490a627SAlexander V. Chernikov 	 */
1685ac35ff17SAlexander V. Chernikov 	tname = oh->ntlv.name;
16869490a627SAlexander V. Chernikov 	aname = i->algoname;
1687ac35ff17SAlexander V. Chernikov 	if (ipfw_check_table_name(tname) != 0 ||
16889490a627SAlexander V. Chernikov 	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
16899490a627SAlexander V. Chernikov 		return (EINVAL);
16909490a627SAlexander V. Chernikov 
16919490a627SAlexander V. Chernikov 	if (aname[0] == '\0') {
16929490a627SAlexander V. Chernikov 		/* Use default algorithm */
16939490a627SAlexander V. Chernikov 		aname = NULL;
16949490a627SAlexander V. Chernikov 	}
16959490a627SAlexander V. Chernikov 
16969490a627SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
1697ac35ff17SAlexander V. Chernikov 	ti.type = i->type;
16989490a627SAlexander V. Chernikov 
16999490a627SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
17009490a627SAlexander V. Chernikov 
17019490a627SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
17029490a627SAlexander V. Chernikov 	if ((tc = find_table(ni, &ti)) != NULL) {
17039490a627SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
17049490a627SAlexander V. Chernikov 		return (EEXIST);
17059490a627SAlexander V. Chernikov 	}
17069490a627SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
17079490a627SAlexander V. Chernikov 
17080468c5baSAlexander V. Chernikov 	return (create_table_internal(ch, &ti, aname, i, NULL, NULL, NULL, 0));
1709db785d31SAlexander V. Chernikov }
1710db785d31SAlexander V. Chernikov 
1711db785d31SAlexander V. Chernikov /*
1712db785d31SAlexander V. Chernikov  * Creates new table based on @ti and @aname.
1713db785d31SAlexander V. Chernikov  *
1714db785d31SAlexander V. Chernikov  * Relies on table name checking inside find_name_tlv()
1715db785d31SAlexander V. Chernikov  * Assume @aname to be checked and valid.
17160468c5baSAlexander V. Chernikov  * Stores allocated table config, used algo and kidx
17170468c5baSAlexander V. Chernikov  * inside @ptc, @pta and @pkidx (if non-NULL).
17180468c5baSAlexander V. Chernikov  * Reference created table if @compat is non-zero.
1719db785d31SAlexander V. Chernikov  *
1720db785d31SAlexander V. Chernikov  * Returns 0 on success.
1721db785d31SAlexander V. Chernikov  */
1722db785d31SAlexander V. Chernikov static int
1723db785d31SAlexander V. Chernikov create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
17240468c5baSAlexander V. Chernikov     char *aname, ipfw_xtable_info *i, struct table_config **ptc,
17250468c5baSAlexander V. Chernikov     struct table_algo **pta, uint16_t *pkidx, int compat)
1726db785d31SAlexander V. Chernikov {
1727db785d31SAlexander V. Chernikov 	struct namedobj_instance *ni;
1728c8d5d308SAlexander V. Chernikov 	struct table_config *tc, *tc_new, *tmp;
1729db785d31SAlexander V. Chernikov 	struct table_algo *ta;
1730db785d31SAlexander V. Chernikov 	uint16_t kidx;
1731db785d31SAlexander V. Chernikov 
1732db785d31SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1733db785d31SAlexander V. Chernikov 
1734db785d31SAlexander V. Chernikov 	ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname);
17359490a627SAlexander V. Chernikov 	if (ta == NULL)
17369490a627SAlexander V. Chernikov 		return (ENOTSUP);
17379490a627SAlexander V. Chernikov 
17384c0c07a5SAlexander V. Chernikov 	tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype);
17394c0c07a5SAlexander V. Chernikov 	if (tc == NULL)
17409490a627SAlexander V. Chernikov 		return (ENOMEM);
17419490a627SAlexander V. Chernikov 
1742adf3b2b9SAlexander V. Chernikov 	tc->vftype = i->vftype;
17434c0c07a5SAlexander V. Chernikov 	tc->limit = i->limit;
1744fd0869d5SAlexander V. Chernikov 	if (ta->flags & TA_FLAG_READONLY)
1745fd0869d5SAlexander V. Chernikov 		tc->locked = 1;
1746fd0869d5SAlexander V. Chernikov 	else
17474f43138aSAlexander V. Chernikov 		tc->locked = (i->flags & IPFW_TGFLAGS_LOCKED) != 0;
17484c0c07a5SAlexander V. Chernikov 
17499490a627SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
1750db785d31SAlexander V. Chernikov 
1751db785d31SAlexander V. Chernikov 	/* Check if table has been already created */
17520468c5baSAlexander V. Chernikov 	tc_new = find_table(ni, ti);
17530468c5baSAlexander V. Chernikov 	if (tc_new != NULL) {
17540468c5baSAlexander V. Chernikov 
17550468c5baSAlexander V. Chernikov 		/*
17560468c5baSAlexander V. Chernikov 		 * Compat: do not fail if we're
17570468c5baSAlexander V. Chernikov 		 * requesting to create existing table
17580468c5baSAlexander V. Chernikov 		 * which has the same type / vtype
17590468c5baSAlexander V. Chernikov 		 */
17600468c5baSAlexander V. Chernikov 		if (compat == 0 || tc_new->no.type != tc->no.type ||
17610468c5baSAlexander V. Chernikov 		    tc_new->vtype != tc->vtype) {
1762db785d31SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1763db785d31SAlexander V. Chernikov 			free_table_config(ni, tc);
1764db785d31SAlexander V. Chernikov 			return (EEXIST);
1765db785d31SAlexander V. Chernikov 		}
1766db785d31SAlexander V. Chernikov 
17670468c5baSAlexander V. Chernikov 		/* Exchange tc and tc_new for proper refcounting & freeing */
17680468c5baSAlexander V. Chernikov 		tmp = tc;
17690468c5baSAlexander V. Chernikov 		tc = tc_new;
17700468c5baSAlexander V. Chernikov 		tc_new = tmp;
17710468c5baSAlexander V. Chernikov 	} else {
17720468c5baSAlexander V. Chernikov 		/* New table */
1773ac35ff17SAlexander V. Chernikov 		if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
17749490a627SAlexander V. Chernikov 			IPFW_UH_WUNLOCK(ch);
1775db785d31SAlexander V. Chernikov 			printf("Unable to allocate table index."
1776db785d31SAlexander V. Chernikov 			    " Consider increasing net.inet.ip.fw.tables_max");
17779490a627SAlexander V. Chernikov 			free_table_config(ni, tc);
17789490a627SAlexander V. Chernikov 			return (EBUSY);
17799490a627SAlexander V. Chernikov 		}
17809490a627SAlexander V. Chernikov 		tc->no.kidx = kidx;
17819490a627SAlexander V. Chernikov 
17829490a627SAlexander V. Chernikov 		IPFW_WLOCK(ch);
17839490a627SAlexander V. Chernikov 		link_table(ch, tc);
17849490a627SAlexander V. Chernikov 		IPFW_WUNLOCK(ch);
17850468c5baSAlexander V. Chernikov 	}
17860468c5baSAlexander V. Chernikov 
17870468c5baSAlexander V. Chernikov 	if (compat != 0)
17880468c5baSAlexander V. Chernikov 		tc->no.refcnt++;
17890468c5baSAlexander V. Chernikov 	if (ptc != NULL)
17900468c5baSAlexander V. Chernikov 		*ptc = tc;
17910468c5baSAlexander V. Chernikov 	if (pta != NULL)
17920468c5baSAlexander V. Chernikov 		*pta = ta;
17930468c5baSAlexander V. Chernikov 	if (pkidx != NULL)
17940468c5baSAlexander V. Chernikov 		*pkidx = tc->no.kidx;
17959490a627SAlexander V. Chernikov 
17969490a627SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
17979490a627SAlexander V. Chernikov 
17980468c5baSAlexander V. Chernikov 	if (tc_new != NULL)
17990468c5baSAlexander V. Chernikov 		free_table_config(ni, tc_new);
18000468c5baSAlexander V. Chernikov 
18019490a627SAlexander V. Chernikov 	return (0);
18029490a627SAlexander V. Chernikov }
18039490a627SAlexander V. Chernikov 
180446d52008SAlexander V. Chernikov static void
180546d52008SAlexander V. Chernikov ntlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti)
1806d3a4f924SAlexander V. Chernikov {
1807d3a4f924SAlexander V. Chernikov 
1808d3a4f924SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct tid_info));
180946d52008SAlexander V. Chernikov 	ti->set = ntlv->set;
181046d52008SAlexander V. Chernikov 	ti->uidx = ntlv->idx;
181146d52008SAlexander V. Chernikov 	ti->tlvs = ntlv;
181246d52008SAlexander V. Chernikov 	ti->tlen = ntlv->head.length;
181346d52008SAlexander V. Chernikov }
181446d52008SAlexander V. Chernikov 
181546d52008SAlexander V. Chernikov static void
181646d52008SAlexander V. Chernikov objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
181746d52008SAlexander V. Chernikov {
181846d52008SAlexander V. Chernikov 
181946d52008SAlexander V. Chernikov 	ntlv_to_ti(&oh->ntlv, ti);
1820d3a4f924SAlexander V. Chernikov }
1821d3a4f924SAlexander V. Chernikov 
182235e1bbd0SAlexander V. Chernikov /*
182335e1bbd0SAlexander V. Chernikov  * Exports basic table info as name TLV.
182435e1bbd0SAlexander V. Chernikov  * Used inside dump_static_rules() to provide info
182535e1bbd0SAlexander V. Chernikov  * about all tables referenced by current ruleset.
182635e1bbd0SAlexander V. Chernikov  *
182735e1bbd0SAlexander V. Chernikov  * Returns 0 on success.
182835e1bbd0SAlexander V. Chernikov  */
1829563b5ab1SAlexander V. Chernikov int
1830563b5ab1SAlexander V. Chernikov ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
1831563b5ab1SAlexander V. Chernikov     struct sockopt_data *sd)
1832563b5ab1SAlexander V. Chernikov {
1833563b5ab1SAlexander V. Chernikov 	struct namedobj_instance *ni;
1834563b5ab1SAlexander V. Chernikov 	struct named_object *no;
1835563b5ab1SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
1836563b5ab1SAlexander V. Chernikov 
1837563b5ab1SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
1838563b5ab1SAlexander V. Chernikov 
1839ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_kidx(ni, kidx);
1840563b5ab1SAlexander V. Chernikov 	KASSERT(no != NULL, ("invalid table kidx passed"));
1841563b5ab1SAlexander V. Chernikov 
1842563b5ab1SAlexander V. Chernikov 	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
1843563b5ab1SAlexander V. Chernikov 	if (ntlv == NULL)
1844563b5ab1SAlexander V. Chernikov 		return (ENOMEM);
1845563b5ab1SAlexander V. Chernikov 
1846563b5ab1SAlexander V. Chernikov 	ntlv->head.type = IPFW_TLV_TBL_NAME;
1847563b5ab1SAlexander V. Chernikov 	ntlv->head.length = sizeof(*ntlv);
1848563b5ab1SAlexander V. Chernikov 	ntlv->idx = no->kidx;
1849563b5ab1SAlexander V. Chernikov 	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
1850563b5ab1SAlexander V. Chernikov 
1851563b5ab1SAlexander V. Chernikov 	return (0);
1852563b5ab1SAlexander V. Chernikov }
1853563b5ab1SAlexander V. Chernikov 
185435e1bbd0SAlexander V. Chernikov /*
1855f99fbf96SAlexander V. Chernikov  * Marks every table kidx used in @rule with bit in @bmask.
1856f99fbf96SAlexander V. Chernikov  * Used to generate bitmask of referenced tables for given ruleset.
1857f99fbf96SAlexander V. Chernikov  *
1858f99fbf96SAlexander V. Chernikov  * Returns number of newly-referenced tables.
1859f99fbf96SAlexander V. Chernikov  */
1860f99fbf96SAlexander V. Chernikov int
1861f99fbf96SAlexander V. Chernikov ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
1862f99fbf96SAlexander V. Chernikov     uint32_t *bmask)
1863f99fbf96SAlexander V. Chernikov {
1864f99fbf96SAlexander V. Chernikov 	int cmdlen, l, count;
1865f99fbf96SAlexander V. Chernikov 	ipfw_insn *cmd;
1866f99fbf96SAlexander V. Chernikov 	uint16_t kidx;
1867f99fbf96SAlexander V. Chernikov 	uint8_t type;
1868f99fbf96SAlexander V. Chernikov 
1869f99fbf96SAlexander V. Chernikov 	l = rule->cmd_len;
1870f99fbf96SAlexander V. Chernikov 	cmd = rule->cmd;
1871f99fbf96SAlexander V. Chernikov 	cmdlen = 0;
1872f99fbf96SAlexander V. Chernikov 	count = 0;
1873f99fbf96SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1874f99fbf96SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
1875f99fbf96SAlexander V. Chernikov 
1876f99fbf96SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
1877f99fbf96SAlexander V. Chernikov 			continue;
1878f99fbf96SAlexander V. Chernikov 
1879f99fbf96SAlexander V. Chernikov 		if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
1880f99fbf96SAlexander V. Chernikov 			count++;
1881f99fbf96SAlexander V. Chernikov 
1882f99fbf96SAlexander V. Chernikov 		bmask[kidx / 32] |= 1 << (kidx % 32);
1883f99fbf96SAlexander V. Chernikov 	}
1884f99fbf96SAlexander V. Chernikov 
1885f99fbf96SAlexander V. Chernikov 	return (count);
1886f99fbf96SAlexander V. Chernikov }
1887f99fbf96SAlexander V. Chernikov 
1888d3b00c08SAlexander V. Chernikov struct dump_args {
1889d3b00c08SAlexander V. Chernikov 	struct table_info *ti;
1890d3b00c08SAlexander V. Chernikov 	struct table_config *tc;
1891d3b00c08SAlexander V. Chernikov 	struct sockopt_data *sd;
1892d3b00c08SAlexander V. Chernikov 	uint32_t cnt;
1893d3b00c08SAlexander V. Chernikov 	uint16_t uidx;
1894d3b00c08SAlexander V. Chernikov 	int error;
1895d3b00c08SAlexander V. Chernikov 	ipfw_table_entry *ent;
1896d3b00c08SAlexander V. Chernikov 	uint32_t size;
1897d3b00c08SAlexander V. Chernikov 	ipfw_obj_tentry tent;
1898d3b00c08SAlexander V. Chernikov };
1899d3b00c08SAlexander V. Chernikov 
1900d3b00c08SAlexander V. Chernikov static int
1901d3b00c08SAlexander V. Chernikov count_ext_entries(void *e, void *arg)
1902d3b00c08SAlexander V. Chernikov {
1903d3b00c08SAlexander V. Chernikov 	struct dump_args *da;
1904d3b00c08SAlexander V. Chernikov 
1905d3b00c08SAlexander V. Chernikov 	da = (struct dump_args *)arg;
1906d3b00c08SAlexander V. Chernikov 	da->cnt++;
1907d3b00c08SAlexander V. Chernikov 
1908d3b00c08SAlexander V. Chernikov 	return (0);
1909d3b00c08SAlexander V. Chernikov }
1910d3b00c08SAlexander V. Chernikov 
1911d3b00c08SAlexander V. Chernikov /*
1912d3b00c08SAlexander V. Chernikov  * Gets number of items from table either using
1913d3b00c08SAlexander V. Chernikov  * internal counter or calling algo callback for
1914d3b00c08SAlexander V. Chernikov  * externally-managed tables.
1915d3b00c08SAlexander V. Chernikov  *
1916d3b00c08SAlexander V. Chernikov  * Returns number of records.
1917d3b00c08SAlexander V. Chernikov  */
1918d3b00c08SAlexander V. Chernikov static uint32_t
1919d3b00c08SAlexander V. Chernikov table_get_count(struct ip_fw_chain *ch, struct table_config *tc)
1920d3b00c08SAlexander V. Chernikov {
1921d3b00c08SAlexander V. Chernikov 	struct table_info *ti;
1922d3b00c08SAlexander V. Chernikov 	struct table_algo *ta;
1923d3b00c08SAlexander V. Chernikov 	struct dump_args da;
1924d3b00c08SAlexander V. Chernikov 
1925d3b00c08SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tc->no.kidx);
1926d3b00c08SAlexander V. Chernikov 	ta = tc->ta;
1927d3b00c08SAlexander V. Chernikov 
1928d3b00c08SAlexander V. Chernikov 	/* Use internal counter for self-managed tables */
1929d3b00c08SAlexander V. Chernikov 	if ((ta->flags & TA_FLAG_READONLY) == 0)
1930d3b00c08SAlexander V. Chernikov 		return (tc->count);
1931d3b00c08SAlexander V. Chernikov 
1932d3b00c08SAlexander V. Chernikov 	/* Use callback to quickly get number of items */
1933d3b00c08SAlexander V. Chernikov 	if ((ta->flags & TA_FLAG_EXTCOUNTER) != 0)
1934d3b00c08SAlexander V. Chernikov 		return (ta->get_count(tc->astate, ti));
1935d3b00c08SAlexander V. Chernikov 
1936d3b00c08SAlexander V. Chernikov 	/* Count number of iterms ourselves */
1937d3b00c08SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
1938d3b00c08SAlexander V. Chernikov 	ta->foreach(tc->astate, ti, count_ext_entries, &da);
1939d3b00c08SAlexander V. Chernikov 
1940d3b00c08SAlexander V. Chernikov 	return (da.cnt);
1941d3b00c08SAlexander V. Chernikov }
1942f99fbf96SAlexander V. Chernikov 
1943f99fbf96SAlexander V. Chernikov /*
194435e1bbd0SAlexander V. Chernikov  * Exports table @tc info into standard ipfw_xtable_info format.
194535e1bbd0SAlexander V. Chernikov  */
19469f7d47b0SAlexander V. Chernikov static void
1947ac35ff17SAlexander V. Chernikov export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
1948ac35ff17SAlexander V. Chernikov     ipfw_xtable_info *i)
19499f7d47b0SAlexander V. Chernikov {
1950ac35ff17SAlexander V. Chernikov 	struct table_info *ti;
19515f379342SAlexander V. Chernikov 	struct table_algo *ta;
19529f7d47b0SAlexander V. Chernikov 
19539f7d47b0SAlexander V. Chernikov 	i->type = tc->no.type;
1954914bffb6SAlexander V. Chernikov 	i->tflags = tc->tflags;
1955ac35ff17SAlexander V. Chernikov 	i->vtype = tc->vtype;
1956adf3b2b9SAlexander V. Chernikov 	i->vftype = tc->vftype;
19579f7d47b0SAlexander V. Chernikov 	i->set = tc->no.set;
19589f7d47b0SAlexander V. Chernikov 	i->kidx = tc->no.kidx;
19599f7d47b0SAlexander V. Chernikov 	i->refcnt = tc->no.refcnt;
1960d3b00c08SAlexander V. Chernikov 	i->count = table_get_count(ch, tc);
19614c0c07a5SAlexander V. Chernikov 	i->limit = tc->limit;
19624f43138aSAlexander V. Chernikov 	i->flags |= (tc->locked != 0) ? IPFW_TGFLAGS_LOCKED : 0;
196381d3153dSAlexander V. Chernikov 	i->size = tc->count * sizeof(ipfw_obj_tentry);
1964f1220db8SAlexander V. Chernikov 	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
19659f7d47b0SAlexander V. Chernikov 	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
1966ac35ff17SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, tc->no.kidx);
19675f379342SAlexander V. Chernikov 	ta = tc->ta;
19685f379342SAlexander V. Chernikov 	if (ta->print_config != NULL) {
19695f379342SAlexander V. Chernikov 		/* Use algo function to print table config to string */
19705f379342SAlexander V. Chernikov 		ta->print_config(tc->astate, ti, i->algoname,
1971ac35ff17SAlexander V. Chernikov 		    sizeof(i->algoname));
1972ac35ff17SAlexander V. Chernikov 	} else
19735f379342SAlexander V. Chernikov 		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
19745f379342SAlexander V. Chernikov 	/* Dump algo-specific data, if possible */
19755f379342SAlexander V. Chernikov 	if (ta->dump_tinfo != NULL) {
19765f379342SAlexander V. Chernikov 		ta->dump_tinfo(tc->astate, ti, &i->ta_info);
19775f379342SAlexander V. Chernikov 		i->ta_info.flags |= IPFW_TATFLAGS_DATA;
19785f379342SAlexander V. Chernikov 	}
19799f7d47b0SAlexander V. Chernikov }
19809f7d47b0SAlexander V. Chernikov 
1981ac35ff17SAlexander V. Chernikov struct dump_table_args {
1982ac35ff17SAlexander V. Chernikov 	struct ip_fw_chain *ch;
1983ac35ff17SAlexander V. Chernikov 	struct sockopt_data *sd;
1984ac35ff17SAlexander V. Chernikov };
1985ac35ff17SAlexander V. Chernikov 
19869f7d47b0SAlexander V. Chernikov static void
19879f7d47b0SAlexander V. Chernikov export_table_internal(struct namedobj_instance *ni, struct named_object *no,
19889f7d47b0SAlexander V. Chernikov     void *arg)
19893b3a8eb9SGleb Smirnoff {
19909f7d47b0SAlexander V. Chernikov 	ipfw_xtable_info *i;
1991ac35ff17SAlexander V. Chernikov 	struct dump_table_args *dta;
19923b3a8eb9SGleb Smirnoff 
1993ac35ff17SAlexander V. Chernikov 	dta = (struct dump_table_args *)arg;
1994ac35ff17SAlexander V. Chernikov 
1995ac35ff17SAlexander V. Chernikov 	i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
199668394ec8SAlexander V. Chernikov 	KASSERT(i != 0, ("previously checked buffer is not enough"));
19979f7d47b0SAlexander V. Chernikov 
1998ac35ff17SAlexander V. Chernikov 	export_table_info(dta->ch, (struct table_config *)no, i);
19999f7d47b0SAlexander V. Chernikov }
20009f7d47b0SAlexander V. Chernikov 
2001f1220db8SAlexander V. Chernikov /*
2002f1220db8SAlexander V. Chernikov  * Export all tables as ipfw_xtable_info structures to
20032d99a349SAlexander V. Chernikov  * storage provided by @sd.
200428ea4fa3SAlexander V. Chernikov  *
200528ea4fa3SAlexander V. Chernikov  * If supplied buffer is too small, fills in required size
200628ea4fa3SAlexander V. Chernikov  * and returns ENOMEM.
2007f1220db8SAlexander V. Chernikov  * Returns 0 on success.
2008f1220db8SAlexander V. Chernikov  */
2009f1220db8SAlexander V. Chernikov static int
20102d99a349SAlexander V. Chernikov export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
20112d99a349SAlexander V. Chernikov     struct sockopt_data *sd)
20129f7d47b0SAlexander V. Chernikov {
20139f7d47b0SAlexander V. Chernikov 	uint32_t size;
20149f7d47b0SAlexander V. Chernikov 	uint32_t count;
2015ac35ff17SAlexander V. Chernikov 	struct dump_table_args dta;
20169f7d47b0SAlexander V. Chernikov 
20179f7d47b0SAlexander V. Chernikov 	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
20189f7d47b0SAlexander V. Chernikov 	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
20192d99a349SAlexander V. Chernikov 
20202d99a349SAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
2021f1220db8SAlexander V. Chernikov 	olh->count = count;
2022f1220db8SAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_xtable_info);
20232d99a349SAlexander V. Chernikov 
20242d99a349SAlexander V. Chernikov 	if (size > olh->size) {
20252d99a349SAlexander V. Chernikov 		olh->size = size;
20269f7d47b0SAlexander V. Chernikov 		return (ENOMEM);
2027f1220db8SAlexander V. Chernikov 	}
202868394ec8SAlexander V. Chernikov 
20299f7d47b0SAlexander V. Chernikov 	olh->size = size;
20302d99a349SAlexander V. Chernikov 
2031ac35ff17SAlexander V. Chernikov 	dta.ch = ch;
2032ac35ff17SAlexander V. Chernikov 	dta.sd = sd;
2033ac35ff17SAlexander V. Chernikov 
2034ac35ff17SAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta);
20359f7d47b0SAlexander V. Chernikov 
20363b3a8eb9SGleb Smirnoff 	return (0);
20373b3a8eb9SGleb Smirnoff }
20383b3a8eb9SGleb Smirnoff 
2039f99fbf96SAlexander V. Chernikov int
2040f99fbf96SAlexander V. Chernikov ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
2041f99fbf96SAlexander V. Chernikov     struct sockopt_data *sd)
2042f99fbf96SAlexander V. Chernikov {
2043f99fbf96SAlexander V. Chernikov 	int error;
2044f99fbf96SAlexander V. Chernikov 
2045f99fbf96SAlexander V. Chernikov 	switch (op3->version) {
2046f99fbf96SAlexander V. Chernikov 	case 0:
2047f99fbf96SAlexander V. Chernikov 		error = ipfw_dump_table_v0(ch, sd);
2048f99fbf96SAlexander V. Chernikov 		break;
2049f99fbf96SAlexander V. Chernikov 	case 1:
2050f99fbf96SAlexander V. Chernikov 		error = ipfw_dump_table_v1(ch, sd);
2051f99fbf96SAlexander V. Chernikov 		break;
2052f99fbf96SAlexander V. Chernikov 	default:
2053f99fbf96SAlexander V. Chernikov 		error = ENOTSUP;
2054f99fbf96SAlexander V. Chernikov 	}
2055f99fbf96SAlexander V. Chernikov 
2056f99fbf96SAlexander V. Chernikov 	return (error);
2057f99fbf96SAlexander V. Chernikov }
2058f99fbf96SAlexander V. Chernikov 
2059f99fbf96SAlexander V. Chernikov /*
2060f99fbf96SAlexander V. Chernikov  * Dumps all table data
2061f99fbf96SAlexander V. Chernikov  * Data layout (v1)(current):
2062f99fbf96SAlexander V. Chernikov  * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
2063f99fbf96SAlexander V. Chernikov  * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ]
2064f99fbf96SAlexander V. Chernikov  *
2065f99fbf96SAlexander V. Chernikov  * Returns 0 on success
2066f99fbf96SAlexander V. Chernikov  */
2067f99fbf96SAlexander V. Chernikov static int
2068f99fbf96SAlexander V. Chernikov ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
2069f99fbf96SAlexander V. Chernikov {
2070f99fbf96SAlexander V. Chernikov 	struct _ipfw_obj_header *oh;
2071f99fbf96SAlexander V. Chernikov 	ipfw_xtable_info *i;
2072f99fbf96SAlexander V. Chernikov 	struct tid_info ti;
2073f99fbf96SAlexander V. Chernikov 	struct table_config *tc;
2074f99fbf96SAlexander V. Chernikov 	struct table_algo *ta;
2075f99fbf96SAlexander V. Chernikov 	struct dump_args da;
2076f99fbf96SAlexander V. Chernikov 	uint32_t sz;
2077f99fbf96SAlexander V. Chernikov 
2078f99fbf96SAlexander V. Chernikov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
2079f99fbf96SAlexander V. Chernikov 	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
2080f99fbf96SAlexander V. Chernikov 	if (oh == NULL)
2081f99fbf96SAlexander V. Chernikov 		return (EINVAL);
2082f99fbf96SAlexander V. Chernikov 
2083f99fbf96SAlexander V. Chernikov 	i = (ipfw_xtable_info *)(oh + 1);
2084f99fbf96SAlexander V. Chernikov 	objheader_to_ti(oh, &ti);
2085f99fbf96SAlexander V. Chernikov 
2086f99fbf96SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
2087f99fbf96SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
2088f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2089f99fbf96SAlexander V. Chernikov 		return (ESRCH);
2090f99fbf96SAlexander V. Chernikov 	}
2091f99fbf96SAlexander V. Chernikov 	export_table_info(ch, tc, i);
2092f99fbf96SAlexander V. Chernikov 
2093f99fbf96SAlexander V. Chernikov 	if (sd->valsize < i->size) {
2094f99fbf96SAlexander V. Chernikov 
2095f99fbf96SAlexander V. Chernikov 		/*
2096f99fbf96SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
2097f99fbf96SAlexander V. Chernikov 		 * WE've already filled in @i structure with
2098f99fbf96SAlexander V. Chernikov 		 * relevant table info including size, so we
2099f99fbf96SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
2100f99fbf96SAlexander V. Chernikov 		 */
2101f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2102f99fbf96SAlexander V. Chernikov 		return (ENOMEM);
2103f99fbf96SAlexander V. Chernikov 	}
2104f99fbf96SAlexander V. Chernikov 
2105f99fbf96SAlexander V. Chernikov 	/*
2106f99fbf96SAlexander V. Chernikov 	 * Do the actual dump in eXtended format
2107f99fbf96SAlexander V. Chernikov 	 */
2108f99fbf96SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
2109f99fbf96SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2110f99fbf96SAlexander V. Chernikov 	da.tc = tc;
2111f99fbf96SAlexander V. Chernikov 	da.sd = sd;
2112f99fbf96SAlexander V. Chernikov 
2113f99fbf96SAlexander V. Chernikov 	ta = tc->ta;
2114f99fbf96SAlexander V. Chernikov 
2115f99fbf96SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_tentry, &da);
2116f99fbf96SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
2117f99fbf96SAlexander V. Chernikov 
2118f99fbf96SAlexander V. Chernikov 	return (da.error);
2119f99fbf96SAlexander V. Chernikov }
2120f99fbf96SAlexander V. Chernikov 
2121f99fbf96SAlexander V. Chernikov /*
2122f99fbf96SAlexander V. Chernikov  * Dumps all table data
2123f99fbf96SAlexander V. Chernikov  * Data layout (version 0)(legacy):
2124f99fbf96SAlexander V. Chernikov  * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
2125f99fbf96SAlexander V. Chernikov  * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
2126f99fbf96SAlexander V. Chernikov  *
2127f99fbf96SAlexander V. Chernikov  * Returns 0 on success
2128f99fbf96SAlexander V. Chernikov  */
2129f99fbf96SAlexander V. Chernikov static int
2130f99fbf96SAlexander V. Chernikov ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
2131f99fbf96SAlexander V. Chernikov {
2132f99fbf96SAlexander V. Chernikov 	ipfw_xtable *xtbl;
2133f99fbf96SAlexander V. Chernikov 	struct tid_info ti;
2134f99fbf96SAlexander V. Chernikov 	struct table_config *tc;
2135f99fbf96SAlexander V. Chernikov 	struct table_algo *ta;
2136f99fbf96SAlexander V. Chernikov 	struct dump_args da;
2137d3b00c08SAlexander V. Chernikov 	size_t sz, count;
2138f99fbf96SAlexander V. Chernikov 
2139f99fbf96SAlexander V. Chernikov 	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
2140f99fbf96SAlexander V. Chernikov 	if (xtbl == NULL)
2141f99fbf96SAlexander V. Chernikov 		return (EINVAL);
2142f99fbf96SAlexander V. Chernikov 
2143f99fbf96SAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
2144f99fbf96SAlexander V. Chernikov 	ti.uidx = xtbl->tbl;
2145f99fbf96SAlexander V. Chernikov 
2146f99fbf96SAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
2147f99fbf96SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
2148f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2149f99fbf96SAlexander V. Chernikov 		return (0);
2150f99fbf96SAlexander V. Chernikov 	}
2151d3b00c08SAlexander V. Chernikov 	count = table_get_count(ch, tc);
2152d3b00c08SAlexander V. Chernikov 	sz = count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
2153f99fbf96SAlexander V. Chernikov 
2154d3b00c08SAlexander V. Chernikov 	xtbl->cnt = count;
2155f99fbf96SAlexander V. Chernikov 	xtbl->size = sz;
2156f99fbf96SAlexander V. Chernikov 	xtbl->type = tc->no.type;
2157f99fbf96SAlexander V. Chernikov 	xtbl->tbl = ti.uidx;
2158f99fbf96SAlexander V. Chernikov 
2159f99fbf96SAlexander V. Chernikov 	if (sd->valsize < sz) {
2160f99fbf96SAlexander V. Chernikov 
2161f99fbf96SAlexander V. Chernikov 		/*
2162f99fbf96SAlexander V. Chernikov 		 * Submitted buffer size is not enough.
2163f99fbf96SAlexander V. Chernikov 		 * WE've already filled in @i structure with
2164f99fbf96SAlexander V. Chernikov 		 * relevant table info including size, so we
2165f99fbf96SAlexander V. Chernikov 		 * can return. Buffer will be flushed automatically.
2166f99fbf96SAlexander V. Chernikov 		 */
2167f99fbf96SAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
2168f99fbf96SAlexander V. Chernikov 		return (ENOMEM);
2169f99fbf96SAlexander V. Chernikov 	}
2170f99fbf96SAlexander V. Chernikov 
2171f99fbf96SAlexander V. Chernikov 	/* Do the actual dump in eXtended format */
2172f99fbf96SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
2173f99fbf96SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2174f99fbf96SAlexander V. Chernikov 	da.tc = tc;
2175f99fbf96SAlexander V. Chernikov 	da.sd = sd;
2176f99fbf96SAlexander V. Chernikov 
2177f99fbf96SAlexander V. Chernikov 	ta = tc->ta;
2178f99fbf96SAlexander V. Chernikov 
2179f99fbf96SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
2180f99fbf96SAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
2181f99fbf96SAlexander V. Chernikov 
2182f99fbf96SAlexander V. Chernikov 	return (0);
2183f99fbf96SAlexander V. Chernikov }
2184f99fbf96SAlexander V. Chernikov 
218581d3153dSAlexander V. Chernikov /*
218681d3153dSAlexander V. Chernikov  * Legacy IP_FW_TABLE_GETSIZE handler
218781d3153dSAlexander V. Chernikov  */
21883b3a8eb9SGleb Smirnoff int
2189b074b7bbSAlexander V. Chernikov ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
21903b3a8eb9SGleb Smirnoff {
2191b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
21923b3a8eb9SGleb Smirnoff 
2193b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
2194b074b7bbSAlexander V. Chernikov 		return (ESRCH);
2195d3b00c08SAlexander V. Chernikov 	*cnt = table_get_count(ch, tc);
21963b3a8eb9SGleb Smirnoff 	return (0);
21973b3a8eb9SGleb Smirnoff }
21983b3a8eb9SGleb Smirnoff 
219981d3153dSAlexander V. Chernikov /*
220081d3153dSAlexander V. Chernikov  * Legacy IP_FW_TABLE_XGETSIZE handler
220181d3153dSAlexander V. Chernikov  */
22029f7d47b0SAlexander V. Chernikov int
22039f7d47b0SAlexander V. Chernikov ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
22043b3a8eb9SGleb Smirnoff {
22059f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
2206d3b00c08SAlexander V. Chernikov 	uint32_t count;
22079f7d47b0SAlexander V. Chernikov 
2208d3a4f924SAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
2209d3a4f924SAlexander V. Chernikov 		*cnt = 0;
22109f7d47b0SAlexander V. Chernikov 		return (0); /* 'table all list' requires success */
2211d3a4f924SAlexander V. Chernikov 	}
2212d3b00c08SAlexander V. Chernikov 
2213d3b00c08SAlexander V. Chernikov 	count = table_get_count(ch, tc);
2214d3b00c08SAlexander V. Chernikov 	*cnt = count * sizeof(ipfw_table_xentry);
2215d3b00c08SAlexander V. Chernikov 	if (count > 0)
22169f7d47b0SAlexander V. Chernikov 		*cnt += sizeof(ipfw_xtable);
22179f7d47b0SAlexander V. Chernikov 	return (0);
22189f7d47b0SAlexander V. Chernikov }
22199f7d47b0SAlexander V. Chernikov 
22209f7d47b0SAlexander V. Chernikov static int
22219f7d47b0SAlexander V. Chernikov dump_table_entry(void *e, void *arg)
22229f7d47b0SAlexander V. Chernikov {
22239f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
22249f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
22259f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
22263b3a8eb9SGleb Smirnoff 	ipfw_table_entry *ent;
222781d3153dSAlexander V. Chernikov 	int error;
22283b3a8eb9SGleb Smirnoff 
22299f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
22309f7d47b0SAlexander V. Chernikov 
22319f7d47b0SAlexander V. Chernikov 	tc = da->tc;
22329f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
22339f7d47b0SAlexander V. Chernikov 
22349f7d47b0SAlexander V. Chernikov 	/* Out of memory, returning */
2235f1220db8SAlexander V. Chernikov 	if (da->cnt == da->size)
22363b3a8eb9SGleb Smirnoff 		return (1);
2237f1220db8SAlexander V. Chernikov 	ent = da->ent++;
2238f1220db8SAlexander V. Chernikov 	ent->tbl = da->uidx;
2239f1220db8SAlexander V. Chernikov 	da->cnt++;
22409f7d47b0SAlexander V. Chernikov 
224181d3153dSAlexander V. Chernikov 	error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent);
224281d3153dSAlexander V. Chernikov 	if (error != 0)
224381d3153dSAlexander V. Chernikov 		return (error);
224481d3153dSAlexander V. Chernikov 
224581d3153dSAlexander V. Chernikov 	ent->addr = da->tent.k.addr.s_addr;
224681d3153dSAlexander V. Chernikov 	ent->masklen = da->tent.masklen;
224781d3153dSAlexander V. Chernikov 	ent->value = da->tent.value;
224881d3153dSAlexander V. Chernikov 
224981d3153dSAlexander V. Chernikov 	return (0);
22503b3a8eb9SGleb Smirnoff }
22513b3a8eb9SGleb Smirnoff 
2252f1220db8SAlexander V. Chernikov /*
2253f1220db8SAlexander V. Chernikov  * Dumps table in pre-8.1 legacy format.
2254f1220db8SAlexander V. Chernikov  */
22553b3a8eb9SGleb Smirnoff int
2256f1220db8SAlexander V. Chernikov ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
2257f1220db8SAlexander V. Chernikov     ipfw_table *tbl)
22583b3a8eb9SGleb Smirnoff {
2259b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
22609f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
22619f7d47b0SAlexander V. Chernikov 	struct dump_args da;
22623b3a8eb9SGleb Smirnoff 
22633b3a8eb9SGleb Smirnoff 	tbl->cnt = 0;
22643b3a8eb9SGleb Smirnoff 
2265b074b7bbSAlexander V. Chernikov 	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
2266b074b7bbSAlexander V. Chernikov 		return (0);	/* XXX: We should return ESRCH */
22679f7d47b0SAlexander V. Chernikov 
22689f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
22699f7d47b0SAlexander V. Chernikov 
227081d3153dSAlexander V. Chernikov 	/* This dump format supports IPv4 only */
227181d3153dSAlexander V. Chernikov 	if (tc->no.type != IPFW_TABLE_CIDR)
227281d3153dSAlexander V. Chernikov 		return (0);
22739f7d47b0SAlexander V. Chernikov 
2274d3a4f924SAlexander V. Chernikov 	memset(&da, 0, sizeof(da));
22759f7d47b0SAlexander V. Chernikov 	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
22769f7d47b0SAlexander V. Chernikov 	da.tc = tc;
2277f1220db8SAlexander V. Chernikov 	da.ent = &tbl->ent[0];
2278f1220db8SAlexander V. Chernikov 	da.size = tbl->size;
22799f7d47b0SAlexander V. Chernikov 
22809f7d47b0SAlexander V. Chernikov 	tbl->cnt = 0;
22819f7d47b0SAlexander V. Chernikov 	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
2282f1220db8SAlexander V. Chernikov 	tbl->cnt = da.cnt;
22839f7d47b0SAlexander V. Chernikov 
22843b3a8eb9SGleb Smirnoff 	return (0);
22853b3a8eb9SGleb Smirnoff }
22863b3a8eb9SGleb Smirnoff 
22879490a627SAlexander V. Chernikov /*
228881d3153dSAlexander V. Chernikov  * Dumps table entry in eXtended format (v1)(current).
228981d3153dSAlexander V. Chernikov  */
229081d3153dSAlexander V. Chernikov static int
229181d3153dSAlexander V. Chernikov dump_table_tentry(void *e, void *arg)
229281d3153dSAlexander V. Chernikov {
229381d3153dSAlexander V. Chernikov 	struct dump_args *da;
229481d3153dSAlexander V. Chernikov 	struct table_config *tc;
229581d3153dSAlexander V. Chernikov 	struct table_algo *ta;
229681d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
229781d3153dSAlexander V. Chernikov 
229881d3153dSAlexander V. Chernikov 	da = (struct dump_args *)arg;
229981d3153dSAlexander V. Chernikov 
230081d3153dSAlexander V. Chernikov 	tc = da->tc;
230181d3153dSAlexander V. Chernikov 	ta = tc->ta;
230281d3153dSAlexander V. Chernikov 
230381d3153dSAlexander V. Chernikov 	tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent));
230481d3153dSAlexander V. Chernikov 	/* Out of memory, returning */
230581d3153dSAlexander V. Chernikov 	if (tent == NULL) {
230681d3153dSAlexander V. Chernikov 		da->error = ENOMEM;
230781d3153dSAlexander V. Chernikov 		return (1);
230881d3153dSAlexander V. Chernikov 	}
230981d3153dSAlexander V. Chernikov 	tent->head.length = sizeof(ipfw_obj_tentry);
231081d3153dSAlexander V. Chernikov 	tent->idx = da->uidx;
231181d3153dSAlexander V. Chernikov 
231281d3153dSAlexander V. Chernikov 	return (ta->dump_tentry(tc->astate, da->ti, e, tent));
231381d3153dSAlexander V. Chernikov }
231481d3153dSAlexander V. Chernikov 
231581d3153dSAlexander V. Chernikov /*
231681d3153dSAlexander V. Chernikov  * Dumps table entry in eXtended format (v0).
23179490a627SAlexander V. Chernikov  */
23183b3a8eb9SGleb Smirnoff static int
23199f7d47b0SAlexander V. Chernikov dump_table_xentry(void *e, void *arg)
23203b3a8eb9SGleb Smirnoff {
23219f7d47b0SAlexander V. Chernikov 	struct dump_args *da;
23229f7d47b0SAlexander V. Chernikov 	struct table_config *tc;
23239f7d47b0SAlexander V. Chernikov 	struct table_algo *ta;
23243b3a8eb9SGleb Smirnoff 	ipfw_table_xentry *xent;
232581d3153dSAlexander V. Chernikov 	ipfw_obj_tentry *tent;
232681d3153dSAlexander V. Chernikov 	int error;
23273b3a8eb9SGleb Smirnoff 
23289f7d47b0SAlexander V. Chernikov 	da = (struct dump_args *)arg;
23299f7d47b0SAlexander V. Chernikov 
23309f7d47b0SAlexander V. Chernikov 	tc = da->tc;
23319f7d47b0SAlexander V. Chernikov 	ta = tc->ta;
23329f7d47b0SAlexander V. Chernikov 
23332d99a349SAlexander V. Chernikov 	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
23343b3a8eb9SGleb Smirnoff 	/* Out of memory, returning */
23352d99a349SAlexander V. Chernikov 	if (xent == NULL)
23363b3a8eb9SGleb Smirnoff 		return (1);
23373b3a8eb9SGleb Smirnoff 	xent->len = sizeof(ipfw_table_xentry);
2338f1220db8SAlexander V. Chernikov 	xent->tbl = da->uidx;
23399f7d47b0SAlexander V. Chernikov 
234081d3153dSAlexander V. Chernikov 	memset(&da->tent, 0, sizeof(da->tent));
234181d3153dSAlexander V. Chernikov 	tent = &da->tent;
234281d3153dSAlexander V. Chernikov 	error = ta->dump_tentry(tc->astate, da->ti, e, tent);
234381d3153dSAlexander V. Chernikov 	if (error != 0)
234481d3153dSAlexander V. Chernikov 		return (error);
234581d3153dSAlexander V. Chernikov 
234681d3153dSAlexander V. Chernikov 	/* Convert current format to previous one */
234781d3153dSAlexander V. Chernikov 	xent->masklen = tent->masklen;
234881d3153dSAlexander V. Chernikov 	xent->value = tent->value;
234981d3153dSAlexander V. Chernikov 	/* Apply some hacks */
235081d3153dSAlexander V. Chernikov 	if (tc->no.type == IPFW_TABLE_CIDR && tent->subtype == AF_INET) {
235181d3153dSAlexander V. Chernikov 		xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr;
235281d3153dSAlexander V. Chernikov 		xent->flags = IPFW_TCF_INET;
235381d3153dSAlexander V. Chernikov 	} else
235481d3153dSAlexander V. Chernikov 		memcpy(&xent->k, &tent->k, sizeof(xent->k));
235581d3153dSAlexander V. Chernikov 
235681d3153dSAlexander V. Chernikov 	return (0);
23579f7d47b0SAlexander V. Chernikov }
23589f7d47b0SAlexander V. Chernikov 
23599f7d47b0SAlexander V. Chernikov /*
23609f7d47b0SAlexander V. Chernikov  * Table algorithms
23619f7d47b0SAlexander V. Chernikov  */
23623b3a8eb9SGleb Smirnoff 
23639f7d47b0SAlexander V. Chernikov /*
236435e1bbd0SAlexander V. Chernikov  * Finds algoritm by index, table type or supplied name.
236535e1bbd0SAlexander V. Chernikov  *
236635e1bbd0SAlexander V. Chernikov  * Returns pointer to algo or NULL.
23679f7d47b0SAlexander V. Chernikov  */
23689f7d47b0SAlexander V. Chernikov static struct table_algo *
23699490a627SAlexander V. Chernikov find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
23709f7d47b0SAlexander V. Chernikov {
23719490a627SAlexander V. Chernikov 	int i, l;
23729490a627SAlexander V. Chernikov 	struct table_algo *ta;
23739f7d47b0SAlexander V. Chernikov 
237457a1cf95SAlexander V. Chernikov 	if (ti->type > IPFW_TABLE_MAXTYPE)
237557a1cf95SAlexander V. Chernikov 		return (NULL);
237657a1cf95SAlexander V. Chernikov 
23779f7d47b0SAlexander V. Chernikov 	/* Search by index */
23789f7d47b0SAlexander V. Chernikov 	if (ti->atype != 0) {
23799f7d47b0SAlexander V. Chernikov 		if (ti->atype > tcfg->algo_count)
23809f7d47b0SAlexander V. Chernikov 			return (NULL);
23819f7d47b0SAlexander V. Chernikov 		return (tcfg->algo[ti->atype]);
23829f7d47b0SAlexander V. Chernikov 	}
23839f7d47b0SAlexander V. Chernikov 
2384fd0869d5SAlexander V. Chernikov 	if (name == NULL) {
2385fd0869d5SAlexander V. Chernikov 		/* Return default algorithm for given type if set */
2386fd0869d5SAlexander V. Chernikov 		return (tcfg->def_algo[ti->type]);
2387fd0869d5SAlexander V. Chernikov 	}
2388fd0869d5SAlexander V. Chernikov 
2389fd0869d5SAlexander V. Chernikov 	/* Search by name */
23909490a627SAlexander V. Chernikov 	/* TODO: better search */
23919490a627SAlexander V. Chernikov 	for (i = 1; i <= tcfg->algo_count; i++) {
23929490a627SAlexander V. Chernikov 		ta = tcfg->algo[i];
23939490a627SAlexander V. Chernikov 
23949490a627SAlexander V. Chernikov 		/*
23959490a627SAlexander V. Chernikov 		 * One can supply additional algorithm
23969490a627SAlexander V. Chernikov 		 * parameters so we compare only the first word
23979490a627SAlexander V. Chernikov 		 * of supplied name:
23989490a627SAlexander V. Chernikov 		 * 'hash_cidr hsize=32'
23999490a627SAlexander V. Chernikov 		 * '^^^^^^^^^'
24009490a627SAlexander V. Chernikov 		 *
24019490a627SAlexander V. Chernikov 		 */
24029490a627SAlexander V. Chernikov 		l = strlen(ta->name);
2403fd0869d5SAlexander V. Chernikov 		if (strncmp(name, ta->name, l) != 0)
2404fd0869d5SAlexander V. Chernikov 			continue;
2405fd0869d5SAlexander V. Chernikov 		if (name[l] != '\0' && name[l] != ' ')
2406fd0869d5SAlexander V. Chernikov 			continue;
2407fd0869d5SAlexander V. Chernikov 		/* Check if we're requesting proper table type */
2408fd0869d5SAlexander V. Chernikov 		if (ti->type != 0 && ti->type != ta->type)
2409fd0869d5SAlexander V. Chernikov 			return (NULL);
24109490a627SAlexander V. Chernikov 		return (ta);
24119490a627SAlexander V. Chernikov 	}
24129490a627SAlexander V. Chernikov 
24139490a627SAlexander V. Chernikov 	return (NULL);
24149490a627SAlexander V. Chernikov }
24159490a627SAlexander V. Chernikov 
2416b6ee846eSAlexander V. Chernikov /*
2417b6ee846eSAlexander V. Chernikov  * Register new table algo @ta.
241835e1bbd0SAlexander V. Chernikov  * Stores algo id inside @idx.
2419b6ee846eSAlexander V. Chernikov  *
2420b6ee846eSAlexander V. Chernikov  * Returns 0 on success.
2421b6ee846eSAlexander V. Chernikov  */
24220b565ac0SAlexander V. Chernikov int
24230b565ac0SAlexander V. Chernikov ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size,
24240b565ac0SAlexander V. Chernikov     int *idx)
24253b3a8eb9SGleb Smirnoff {
24269f7d47b0SAlexander V. Chernikov 	struct tables_config *tcfg;
24270b565ac0SAlexander V. Chernikov 	struct table_algo *ta_new;
2428b6ee846eSAlexander V. Chernikov 	size_t sz;
24290b565ac0SAlexander V. Chernikov 
24300b565ac0SAlexander V. Chernikov 	if (size > sizeof(struct table_algo))
24310b565ac0SAlexander V. Chernikov 		return (EINVAL);
24320b565ac0SAlexander V. Chernikov 
2433b6ee846eSAlexander V. Chernikov 	/* Check for the required on-stack size for add/del */
2434b6ee846eSAlexander V. Chernikov 	sz = roundup2(ta->ta_buf_size, sizeof(void *));
2435b6ee846eSAlexander V. Chernikov 	if (sz > TA_BUF_SZ)
2436b6ee846eSAlexander V. Chernikov 		return (EINVAL);
2437b6ee846eSAlexander V. Chernikov 
2438fddbbf75SAlexander V. Chernikov 	KASSERT(ta->type <= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE"));
243957a1cf95SAlexander V. Chernikov 
244035e1bbd0SAlexander V. Chernikov 	/* Copy algorithm data to stable storage. */
24410b565ac0SAlexander V. Chernikov 	ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO);
24420b565ac0SAlexander V. Chernikov 	memcpy(ta_new, ta, size);
24433b3a8eb9SGleb Smirnoff 
24449f7d47b0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
2445b074b7bbSAlexander V. Chernikov 
24469f7d47b0SAlexander V. Chernikov 	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
24479f7d47b0SAlexander V. Chernikov 
24480b565ac0SAlexander V. Chernikov 	tcfg->algo[++tcfg->algo_count] = ta_new;
24490b565ac0SAlexander V. Chernikov 	ta_new->idx = tcfg->algo_count;
24500b565ac0SAlexander V. Chernikov 
245157a1cf95SAlexander V. Chernikov 	/* Set algorithm as default one for given type */
245257a1cf95SAlexander V. Chernikov 	if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 &&
245357a1cf95SAlexander V. Chernikov 	    tcfg->def_algo[ta_new->type] == NULL)
245457a1cf95SAlexander V. Chernikov 		tcfg->def_algo[ta_new->type] = ta_new;
245557a1cf95SAlexander V. Chernikov 
24560b565ac0SAlexander V. Chernikov 	*idx = ta_new->idx;
24570b565ac0SAlexander V. Chernikov 
24580b565ac0SAlexander V. Chernikov 	return (0);
24590b565ac0SAlexander V. Chernikov }
24600b565ac0SAlexander V. Chernikov 
2461b6ee846eSAlexander V. Chernikov /*
2462b6ee846eSAlexander V. Chernikov  * Unregisters table algo using @idx as id.
2463c8d5d308SAlexander V. Chernikov  * XXX: It is NOT safe to call this function in any place
2464c8d5d308SAlexander V. Chernikov  * other than ipfw instance destroy handler.
2465b6ee846eSAlexander V. Chernikov  */
24660b565ac0SAlexander V. Chernikov void
24670b565ac0SAlexander V. Chernikov ipfw_del_table_algo(struct ip_fw_chain *ch, int idx)
24680b565ac0SAlexander V. Chernikov {
24690b565ac0SAlexander V. Chernikov 	struct tables_config *tcfg;
24700b565ac0SAlexander V. Chernikov 	struct table_algo *ta;
24710b565ac0SAlexander V. Chernikov 
24720b565ac0SAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
24730b565ac0SAlexander V. Chernikov 
2474b6ee846eSAlexander V. Chernikov 	KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d",
2475b6ee846eSAlexander V. Chernikov 	    idx, tcfg->algo_count));
24760b565ac0SAlexander V. Chernikov 
24770b565ac0SAlexander V. Chernikov 	ta = tcfg->algo[idx];
24780b565ac0SAlexander V. Chernikov 	KASSERT(ta != NULL, ("algo idx %d is NULL", idx));
247957a1cf95SAlexander V. Chernikov 
248057a1cf95SAlexander V. Chernikov 	if (tcfg->def_algo[ta->type] == ta)
248157a1cf95SAlexander V. Chernikov 		tcfg->def_algo[ta->type] = NULL;
248257a1cf95SAlexander V. Chernikov 
24830b565ac0SAlexander V. Chernikov 	free(ta, M_IPFW);
24843b3a8eb9SGleb Smirnoff }
24853b3a8eb9SGleb Smirnoff 
24869d099b4fSAlexander V. Chernikov /*
24879d099b4fSAlexander V. Chernikov  * Lists all table algorithms currently available.
24889d099b4fSAlexander V. Chernikov  * Data layout (v0)(current):
24899d099b4fSAlexander V. Chernikov  * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
24909d099b4fSAlexander V. Chernikov  * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ]
24919d099b4fSAlexander V. Chernikov  *
24929d099b4fSAlexander V. Chernikov  * Returns 0 on success
24939d099b4fSAlexander V. Chernikov  */
24949d099b4fSAlexander V. Chernikov int
24959d099b4fSAlexander V. Chernikov ipfw_list_table_algo(struct ip_fw_chain *ch, struct sockopt_data *sd)
24969d099b4fSAlexander V. Chernikov {
24979d099b4fSAlexander V. Chernikov 	struct _ipfw_obj_lheader *olh;
24989d099b4fSAlexander V. Chernikov 	struct tables_config *tcfg;
24999d099b4fSAlexander V. Chernikov 	ipfw_ta_info *i;
25009d099b4fSAlexander V. Chernikov 	struct table_algo *ta;
25019d099b4fSAlexander V. Chernikov 	uint32_t count, n, size;
25029d099b4fSAlexander V. Chernikov 
25039d099b4fSAlexander V. Chernikov 	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
25049d099b4fSAlexander V. Chernikov 	if (olh == NULL)
25059d099b4fSAlexander V. Chernikov 		return (EINVAL);
25069d099b4fSAlexander V. Chernikov 	if (sd->valsize < olh->size)
25079d099b4fSAlexander V. Chernikov 		return (EINVAL);
25089d099b4fSAlexander V. Chernikov 
25099d099b4fSAlexander V. Chernikov 	IPFW_UH_RLOCK(ch);
25109d099b4fSAlexander V. Chernikov 	tcfg = CHAIN_TO_TCFG(ch);
25119d099b4fSAlexander V. Chernikov 	count = tcfg->algo_count;
25129d099b4fSAlexander V. Chernikov 	size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader);
25139d099b4fSAlexander V. Chernikov 
25149d099b4fSAlexander V. Chernikov 	/* Fill in header regadless of buffer size */
25159d099b4fSAlexander V. Chernikov 	olh->count = count;
25169d099b4fSAlexander V. Chernikov 	olh->objsize = sizeof(ipfw_ta_info);
25179d099b4fSAlexander V. Chernikov 
25189d099b4fSAlexander V. Chernikov 	if (size > olh->size) {
25199d099b4fSAlexander V. Chernikov 		olh->size = size;
25209d099b4fSAlexander V. Chernikov 		IPFW_UH_RUNLOCK(ch);
25219d099b4fSAlexander V. Chernikov 		return (ENOMEM);
25229d099b4fSAlexander V. Chernikov 	}
25239d099b4fSAlexander V. Chernikov 	olh->size = size;
25249d099b4fSAlexander V. Chernikov 
25259d099b4fSAlexander V. Chernikov 	for (n = 1; n <= count; n++) {
25269d099b4fSAlexander V. Chernikov 		i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i));
25279d099b4fSAlexander V. Chernikov 		KASSERT(i != 0, ("previously checked buffer is not enough"));
25289d099b4fSAlexander V. Chernikov 		ta = tcfg->algo[n];
25299d099b4fSAlexander V. Chernikov 		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
25309d099b4fSAlexander V. Chernikov 		i->type = ta->type;
25319d099b4fSAlexander V. Chernikov 		i->refcnt = ta->refcnt;
25329d099b4fSAlexander V. Chernikov 	}
25339d099b4fSAlexander V. Chernikov 
25349d099b4fSAlexander V. Chernikov 	IPFW_UH_RUNLOCK(ch);
25359d099b4fSAlexander V. Chernikov 
25369d099b4fSAlexander V. Chernikov 	return (0);
25379d099b4fSAlexander V. Chernikov }
25389d099b4fSAlexander V. Chernikov 
2539b074b7bbSAlexander V. Chernikov /*
2540b074b7bbSAlexander V. Chernikov  * Tables rewriting code
2541b074b7bbSAlexander V. Chernikov  */
2542b074b7bbSAlexander V. Chernikov 
2543b074b7bbSAlexander V. Chernikov /*
2544b074b7bbSAlexander V. Chernikov  * Determine table number and lookup type for @cmd.
2545b074b7bbSAlexander V. Chernikov  * Fill @tbl and @type with appropriate values.
2546b074b7bbSAlexander V. Chernikov  * Returns 0 for relevant opcodes, 1 otherwise.
2547b074b7bbSAlexander V. Chernikov  */
2548b074b7bbSAlexander V. Chernikov static int
2549b074b7bbSAlexander V. Chernikov classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
2550b074b7bbSAlexander V. Chernikov {
2551b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
2552b074b7bbSAlexander V. Chernikov 	int skip;
2553b074b7bbSAlexander V. Chernikov 	uint16_t v;
2554b074b7bbSAlexander V. Chernikov 
2555b074b7bbSAlexander V. Chernikov 	skip = 1;
2556b074b7bbSAlexander V. Chernikov 
2557b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
2558b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
2559b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
2560b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
2561b074b7bbSAlexander V. Chernikov 		*puidx = cmd->arg1;
2562b074b7bbSAlexander V. Chernikov 		/* Assume CIDR by default */
2563b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_CIDR;
2564b074b7bbSAlexander V. Chernikov 		skip = 0;
2565b074b7bbSAlexander V. Chernikov 
2566b074b7bbSAlexander V. Chernikov 		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
2567b074b7bbSAlexander V. Chernikov 			/*
2568b074b7bbSAlexander V. Chernikov 			 * generic lookup. The key must be
2569b074b7bbSAlexander V. Chernikov 			 * in 32bit big-endian format.
2570b074b7bbSAlexander V. Chernikov 			 */
2571b074b7bbSAlexander V. Chernikov 			v = ((ipfw_insn_u32 *)cmd)->d[1];
2572b074b7bbSAlexander V. Chernikov 			switch (v) {
2573b074b7bbSAlexander V. Chernikov 			case 0:
2574b074b7bbSAlexander V. Chernikov 			case 1:
2575b074b7bbSAlexander V. Chernikov 				/* IPv4 src/dst */
2576b074b7bbSAlexander V. Chernikov 				break;
2577b074b7bbSAlexander V. Chernikov 			case 2:
2578b074b7bbSAlexander V. Chernikov 			case 3:
2579b074b7bbSAlexander V. Chernikov 				/* src/dst port */
2580b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2581b074b7bbSAlexander V. Chernikov 				break;
2582b074b7bbSAlexander V. Chernikov 			case 4:
2583b074b7bbSAlexander V. Chernikov 				/* uid/gid */
2584b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2585b23d5de9SAlexander V. Chernikov 				break;
2586b074b7bbSAlexander V. Chernikov 			case 5:
2587b074b7bbSAlexander V. Chernikov 				/* jid */
2588b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2589b23d5de9SAlexander V. Chernikov 				break;
2590b074b7bbSAlexander V. Chernikov 			case 6:
2591b074b7bbSAlexander V. Chernikov 				/* dscp */
2592b23d5de9SAlexander V. Chernikov 				*ptype = IPFW_TABLE_NUMBER;
2593b074b7bbSAlexander V. Chernikov 				break;
2594b074b7bbSAlexander V. Chernikov 			}
2595b074b7bbSAlexander V. Chernikov 		}
2596b074b7bbSAlexander V. Chernikov 		break;
2597b074b7bbSAlexander V. Chernikov 	case O_XMIT:
2598b074b7bbSAlexander V. Chernikov 	case O_RECV:
2599b074b7bbSAlexander V. Chernikov 	case O_VIA:
2600b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
2601b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
2602b074b7bbSAlexander V. Chernikov 		if (cmdif->name[0] != '\1')
2603b074b7bbSAlexander V. Chernikov 			break;
2604b074b7bbSAlexander V. Chernikov 
2605b074b7bbSAlexander V. Chernikov 		*ptype = IPFW_TABLE_INTERFACE;
26061940fa77SAlexander V. Chernikov 		*puidx = cmdif->p.kidx;
2607b074b7bbSAlexander V. Chernikov 		skip = 0;
2608b074b7bbSAlexander V. Chernikov 		break;
2609914bffb6SAlexander V. Chernikov 	case O_IP_FLOW_LOOKUP:
2610914bffb6SAlexander V. Chernikov 		*puidx = cmd->arg1;
2611914bffb6SAlexander V. Chernikov 		*ptype = IPFW_TABLE_FLOW;
2612914bffb6SAlexander V. Chernikov 		skip = 0;
2613914bffb6SAlexander V. Chernikov 		break;
2614b074b7bbSAlexander V. Chernikov 	}
2615b074b7bbSAlexander V. Chernikov 
2616b074b7bbSAlexander V. Chernikov 	return (skip);
2617b074b7bbSAlexander V. Chernikov }
2618b074b7bbSAlexander V. Chernikov 
2619b074b7bbSAlexander V. Chernikov /*
2620b074b7bbSAlexander V. Chernikov  * Sets new table value for given opcode.
2621b074b7bbSAlexander V. Chernikov  * Assume the same opcodes as classify_table_opcode()
2622b074b7bbSAlexander V. Chernikov  */
2623b074b7bbSAlexander V. Chernikov static void
2624b074b7bbSAlexander V. Chernikov update_table_opcode(ipfw_insn *cmd, uint16_t idx)
2625b074b7bbSAlexander V. Chernikov {
2626b074b7bbSAlexander V. Chernikov 	ipfw_insn_if *cmdif;
2627b074b7bbSAlexander V. Chernikov 
2628b074b7bbSAlexander V. Chernikov 	switch (cmd->opcode) {
2629b074b7bbSAlexander V. Chernikov 	case O_IP_SRC_LOOKUP:
2630b074b7bbSAlexander V. Chernikov 	case O_IP_DST_LOOKUP:
2631b074b7bbSAlexander V. Chernikov 		/* Basic IPv4/IPv6 or u32 lookups */
2632b074b7bbSAlexander V. Chernikov 		cmd->arg1 = idx;
2633b074b7bbSAlexander V. Chernikov 		break;
2634b074b7bbSAlexander V. Chernikov 	case O_XMIT:
2635b074b7bbSAlexander V. Chernikov 	case O_RECV:
2636b074b7bbSAlexander V. Chernikov 	case O_VIA:
2637b074b7bbSAlexander V. Chernikov 		/* Interface table, possibly */
2638b074b7bbSAlexander V. Chernikov 		cmdif = (ipfw_insn_if *)cmd;
26391940fa77SAlexander V. Chernikov 		cmdif->p.kidx = idx;
2640b074b7bbSAlexander V. Chernikov 		break;
2641914bffb6SAlexander V. Chernikov 	case O_IP_FLOW_LOOKUP:
2642914bffb6SAlexander V. Chernikov 		cmd->arg1 = idx;
2643914bffb6SAlexander V. Chernikov 		break;
2644b074b7bbSAlexander V. Chernikov 	}
2645b074b7bbSAlexander V. Chernikov }
2646b074b7bbSAlexander V. Chernikov 
2647ac35ff17SAlexander V. Chernikov /*
2648ac35ff17SAlexander V. Chernikov  * Checks table name for validity.
2649ac35ff17SAlexander V. Chernikov  * Enforce basic length checks, the rest
2650ac35ff17SAlexander V. Chernikov  * should be done in userland.
2651ac35ff17SAlexander V. Chernikov  *
2652ac35ff17SAlexander V. Chernikov  * Returns 0 if name is considered valid.
2653ac35ff17SAlexander V. Chernikov  */
2654ac35ff17SAlexander V. Chernikov int
2655ac35ff17SAlexander V. Chernikov ipfw_check_table_name(char *name)
2656ac35ff17SAlexander V. Chernikov {
2657ac35ff17SAlexander V. Chernikov 	int nsize;
2658ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv = NULL;
2659ac35ff17SAlexander V. Chernikov 
2660ac35ff17SAlexander V. Chernikov 	nsize = sizeof(ntlv->name);
2661ac35ff17SAlexander V. Chernikov 
2662ac35ff17SAlexander V. Chernikov 	if (strnlen(name, nsize) == nsize)
2663ac35ff17SAlexander V. Chernikov 		return (EINVAL);
2664ac35ff17SAlexander V. Chernikov 
2665ac35ff17SAlexander V. Chernikov 	if (name[0] == '\0')
2666ac35ff17SAlexander V. Chernikov 		return (EINVAL);
2667ac35ff17SAlexander V. Chernikov 
2668ac35ff17SAlexander V. Chernikov 	/*
2669ac35ff17SAlexander V. Chernikov 	 * TODO: do some more complicated checks
2670ac35ff17SAlexander V. Chernikov 	 */
2671ac35ff17SAlexander V. Chernikov 
2672ac35ff17SAlexander V. Chernikov 	return (0);
2673ac35ff17SAlexander V. Chernikov }
2674ac35ff17SAlexander V. Chernikov 
2675ac35ff17SAlexander V. Chernikov /*
2676ac35ff17SAlexander V. Chernikov  * Find tablename TLV by @uid.
2677ac35ff17SAlexander V. Chernikov  * Check @tlvs for valid data inside.
2678ac35ff17SAlexander V. Chernikov  *
2679ac35ff17SAlexander V. Chernikov  * Returns pointer to found TLV or NULL.
2680ac35ff17SAlexander V. Chernikov  */
2681ac35ff17SAlexander V. Chernikov static ipfw_obj_ntlv *
2682b074b7bbSAlexander V. Chernikov find_name_tlv(void *tlvs, int len, uint16_t uidx)
2683b074b7bbSAlexander V. Chernikov {
26849f7d47b0SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2685b074b7bbSAlexander V. Chernikov 	uintptr_t pa, pe;
2686b074b7bbSAlexander V. Chernikov 	int l;
2687b074b7bbSAlexander V. Chernikov 
2688b074b7bbSAlexander V. Chernikov 	pa = (uintptr_t)tlvs;
2689b074b7bbSAlexander V. Chernikov 	pe = pa + len;
2690b074b7bbSAlexander V. Chernikov 	l = 0;
2691b074b7bbSAlexander V. Chernikov 	for (; pa < pe; pa += l) {
26929f7d47b0SAlexander V. Chernikov 		ntlv = (ipfw_obj_ntlv *)pa;
2693b074b7bbSAlexander V. Chernikov 		l = ntlv->head.length;
2694ac35ff17SAlexander V. Chernikov 
2695ac35ff17SAlexander V. Chernikov 		if (l != sizeof(*ntlv))
2696ac35ff17SAlexander V. Chernikov 			return (NULL);
2697ac35ff17SAlexander V. Chernikov 
2698563b5ab1SAlexander V. Chernikov 		if (ntlv->head.type != IPFW_TLV_TBL_NAME)
2699b074b7bbSAlexander V. Chernikov 			continue;
2700ac35ff17SAlexander V. Chernikov 
2701b074b7bbSAlexander V. Chernikov 		if (ntlv->idx != uidx)
2702b074b7bbSAlexander V. Chernikov 			continue;
2703b074b7bbSAlexander V. Chernikov 
2704ac35ff17SAlexander V. Chernikov 		if (ipfw_check_table_name(ntlv->name) != 0)
2705ac35ff17SAlexander V. Chernikov 			return (NULL);
2706ac35ff17SAlexander V. Chernikov 
2707ac35ff17SAlexander V. Chernikov 		return (ntlv);
2708b074b7bbSAlexander V. Chernikov 	}
2709b074b7bbSAlexander V. Chernikov 
2710b074b7bbSAlexander V. Chernikov 	return (NULL);
2711b074b7bbSAlexander V. Chernikov }
2712b074b7bbSAlexander V. Chernikov 
2713ac35ff17SAlexander V. Chernikov /*
2714ac35ff17SAlexander V. Chernikov  * Finds table config based on either legacy index
2715ac35ff17SAlexander V. Chernikov  * or name in ntlv.
2716ac35ff17SAlexander V. Chernikov  * Note @ti structure contains unchecked data from userland.
2717ac35ff17SAlexander V. Chernikov  *
2718ac35ff17SAlexander V. Chernikov  * Returns pointer to table_config or NULL.
2719ac35ff17SAlexander V. Chernikov  */
2720b074b7bbSAlexander V. Chernikov static struct table_config *
2721b074b7bbSAlexander V. Chernikov find_table(struct namedobj_instance *ni, struct tid_info *ti)
2722b074b7bbSAlexander V. Chernikov {
2723b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
2724b074b7bbSAlexander V. Chernikov 	struct named_object *no;
2725ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2726ac35ff17SAlexander V. Chernikov 	uint32_t set;
2727b074b7bbSAlexander V. Chernikov 
2728b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
2729ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
2730ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
2731b074b7bbSAlexander V. Chernikov 			return (NULL);
2732ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
273340e5f498SAlexander V. Chernikov 
273440e5f498SAlexander V. Chernikov 		/*
273540e5f498SAlexander V. Chernikov 		 * Use set provided by @ti instead of @ntlv one.
273640e5f498SAlexander V. Chernikov 		 * This is needed due to different sets behavior
273740e5f498SAlexander V. Chernikov 		 * controlled by V_fw_tables_sets.
273840e5f498SAlexander V. Chernikov 		 */
273940e5f498SAlexander V. Chernikov 		set = ti->set;
2740b074b7bbSAlexander V. Chernikov 	} else {
2741b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
2742b074b7bbSAlexander V. Chernikov 		name = bname;
2743ac35ff17SAlexander V. Chernikov 		set = 0;
2744b074b7bbSAlexander V. Chernikov 	}
2745b074b7bbSAlexander V. Chernikov 
2746ac35ff17SAlexander V. Chernikov 	no = ipfw_objhash_lookup_name(ni, set, name);
2747b074b7bbSAlexander V. Chernikov 
2748b074b7bbSAlexander V. Chernikov 	return ((struct table_config *)no);
2749b074b7bbSAlexander V. Chernikov }
2750b074b7bbSAlexander V. Chernikov 
275135e1bbd0SAlexander V. Chernikov /*
275235e1bbd0SAlexander V. Chernikov  * Allocate new table config structure using
275335e1bbd0SAlexander V. Chernikov  * specified @algo and @aname.
275435e1bbd0SAlexander V. Chernikov  *
275535e1bbd0SAlexander V. Chernikov  * Returns pointer to config or NULL.
275635e1bbd0SAlexander V. Chernikov  */
2757b074b7bbSAlexander V. Chernikov static struct table_config *
275868394ec8SAlexander V. Chernikov alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti,
2759914bffb6SAlexander V. Chernikov     struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype)
2760b074b7bbSAlexander V. Chernikov {
2761b074b7bbSAlexander V. Chernikov 	char *name, bname[16];
2762b074b7bbSAlexander V. Chernikov 	struct table_config *tc;
2763b074b7bbSAlexander V. Chernikov 	int error;
2764ac35ff17SAlexander V. Chernikov 	ipfw_obj_ntlv *ntlv;
2765ac35ff17SAlexander V. Chernikov 	uint32_t set;
2766b074b7bbSAlexander V. Chernikov 
2767b074b7bbSAlexander V. Chernikov 	if (ti->tlvs != NULL) {
2768ac35ff17SAlexander V. Chernikov 		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
2769ac35ff17SAlexander V. Chernikov 		if (ntlv == NULL)
2770b074b7bbSAlexander V. Chernikov 			return (NULL);
2771ac35ff17SAlexander V. Chernikov 		name = ntlv->name;
2772ac35ff17SAlexander V. Chernikov 		set = ntlv->set;
2773b074b7bbSAlexander V. Chernikov 	} else {
2774b074b7bbSAlexander V. Chernikov 		snprintf(bname, sizeof(bname), "%d", ti->uidx);
2775b074b7bbSAlexander V. Chernikov 		name = bname;
2776ac35ff17SAlexander V. Chernikov 		set = 0;
2777b074b7bbSAlexander V. Chernikov 	}
2778b074b7bbSAlexander V. Chernikov 
2779b074b7bbSAlexander V. Chernikov 	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
2780b074b7bbSAlexander V. Chernikov 	tc->no.name = tc->tablename;
2781fd0869d5SAlexander V. Chernikov 	tc->no.type = ta->type;
2782ac35ff17SAlexander V. Chernikov 	tc->no.set = set;
2783914bffb6SAlexander V. Chernikov 	tc->tflags = tflags;
27849f7d47b0SAlexander V. Chernikov 	tc->ta = ta;
2785b074b7bbSAlexander V. Chernikov 	strlcpy(tc->tablename, name, sizeof(tc->tablename));
2786db785d31SAlexander V. Chernikov 	tc->vtype = vtype;
2787b074b7bbSAlexander V. Chernikov 
2788b074b7bbSAlexander V. Chernikov 	if (ti->tlvs == NULL) {
2789b074b7bbSAlexander V. Chernikov 		tc->no.compat = 1;
2790b074b7bbSAlexander V. Chernikov 		tc->no.uidx = ti->uidx;
2791b074b7bbSAlexander V. Chernikov 	}
2792b074b7bbSAlexander V. Chernikov 
2793b074b7bbSAlexander V. Chernikov 	/* Preallocate data structures for new tables */
2794914bffb6SAlexander V. Chernikov 	error = ta->init(ch, &tc->astate, &tc->ti, aname, tflags);
2795b074b7bbSAlexander V. Chernikov 	if (error != 0) {
2796b074b7bbSAlexander V. Chernikov 		free(tc, M_IPFW);
2797b074b7bbSAlexander V. Chernikov 		return (NULL);
2798b074b7bbSAlexander V. Chernikov 	}
2799b074b7bbSAlexander V. Chernikov 
2800b074b7bbSAlexander V. Chernikov 	return (tc);
2801b074b7bbSAlexander V. Chernikov }
2802b074b7bbSAlexander V. Chernikov 
280335e1bbd0SAlexander V. Chernikov /*
280435e1bbd0SAlexander V. Chernikov  * Destroys table state and config.
280535e1bbd0SAlexander V. Chernikov  */
2806b074b7bbSAlexander V. Chernikov static void
2807b074b7bbSAlexander V. Chernikov free_table_config(struct namedobj_instance *ni, struct table_config *tc)
2808b074b7bbSAlexander V. Chernikov {
2809b074b7bbSAlexander V. Chernikov 
281035e1bbd0SAlexander V. Chernikov 	KASSERT(tc->linked == 0, ("free() on linked config"));
2811b074b7bbSAlexander V. Chernikov 
28120468c5baSAlexander V. Chernikov 	/*
28130468c5baSAlexander V. Chernikov 	 * We're using ta without any locking/referencing.
28140468c5baSAlexander V. Chernikov 	 * TODO: fix this if we're going to use unloadable algos.
28150468c5baSAlexander V. Chernikov 	 */
281635e1bbd0SAlexander V. Chernikov 	tc->ta->destroy(tc->astate, &tc->ti);
2817b074b7bbSAlexander V. Chernikov 	free(tc, M_IPFW);
2818b074b7bbSAlexander V. Chernikov }
2819b074b7bbSAlexander V. Chernikov 
2820b074b7bbSAlexander V. Chernikov /*
2821b074b7bbSAlexander V. Chernikov  * Links @tc to @chain table named instance.
2822b074b7bbSAlexander V. Chernikov  * Sets appropriate type/states in @chain table info.
2823b074b7bbSAlexander V. Chernikov  */
2824b074b7bbSAlexander V. Chernikov static void
28259f7d47b0SAlexander V. Chernikov link_table(struct ip_fw_chain *ch, struct table_config *tc)
2826b074b7bbSAlexander V. Chernikov {
2827b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
28289f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
2829b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2830b074b7bbSAlexander V. Chernikov 
28319f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
28329f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
2833b074b7bbSAlexander V. Chernikov 
28349f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2835b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
2836b074b7bbSAlexander V. Chernikov 
2837b074b7bbSAlexander V. Chernikov 	ipfw_objhash_add(ni, &tc->no);
28389f7d47b0SAlexander V. Chernikov 
28399f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
28409f7d47b0SAlexander V. Chernikov 	*ti = tc->ti;
2841b074b7bbSAlexander V. Chernikov 
284268394ec8SAlexander V. Chernikov 	/* Notify algo on real @ti address */
284368394ec8SAlexander V. Chernikov 	if (tc->ta->change_ti != NULL)
284468394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, ti);
284568394ec8SAlexander V. Chernikov 
2846b074b7bbSAlexander V. Chernikov 	tc->linked = 1;
28479d099b4fSAlexander V. Chernikov 	tc->ta->refcnt++;
2848b074b7bbSAlexander V. Chernikov }
2849b074b7bbSAlexander V. Chernikov 
2850b074b7bbSAlexander V. Chernikov /*
2851b074b7bbSAlexander V. Chernikov  * Unlinks @tc from @chain table named instance.
2852b074b7bbSAlexander V. Chernikov  * Zeroes states in @chain and stores them in @tc.
2853b074b7bbSAlexander V. Chernikov  */
2854b074b7bbSAlexander V. Chernikov static void
28559f7d47b0SAlexander V. Chernikov unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
2856b074b7bbSAlexander V. Chernikov {
2857b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
28589f7d47b0SAlexander V. Chernikov 	struct table_info *ti;
2859b074b7bbSAlexander V. Chernikov 	uint16_t kidx;
2860b074b7bbSAlexander V. Chernikov 
28619f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
28629f7d47b0SAlexander V. Chernikov 	IPFW_WLOCK_ASSERT(ch);
2863b074b7bbSAlexander V. Chernikov 
28649f7d47b0SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2865b074b7bbSAlexander V. Chernikov 	kidx = tc->no.kidx;
2866b074b7bbSAlexander V. Chernikov 
28679f7d47b0SAlexander V. Chernikov 	/* Clear state. @ti copy is already saved inside @tc */
2868b074b7bbSAlexander V. Chernikov 	ipfw_objhash_del(ni, &tc->no);
28699f7d47b0SAlexander V. Chernikov 	ti = KIDX_TO_TI(ch, kidx);
28709f7d47b0SAlexander V. Chernikov 	memset(ti, 0, sizeof(struct table_info));
2871b074b7bbSAlexander V. Chernikov 	tc->linked = 0;
28729d099b4fSAlexander V. Chernikov 	tc->ta->refcnt--;
287368394ec8SAlexander V. Chernikov 
287468394ec8SAlexander V. Chernikov 	/* Notify algo on real @ti address */
287568394ec8SAlexander V. Chernikov 	if (tc->ta->change_ti != NULL)
287668394ec8SAlexander V. Chernikov 		tc->ta->change_ti(tc->astate, NULL);
2877b074b7bbSAlexander V. Chernikov }
2878b074b7bbSAlexander V. Chernikov 
2879a73d728dSAlexander V. Chernikov struct swap_table_args {
2880a73d728dSAlexander V. Chernikov 	int set;
2881a73d728dSAlexander V. Chernikov 	int new_set;
2882a73d728dSAlexander V. Chernikov 	int mv;
2883a73d728dSAlexander V. Chernikov };
2884a73d728dSAlexander V. Chernikov 
2885a73d728dSAlexander V. Chernikov /*
2886a73d728dSAlexander V. Chernikov  * Change set for each matching table.
2887a73d728dSAlexander V. Chernikov  *
2888a73d728dSAlexander V. Chernikov  * Ensure we dispatch each table once by setting/checking ochange
2889a73d728dSAlexander V. Chernikov  * fields.
2890a73d728dSAlexander V. Chernikov  */
2891a73d728dSAlexander V. Chernikov static void
2892a73d728dSAlexander V. Chernikov swap_table_set(struct namedobj_instance *ni, struct named_object *no,
2893a73d728dSAlexander V. Chernikov     void *arg)
2894a73d728dSAlexander V. Chernikov {
2895a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2896a73d728dSAlexander V. Chernikov 	struct swap_table_args *sta;
2897a73d728dSAlexander V. Chernikov 
2898a73d728dSAlexander V. Chernikov 	tc = (struct table_config *)no;
2899a73d728dSAlexander V. Chernikov 	sta = (struct swap_table_args *)arg;
2900a73d728dSAlexander V. Chernikov 
2901a73d728dSAlexander V. Chernikov 	if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0))
2902a73d728dSAlexander V. Chernikov 		return;
2903a73d728dSAlexander V. Chernikov 
2904a73d728dSAlexander V. Chernikov 	if (tc->ochanged != 0)
2905a73d728dSAlexander V. Chernikov 		return;
2906a73d728dSAlexander V. Chernikov 
2907a73d728dSAlexander V. Chernikov 	tc->ochanged = 1;
2908a73d728dSAlexander V. Chernikov 	ipfw_objhash_del(ni, no);
2909a73d728dSAlexander V. Chernikov 	if (no->set == sta->set)
2910a73d728dSAlexander V. Chernikov 		no->set = sta->new_set;
2911a73d728dSAlexander V. Chernikov 	else
2912a73d728dSAlexander V. Chernikov 		no->set = sta->set;
2913a73d728dSAlexander V. Chernikov 	ipfw_objhash_add(ni, no);
2914a73d728dSAlexander V. Chernikov }
2915a73d728dSAlexander V. Chernikov 
2916a73d728dSAlexander V. Chernikov /*
2917a73d728dSAlexander V. Chernikov  * Cleans up ochange field for all tables.
2918a73d728dSAlexander V. Chernikov  */
2919a73d728dSAlexander V. Chernikov static void
2920a73d728dSAlexander V. Chernikov clean_table_set_data(struct namedobj_instance *ni, struct named_object *no,
2921a73d728dSAlexander V. Chernikov     void *arg)
2922a73d728dSAlexander V. Chernikov {
2923a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2924a73d728dSAlexander V. Chernikov 	struct swap_table_args *sta;
2925a73d728dSAlexander V. Chernikov 
2926a73d728dSAlexander V. Chernikov 	tc = (struct table_config *)no;
2927a73d728dSAlexander V. Chernikov 	sta = (struct swap_table_args *)arg;
2928a73d728dSAlexander V. Chernikov 
2929a73d728dSAlexander V. Chernikov 	tc->ochanged = 0;
2930a73d728dSAlexander V. Chernikov }
2931a73d728dSAlexander V. Chernikov 
2932a73d728dSAlexander V. Chernikov /*
2933a73d728dSAlexander V. Chernikov  * Swaps tables within two sets.
2934a73d728dSAlexander V. Chernikov  */
2935a73d728dSAlexander V. Chernikov void
2936a73d728dSAlexander V. Chernikov ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set,
2937a73d728dSAlexander V. Chernikov     uint32_t new_set, int mv)
2938a73d728dSAlexander V. Chernikov {
2939a73d728dSAlexander V. Chernikov 	struct swap_table_args sta;
2940a73d728dSAlexander V. Chernikov 
2941a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
2942a73d728dSAlexander V. Chernikov 
2943a73d728dSAlexander V. Chernikov 	sta.set = set;
2944a73d728dSAlexander V. Chernikov 	sta.new_set = new_set;
2945a73d728dSAlexander V. Chernikov 	sta.mv = mv;
2946a73d728dSAlexander V. Chernikov 
2947a73d728dSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta);
2948a73d728dSAlexander V. Chernikov 	ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta);
2949a73d728dSAlexander V. Chernikov }
2950a73d728dSAlexander V. Chernikov 
2951a73d728dSAlexander V. Chernikov /*
2952a73d728dSAlexander V. Chernikov  * Move all tables which are reference by rules in @rr to set @new_set.
2953a73d728dSAlexander V. Chernikov  * Makes sure that all relevant tables are referenced ONLLY by given rules.
2954a73d728dSAlexander V. Chernikov  *
2955a73d728dSAlexander V. Chernikov  * Retuns 0 on success,
2956a73d728dSAlexander V. Chernikov  */
2957a73d728dSAlexander V. Chernikov int
2958a73d728dSAlexander V. Chernikov ipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
2959a73d728dSAlexander V. Chernikov     uint32_t new_set)
2960a73d728dSAlexander V. Chernikov {
2961a73d728dSAlexander V. Chernikov 	struct ip_fw *rule;
2962a73d728dSAlexander V. Chernikov 	struct table_config *tc;
2963a73d728dSAlexander V. Chernikov 	struct named_object *no;
2964a73d728dSAlexander V. Chernikov 	struct namedobj_instance *ni;
2965a73d728dSAlexander V. Chernikov 	int bad, i, l, cmdlen;
2966a73d728dSAlexander V. Chernikov 	uint16_t kidx;
2967a73d728dSAlexander V. Chernikov 	uint8_t type;
2968a73d728dSAlexander V. Chernikov 	ipfw_insn *cmd;
2969a73d728dSAlexander V. Chernikov 
2970a73d728dSAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(ch);
2971a73d728dSAlexander V. Chernikov 
2972a73d728dSAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
2973a73d728dSAlexander V. Chernikov 
2974a73d728dSAlexander V. Chernikov 	/* Stage 1: count number of references by given rules */
2975a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
2976a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
2977a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
2978a73d728dSAlexander V. Chernikov 			continue;
2979a73d728dSAlexander V. Chernikov 
2980a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
2981a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
2982a73d728dSAlexander V. Chernikov 		cmdlen = 0;
2983a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2984a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
2985a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
2986a73d728dSAlexander V. Chernikov 				continue;
2987a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
2988a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
2989a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
2990a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
2991a73d728dSAlexander V. Chernikov 			tc->ocount++;
2992a73d728dSAlexander V. Chernikov 		}
2993a73d728dSAlexander V. Chernikov 
2994a73d728dSAlexander V. Chernikov 	}
2995a73d728dSAlexander V. Chernikov 
2996a73d728dSAlexander V. Chernikov 	/* Stage 2: verify "ownership" */
2997a73d728dSAlexander V. Chernikov 	bad = 0;
2998a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
2999a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
3000a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
3001a73d728dSAlexander V. Chernikov 			continue;
3002a73d728dSAlexander V. Chernikov 
3003a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
3004a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
3005a73d728dSAlexander V. Chernikov 		cmdlen = 0;
3006a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3007a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
3008a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3009a73d728dSAlexander V. Chernikov 				continue;
3010a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
3011a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
3012a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
3013a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
3014a73d728dSAlexander V. Chernikov 			if (tc->no.refcnt != tc->ocount) {
3015a73d728dSAlexander V. Chernikov 
3016a73d728dSAlexander V. Chernikov 				/*
3017a73d728dSAlexander V. Chernikov 				 * Number of references differ:
3018a73d728dSAlexander V. Chernikov 				 * Other rule(s) are holding reference to given
3019a73d728dSAlexander V. Chernikov 				 * table, so it is not possible to change its set.
3020a73d728dSAlexander V. Chernikov 				 *
3021a73d728dSAlexander V. Chernikov 				 * Note that refcnt may account
3022a73d728dSAlexander V. Chernikov 				 * references to some going-to-be-added rules.
3023a73d728dSAlexander V. Chernikov 				 * Since we don't know their numbers (and event
3024a73d728dSAlexander V. Chernikov 				 * if they will be added) it is perfectly OK
3025a73d728dSAlexander V. Chernikov 				 * to return error here.
3026a73d728dSAlexander V. Chernikov 				 */
3027a73d728dSAlexander V. Chernikov 				bad = 1;
3028a73d728dSAlexander V. Chernikov 				break;
3029a73d728dSAlexander V. Chernikov 			}
3030a73d728dSAlexander V. Chernikov 		}
3031a73d728dSAlexander V. Chernikov 
3032a73d728dSAlexander V. Chernikov 		if (bad != 0)
3033a73d728dSAlexander V. Chernikov 			break;
3034a73d728dSAlexander V. Chernikov 	}
3035a73d728dSAlexander V. Chernikov 
3036a73d728dSAlexander V. Chernikov 	/* Stage 3: change set or cleanup */
3037a73d728dSAlexander V. Chernikov 	for (i = 0; i < ch->n_rules - 1; i++) {
3038a73d728dSAlexander V. Chernikov 		rule = ch->map[i];
3039a73d728dSAlexander V. Chernikov 		if (ipfw_match_range(rule, rt) == 0)
3040a73d728dSAlexander V. Chernikov 			continue;
3041a73d728dSAlexander V. Chernikov 
3042a73d728dSAlexander V. Chernikov 		l = rule->cmd_len;
3043a73d728dSAlexander V. Chernikov 		cmd = rule->cmd;
3044a73d728dSAlexander V. Chernikov 		cmdlen = 0;
3045a73d728dSAlexander V. Chernikov 		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3046a73d728dSAlexander V. Chernikov 			cmdlen = F_LEN(cmd);
3047a73d728dSAlexander V. Chernikov 			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3048a73d728dSAlexander V. Chernikov 				continue;
3049a73d728dSAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, kidx);
3050a73d728dSAlexander V. Chernikov 			KASSERT(no != NULL,
3051a73d728dSAlexander V. Chernikov 			    ("objhash lookup failed on index %d", kidx));
3052a73d728dSAlexander V. Chernikov 			tc = (struct table_config *)no;
3053a73d728dSAlexander V. Chernikov 
3054a73d728dSAlexander V. Chernikov 			tc->ocount = 0;
3055a73d728dSAlexander V. Chernikov 			if (bad != 0)
3056a73d728dSAlexander V. Chernikov 				continue;
3057a73d728dSAlexander V. Chernikov 
3058a73d728dSAlexander V. Chernikov 			/* Actually change set. */
3059a73d728dSAlexander V. Chernikov 			ipfw_objhash_del(ni, no);
3060a73d728dSAlexander V. Chernikov 			no->set = new_set;
3061a73d728dSAlexander V. Chernikov 			ipfw_objhash_add(ni, no);
3062a73d728dSAlexander V. Chernikov 		}
3063a73d728dSAlexander V. Chernikov 	}
3064a73d728dSAlexander V. Chernikov 
3065a73d728dSAlexander V. Chernikov 	return (bad);
3066a73d728dSAlexander V. Chernikov }
3067a73d728dSAlexander V. Chernikov 
3068b074b7bbSAlexander V. Chernikov /*
3069f99fbf96SAlexander V. Chernikov  * Finds and bumps refcount for tables referenced by given @rule.
3070e5eec6ddSAlexander V. Chernikov  * Auto-creates non-existing tables.
3071f99fbf96SAlexander V. Chernikov  * Fills in @oib array with userland/kernel indexes.
3072f99fbf96SAlexander V. Chernikov  * First free oidx pointer is saved back in @oib.
3073f99fbf96SAlexander V. Chernikov  *
3074f99fbf96SAlexander V. Chernikov  * Returns 0 on success.
3075f99fbf96SAlexander V. Chernikov  */
3076f99fbf96SAlexander V. Chernikov static int
3077e5eec6ddSAlexander V. Chernikov find_ref_rule_tables(struct ip_fw_chain *ch, struct ip_fw *rule,
3078f99fbf96SAlexander V. Chernikov     struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
3079f99fbf96SAlexander V. Chernikov {
3080f99fbf96SAlexander V. Chernikov 	struct table_config *tc;
3081f99fbf96SAlexander V. Chernikov 	struct namedobj_instance *ni;
3082f99fbf96SAlexander V. Chernikov 	struct named_object *no;
3083f99fbf96SAlexander V. Chernikov 	int cmdlen, error, l, numnew;
3084f99fbf96SAlexander V. Chernikov 	uint16_t kidx;
3085f99fbf96SAlexander V. Chernikov 	ipfw_insn *cmd;
3086f99fbf96SAlexander V. Chernikov 	struct obj_idx *pidx, *pidx_first, *p;
3087f99fbf96SAlexander V. Chernikov 
3088f99fbf96SAlexander V. Chernikov 	pidx_first = *oib;
3089f99fbf96SAlexander V. Chernikov 	pidx = pidx_first;
3090f99fbf96SAlexander V. Chernikov 	l = rule->cmd_len;
3091f99fbf96SAlexander V. Chernikov 	cmd = rule->cmd;
3092f99fbf96SAlexander V. Chernikov 	cmdlen = 0;
3093f99fbf96SAlexander V. Chernikov 	error = 0;
3094f99fbf96SAlexander V. Chernikov 	numnew = 0;
3095f99fbf96SAlexander V. Chernikov 
3096f99fbf96SAlexander V. Chernikov 	IPFW_UH_WLOCK(ch);
3097f99fbf96SAlexander V. Chernikov 	ni = CHAIN_TO_NI(ch);
3098f99fbf96SAlexander V. Chernikov 
3099e5eec6ddSAlexander V. Chernikov 	/* Increase refcount on each existing referenced table. */
3100f99fbf96SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3101f99fbf96SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3102f99fbf96SAlexander V. Chernikov 
3103f99fbf96SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
3104f99fbf96SAlexander V. Chernikov 			continue;
3105f99fbf96SAlexander V. Chernikov 
3106f99fbf96SAlexander V. Chernikov 		pidx->uidx = ti->uidx;
3107f99fbf96SAlexander V. Chernikov 		pidx->type = ti->type;
3108f99fbf96SAlexander V. Chernikov 
3109f99fbf96SAlexander V. Chernikov 		if ((tc = find_table(ni, ti)) != NULL) {
3110f99fbf96SAlexander V. Chernikov 			if (tc->no.type != ti->type) {
3111f99fbf96SAlexander V. Chernikov 				/* Incompatible types */
3112f99fbf96SAlexander V. Chernikov 				error = EINVAL;
3113f99fbf96SAlexander V. Chernikov 				break;
3114f99fbf96SAlexander V. Chernikov 			}
3115f99fbf96SAlexander V. Chernikov 
3116f99fbf96SAlexander V. Chernikov 			/* Reference found table and save kidx */
3117f99fbf96SAlexander V. Chernikov 			tc->no.refcnt++;
3118f99fbf96SAlexander V. Chernikov 			pidx->kidx = tc->no.kidx;
3119f99fbf96SAlexander V. Chernikov 			pidx++;
3120f99fbf96SAlexander V. Chernikov 			continue;
3121f99fbf96SAlexander V. Chernikov 		}
3122f99fbf96SAlexander V. Chernikov 
3123f99fbf96SAlexander V. Chernikov 		/*
3124f99fbf96SAlexander V. Chernikov 		 * Compability stuff for old clients:
3125f99fbf96SAlexander V. Chernikov 		 * prepare to manually create non-existing tables.
3126f99fbf96SAlexander V. Chernikov 		 */
3127f99fbf96SAlexander V. Chernikov 		pidx++;
3128f99fbf96SAlexander V. Chernikov 		numnew++;
3129f99fbf96SAlexander V. Chernikov 	}
3130f99fbf96SAlexander V. Chernikov 
3131f99fbf96SAlexander V. Chernikov 	if (error != 0) {
3132f99fbf96SAlexander V. Chernikov 		/* Unref everything we have already done */
3133f99fbf96SAlexander V. Chernikov 		for (p = *oib; p < pidx; p++) {
3134f99fbf96SAlexander V. Chernikov 			if (p->kidx == 0)
3135f99fbf96SAlexander V. Chernikov 				continue;
3136f99fbf96SAlexander V. Chernikov 
3137f99fbf96SAlexander V. Chernikov 			/* Find & unref by existing idx */
3138f99fbf96SAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
3139f99fbf96SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
3140f99fbf96SAlexander V. Chernikov 			    p->kidx));
3141f99fbf96SAlexander V. Chernikov 
3142f99fbf96SAlexander V. Chernikov 			no->refcnt--;
3143f99fbf96SAlexander V. Chernikov 		}
3144f99fbf96SAlexander V. Chernikov 	}
3145f99fbf96SAlexander V. Chernikov 
3146f99fbf96SAlexander V. Chernikov 	IPFW_UH_WUNLOCK(ch);
3147f99fbf96SAlexander V. Chernikov 
3148f99fbf96SAlexander V. Chernikov 	if (numnew == 0) {
3149f99fbf96SAlexander V. Chernikov 		*oib = pidx;
3150f99fbf96SAlexander V. Chernikov 		return (error);
3151f99fbf96SAlexander V. Chernikov 	}
3152f99fbf96SAlexander V. Chernikov 
3153f99fbf96SAlexander V. Chernikov 	/*
3154f99fbf96SAlexander V. Chernikov 	 * Compatibility stuff: do actual creation for non-existing,
3155f99fbf96SAlexander V. Chernikov 	 * but referenced tables.
3156f99fbf96SAlexander V. Chernikov 	 */
3157f99fbf96SAlexander V. Chernikov 	for (p = pidx_first; p < pidx; p++) {
3158f99fbf96SAlexander V. Chernikov 		if (p->kidx != 0)
3159f99fbf96SAlexander V. Chernikov 			continue;
3160f99fbf96SAlexander V. Chernikov 
3161f99fbf96SAlexander V. Chernikov 		ti->uidx = p->uidx;
3162f99fbf96SAlexander V. Chernikov 		ti->type = p->type;
3163f99fbf96SAlexander V. Chernikov 		ti->atype = 0;
3164f99fbf96SAlexander V. Chernikov 
3165f99fbf96SAlexander V. Chernikov 		error = create_table_compat(ch, ti, NULL, NULL, &kidx);
3166f99fbf96SAlexander V. Chernikov 		if (error == 0) {
3167f99fbf96SAlexander V. Chernikov 			p->kidx = kidx;
3168f99fbf96SAlexander V. Chernikov 			continue;
3169f99fbf96SAlexander V. Chernikov 		}
3170f99fbf96SAlexander V. Chernikov 
3171f99fbf96SAlexander V. Chernikov 		/* Error. We have to drop references */
3172f99fbf96SAlexander V. Chernikov 		IPFW_UH_WLOCK(ch);
3173f99fbf96SAlexander V. Chernikov 		for (p = pidx_first; p < pidx; p++) {
3174f99fbf96SAlexander V. Chernikov 			if (p->kidx == 0)
3175f99fbf96SAlexander V. Chernikov 				continue;
3176f99fbf96SAlexander V. Chernikov 
3177f99fbf96SAlexander V. Chernikov 			/* Find & unref by existing idx */
3178f99fbf96SAlexander V. Chernikov 			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
3179f99fbf96SAlexander V. Chernikov 			KASSERT(no != NULL, ("Ref'd table %d disappeared",
3180f99fbf96SAlexander V. Chernikov 			    p->kidx));
3181f99fbf96SAlexander V. Chernikov 
3182f99fbf96SAlexander V. Chernikov 			no->refcnt--;
3183f99fbf96SAlexander V. Chernikov 		}
3184f99fbf96SAlexander V. Chernikov 		IPFW_UH_WUNLOCK(ch);
3185f99fbf96SAlexander V. Chernikov 
3186f99fbf96SAlexander V. Chernikov 		return (error);
3187f99fbf96SAlexander V. Chernikov 	}
3188f99fbf96SAlexander V. Chernikov 
3189f99fbf96SAlexander V. Chernikov 	*oib = pidx;
3190f99fbf96SAlexander V. Chernikov 
3191f99fbf96SAlexander V. Chernikov 	return (error);
3192f99fbf96SAlexander V. Chernikov }
3193f99fbf96SAlexander V. Chernikov 
3194f99fbf96SAlexander V. Chernikov /*
3195f99fbf96SAlexander V. Chernikov  * Remove references from every table used in @rule.
3196f99fbf96SAlexander V. Chernikov  */
3197f99fbf96SAlexander V. Chernikov void
3198e5eec6ddSAlexander V. Chernikov ipfw_unref_rule_tables(struct ip_fw_chain *chain, struct ip_fw *rule)
3199f99fbf96SAlexander V. Chernikov {
3200f99fbf96SAlexander V. Chernikov 	int cmdlen, l;
3201f99fbf96SAlexander V. Chernikov 	ipfw_insn *cmd;
3202f99fbf96SAlexander V. Chernikov 	struct namedobj_instance *ni;
3203f99fbf96SAlexander V. Chernikov 	struct named_object *no;
3204f99fbf96SAlexander V. Chernikov 	uint16_t kidx;
3205f99fbf96SAlexander V. Chernikov 	uint8_t type;
3206f99fbf96SAlexander V. Chernikov 
3207f99fbf96SAlexander V. Chernikov 	IPFW_UH_WLOCK_ASSERT(chain);
3208f99fbf96SAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
3209f99fbf96SAlexander V. Chernikov 
3210f99fbf96SAlexander V. Chernikov 	l = rule->cmd_len;
3211f99fbf96SAlexander V. Chernikov 	cmd = rule->cmd;
3212f99fbf96SAlexander V. Chernikov 	cmdlen = 0;
3213f99fbf96SAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3214f99fbf96SAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3215f99fbf96SAlexander V. Chernikov 
3216f99fbf96SAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
3217f99fbf96SAlexander V. Chernikov 			continue;
3218f99fbf96SAlexander V. Chernikov 
3219f99fbf96SAlexander V. Chernikov 		no = ipfw_objhash_lookup_kidx(ni, kidx);
3220f99fbf96SAlexander V. Chernikov 
3221f99fbf96SAlexander V. Chernikov 		KASSERT(no != NULL, ("table id %d not found", kidx));
3222f99fbf96SAlexander V. Chernikov 		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
3223f99fbf96SAlexander V. Chernikov 		    no->type, type, kidx));
3224f99fbf96SAlexander V. Chernikov 		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
3225f99fbf96SAlexander V. Chernikov 		    kidx, no->refcnt));
3226f99fbf96SAlexander V. Chernikov 
3227f99fbf96SAlexander V. Chernikov 		no->refcnt--;
3228f99fbf96SAlexander V. Chernikov 	}
3229f99fbf96SAlexander V. Chernikov }
3230f99fbf96SAlexander V. Chernikov 
3231f99fbf96SAlexander V. Chernikov /*
3232b074b7bbSAlexander V. Chernikov  * Compatibility function for old ipfw(8) binaries.
3233b074b7bbSAlexander V. Chernikov  * Rewrites table kernel indices with userland ones.
323435e1bbd0SAlexander V. Chernikov  * Convert tables matching '/^\d+$/' to their atoi() value.
323535e1bbd0SAlexander V. Chernikov  * Use number 65535 for other tables.
3236b074b7bbSAlexander V. Chernikov  *
3237b074b7bbSAlexander V. Chernikov  * Returns 0 on success.
3238b074b7bbSAlexander V. Chernikov  */
3239b074b7bbSAlexander V. Chernikov int
32407e767c79SAlexander V. Chernikov ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule)
3241b074b7bbSAlexander V. Chernikov {
32421832a7b3SAlexander V. Chernikov 	int cmdlen, error, l;
3243b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
32441832a7b3SAlexander V. Chernikov 	uint16_t kidx, uidx;
3245b074b7bbSAlexander V. Chernikov 	uint8_t type;
3246b074b7bbSAlexander V. Chernikov 	struct named_object *no;
3247b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
3248b074b7bbSAlexander V. Chernikov 
3249b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
32501832a7b3SAlexander V. Chernikov 	error = 0;
3251b074b7bbSAlexander V. Chernikov 
3252b074b7bbSAlexander V. Chernikov 	l = rule->cmd_len;
3253b074b7bbSAlexander V. Chernikov 	cmd = rule->cmd;
3254b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
3255b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3256b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3257b074b7bbSAlexander V. Chernikov 
3258b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &kidx, &type) != 0)
3259b074b7bbSAlexander V. Chernikov 			continue;
3260b074b7bbSAlexander V. Chernikov 
3261ac35ff17SAlexander V. Chernikov 		if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL)
3262b074b7bbSAlexander V. Chernikov 			return (1);
3263b074b7bbSAlexander V. Chernikov 
32641832a7b3SAlexander V. Chernikov 		uidx = no->uidx;
32651832a7b3SAlexander V. Chernikov 		if (no->compat == 0) {
3266b074b7bbSAlexander V. Chernikov 
32671832a7b3SAlexander V. Chernikov 			/*
32681832a7b3SAlexander V. Chernikov 			 * We are called via legacy opcode.
32691832a7b3SAlexander V. Chernikov 			 * Save error and show table as fake number
32701832a7b3SAlexander V. Chernikov 			 * not to make ipfw(8) hang.
32711832a7b3SAlexander V. Chernikov 			 */
32721832a7b3SAlexander V. Chernikov 			uidx = 65535;
32731832a7b3SAlexander V. Chernikov 			error = 2;
3274b074b7bbSAlexander V. Chernikov 		}
3275b074b7bbSAlexander V. Chernikov 
32761832a7b3SAlexander V. Chernikov 		update_table_opcode(cmd, uidx);
32771832a7b3SAlexander V. Chernikov 	}
32781832a7b3SAlexander V. Chernikov 
32791832a7b3SAlexander V. Chernikov 	return (error);
3280b074b7bbSAlexander V. Chernikov }
3281b074b7bbSAlexander V. Chernikov 
3282563b5ab1SAlexander V. Chernikov /*
3283b074b7bbSAlexander V. Chernikov  * Checks is opcode is referencing table of appropriate type.
3284b074b7bbSAlexander V. Chernikov  * Adds reference count for found table if true.
3285b074b7bbSAlexander V. Chernikov  * Rewrites user-supplied opcode values with kernel ones.
3286b074b7bbSAlexander V. Chernikov  *
3287b074b7bbSAlexander V. Chernikov  * Returns 0 on success and appropriate error code otherwise.
3288b074b7bbSAlexander V. Chernikov  */
3289b074b7bbSAlexander V. Chernikov int
3290b074b7bbSAlexander V. Chernikov ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
3291b074b7bbSAlexander V. Chernikov     struct rule_check_info *ci)
3292b074b7bbSAlexander V. Chernikov {
32930468c5baSAlexander V. Chernikov 	int cmdlen, error, l;
3294b074b7bbSAlexander V. Chernikov 	ipfw_insn *cmd;
3295b074b7bbSAlexander V. Chernikov 	uint16_t uidx;
3296b074b7bbSAlexander V. Chernikov 	uint8_t type;
3297b074b7bbSAlexander V. Chernikov 	struct namedobj_instance *ni;
32989490a627SAlexander V. Chernikov 	struct obj_idx *p, *pidx_first, *pidx_last;
3299b074b7bbSAlexander V. Chernikov 	struct tid_info ti;
3300b074b7bbSAlexander V. Chernikov 
3301b074b7bbSAlexander V. Chernikov 	ni = CHAIN_TO_NI(chain);
3302b074b7bbSAlexander V. Chernikov 
3303b074b7bbSAlexander V. Chernikov 	/*
3304b074b7bbSAlexander V. Chernikov 	 * Prepare an array for storing opcode indices.
3305b074b7bbSAlexander V. Chernikov 	 * Use stack allocation by default.
3306b074b7bbSAlexander V. Chernikov 	 */
3307b074b7bbSAlexander V. Chernikov 	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
3308b074b7bbSAlexander V. Chernikov 		/* Stack */
33099490a627SAlexander V. Chernikov 		pidx_first = ci->obuf;
3310b074b7bbSAlexander V. Chernikov 	} else
33119490a627SAlexander V. Chernikov 		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
3312b074b7bbSAlexander V. Chernikov 		    M_IPFW, M_WAITOK | M_ZERO);
3313b074b7bbSAlexander V. Chernikov 
33149490a627SAlexander V. Chernikov 	pidx_last = pidx_first;
3315b074b7bbSAlexander V. Chernikov 	error = 0;
3316b074b7bbSAlexander V. Chernikov 	type = 0;
3317b074b7bbSAlexander V. Chernikov 	memset(&ti, 0, sizeof(ti));
33181832a7b3SAlexander V. Chernikov 
33191832a7b3SAlexander V. Chernikov 	/*
33201832a7b3SAlexander V. Chernikov 	 * Use default set for looking up tables (old way) or
33211832a7b3SAlexander V. Chernikov 	 * use set rule is assigned to (new way).
33221832a7b3SAlexander V. Chernikov 	 */
33231832a7b3SAlexander V. Chernikov 	ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0;
33246c2997ffSAlexander V. Chernikov 	if (ci->ctlv != NULL) {
33256c2997ffSAlexander V. Chernikov 		ti.tlvs = (void *)(ci->ctlv + 1);
33266c2997ffSAlexander V. Chernikov 		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
33276c2997ffSAlexander V. Chernikov 	}
3328b074b7bbSAlexander V. Chernikov 
33290468c5baSAlexander V. Chernikov 	/* Reference all used tables */
3330e5eec6ddSAlexander V. Chernikov 	error = find_ref_rule_tables(chain, ci->krule, ci, &pidx_last, &ti);
33310468c5baSAlexander V. Chernikov 	if (error != 0)
33329f7d47b0SAlexander V. Chernikov 		goto free;
3333b074b7bbSAlexander V. Chernikov 
33349f7d47b0SAlexander V. Chernikov 	IPFW_UH_WLOCK(chain);
33359f7d47b0SAlexander V. Chernikov 
3336b074b7bbSAlexander V. Chernikov 	/* Perform rule rewrite */
3337b074b7bbSAlexander V. Chernikov 	l = ci->krule->cmd_len;
3338b074b7bbSAlexander V. Chernikov 	cmd = ci->krule->cmd;
3339b074b7bbSAlexander V. Chernikov 	cmdlen = 0;
33409490a627SAlexander V. Chernikov 	p = pidx_first;
3341b074b7bbSAlexander V. Chernikov 	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3342b074b7bbSAlexander V. Chernikov 		cmdlen = F_LEN(cmd);
3343b074b7bbSAlexander V. Chernikov 		if (classify_table_opcode(cmd, &uidx, &type) != 0)
3344b074b7bbSAlexander V. Chernikov 			continue;
33459490a627SAlexander V. Chernikov 		update_table_opcode(cmd, p->kidx);
33469490a627SAlexander V. Chernikov 		p++;
3347b074b7bbSAlexander V. Chernikov 	}
3348b074b7bbSAlexander V. Chernikov 
3349b074b7bbSAlexander V. Chernikov 	IPFW_UH_WUNLOCK(chain);
3350b074b7bbSAlexander V. Chernikov 
3351b074b7bbSAlexander V. Chernikov free:
33529490a627SAlexander V. Chernikov 	if (pidx_first != ci->obuf)
33539490a627SAlexander V. Chernikov 		free(pidx_first, M_IPFW);
3354b074b7bbSAlexander V. Chernikov 
3355b074b7bbSAlexander V. Chernikov 	return (error);
3356b074b7bbSAlexander V. Chernikov }
3357b074b7bbSAlexander V. Chernikov 
3358