ip_fw_table.c revision 278259
1200590Sluigi/*-
2200673Sru * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko.
3272840Smelifaro * Copyright (c) 2014 Yandex LLC
4272840Smelifaro * Copyright (c) 2014 Alexander V. Chernikov
5200590Sluigi *
6200590Sluigi * Redistribution and use in source and binary forms, with or without
7200590Sluigi * modification, are permitted provided that the following conditions
8200590Sluigi * are met:
9200590Sluigi * 1. Redistributions of source code must retain the above copyright
10200590Sluigi *    notice, this list of conditions and the following disclaimer.
11200590Sluigi * 2. Redistributions in binary form must reproduce the above copyright
12200590Sluigi *    notice, this list of conditions and the following disclaimer in the
13200590Sluigi *    documentation and/or other materials provided with the distribution.
14200590Sluigi *
15200590Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16200590Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17200590Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18200590Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19200590Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20200590Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21200590Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22200590Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23200590Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24200590Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25200590Sluigi * SUCH DAMAGE.
26200590Sluigi */
27200590Sluigi
28200590Sluigi#include <sys/cdefs.h>
29200590Sluigi__FBSDID("$FreeBSD: head/sys/netpfil/ipfw/ip_fw_table.c 278259 2015-02-05 13:49:04Z melifaro $");
30200590Sluigi
31200590Sluigi/*
32272840Smelifaro * Lookup table support for ipfw.
33200601Sluigi *
34272840Smelifaro * This file contains handlers for all generic tables' operations:
35272840Smelifaro * add/del/flush entries, list/dump tables etc..
36200838Sluigi *
37272840Smelifaro * Table data modification is protected by both UH and runtime lock
38272840Smelifaro * while reading configuration/data is protected by UH lock.
39272840Smelifaro *
40272840Smelifaro * Lookup algorithms for all table types are located in ip_fw_table_algo.c
41200590Sluigi */
42200590Sluigi
43225518Sjhb#include "opt_ipfw.h"
44200590Sluigi
45200590Sluigi#include <sys/param.h>
46200590Sluigi#include <sys/systm.h>
47200590Sluigi#include <sys/malloc.h>
48200590Sluigi#include <sys/kernel.h>
49200590Sluigi#include <sys/lock.h>
50200590Sluigi#include <sys/rwlock.h>
51272840Smelifaro#include <sys/rmlock.h>
52200590Sluigi#include <sys/socket.h>
53272840Smelifaro#include <sys/socketvar.h>
54240494Sglebius#include <sys/queue.h>
55200590Sluigi#include <net/if.h>	/* ip_fw.h requires IFNAMSIZ */
56200590Sluigi
57200590Sluigi#include <netinet/in.h>
58201732Sluigi#include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
59200590Sluigi#include <netinet/ip_fw.h>
60200590Sluigi
61240494Sglebius#include <netpfil/ipfw/ip_fw_private.h>
62272840Smelifaro#include <netpfil/ipfw/ip_fw_table.h>
63240494Sglebius
64272840Smelifaro /*
65272840Smelifaro * Table has the following `type` concepts:
66272840Smelifaro *
67272840Smelifaro * `no.type` represents lookup key type (addr, ifp, uid, etc..)
68272840Smelifaro * vmask represents bitmask of table values which are present at the moment.
69272840Smelifaro * Special IPFW_VTYPE_LEGACY ( (uint32_t)-1 ) represents old
70272840Smelifaro * single-value-for-all approach.
71272840Smelifaro */
72272840Smelifarostruct table_config {
73272840Smelifaro	struct named_object	no;
74272840Smelifaro	uint8_t		tflags;		/* type flags */
75272840Smelifaro	uint8_t		locked;		/* 1 if locked from changes */
76272840Smelifaro	uint8_t		linked;		/* 1 if already linked */
77272840Smelifaro	uint8_t		ochanged;	/* used by set swapping */
78272840Smelifaro	uint8_t		vshared;	/* 1 if using shared value array */
79272840Smelifaro	uint8_t		spare[3];
80272840Smelifaro	uint32_t	count;		/* Number of records */
81272840Smelifaro	uint32_t	limit;		/* Max number of records */
82272840Smelifaro	uint32_t	vmask;		/* bitmask with supported values */
83272840Smelifaro	uint32_t	ocount;		/* used by set swapping */
84272840Smelifaro	uint64_t	gencnt;		/* generation count */
85272840Smelifaro	char		tablename[64];	/* table name */
86272840Smelifaro	struct table_algo	*ta;	/* Callbacks for given algo */
87272840Smelifaro	void		*astate;	/* algorithm state */
88272840Smelifaro	struct table_info	ti_copy;	/* data to put to table_info */
89272840Smelifaro	struct namedobj_instance	*vi;
90272840Smelifaro};
91200590Sluigi
92272840Smelifarostatic struct table_config *find_table(struct namedobj_instance *ni,
93272840Smelifaro    struct tid_info *ti);
94272840Smelifarostatic struct table_config *alloc_table_config(struct ip_fw_chain *ch,
95272840Smelifaro    struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags);
96272840Smelifarostatic void free_table_config(struct namedobj_instance *ni,
97272840Smelifaro    struct table_config *tc);
98272840Smelifarostatic int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
99272840Smelifaro    char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int ref);
100272840Smelifarostatic void link_table(struct ip_fw_chain *ch, struct table_config *tc);
101272840Smelifarostatic void unlink_table(struct ip_fw_chain *ch, struct table_config *tc);
102272840Smelifarostatic int find_ref_table(struct ip_fw_chain *ch, struct tid_info *ti,
103272840Smelifaro    struct tentry_info *tei, uint32_t count, int op, struct table_config **ptc);
104272840Smelifaro#define	OP_ADD	1
105272840Smelifaro#define	OP_DEL	0
106272840Smelifarostatic int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
107272840Smelifaro    struct sockopt_data *sd);
108272840Smelifarostatic void export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
109272840Smelifaro    ipfw_xtable_info *i);
110272840Smelifarostatic int dump_table_tentry(void *e, void *arg);
111272840Smelifarostatic int dump_table_xentry(void *e, void *arg);
112200590Sluigi
113272840Smelifarostatic int swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
114272840Smelifaro    struct tid_info *b);
115200590Sluigi
116272840Smelifarostatic int check_table_space(struct ip_fw_chain *ch, struct tableop_state *ts,
117272840Smelifaro    struct table_config *tc, struct table_info *ti, uint32_t count);
118272840Smelifarostatic int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
119232865Smelifaro
120272840Smelifarostatic struct table_algo *find_table_algo(struct tables_config *tableconf,
121272840Smelifaro    struct tid_info *ti, char *name);
122232865Smelifaro
123272840Smelifarostatic void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
124272840Smelifarostatic void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti);
125272840Smelifarostatic int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
126272840Smelifaro
127272840Smelifaro#define	CHAIN_TO_NI(chain)	(CHAIN_TO_TCFG(chain)->namehash)
128272840Smelifaro#define	KIDX_TO_TI(ch, k)	(&(((struct table_info *)(ch)->tablestate)[k]))
129272840Smelifaro
130272840Smelifaro#define	TA_BUF_SZ	128	/* On-stack buffer for add/delete state */
131272840Smelifaro
132272840Smelifarovoid
133272840Smelifarorollback_toperation_state(struct ip_fw_chain *ch, void *object)
134272840Smelifaro{
135272840Smelifaro	struct tables_config *tcfg;
136272840Smelifaro	struct op_state *os;
137272840Smelifaro
138272840Smelifaro	tcfg = CHAIN_TO_TCFG(ch);
139272840Smelifaro	TAILQ_FOREACH(os, &tcfg->state_list, next)
140272840Smelifaro		os->func(object, os);
141272840Smelifaro}
142272840Smelifaro
143272840Smelifarovoid
144272840Smelifaroadd_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts)
145272840Smelifaro{
146272840Smelifaro	struct tables_config *tcfg;
147272840Smelifaro
148272840Smelifaro	tcfg = CHAIN_TO_TCFG(ch);
149272840Smelifaro	TAILQ_INSERT_HEAD(&tcfg->state_list, &ts->opstate, next);
150272840Smelifaro}
151272840Smelifaro
152272840Smelifarovoid
153272840Smelifarodel_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts)
154272840Smelifaro{
155272840Smelifaro	struct tables_config *tcfg;
156272840Smelifaro
157272840Smelifaro	tcfg = CHAIN_TO_TCFG(ch);
158272840Smelifaro	TAILQ_REMOVE(&tcfg->state_list, &ts->opstate, next);
159272840Smelifaro}
160272840Smelifaro
161272840Smelifarovoid
162272840Smelifarotc_ref(struct table_config *tc)
163272840Smelifaro{
164272840Smelifaro
165272840Smelifaro	tc->no.refcnt++;
166272840Smelifaro}
167272840Smelifaro
168272840Smelifarovoid
169272840Smelifarotc_unref(struct table_config *tc)
170272840Smelifaro{
171272840Smelifaro
172272840Smelifaro	tc->no.refcnt--;
173272840Smelifaro}
174272840Smelifaro
175272840Smelifarostatic struct table_value *
176272840Smelifaroget_table_value(struct ip_fw_chain *ch, struct table_config *tc, uint32_t kidx)
177272840Smelifaro{
178272840Smelifaro	struct table_value *pval;
179272840Smelifaro
180272840Smelifaro	pval = (struct table_value *)ch->valuestate;
181272840Smelifaro
182272840Smelifaro	return (&pval[kidx]);
183272840Smelifaro}
184272840Smelifaro
185272840Smelifaro
186201120Sluigi/*
187272840Smelifaro * Checks if we're able to insert/update entry @tei into table
188272840Smelifaro * w.r.t @tc limits.
189272840Smelifaro * May alter @tei to indicate insertion error / insert
190272840Smelifaro * options.
191272840Smelifaro *
192272840Smelifaro * Returns 0 if operation can be performed/
193201120Sluigi */
194272840Smelifarostatic int
195272840Smelifarocheck_table_limit(struct table_config *tc, struct tentry_info *tei)
196272840Smelifaro{
197272840Smelifaro
198272840Smelifaro	if (tc->limit == 0 || tc->count < tc->limit)
199272840Smelifaro		return (0);
200272840Smelifaro
201272840Smelifaro	if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
202272840Smelifaro		/* Notify userland on error cause */
203272840Smelifaro		tei->flags |= TEI_FLAGS_LIMIT;
204272840Smelifaro		return (EFBIG);
205272840Smelifaro	}
206272840Smelifaro
207272840Smelifaro	/*
208272840Smelifaro	 * We have UPDATE flag set.
209272840Smelifaro	 * Permit updating record (if found),
210272840Smelifaro	 * but restrict adding new one since we've
211272840Smelifaro	 * already hit the limit.
212272840Smelifaro	 */
213272840Smelifaro	tei->flags |= TEI_FLAGS_DONTADD;
214272840Smelifaro
215272840Smelifaro	return (0);
216272840Smelifaro}
217272840Smelifaro
218232865Smelifaro/*
219272840Smelifaro * Convert algorithm callback return code into
220272840Smelifaro * one of pre-defined states known by userland.
221232865Smelifaro */
222272840Smelifarostatic void
223272840Smelifarostore_tei_result(struct tentry_info *tei, int op, int error, uint32_t num)
224272840Smelifaro{
225272840Smelifaro	int flag;
226201120Sluigi
227272840Smelifaro	flag = 0;
228232865Smelifaro
229272840Smelifaro	switch (error) {
230272840Smelifaro	case 0:
231272840Smelifaro		if (op == OP_ADD && num != 0)
232272840Smelifaro			flag = TEI_FLAGS_ADDED;
233272840Smelifaro		if (op == OP_DEL)
234272840Smelifaro			flag = TEI_FLAGS_DELETED;
235272840Smelifaro		break;
236272840Smelifaro	case ENOENT:
237272840Smelifaro		flag = TEI_FLAGS_NOTFOUND;
238272840Smelifaro		break;
239272840Smelifaro	case EEXIST:
240272840Smelifaro		flag = TEI_FLAGS_EXISTS;
241272840Smelifaro		break;
242272840Smelifaro	default:
243272840Smelifaro		flag = TEI_FLAGS_ERROR;
244272840Smelifaro	}
245232865Smelifaro
246272840Smelifaro	tei->flags |= flag;
247272840Smelifaro}
248272840Smelifaro
249272840Smelifaro/*
250272840Smelifaro * Creates and references table with default parameters.
251272840Smelifaro * Saves table config, algo and allocated kidx info @ptc, @pta and
252272840Smelifaro * @pkidx if non-zero.
253272840Smelifaro * Used for table auto-creation to support old binaries.
254272840Smelifaro *
255272840Smelifaro * Returns 0 on success.
256272840Smelifaro */
257272840Smelifarostatic int
258272840Smelifarocreate_table_compat(struct ip_fw_chain *ch, struct tid_info *ti,
259272840Smelifaro    uint16_t *pkidx)
260232865Smelifaro{
261272840Smelifaro	ipfw_xtable_info xi;
262272840Smelifaro	int error;
263232865Smelifaro
264272840Smelifaro	memset(&xi, 0, sizeof(xi));
265272840Smelifaro	/* Set default value mask for legacy clients */
266272840Smelifaro	xi.vmask = IPFW_VTYPE_LEGACY;
267272840Smelifaro
268272840Smelifaro	error = create_table_internal(ch, ti, NULL, &xi, pkidx, 1);
269272840Smelifaro	if (error != 0)
270272840Smelifaro		return (error);
271272840Smelifaro
272272840Smelifaro	return (0);
273232865Smelifaro}
274232865Smelifaro
275272840Smelifaro/*
276272840Smelifaro * Find and reference existing table optionally
277272840Smelifaro * creating new one.
278272840Smelifaro *
279272840Smelifaro * Saves found table config into @ptc.
280272840Smelifaro * Note function may drop/acquire UH_WLOCK.
281272840Smelifaro * Returns 0 if table was found/created and referenced
282272840Smelifaro * or non-zero return code.
283272840Smelifaro */
284272840Smelifarostatic int
285272840Smelifarofind_ref_table(struct ip_fw_chain *ch, struct tid_info *ti,
286272840Smelifaro    struct tentry_info *tei, uint32_t count, int op,
287272840Smelifaro    struct table_config **ptc)
288200590Sluigi{
289272840Smelifaro	struct namedobj_instance *ni;
290272840Smelifaro	struct table_config *tc;
291272840Smelifaro	uint16_t kidx;
292272840Smelifaro	int error;
293200590Sluigi
294272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
295232865Smelifaro
296272840Smelifaro	ni = CHAIN_TO_NI(ch);
297272840Smelifaro	tc = NULL;
298272840Smelifaro	if ((tc = find_table(ni, ti)) != NULL) {
299272840Smelifaro		/* check table type */
300272840Smelifaro		if (tc->no.type != ti->type)
301232865Smelifaro			return (EINVAL);
302272840Smelifaro
303272840Smelifaro		if (tc->locked != 0)
304272840Smelifaro			return (EACCES);
305272840Smelifaro
306272840Smelifaro		/* Try to exit early on limit hit */
307272840Smelifaro		if (op == OP_ADD && count == 1 &&
308272840Smelifaro		    check_table_limit(tc, tei) != 0)
309272840Smelifaro			return (EFBIG);
310272840Smelifaro
311272840Smelifaro		/* Reference and return */
312272840Smelifaro		tc->no.refcnt++;
313272840Smelifaro		*ptc = tc;
314272840Smelifaro		return (0);
315272840Smelifaro	}
316272840Smelifaro
317272840Smelifaro	if (op == OP_DEL)
318272840Smelifaro		return (ESRCH);
319272840Smelifaro
320272840Smelifaro	/* Compability mode: create new table for old clients */
321272840Smelifaro	if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
322272840Smelifaro		return (ESRCH);
323272840Smelifaro
324272840Smelifaro	IPFW_UH_WUNLOCK(ch);
325272840Smelifaro	error = create_table_compat(ch, ti, &kidx);
326272840Smelifaro	IPFW_UH_WLOCK(ch);
327272840Smelifaro
328272840Smelifaro	if (error != 0)
329272840Smelifaro		return (error);
330272840Smelifaro
331272840Smelifaro	tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx);
332272840Smelifaro	KASSERT(tc != NULL, ("create_table_compat returned bad idx %d", kidx));
333272840Smelifaro
334272840Smelifaro	/* OK, now we've got referenced table. */
335272840Smelifaro	*ptc = tc;
336272840Smelifaro	return (0);
337272840Smelifaro}
338272840Smelifaro
339272840Smelifaro/*
340272840Smelifaro * Rolls back already @added to @tc entries using state array @ta_buf_m.
341272840Smelifaro * Assume the following layout:
342272840Smelifaro * 1) ADD state (ta_buf_m[0] ... t_buf_m[added - 1]) for handling update cases
343272840Smelifaro * 2) DEL state (ta_buf_m[count[ ... t_buf_m[count + added - 1])
344272840Smelifaro *   for storing deleted state
345272840Smelifaro */
346272840Smelifarostatic void
347272840Smelifarorollback_added_entries(struct ip_fw_chain *ch, struct table_config *tc,
348272840Smelifaro    struct table_info *tinfo, struct tentry_info *tei, caddr_t ta_buf_m,
349272840Smelifaro    uint32_t count, uint32_t added)
350272840Smelifaro{
351272840Smelifaro	struct table_algo *ta;
352272840Smelifaro	struct tentry_info *ptei;
353272840Smelifaro	caddr_t v, vv;
354272840Smelifaro	size_t ta_buf_sz;
355272840Smelifaro	int error, i;
356272840Smelifaro	uint32_t num;
357272840Smelifaro
358272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
359272840Smelifaro
360272840Smelifaro	ta = tc->ta;
361272840Smelifaro	ta_buf_sz = ta->ta_buf_size;
362272840Smelifaro	v = ta_buf_m;
363272840Smelifaro	vv = v + count * ta_buf_sz;
364272840Smelifaro	for (i = 0; i < added; i++, v += ta_buf_sz, vv += ta_buf_sz) {
365272840Smelifaro		ptei = &tei[i];
366272840Smelifaro		if ((ptei->flags & TEI_FLAGS_UPDATED) != 0) {
367272840Smelifaro
368272840Smelifaro			/*
369272840Smelifaro			 * We have old value stored by previous
370272840Smelifaro			 * call in @ptei->value. Do add once again
371272840Smelifaro			 * to restore it.
372272840Smelifaro			 */
373272840Smelifaro			error = ta->add(tc->astate, tinfo, ptei, v, &num);
374272840Smelifaro			KASSERT(error == 0, ("rollback UPDATE fail"));
375272840Smelifaro			KASSERT(num == 0, ("rollback UPDATE fail2"));
376272840Smelifaro			continue;
377232865Smelifaro		}
378272840Smelifaro
379272840Smelifaro		error = ta->prepare_del(ch, ptei, vv);
380272840Smelifaro		KASSERT(error == 0, ("pre-rollback INSERT failed"));
381272840Smelifaro		error = ta->del(tc->astate, tinfo, ptei, vv, &num);
382272840Smelifaro		KASSERT(error == 0, ("rollback INSERT failed"));
383272840Smelifaro		tc->count -= num;
384272840Smelifaro	}
385272840Smelifaro}
386272840Smelifaro
387272840Smelifaro/*
388272840Smelifaro * Prepares add/del state for all @count entries in @tei.
389272840Smelifaro * Uses either stack buffer (@ta_buf) or allocates a new one.
390272840Smelifaro * Stores pointer to allocated buffer back to @ta_buf.
391272840Smelifaro *
392272840Smelifaro * Returns 0 on success.
393272840Smelifaro */
394272840Smelifarostatic int
395272840Smelifaroprepare_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
396272840Smelifaro    struct tentry_info *tei, uint32_t count, int op, caddr_t *ta_buf)
397272840Smelifaro{
398272840Smelifaro	caddr_t ta_buf_m, v;
399272840Smelifaro	size_t ta_buf_sz, sz;
400272840Smelifaro	struct tentry_info *ptei;
401272840Smelifaro	int error, i;
402272840Smelifaro
403272840Smelifaro	error = 0;
404272840Smelifaro	ta_buf_sz = ta->ta_buf_size;
405272840Smelifaro	if (count == 1) {
406272840Smelifaro		/* Sigle add/delete, use on-stack buffer */
407272840Smelifaro		memset(*ta_buf, 0, TA_BUF_SZ);
408272840Smelifaro		ta_buf_m = *ta_buf;
409272840Smelifaro	} else {
410272840Smelifaro
411272840Smelifaro		/*
412272840Smelifaro		 * Multiple adds/deletes, allocate larger buffer
413272840Smelifaro		 *
414272840Smelifaro		 * Note we need 2xcount buffer for add case:
415272840Smelifaro		 * we have hold both ADD state
416272840Smelifaro		 * and DELETE state (this may be needed
417272840Smelifaro		 * if we need to rollback all changes)
418272840Smelifaro		 */
419272840Smelifaro		sz = count * ta_buf_sz;
420272840Smelifaro		ta_buf_m = malloc((op == OP_ADD) ? sz * 2 : sz, M_TEMP,
421272840Smelifaro		    M_WAITOK | M_ZERO);
422272840Smelifaro	}
423272840Smelifaro
424272840Smelifaro	v = ta_buf_m;
425272840Smelifaro	for (i = 0; i < count; i++, v += ta_buf_sz) {
426272840Smelifaro		ptei = &tei[i];
427272840Smelifaro		error = (op == OP_ADD) ?
428272840Smelifaro		    ta->prepare_add(ch, ptei, v) : ta->prepare_del(ch, ptei, v);
429272840Smelifaro
430272840Smelifaro		/*
431272840Smelifaro		 * Some syntax error (incorrect mask, or address, or
432272840Smelifaro		 * anything). Return error regardless of atomicity
433272840Smelifaro		 * settings.
434272840Smelifaro		 */
435272840Smelifaro		if (error != 0)
436272840Smelifaro			break;
437272840Smelifaro	}
438272840Smelifaro
439272840Smelifaro	*ta_buf = ta_buf_m;
440272840Smelifaro	return (error);
441272840Smelifaro}
442272840Smelifaro
443272840Smelifaro/*
444272840Smelifaro * Flushes allocated state for each @count entries in @tei.
445272840Smelifaro * Frees @ta_buf_m if differs from stack buffer @ta_buf.
446272840Smelifaro */
447272840Smelifarostatic void
448272840Smelifaroflush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
449272840Smelifaro    struct tentry_info *tei, uint32_t count, int rollback,
450272840Smelifaro    caddr_t ta_buf_m, caddr_t ta_buf)
451272840Smelifaro{
452272840Smelifaro	caddr_t v;
453272840Smelifaro	struct tentry_info *ptei;
454272840Smelifaro	size_t ta_buf_sz;
455272840Smelifaro	int i;
456272840Smelifaro
457272840Smelifaro	ta_buf_sz = ta->ta_buf_size;
458272840Smelifaro
459272840Smelifaro	/* Run cleaning callback anyway */
460272840Smelifaro	v = ta_buf_m;
461272840Smelifaro	for (i = 0; i < count; i++, v += ta_buf_sz) {
462272840Smelifaro		ptei = &tei[i];
463272840Smelifaro		ta->flush_entry(ch, ptei, v);
464272840Smelifaro		if (ptei->ptv != NULL) {
465272840Smelifaro			free(ptei->ptv, M_IPFW);
466272840Smelifaro			ptei->ptv = NULL;
467272840Smelifaro		}
468272840Smelifaro	}
469272840Smelifaro
470272840Smelifaro	/* Clean up "deleted" state in case of rollback */
471272840Smelifaro	if (rollback != 0) {
472272840Smelifaro		v = ta_buf_m + count * ta_buf_sz;
473272840Smelifaro		for (i = 0; i < count; i++, v += ta_buf_sz)
474272840Smelifaro			ta->flush_entry(ch, &tei[i], v);
475272840Smelifaro	}
476272840Smelifaro
477272840Smelifaro	if (ta_buf_m != ta_buf)
478272840Smelifaro		free(ta_buf_m, M_TEMP);
479272840Smelifaro}
480272840Smelifaro
481272840Smelifaro
482272840Smelifarostatic void
483272840Smelifarorollback_add_entry(void *object, struct op_state *_state)
484272840Smelifaro{
485272840Smelifaro	struct ip_fw_chain *ch;
486272840Smelifaro	struct tableop_state *ts;
487272840Smelifaro
488272840Smelifaro	ts = (struct tableop_state *)_state;
489272840Smelifaro
490272840Smelifaro	if (ts->tc != object && ts->ch != object)
491272840Smelifaro		return;
492272840Smelifaro
493272840Smelifaro	ch = ts->ch;
494272840Smelifaro
495272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
496272840Smelifaro
497272840Smelifaro	/* Call specifid unlockers */
498272840Smelifaro	rollback_table_values(ts);
499272840Smelifaro
500272840Smelifaro	/* Indicate we've called */
501272840Smelifaro	ts->modified = 1;
502272840Smelifaro}
503272840Smelifaro
504272840Smelifaro/*
505272840Smelifaro * Adds/updates one or more entries in table @ti.
506272840Smelifaro *
507272840Smelifaro * Function may drop/reacquire UH wlock multiple times due to
508272840Smelifaro * items alloc, algorithm callbacks (check_space), value linkage
509272840Smelifaro * (new values, value storage realloc), etc..
510272840Smelifaro * Other processes like other adds (which may involve storage resize),
511272840Smelifaro * table swaps (which changes table data and may change algo type),
512272840Smelifaro * table modify (which may change value mask) may be executed
513272840Smelifaro * simultaneously so we need to deal with it.
514272840Smelifaro *
515272840Smelifaro * The following approach was implemented:
516272840Smelifaro * we have per-chain linked list, protected with UH lock.
517272840Smelifaro * add_table_entry prepares special on-stack structure wthich is passed
518272840Smelifaro * to its descendants. Users add this structure to this list before unlock.
519272840Smelifaro * After performing needed operations and acquiring UH lock back, each user
520272840Smelifaro * checks if structure has changed. If true, it rolls local state back and
521272840Smelifaro * returns without error to the caller.
522272840Smelifaro * add_table_entry() on its own checks if structure has changed and restarts
523272840Smelifaro * its operation from the beginning (goto restart).
524272840Smelifaro *
525272840Smelifaro * Functions which are modifying fields of interest (currently
526272840Smelifaro *   resize_shared_value_storage() and swap_tables() )
527272840Smelifaro * traverses given list while holding UH lock immediately before
528272840Smelifaro * performing their operations calling function provided be list entry
529272840Smelifaro * ( currently rollback_add_entry  ) which performs rollback for all necessary
530272840Smelifaro * state and sets appropriate values in structure indicating rollback
531272840Smelifaro * has happened.
532272840Smelifaro *
533272840Smelifaro * Algo interaction:
534272840Smelifaro * Function references @ti first to ensure table won't
535272840Smelifaro * disappear or change its type.
536272840Smelifaro * After that, prepare_add callback is called for each @tei entry.
537272840Smelifaro * Next, we try to add each entry under UH+WHLOCK
538272840Smelifaro * using add() callback.
539272840Smelifaro * Finally, we free all state by calling flush_entry callback
540272840Smelifaro * for each @tei.
541272840Smelifaro *
542272840Smelifaro * Returns 0 on success.
543272840Smelifaro */
544272840Smelifaroint
545272840Smelifaroadd_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
546272840Smelifaro    struct tentry_info *tei, uint8_t flags, uint32_t count)
547272840Smelifaro{
548272840Smelifaro	struct table_config *tc;
549272840Smelifaro	struct table_algo *ta;
550272840Smelifaro	uint16_t kidx;
551272840Smelifaro	int error, first_error, i, rollback;
552272840Smelifaro	uint32_t num, numadd;
553272840Smelifaro	struct tentry_info *ptei;
554272840Smelifaro	struct tableop_state ts;
555272840Smelifaro	char ta_buf[TA_BUF_SZ];
556272840Smelifaro	caddr_t ta_buf_m, v;
557272840Smelifaro
558272840Smelifaro	memset(&ts, 0, sizeof(ts));
559272840Smelifaro	ta = NULL;
560272840Smelifaro	IPFW_UH_WLOCK(ch);
561272840Smelifaro
562272840Smelifaro	/*
563272840Smelifaro	 * Find and reference existing table.
564272840Smelifaro	 */
565272840Smelifarorestart:
566272840Smelifaro	if (ts.modified != 0) {
567272840Smelifaro		IPFW_UH_WUNLOCK(ch);
568272840Smelifaro		flush_batch_buffer(ch, ta, tei, count, rollback,
569272840Smelifaro		    ta_buf_m, ta_buf);
570272840Smelifaro		memset(&ts, 0, sizeof(ts));
571272840Smelifaro		ta = NULL;
572272840Smelifaro		IPFW_UH_WLOCK(ch);
573272840Smelifaro	}
574272840Smelifaro
575272840Smelifaro	error = find_ref_table(ch, ti, tei, count, OP_ADD, &tc);
576272840Smelifaro	if (error != 0) {
577272840Smelifaro		IPFW_UH_WUNLOCK(ch);
578272840Smelifaro		return (error);
579272840Smelifaro	}
580272840Smelifaro	ta = tc->ta;
581272840Smelifaro
582272840Smelifaro	/* Fill in tablestate */
583272840Smelifaro	ts.ch = ch;
584272840Smelifaro	ts.opstate.func = rollback_add_entry;
585272840Smelifaro	ts.tc = tc;
586272840Smelifaro	ts.vshared = tc->vshared;
587272840Smelifaro	ts.vmask = tc->vmask;
588272840Smelifaro	ts.ta = ta;
589272840Smelifaro	ts.tei = tei;
590272840Smelifaro	ts.count = count;
591272840Smelifaro	rollback = 0;
592272840Smelifaro	add_toperation_state(ch, &ts);
593272840Smelifaro	IPFW_UH_WUNLOCK(ch);
594272840Smelifaro
595272840Smelifaro	/* Allocate memory and prepare record(s) */
596272840Smelifaro	/* Pass stack buffer by default */
597272840Smelifaro	ta_buf_m = ta_buf;
598272840Smelifaro	error = prepare_batch_buffer(ch, ta, tei, count, OP_ADD, &ta_buf_m);
599272840Smelifaro	if (error != 0)
600272840Smelifaro		goto cleanup;
601272840Smelifaro
602272840Smelifaro	IPFW_UH_WLOCK(ch);
603272840Smelifaro	/* Drop reference we've used in first search */
604272840Smelifaro	tc->no.refcnt--;
605272840Smelifaro
606272840Smelifaro	/*
607272840Smelifaro	 * Check if table swap has happened.
608272840Smelifaro	 * (so table algo might be changed).
609272840Smelifaro	 * Restart operation to achieve consistent behavior.
610272840Smelifaro	 */
611272840Smelifaro	del_toperation_state(ch, &ts);
612272840Smelifaro	if (ts.modified != 0)
613272840Smelifaro		goto restart;
614272840Smelifaro
615272840Smelifaro	/*
616272840Smelifaro	 * Link all values values to shared/per-table value array.
617272840Smelifaro	 *
618272840Smelifaro	 * May release/reacquire UH_WLOCK.
619272840Smelifaro	 */
620272840Smelifaro	error = ipfw_link_table_values(ch, &ts);
621272840Smelifaro	if (error != 0)
622272840Smelifaro		goto cleanup;
623272840Smelifaro	if (ts.modified != 0)
624272840Smelifaro		goto restart;
625272840Smelifaro
626272840Smelifaro	/*
627272840Smelifaro	 * Ensure we are able to add all entries without additional
628272840Smelifaro	 * memory allocations. May release/reacquire UH_WLOCK.
629272840Smelifaro	 */
630272840Smelifaro	kidx = tc->no.kidx;
631272840Smelifaro	error = check_table_space(ch, &ts, tc, KIDX_TO_TI(ch, kidx), count);
632272840Smelifaro	if (error != 0)
633272840Smelifaro		goto cleanup;
634272840Smelifaro	if (ts.modified != 0)
635272840Smelifaro		goto restart;
636272840Smelifaro
637272840Smelifaro	/* We've got valid table in @tc. Let's try to add data */
638272840Smelifaro	kidx = tc->no.kidx;
639272840Smelifaro	ta = tc->ta;
640272840Smelifaro	numadd = 0;
641272840Smelifaro	first_error = 0;
642272840Smelifaro
643272840Smelifaro	IPFW_WLOCK(ch);
644272840Smelifaro
645272840Smelifaro	v = ta_buf_m;
646272840Smelifaro	for (i = 0; i < count; i++, v += ta->ta_buf_size) {
647272840Smelifaro		ptei = &tei[i];
648272840Smelifaro		num = 0;
649272840Smelifaro		/* check limit before adding */
650272840Smelifaro		if ((error = check_table_limit(tc, ptei)) == 0) {
651272840Smelifaro			error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx),
652272840Smelifaro			    ptei, v, &num);
653272840Smelifaro			/* Set status flag to inform userland */
654272840Smelifaro			store_tei_result(ptei, OP_ADD, error, num);
655272840Smelifaro		}
656272840Smelifaro		if (error == 0) {
657272840Smelifaro			/* Update number of records to ease limit checking */
658272840Smelifaro			tc->count += num;
659272840Smelifaro			numadd += num;
660272840Smelifaro			continue;
661272840Smelifaro		}
662272840Smelifaro
663272840Smelifaro		if (first_error == 0)
664272840Smelifaro			first_error = error;
665272840Smelifaro
666272840Smelifaro		/*
667272840Smelifaro		 * Some error have happened. Check our atomicity
668272840Smelifaro		 * settings: continue if atomicity is not required,
669272840Smelifaro		 * rollback changes otherwise.
670272840Smelifaro		 */
671272840Smelifaro		if ((flags & IPFW_CTF_ATOMIC) == 0)
672272840Smelifaro			continue;
673272840Smelifaro
674272840Smelifaro		rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx),
675272840Smelifaro		    tei, ta_buf_m, count, i);
676272840Smelifaro
677272840Smelifaro		rollback = 1;
678232865Smelifaro		break;
679272840Smelifaro	}
680272840Smelifaro
681272840Smelifaro	IPFW_WUNLOCK(ch);
682272840Smelifaro
683272840Smelifaro	ipfw_garbage_table_values(ch, tc, tei, count, rollback);
684272840Smelifaro
685272840Smelifaro	/* Permit post-add algorithm grow/rehash. */
686272840Smelifaro	if (numadd != 0)
687272840Smelifaro		check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0);
688272840Smelifaro
689272840Smelifaro	/* Return first error to user, if any */
690272840Smelifaro	error = first_error;
691272840Smelifaro
692272840Smelifarocleanup:
693272840Smelifaro	IPFW_UH_WUNLOCK(ch);
694272840Smelifaro
695272840Smelifaro	flush_batch_buffer(ch, ta, tei, count, rollback, ta_buf_m, ta_buf);
696232865Smelifaro
697272840Smelifaro	return (error);
698272840Smelifaro}
699232865Smelifaro
700272840Smelifaro/*
701272840Smelifaro * Deletes one or more entries in table @ti.
702272840Smelifaro *
703272840Smelifaro * Returns 0 on success.
704272840Smelifaro */
705272840Smelifaroint
706272840Smelifarodel_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
707272840Smelifaro    struct tentry_info *tei, uint8_t flags, uint32_t count)
708272840Smelifaro{
709272840Smelifaro	struct table_config *tc;
710272840Smelifaro	struct table_algo *ta;
711272840Smelifaro	struct tentry_info *ptei;
712272840Smelifaro	uint16_t kidx;
713272840Smelifaro	int error, first_error, i;
714272840Smelifaro	uint32_t num, numdel;
715272840Smelifaro	char ta_buf[TA_BUF_SZ];
716272840Smelifaro	caddr_t ta_buf_m, v;
717232865Smelifaro
718272840Smelifaro	/*
719272840Smelifaro	 * Find and reference existing table.
720272840Smelifaro	 */
721272840Smelifaro	IPFW_UH_WLOCK(ch);
722272840Smelifaro	error = find_ref_table(ch, ti, tei, count, OP_DEL, &tc);
723272840Smelifaro	if (error != 0) {
724272840Smelifaro		IPFW_UH_WUNLOCK(ch);
725272840Smelifaro		return (error);
726272840Smelifaro	}
727272840Smelifaro	ta = tc->ta;
728272840Smelifaro	IPFW_UH_WUNLOCK(ch);
729232865Smelifaro
730272840Smelifaro	/* Allocate memory and prepare record(s) */
731272840Smelifaro	/* Pass stack buffer by default */
732272840Smelifaro	ta_buf_m = ta_buf;
733272840Smelifaro	error = prepare_batch_buffer(ch, ta, tei, count, OP_DEL, &ta_buf_m);
734272840Smelifaro	if (error != 0)
735272840Smelifaro		goto cleanup;
736272840Smelifaro
737272840Smelifaro	IPFW_UH_WLOCK(ch);
738272840Smelifaro
739272840Smelifaro	/* Drop reference we've used in first search */
740272840Smelifaro	tc->no.refcnt--;
741272840Smelifaro
742272840Smelifaro	/*
743272840Smelifaro	 * Check if table algo is still the same.
744272840Smelifaro	 * (changed ta may be the result of table swap).
745272840Smelifaro	 */
746272840Smelifaro	if (ta != tc->ta) {
747272840Smelifaro		IPFW_UH_WUNLOCK(ch);
748272840Smelifaro		error = EINVAL;
749272840Smelifaro		goto cleanup;
750232865Smelifaro	}
751232865Smelifaro
752272840Smelifaro	kidx = tc->no.kidx;
753272840Smelifaro	numdel = 0;
754272840Smelifaro	first_error = 0;
755272840Smelifaro
756200590Sluigi	IPFW_WLOCK(ch);
757272840Smelifaro	v = ta_buf_m;
758272840Smelifaro	for (i = 0; i < count; i++, v += ta->ta_buf_size) {
759272840Smelifaro		ptei = &tei[i];
760272840Smelifaro		num = 0;
761272840Smelifaro		error = ta->del(tc->astate, KIDX_TO_TI(ch, kidx), ptei, v,
762272840Smelifaro		    &num);
763272840Smelifaro		/* Save state for userland */
764272840Smelifaro		store_tei_result(ptei, OP_DEL, error, num);
765272840Smelifaro		if (error != 0 && first_error == 0)
766272840Smelifaro			first_error = error;
767272840Smelifaro		tc->count -= num;
768272840Smelifaro		numdel += num;
769272840Smelifaro	}
770272840Smelifaro	IPFW_WUNLOCK(ch);
771232865Smelifaro
772272840Smelifaro	/* Unlink non-used values */
773272840Smelifaro	ipfw_garbage_table_values(ch, tc, tei, count, 0);
774272840Smelifaro
775272840Smelifaro	if (numdel != 0) {
776272840Smelifaro		/* Run post-del hook to permit shrinking */
777272840Smelifaro		check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0);
778232865Smelifaro	}
779232865Smelifaro
780272840Smelifaro	IPFW_UH_WUNLOCK(ch);
781272840Smelifaro
782272840Smelifaro	/* Return first error to user, if any */
783272840Smelifaro	error = first_error;
784272840Smelifaro
785272840Smelifarocleanup:
786272840Smelifaro	flush_batch_buffer(ch, ta, tei, count, 0, ta_buf_m, ta_buf);
787272840Smelifaro
788272840Smelifaro	return (error);
789272840Smelifaro}
790272840Smelifaro
791272840Smelifaro/*
792272840Smelifaro * Ensure that table @tc has enough space to add @count entries without
793272840Smelifaro * need for reallocation.
794272840Smelifaro *
795272840Smelifaro * Callbacks order:
796272840Smelifaro * 0) need_modify() (UH_WLOCK) - checks if @count items can be added w/o resize.
797272840Smelifaro *
798272840Smelifaro * 1) alloc_modify (no locks, M_WAITOK) - alloc new state based on @pflags.
799272840Smelifaro * 2) prepare_modifyt (UH_WLOCK) - copy old data into new storage
800272840Smelifaro * 3) modify (UH_WLOCK + WLOCK) - switch pointers
801272840Smelifaro * 4) flush_modify (UH_WLOCK) - free state, if needed
802272840Smelifaro *
803272840Smelifaro * Returns 0 on success.
804272840Smelifaro */
805272840Smelifarostatic int
806272840Smelifarocheck_table_space(struct ip_fw_chain *ch, struct tableop_state *ts,
807272840Smelifaro    struct table_config *tc, struct table_info *ti, uint32_t count)
808272840Smelifaro{
809272840Smelifaro	struct table_algo *ta;
810272840Smelifaro	uint64_t pflags;
811272840Smelifaro	char ta_buf[TA_BUF_SZ];
812272840Smelifaro	int error;
813272840Smelifaro
814272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
815272840Smelifaro
816272840Smelifaro	error = 0;
817272840Smelifaro	ta = tc->ta;
818272840Smelifaro	if (ta->need_modify == NULL)
819272840Smelifaro		return (0);
820272840Smelifaro
821272840Smelifaro	/* Acquire reference not to loose @tc between locks/unlocks */
822272840Smelifaro	tc->no.refcnt++;
823272840Smelifaro
824272840Smelifaro	/*
825272840Smelifaro	 * TODO: think about avoiding race between large add/large delete
826272840Smelifaro	 * operation on algorithm which implements shrinking along with
827272840Smelifaro	 * growing.
828272840Smelifaro	 */
829272840Smelifaro	while (true) {
830272840Smelifaro		pflags = 0;
831272840Smelifaro		if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) {
832272840Smelifaro			error = 0;
833272840Smelifaro			break;
834232865Smelifaro		}
835232865Smelifaro
836272840Smelifaro		/* We have to shrink/grow table */
837272840Smelifaro		if (ts != NULL)
838272840Smelifaro			add_toperation_state(ch, ts);
839272840Smelifaro		IPFW_UH_WUNLOCK(ch);
840272840Smelifaro
841272840Smelifaro		memset(&ta_buf, 0, sizeof(ta_buf));
842272840Smelifaro		error = ta->prepare_mod(ta_buf, &pflags);
843272840Smelifaro
844272840Smelifaro		IPFW_UH_WLOCK(ch);
845272840Smelifaro		if (ts != NULL)
846272840Smelifaro			del_toperation_state(ch, ts);
847272840Smelifaro
848272840Smelifaro		if (error != 0)
849272840Smelifaro			break;
850272840Smelifaro
851272840Smelifaro		if (ts != NULL && ts->modified != 0) {
852272840Smelifaro
853272840Smelifaro			/*
854272840Smelifaro			 * Swap operation has happened
855272840Smelifaro			 * so we're currently operating on other
856272840Smelifaro			 * table data. Stop doing this.
857232865Smelifaro			 */
858272840Smelifaro			ta->flush_mod(ta_buf);
859272840Smelifaro			break;
860232865Smelifaro		}
861272840Smelifaro
862272840Smelifaro		/* Check if we still need to alter table */
863272840Smelifaro		ti = KIDX_TO_TI(ch, tc->no.kidx);
864272840Smelifaro		if (ta->need_modify(tc->astate, ti, count, &pflags) == 0) {
865272840Smelifaro			IPFW_UH_WUNLOCK(ch);
866272840Smelifaro
867272840Smelifaro			/*
868272840Smelifaro			 * Other thread has already performed resize.
869272840Smelifaro			 * Flush our state and return.
870272840Smelifaro			 */
871272840Smelifaro			ta->flush_mod(ta_buf);
872272840Smelifaro			break;
873272840Smelifaro		}
874272840Smelifaro
875272840Smelifaro		error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags);
876272840Smelifaro		if (error == 0) {
877272840Smelifaro			/* Do actual modification */
878272840Smelifaro			IPFW_WLOCK(ch);
879272840Smelifaro			ta->modify(tc->astate, ti, ta_buf, pflags);
880272840Smelifaro			IPFW_WUNLOCK(ch);
881272840Smelifaro		}
882272840Smelifaro
883272840Smelifaro		/* Anyway, flush data and retry */
884272840Smelifaro		ta->flush_mod(ta_buf);
885232865Smelifaro	}
886232865Smelifaro
887272840Smelifaro	tc->no.refcnt--;
888272840Smelifaro	return (error);
889272840Smelifaro}
890232865Smelifaro
891272840Smelifaro/*
892272840Smelifaro * Adds or deletes record in table.
893272840Smelifaro * Data layout (v0):
894272840Smelifaro * Request: [ ip_fw3_opheader ipfw_table_xentry ]
895272840Smelifaro *
896272840Smelifaro * Returns 0 on success
897272840Smelifaro */
898272840Smelifarostatic int
899272840Smelifaromanage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
900272840Smelifaro    struct sockopt_data *sd)
901272840Smelifaro{
902272840Smelifaro	ipfw_table_xentry *xent;
903272840Smelifaro	struct tentry_info tei;
904272840Smelifaro	struct tid_info ti;
905272840Smelifaro	struct table_value v;
906272840Smelifaro	int error, hdrlen, read;
907272840Smelifaro
908272840Smelifaro	hdrlen = offsetof(ipfw_table_xentry, k);
909272840Smelifaro
910272840Smelifaro	/* Check minimum header size */
911272840Smelifaro	if (sd->valsize < (sizeof(*op3) + hdrlen))
912272840Smelifaro		return (EINVAL);
913272840Smelifaro
914272840Smelifaro	read = sizeof(ip_fw3_opheader);
915272840Smelifaro
916272840Smelifaro	/* Check if xentry len field is valid */
917272840Smelifaro	xent = (ipfw_table_xentry *)(op3 + 1);
918272840Smelifaro	if (xent->len < hdrlen || xent->len + read > sd->valsize)
919272840Smelifaro		return (EINVAL);
920272840Smelifaro
921272840Smelifaro	memset(&tei, 0, sizeof(tei));
922272840Smelifaro	tei.paddr = &xent->k;
923272840Smelifaro	tei.masklen = xent->masklen;
924272840Smelifaro	ipfw_import_table_value_legacy(xent->value, &v);
925272840Smelifaro	tei.pvalue = &v;
926272840Smelifaro	/* Old requests compability */
927272840Smelifaro	tei.flags = TEI_FLAGS_COMPAT;
928272840Smelifaro	if (xent->type == IPFW_TABLE_ADDR) {
929272840Smelifaro		if (xent->len - hdrlen == sizeof(in_addr_t))
930272840Smelifaro			tei.subtype = AF_INET;
931272840Smelifaro		else
932272840Smelifaro			tei.subtype = AF_INET6;
933200590Sluigi	}
934272840Smelifaro
935272840Smelifaro	memset(&ti, 0, sizeof(ti));
936272840Smelifaro	ti.uidx = xent->tbl;
937272840Smelifaro	ti.type = xent->type;
938272840Smelifaro
939272840Smelifaro	error = (op3->opcode == IP_FW_TABLE_XADD) ?
940272840Smelifaro	    add_table_entry(ch, &ti, &tei, 0, 1) :
941272840Smelifaro	    del_table_entry(ch, &ti, &tei, 0, 1);
942272840Smelifaro
943272840Smelifaro	return (error);
944200590Sluigi}
945200590Sluigi
946272840Smelifaro/*
947272840Smelifaro * Adds or deletes record in table.
948272840Smelifaro * Data layout (v1)(current):
949272840Smelifaro * Request: [ ipfw_obj_header
950272840Smelifaro *   ipfw_obj_ctlv(IPFW_TLV_TBLENT_LIST) [ ipfw_obj_tentry x N ]
951272840Smelifaro * ]
952272840Smelifaro *
953272840Smelifaro * Returns 0 on success
954272840Smelifaro */
955272840Smelifarostatic int
956272840Smelifaromanage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
957272840Smelifaro    struct sockopt_data *sd)
958200590Sluigi{
959272840Smelifaro	ipfw_obj_tentry *tent, *ptent;
960272840Smelifaro	ipfw_obj_ctlv *ctlv;
961272840Smelifaro	ipfw_obj_header *oh;
962272840Smelifaro	struct tentry_info *ptei, tei, *tei_buf;
963272840Smelifaro	struct tid_info ti;
964272840Smelifaro	int error, i, kidx, read;
965200590Sluigi
966272840Smelifaro	/* Check minimum header size */
967272840Smelifaro	if (sd->valsize < (sizeof(*oh) + sizeof(*ctlv)))
968200590Sluigi		return (EINVAL);
969232865Smelifaro
970272840Smelifaro	/* Check if passed data is too long */
971272840Smelifaro	if (sd->valsize != sd->kavail)
972272840Smelifaro		return (EINVAL);
973232865Smelifaro
974272840Smelifaro	oh = (ipfw_obj_header *)sd->kbuf;
975272840Smelifaro
976272840Smelifaro	/* Basic length checks for TLVs */
977272840Smelifaro	if (oh->ntlv.head.length != sizeof(oh->ntlv))
978272840Smelifaro		return (EINVAL);
979272840Smelifaro
980272840Smelifaro	read = sizeof(*oh);
981272840Smelifaro
982272840Smelifaro	ctlv = (ipfw_obj_ctlv *)(oh + 1);
983272840Smelifaro	if (ctlv->head.length + read != sd->valsize)
984272840Smelifaro		return (EINVAL);
985272840Smelifaro
986272840Smelifaro	read += sizeof(*ctlv);
987272840Smelifaro	tent = (ipfw_obj_tentry *)(ctlv + 1);
988272840Smelifaro	if (ctlv->count * sizeof(*tent) + read != sd->valsize)
989272840Smelifaro		return (EINVAL);
990272840Smelifaro
991272840Smelifaro	if (ctlv->count == 0)
992272840Smelifaro		return (0);
993272840Smelifaro
994272840Smelifaro	/*
995272840Smelifaro	 * Mark entire buffer as "read".
996272840Smelifaro	 * This instructs sopt api write it back
997272840Smelifaro	 * after function return.
998272840Smelifaro	 */
999272840Smelifaro	ipfw_get_sopt_header(sd, sd->valsize);
1000272840Smelifaro
1001272840Smelifaro	/* Perform basic checks for each entry */
1002272840Smelifaro	ptent = tent;
1003272840Smelifaro	kidx = tent->idx;
1004272840Smelifaro	for (i = 0; i < ctlv->count; i++, ptent++) {
1005272840Smelifaro		if (ptent->head.length != sizeof(*ptent))
1006232865Smelifaro			return (EINVAL);
1007272840Smelifaro		if (ptent->idx != kidx)
1008272840Smelifaro			return (ENOTSUP);
1009272840Smelifaro	}
1010232865Smelifaro
1011272840Smelifaro	/* Convert data into kernel request objects */
1012272840Smelifaro	objheader_to_ti(oh, &ti);
1013272840Smelifaro	ti.type = oh->ntlv.type;
1014272840Smelifaro	ti.uidx = kidx;
1015232865Smelifaro
1016272840Smelifaro	/* Use on-stack buffer for single add/del */
1017272840Smelifaro	if (ctlv->count == 1) {
1018272840Smelifaro		memset(&tei, 0, sizeof(tei));
1019272840Smelifaro		tei_buf = &tei;
1020272840Smelifaro	} else
1021272840Smelifaro		tei_buf = malloc(ctlv->count * sizeof(tei), M_TEMP,
1022272840Smelifaro		    M_WAITOK | M_ZERO);
1023238265Smelifaro
1024272840Smelifaro	ptei = tei_buf;
1025272840Smelifaro	ptent = tent;
1026272840Smelifaro	for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
1027272840Smelifaro		ptei->paddr = &ptent->k;
1028272840Smelifaro		ptei->subtype = ptent->subtype;
1029272840Smelifaro		ptei->masklen = ptent->masklen;
1030272840Smelifaro		if (ptent->head.flags & IPFW_TF_UPDATE)
1031272840Smelifaro			ptei->flags |= TEI_FLAGS_UPDATE;
1032232865Smelifaro
1033272840Smelifaro		ipfw_import_table_value_v1(&ptent->v.value);
1034272840Smelifaro		ptei->pvalue = (struct table_value *)&ptent->v.value;
1035272840Smelifaro	}
1036232865Smelifaro
1037272840Smelifaro	error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
1038272840Smelifaro	    add_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count) :
1039272840Smelifaro	    del_table_entry(ch, &ti, tei_buf, ctlv->flags, ctlv->count);
1040272840Smelifaro
1041272840Smelifaro	/* Translate result back to userland */
1042272840Smelifaro	ptei = tei_buf;
1043272840Smelifaro	ptent = tent;
1044272840Smelifaro	for (i = 0; i < ctlv->count; i++, ptent++, ptei++) {
1045272840Smelifaro		if (ptei->flags & TEI_FLAGS_ADDED)
1046272840Smelifaro			ptent->result = IPFW_TR_ADDED;
1047272840Smelifaro		else if (ptei->flags & TEI_FLAGS_DELETED)
1048272840Smelifaro			ptent->result = IPFW_TR_DELETED;
1049272840Smelifaro		else if (ptei->flags & TEI_FLAGS_UPDATED)
1050272840Smelifaro			ptent->result = IPFW_TR_UPDATED;
1051272840Smelifaro		else if (ptei->flags & TEI_FLAGS_LIMIT)
1052272840Smelifaro			ptent->result = IPFW_TR_LIMIT;
1053272840Smelifaro		else if (ptei->flags & TEI_FLAGS_ERROR)
1054272840Smelifaro			ptent->result = IPFW_TR_ERROR;
1055272840Smelifaro		else if (ptei->flags & TEI_FLAGS_NOTFOUND)
1056272840Smelifaro			ptent->result = IPFW_TR_NOTFOUND;
1057272840Smelifaro		else if (ptei->flags & TEI_FLAGS_EXISTS)
1058272840Smelifaro			ptent->result = IPFW_TR_EXISTS;
1059272840Smelifaro		ipfw_export_table_value_v1(ptei->pvalue, &ptent->v.value);
1060232865Smelifaro	}
1061232865Smelifaro
1062272840Smelifaro	if (tei_buf != &tei)
1063272840Smelifaro		free(tei_buf, M_TEMP);
1064272840Smelifaro
1065272840Smelifaro	return (error);
1066272840Smelifaro}
1067272840Smelifaro
1068272840Smelifaro/*
1069272840Smelifaro * Looks up an entry in given table.
1070272840Smelifaro * Data layout (v0)(current):
1071272840Smelifaro * Request: [ ipfw_obj_header ipfw_obj_tentry ]
1072272840Smelifaro * Reply: [ ipfw_obj_header ipfw_obj_tentry ]
1073272840Smelifaro *
1074272840Smelifaro * Returns 0 on success
1075272840Smelifaro */
1076272840Smelifarostatic int
1077272840Smelifarofind_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1078272840Smelifaro    struct sockopt_data *sd)
1079272840Smelifaro{
1080272840Smelifaro	ipfw_obj_tentry *tent;
1081272840Smelifaro	ipfw_obj_header *oh;
1082272840Smelifaro	struct tid_info ti;
1083272840Smelifaro	struct table_config *tc;
1084272840Smelifaro	struct table_algo *ta;
1085272840Smelifaro	struct table_info *kti;
1086272840Smelifaro	struct namedobj_instance *ni;
1087272840Smelifaro	int error;
1088272840Smelifaro	size_t sz;
1089272840Smelifaro
1090272840Smelifaro	/* Check minimum header size */
1091272840Smelifaro	sz = sizeof(*oh) + sizeof(*tent);
1092272840Smelifaro	if (sd->valsize != sz)
1093272840Smelifaro		return (EINVAL);
1094272840Smelifaro
1095272840Smelifaro	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
1096272840Smelifaro	tent = (ipfw_obj_tentry *)(oh + 1);
1097272840Smelifaro
1098272840Smelifaro	/* Basic length checks for TLVs */
1099272840Smelifaro	if (oh->ntlv.head.length != sizeof(oh->ntlv))
1100272840Smelifaro		return (EINVAL);
1101272840Smelifaro
1102272840Smelifaro	objheader_to_ti(oh, &ti);
1103272840Smelifaro	ti.type = oh->ntlv.type;
1104272840Smelifaro	ti.uidx = tent->idx;
1105272840Smelifaro
1106272840Smelifaro	IPFW_UH_RLOCK(ch);
1107272840Smelifaro	ni = CHAIN_TO_NI(ch);
1108272840Smelifaro
1109272840Smelifaro	/*
1110272840Smelifaro	 * Find existing table and check its type .
1111272840Smelifaro	 */
1112272840Smelifaro	ta = NULL;
1113272840Smelifaro	if ((tc = find_table(ni, &ti)) == NULL) {
1114272840Smelifaro		IPFW_UH_RUNLOCK(ch);
1115200590Sluigi		return (ESRCH);
1116200590Sluigi	}
1117232865Smelifaro
1118272840Smelifaro	/* check table type */
1119272840Smelifaro	if (tc->no.type != ti.type) {
1120272840Smelifaro		IPFW_UH_RUNLOCK(ch);
1121232865Smelifaro		return (EINVAL);
1122232865Smelifaro	}
1123232865Smelifaro
1124272840Smelifaro	kti = KIDX_TO_TI(ch, tc->no.kidx);
1125272840Smelifaro	ta = tc->ta;
1126232865Smelifaro
1127272840Smelifaro	if (ta->find_tentry == NULL)
1128272840Smelifaro		return (ENOTSUP);
1129232865Smelifaro
1130272840Smelifaro	error = ta->find_tentry(tc->astate, kti, tent);
1131272840Smelifaro
1132272840Smelifaro	IPFW_UH_RUNLOCK(ch);
1133272840Smelifaro
1134272840Smelifaro	return (error);
1135200590Sluigi}
1136200590Sluigi
1137272840Smelifaro/*
1138272840Smelifaro * Flushes all entries or destroys given table.
1139272840Smelifaro * Data layout (v0)(current):
1140272840Smelifaro * Request: [ ipfw_obj_header ]
1141272840Smelifaro *
1142272840Smelifaro * Returns 0 on success
1143272840Smelifaro */
1144200590Sluigistatic int
1145272840Smelifaroflush_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1146272840Smelifaro    struct sockopt_data *sd)
1147200590Sluigi{
1148272840Smelifaro	int error;
1149272840Smelifaro	struct _ipfw_obj_header *oh;
1150272840Smelifaro	struct tid_info ti;
1151200590Sluigi
1152272840Smelifaro	if (sd->valsize != sizeof(*oh))
1153272840Smelifaro		return (EINVAL);
1154272840Smelifaro
1155272840Smelifaro	oh = (struct _ipfw_obj_header *)op3;
1156272840Smelifaro	objheader_to_ti(oh, &ti);
1157272840Smelifaro
1158272840Smelifaro	if (op3->opcode == IP_FW_TABLE_XDESTROY)
1159272840Smelifaro		error = destroy_table(ch, &ti);
1160272840Smelifaro	else if (op3->opcode == IP_FW_TABLE_XFLUSH)
1161272840Smelifaro		error = flush_table(ch, &ti);
1162272840Smelifaro	else
1163272840Smelifaro		return (ENOTSUP);
1164272840Smelifaro
1165272840Smelifaro	return (error);
1166200590Sluigi}
1167200590Sluigi
1168272840Smelifarostatic void
1169272840Smelifarorestart_flush(void *object, struct op_state *_state)
1170272840Smelifaro{
1171272840Smelifaro	struct tableop_state *ts;
1172272840Smelifaro
1173272840Smelifaro	ts = (struct tableop_state *)_state;
1174272840Smelifaro
1175272840Smelifaro	if (ts->tc != object)
1176272840Smelifaro		return;
1177272840Smelifaro
1178272840Smelifaro	/* Indicate we've called */
1179272840Smelifaro	ts->modified = 1;
1180272840Smelifaro}
1181272840Smelifaro
1182272840Smelifaro/*
1183272840Smelifaro * Flushes given table.
1184272840Smelifaro *
1185272840Smelifaro * Function create new table instance with the same
1186272840Smelifaro * parameters, swaps it with old one and
1187272840Smelifaro * flushes state without holding runtime WLOCK.
1188272840Smelifaro *
1189272840Smelifaro * Returns 0 on success.
1190272840Smelifaro */
1191200590Sluigiint
1192272840Smelifaroflush_table(struct ip_fw_chain *ch, struct tid_info *ti)
1193200590Sluigi{
1194272840Smelifaro	struct namedobj_instance *ni;
1195272840Smelifaro	struct table_config *tc;
1196272840Smelifaro	struct table_algo *ta;
1197272840Smelifaro	struct table_info ti_old, ti_new, *tablestate;
1198272840Smelifaro	void *astate_old, *astate_new;
1199272840Smelifaro	char algostate[64], *pstate;
1200272840Smelifaro	struct tableop_state ts;
1201278259Smelifaro	int error, need_gc;
1202272840Smelifaro	uint16_t kidx;
1203272840Smelifaro	uint8_t tflags;
1204200590Sluigi
1205272840Smelifaro	/*
1206272840Smelifaro	 * Stage 1: save table algoritm.
1207272840Smelifaro	 * Reference found table to ensure it won't disappear.
1208272840Smelifaro	 */
1209272840Smelifaro	IPFW_UH_WLOCK(ch);
1210272840Smelifaro	ni = CHAIN_TO_NI(ch);
1211272840Smelifaro	if ((tc = find_table(ni, ti)) == NULL) {
1212272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1213272840Smelifaro		return (ESRCH);
1214272840Smelifaro	}
1215278259Smelifaro	need_gc = 0;
1216278259Smelifaro	astate_new = NULL;
1217278259Smelifaro	memset(&ti_new, 0, sizeof(ti_new));
1218272840Smelifarorestart:
1219272840Smelifaro	/* Set up swap handler */
1220272840Smelifaro	memset(&ts, 0, sizeof(ts));
1221272840Smelifaro	ts.opstate.func = restart_flush;
1222272840Smelifaro	ts.tc = tc;
1223200590Sluigi
1224272840Smelifaro	ta = tc->ta;
1225272840Smelifaro	/* Do not flush readonly tables */
1226272840Smelifaro	if ((ta->flags & TA_FLAG_READONLY) != 0) {
1227272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1228272840Smelifaro		return (EACCES);
1229272840Smelifaro	}
1230272840Smelifaro	/* Save startup algo parameters */
1231272840Smelifaro	if (ta->print_config != NULL) {
1232272840Smelifaro		ta->print_config(tc->astate, KIDX_TO_TI(ch, tc->no.kidx),
1233272840Smelifaro		    algostate, sizeof(algostate));
1234272840Smelifaro		pstate = algostate;
1235272840Smelifaro	} else
1236272840Smelifaro		pstate = NULL;
1237272840Smelifaro	tflags = tc->tflags;
1238272840Smelifaro	tc->no.refcnt++;
1239272840Smelifaro	add_toperation_state(ch, &ts);
1240272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1241272840Smelifaro
1242232865Smelifaro	/*
1243278259Smelifaro	 * Stage 1.5: if this is not the first attempt, destroy previous state
1244278259Smelifaro	 */
1245278259Smelifaro	if (need_gc != 0) {
1246278259Smelifaro		ta->destroy(astate_new, &ti_new);
1247278259Smelifaro		need_gc = 0;
1248278259Smelifaro	}
1249278259Smelifaro
1250278259Smelifaro	/*
1251272840Smelifaro	 * Stage 2: allocate new table instance using same algo.
1252232865Smelifaro	 */
1253272840Smelifaro	memset(&ti_new, 0, sizeof(struct table_info));
1254272840Smelifaro	error = ta->init(ch, &astate_new, &ti_new, pstate, tflags);
1255232865Smelifaro
1256272840Smelifaro	/*
1257272840Smelifaro	 * Stage 3: swap old state pointers with newly-allocated ones.
1258272840Smelifaro	 * Decrease refcount.
1259272840Smelifaro	 */
1260272840Smelifaro	IPFW_UH_WLOCK(ch);
1261272840Smelifaro	tc->no.refcnt--;
1262272840Smelifaro	del_toperation_state(ch, &ts);
1263232865Smelifaro
1264272840Smelifaro	if (error != 0) {
1265272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1266272840Smelifaro		return (error);
1267232865Smelifaro	}
1268232865Smelifaro
1269272840Smelifaro	/*
1270272840Smelifaro	 * Restart operation if table swap has happened:
1271272840Smelifaro	 * even if algo may be the same, algo init parameters
1272272840Smelifaro	 * may change. Restart operation instead of doing
1273272840Smelifaro	 * complex checks.
1274272840Smelifaro	 */
1275272840Smelifaro	if (ts.modified != 0) {
1276278259Smelifaro		/* Delay destroying data since we're holding UH lock */
1277278259Smelifaro		need_gc = 1;
1278272840Smelifaro		goto restart;
1279232865Smelifaro	}
1280232865Smelifaro
1281272840Smelifaro	ni = CHAIN_TO_NI(ch);
1282272840Smelifaro	kidx = tc->no.kidx;
1283272840Smelifaro	tablestate = (struct table_info *)ch->tablestate;
1284272840Smelifaro
1285272840Smelifaro	IPFW_WLOCK(ch);
1286272840Smelifaro	ti_old = tablestate[kidx];
1287272840Smelifaro	tablestate[kidx] = ti_new;
1288272840Smelifaro	IPFW_WUNLOCK(ch);
1289272840Smelifaro
1290272840Smelifaro	astate_old = tc->astate;
1291272840Smelifaro	tc->astate = astate_new;
1292272840Smelifaro	tc->ti_copy = ti_new;
1293272840Smelifaro	tc->count = 0;
1294272840Smelifaro
1295272840Smelifaro	/* Notify algo on real @ti address */
1296272840Smelifaro	if (ta->change_ti != NULL)
1297272840Smelifaro		ta->change_ti(tc->astate, &tablestate[kidx]);
1298272840Smelifaro
1299272840Smelifaro	/*
1300272840Smelifaro	 * Stage 4: unref values.
1301272840Smelifaro	 */
1302272840Smelifaro	ipfw_unref_table_values(ch, tc, ta, astate_old, &ti_old);
1303272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1304272840Smelifaro
1305272840Smelifaro	/*
1306272840Smelifaro	 * Stage 5: perform real flush/destroy.
1307272840Smelifaro	 */
1308272840Smelifaro	ta->destroy(astate_old, &ti_old);
1309272840Smelifaro
1310200590Sluigi	return (0);
1311200590Sluigi}
1312200590Sluigi
1313272840Smelifaro/*
1314272840Smelifaro * Swaps two tables.
1315272840Smelifaro * Data layout (v0)(current):
1316272840Smelifaro * Request: [ ipfw_obj_header ipfw_obj_ntlv ]
1317272840Smelifaro *
1318272840Smelifaro * Returns 0 on success
1319272840Smelifaro */
1320272840Smelifarostatic int
1321272840Smelifaroswap_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1322272840Smelifaro    struct sockopt_data *sd)
1323200590Sluigi{
1324272840Smelifaro	int error;
1325272840Smelifaro	struct _ipfw_obj_header *oh;
1326272840Smelifaro	struct tid_info ti_a, ti_b;
1327200590Sluigi
1328272840Smelifaro	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_obj_ntlv))
1329272840Smelifaro		return (EINVAL);
1330200590Sluigi
1331272840Smelifaro	oh = (struct _ipfw_obj_header *)op3;
1332272840Smelifaro	ntlv_to_ti(&oh->ntlv, &ti_a);
1333272840Smelifaro	ntlv_to_ti((ipfw_obj_ntlv *)(oh + 1), &ti_b);
1334272840Smelifaro
1335272840Smelifaro	error = swap_tables(ch, &ti_a, &ti_b);
1336272840Smelifaro
1337272840Smelifaro	return (error);
1338200590Sluigi}
1339200590Sluigi
1340272840Smelifaro/*
1341272840Smelifaro * Swaps two tables of the same type/valtype.
1342272840Smelifaro *
1343272840Smelifaro * Checks if tables are compatible and limits
1344272840Smelifaro * permits swap, than actually perform swap.
1345272840Smelifaro *
1346272840Smelifaro * Each table consists of 2 different parts:
1347272840Smelifaro * config:
1348272840Smelifaro *   @tc (with name, set, kidx) and rule bindings, which is "stable".
1349272840Smelifaro *   number of items
1350272840Smelifaro *   table algo
1351272840Smelifaro * runtime:
1352272840Smelifaro *   runtime data @ti (ch->tablestate)
1353272840Smelifaro *   runtime cache in @tc
1354272840Smelifaro *   algo-specific data (@tc->astate)
1355272840Smelifaro *
1356272840Smelifaro * So we switch:
1357272840Smelifaro *  all runtime data
1358272840Smelifaro *   number of items
1359272840Smelifaro *   table algo
1360272840Smelifaro *
1361272840Smelifaro * After that we call @ti change handler for each table.
1362272840Smelifaro *
1363272840Smelifaro * Note that referencing @tc won't protect tc->ta from change.
1364272840Smelifaro * XXX: Do we need to restrict swap between locked tables?
1365272840Smelifaro * XXX: Do we need to exchange ftype?
1366272840Smelifaro *
1367272840Smelifaro * Returns 0 on success.
1368272840Smelifaro */
1369272840Smelifarostatic int
1370272840Smelifaroswap_tables(struct ip_fw_chain *ch, struct tid_info *a,
1371272840Smelifaro    struct tid_info *b)
1372232865Smelifaro{
1373272840Smelifaro	struct namedobj_instance *ni;
1374272840Smelifaro	struct table_config *tc_a, *tc_b;
1375272840Smelifaro	struct table_algo *ta;
1376272840Smelifaro	struct table_info ti, *tablestate;
1377272840Smelifaro	void *astate;
1378272840Smelifaro	uint32_t count;
1379272840Smelifaro
1380272840Smelifaro	/*
1381272840Smelifaro	 * Stage 1: find both tables and ensure they are of
1382272840Smelifaro	 * the same type.
1383272840Smelifaro	 */
1384272840Smelifaro	IPFW_UH_WLOCK(ch);
1385272840Smelifaro	ni = CHAIN_TO_NI(ch);
1386272840Smelifaro	if ((tc_a = find_table(ni, a)) == NULL) {
1387272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1388272840Smelifaro		return (ESRCH);
1389272840Smelifaro	}
1390272840Smelifaro	if ((tc_b = find_table(ni, b)) == NULL) {
1391272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1392272840Smelifaro		return (ESRCH);
1393272840Smelifaro	}
1394272840Smelifaro
1395272840Smelifaro	/* It is very easy to swap between the same table */
1396272840Smelifaro	if (tc_a == tc_b) {
1397272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1398272840Smelifaro		return (0);
1399272840Smelifaro	}
1400272840Smelifaro
1401272840Smelifaro	/* Check type and value are the same */
1402272840Smelifaro	if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags) {
1403272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1404272840Smelifaro		return (EINVAL);
1405272840Smelifaro	}
1406272840Smelifaro
1407272840Smelifaro	/* Check limits before swap */
1408272840Smelifaro	if ((tc_a->limit != 0 && tc_b->count > tc_a->limit) ||
1409272840Smelifaro	    (tc_b->limit != 0 && tc_a->count > tc_b->limit)) {
1410272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1411272840Smelifaro		return (EFBIG);
1412272840Smelifaro	}
1413272840Smelifaro
1414272840Smelifaro	/* Check if one of the tables is readonly */
1415272840Smelifaro	if (((tc_a->ta->flags | tc_b->ta->flags) & TA_FLAG_READONLY) != 0) {
1416272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1417272840Smelifaro		return (EACCES);
1418272840Smelifaro	}
1419272840Smelifaro
1420272840Smelifaro	/* Notify we're going to swap */
1421272840Smelifaro	rollback_toperation_state(ch, tc_a);
1422272840Smelifaro	rollback_toperation_state(ch, tc_b);
1423272840Smelifaro
1424272840Smelifaro	/* Everything is fine, prepare to swap */
1425272840Smelifaro	tablestate = (struct table_info *)ch->tablestate;
1426272840Smelifaro	ti = tablestate[tc_a->no.kidx];
1427272840Smelifaro	ta = tc_a->ta;
1428272840Smelifaro	astate = tc_a->astate;
1429272840Smelifaro	count = tc_a->count;
1430272840Smelifaro
1431272840Smelifaro	IPFW_WLOCK(ch);
1432272840Smelifaro	/* a <- b */
1433272840Smelifaro	tablestate[tc_a->no.kidx] = tablestate[tc_b->no.kidx];
1434272840Smelifaro	tc_a->ta = tc_b->ta;
1435272840Smelifaro	tc_a->astate = tc_b->astate;
1436272840Smelifaro	tc_a->count = tc_b->count;
1437272840Smelifaro	/* b <- a */
1438272840Smelifaro	tablestate[tc_b->no.kidx] = ti;
1439272840Smelifaro	tc_b->ta = ta;
1440272840Smelifaro	tc_b->astate = astate;
1441272840Smelifaro	tc_b->count = count;
1442272840Smelifaro	IPFW_WUNLOCK(ch);
1443272840Smelifaro
1444272840Smelifaro	/* Ensure tc.ti copies are in sync */
1445272840Smelifaro	tc_a->ti_copy = tablestate[tc_a->no.kidx];
1446272840Smelifaro	tc_b->ti_copy = tablestate[tc_b->no.kidx];
1447272840Smelifaro
1448272840Smelifaro	/* Notify both tables on @ti change */
1449272840Smelifaro	if (tc_a->ta->change_ti != NULL)
1450272840Smelifaro		tc_a->ta->change_ti(tc_a->astate, &tablestate[tc_a->no.kidx]);
1451272840Smelifaro	if (tc_b->ta->change_ti != NULL)
1452272840Smelifaro		tc_b->ta->change_ti(tc_b->astate, &tablestate[tc_b->no.kidx]);
1453272840Smelifaro
1454272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1455272840Smelifaro
1456200590Sluigi	return (0);
1457200590Sluigi}
1458200590Sluigi
1459272840Smelifaro/*
1460272840Smelifaro * Destroys table specified by @ti.
1461272840Smelifaro * Data layout (v0)(current):
1462272840Smelifaro * Request: [ ip_fw3_opheader ]
1463272840Smelifaro *
1464272840Smelifaro * Returns 0 on success
1465272840Smelifaro */
1466272840Smelifarostatic int
1467272840Smelifarodestroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
1468272840Smelifaro{
1469272840Smelifaro	struct namedobj_instance *ni;
1470272840Smelifaro	struct table_config *tc;
1471272840Smelifaro
1472272840Smelifaro	IPFW_UH_WLOCK(ch);
1473272840Smelifaro
1474272840Smelifaro	ni = CHAIN_TO_NI(ch);
1475272840Smelifaro	if ((tc = find_table(ni, ti)) == NULL) {
1476272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1477272840Smelifaro		return (ESRCH);
1478272840Smelifaro	}
1479272840Smelifaro
1480272840Smelifaro	/* Do not permit destroying referenced tables */
1481272840Smelifaro	if (tc->no.refcnt > 0) {
1482272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1483272840Smelifaro		return (EBUSY);
1484272840Smelifaro	}
1485272840Smelifaro
1486272840Smelifaro	IPFW_WLOCK(ch);
1487272840Smelifaro	unlink_table(ch, tc);
1488272840Smelifaro	IPFW_WUNLOCK(ch);
1489272840Smelifaro
1490272840Smelifaro	/* Free obj index */
1491272840Smelifaro	if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0)
1492272840Smelifaro		printf("Error unlinking kidx %d from table %s\n",
1493272840Smelifaro		    tc->no.kidx, tc->tablename);
1494272840Smelifaro
1495272840Smelifaro	/* Unref values used in tables while holding UH lock */
1496272840Smelifaro	ipfw_unref_table_values(ch, tc, tc->ta, tc->astate, &tc->ti_copy);
1497272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1498272840Smelifaro
1499272840Smelifaro	free_table_config(ni, tc);
1500272840Smelifaro
1501272840Smelifaro	return (0);
1502272840Smelifaro}
1503272840Smelifaro
1504273274Smelifarostatic uint32_t
1505273274Smelifaroroundup2p(uint32_t v)
1506273274Smelifaro{
1507273274Smelifaro
1508273274Smelifaro	v--;
1509273274Smelifaro	v |= v >> 1;
1510273274Smelifaro	v |= v >> 2;
1511273274Smelifaro	v |= v >> 4;
1512273274Smelifaro	v |= v >> 8;
1513273274Smelifaro	v |= v >> 16;
1514273274Smelifaro	v++;
1515273274Smelifaro
1516273274Smelifaro	return (v);
1517273274Smelifaro}
1518273274Smelifaro
1519272840Smelifaro/*
1520272840Smelifaro * Grow tables index.
1521272840Smelifaro *
1522272840Smelifaro * Returns 0 on success.
1523272840Smelifaro */
1524200590Sluigiint
1525233478Smelifaroipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
1526233478Smelifaro{
1527233478Smelifaro	unsigned int ntables_old, tbl;
1528272840Smelifaro	struct namedobj_instance *ni;
1529272840Smelifaro	void *new_idx, *old_tablestate, *tablestate;
1530272840Smelifaro	struct table_info *ti;
1531272840Smelifaro	struct table_config *tc;
1532272840Smelifaro	int i, new_blocks;
1533233478Smelifaro
1534233478Smelifaro	/* Check new value for validity */
1535273274Smelifaro	if (ntables == 0)
1536273274Smelifaro		return (EINVAL);
1537233478Smelifaro	if (ntables > IPFW_TABLES_MAX)
1538233478Smelifaro		ntables = IPFW_TABLES_MAX;
1539273274Smelifaro	/* Alight to nearest power of 2 */
1540273274Smelifaro	ntables = (unsigned int)roundup2p(ntables);
1541233478Smelifaro
1542233478Smelifaro	/* Allocate new pointers */
1543272840Smelifaro	tablestate = malloc(ntables * sizeof(struct table_info),
1544272840Smelifaro	    M_IPFW, M_WAITOK | M_ZERO);
1545233478Smelifaro
1546272840Smelifaro	ipfw_objhash_bitmap_alloc(ntables, (void *)&new_idx, &new_blocks);
1547233478Smelifaro
1548272840Smelifaro	IPFW_UH_WLOCK(ch);
1549272840Smelifaro
1550233478Smelifaro	tbl = (ntables >= V_fw_tables_max) ? V_fw_tables_max : ntables;
1551272840Smelifaro	ni = CHAIN_TO_NI(ch);
1552233478Smelifaro
1553272840Smelifaro	/* Temporary restrict decreasing max_tables */
1554272840Smelifaro	if (ntables < V_fw_tables_max) {
1555233478Smelifaro
1556272840Smelifaro		/*
1557272840Smelifaro		 * FIXME: Check if we really can shrink
1558272840Smelifaro		 */
1559272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1560272840Smelifaro		return (EINVAL);
1561272840Smelifaro	}
1562233478Smelifaro
1563272840Smelifaro	/* Copy table info/indices */
1564272840Smelifaro	memcpy(tablestate, ch->tablestate, sizeof(struct table_info) * tbl);
1565272840Smelifaro	ipfw_objhash_bitmap_merge(ni, &new_idx, &new_blocks);
1566272840Smelifaro
1567272840Smelifaro	IPFW_WLOCK(ch);
1568272840Smelifaro
1569272840Smelifaro	/* Change pointers */
1570272840Smelifaro	old_tablestate = ch->tablestate;
1571272840Smelifaro	ch->tablestate = tablestate;
1572272840Smelifaro	ipfw_objhash_bitmap_swap(ni, &new_idx, &new_blocks);
1573272840Smelifaro
1574233478Smelifaro	ntables_old = V_fw_tables_max;
1575233478Smelifaro	V_fw_tables_max = ntables;
1576233478Smelifaro
1577233478Smelifaro	IPFW_WUNLOCK(ch);
1578233478Smelifaro
1579272840Smelifaro	/* Notify all consumers that their @ti pointer has changed */
1580272840Smelifaro	ti = (struct table_info *)ch->tablestate;
1581272840Smelifaro	for (i = 0; i < tbl; i++, ti++) {
1582272840Smelifaro		if (ti->lookup == NULL)
1583272840Smelifaro			continue;
1584272840Smelifaro		tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i);
1585272840Smelifaro		if (tc == NULL || tc->ta->change_ti == NULL)
1586272840Smelifaro			continue;
1587272840Smelifaro
1588272840Smelifaro		tc->ta->change_ti(tc->astate, ti);
1589272840Smelifaro	}
1590272840Smelifaro
1591272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1592272840Smelifaro
1593272840Smelifaro	/* Free old pointers */
1594272840Smelifaro	free(old_tablestate, M_IPFW);
1595272840Smelifaro	ipfw_objhash_bitmap_free(new_idx, new_blocks);
1596272840Smelifaro
1597272840Smelifaro	return (0);
1598272840Smelifaro}
1599272840Smelifaro
1600272840Smelifaro/*
1601272840Smelifaro * Switch between "set 0" and "rule's set" table binding,
1602272840Smelifaro * Check all ruleset bindings and permits changing
1603272840Smelifaro * IFF each binding has both rule AND table in default set (set 0).
1604272840Smelifaro *
1605272840Smelifaro * Returns 0 on success.
1606272840Smelifaro */
1607272840Smelifaroint
1608272840Smelifaroipfw_switch_tables_namespace(struct ip_fw_chain *ch, unsigned int sets)
1609272840Smelifaro{
1610272840Smelifaro	struct namedobj_instance *ni;
1611272840Smelifaro	struct named_object *no;
1612272840Smelifaro	struct ip_fw *rule;
1613272840Smelifaro	ipfw_insn *cmd;
1614272840Smelifaro	int cmdlen, i, l;
1615272840Smelifaro	uint16_t kidx;
1616272840Smelifaro	uint8_t type;
1617272840Smelifaro
1618272840Smelifaro	IPFW_UH_WLOCK(ch);
1619272840Smelifaro
1620272840Smelifaro	if (V_fw_tables_sets == sets) {
1621272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1622272840Smelifaro		return (0);
1623272840Smelifaro	}
1624272840Smelifaro
1625272840Smelifaro	ni = CHAIN_TO_NI(ch);
1626272840Smelifaro
1627272840Smelifaro	/*
1628272840Smelifaro	 * Scan all rules and examine tables opcodes.
1629272840Smelifaro	 */
1630272840Smelifaro	for (i = 0; i < ch->n_rules; i++) {
1631272840Smelifaro		rule = ch->map[i];
1632272840Smelifaro
1633272840Smelifaro		l = rule->cmd_len;
1634272840Smelifaro		cmd = rule->cmd;
1635272840Smelifaro		cmdlen = 0;
1636272840Smelifaro		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
1637272840Smelifaro			cmdlen = F_LEN(cmd);
1638272840Smelifaro
1639272840Smelifaro			if (classify_table_opcode(cmd, &kidx, &type) != 0)
1640272840Smelifaro				continue;
1641272840Smelifaro
1642272840Smelifaro			no = ipfw_objhash_lookup_kidx(ni, kidx);
1643272840Smelifaro
1644272840Smelifaro			/* Check if both table object and rule has the set 0 */
1645272840Smelifaro			if (no->set != 0 || rule->set != 0) {
1646272840Smelifaro				IPFW_UH_WUNLOCK(ch);
1647272840Smelifaro				return (EBUSY);
1648233478Smelifaro			}
1649233478Smelifaro
1650233478Smelifaro		}
1651233478Smelifaro	}
1652272840Smelifaro	V_fw_tables_sets = sets;
1653233478Smelifaro
1654272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1655233478Smelifaro
1656233478Smelifaro	return (0);
1657233478Smelifaro}
1658233478Smelifaro
1659272840Smelifaro/*
1660272840Smelifaro * Lookup an IP @addr in table @tbl.
1661272840Smelifaro * Stores found value in @val.
1662272840Smelifaro *
1663272840Smelifaro * Returns 1 if @addr was found.
1664272840Smelifaro */
1665233478Smelifaroint
1666200590Sluigiipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
1667200590Sluigi    uint32_t *val)
1668200590Sluigi{
1669272840Smelifaro	struct table_info *ti;
1670200590Sluigi
1671272840Smelifaro	ti = KIDX_TO_TI(ch, tbl);
1672272840Smelifaro
1673272840Smelifaro	return (ti->lookup(ti, &addr, sizeof(in_addr_t), val));
1674272840Smelifaro}
1675272840Smelifaro
1676272840Smelifaro/*
1677272840Smelifaro * Lookup an arbtrary key @paddr of legth @plen in table @tbl.
1678272840Smelifaro * Stores found value in @val.
1679272840Smelifaro *
1680272840Smelifaro * Returns 1 if key was found.
1681272840Smelifaro */
1682272840Smelifaroint
1683272840Smelifaroipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
1684272840Smelifaro    void *paddr, uint32_t *val)
1685272840Smelifaro{
1686272840Smelifaro	struct table_info *ti;
1687272840Smelifaro
1688272840Smelifaro	ti = KIDX_TO_TI(ch, tbl);
1689272840Smelifaro
1690272840Smelifaro	return (ti->lookup(ti, paddr, plen, val));
1691272840Smelifaro}
1692272840Smelifaro
1693272840Smelifaro/*
1694272840Smelifaro * Info/List/dump support for tables.
1695272840Smelifaro *
1696272840Smelifaro */
1697272840Smelifaro
1698272840Smelifaro/*
1699272840Smelifaro * High-level 'get' cmds sysctl handlers
1700272840Smelifaro */
1701272840Smelifaro
1702272840Smelifaro/*
1703272840Smelifaro * Lists all tables currently available in kernel.
1704272840Smelifaro * Data layout (v0)(current):
1705272840Smelifaro * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
1706272840Smelifaro * Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
1707272840Smelifaro *
1708272840Smelifaro * Returns 0 on success
1709272840Smelifaro */
1710272840Smelifarostatic int
1711272840Smelifarolist_tables(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1712272840Smelifaro    struct sockopt_data *sd)
1713272840Smelifaro{
1714272840Smelifaro	struct _ipfw_obj_lheader *olh;
1715272840Smelifaro	int error;
1716272840Smelifaro
1717272840Smelifaro	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
1718272840Smelifaro	if (olh == NULL)
1719272840Smelifaro		return (EINVAL);
1720272840Smelifaro	if (sd->valsize < olh->size)
1721272840Smelifaro		return (EINVAL);
1722272840Smelifaro
1723272840Smelifaro	IPFW_UH_RLOCK(ch);
1724272840Smelifaro	error = export_tables(ch, olh, sd);
1725272840Smelifaro	IPFW_UH_RUNLOCK(ch);
1726272840Smelifaro
1727272840Smelifaro	return (error);
1728272840Smelifaro}
1729272840Smelifaro
1730272840Smelifaro/*
1731272840Smelifaro * Store table info to buffer provided by @sd.
1732272840Smelifaro * Data layout (v0)(current):
1733272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
1734272840Smelifaro * Reply: [ ipfw_obj_header ipfw_xtable_info ]
1735272840Smelifaro *
1736272840Smelifaro * Returns 0 on success.
1737272840Smelifaro */
1738272840Smelifarostatic int
1739272840Smelifarodescribe_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1740272840Smelifaro    struct sockopt_data *sd)
1741272840Smelifaro{
1742272840Smelifaro	struct _ipfw_obj_header *oh;
1743272840Smelifaro	struct table_config *tc;
1744272840Smelifaro	struct tid_info ti;
1745272840Smelifaro	size_t sz;
1746272840Smelifaro
1747272840Smelifaro	sz = sizeof(*oh) + sizeof(ipfw_xtable_info);
1748272840Smelifaro	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
1749272840Smelifaro	if (oh == NULL)
1750272840Smelifaro		return (EINVAL);
1751272840Smelifaro
1752272840Smelifaro	objheader_to_ti(oh, &ti);
1753272840Smelifaro
1754272840Smelifaro	IPFW_UH_RLOCK(ch);
1755272840Smelifaro	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
1756272840Smelifaro		IPFW_UH_RUNLOCK(ch);
1757272840Smelifaro		return (ESRCH);
1758200590Sluigi	}
1759272840Smelifaro
1760272840Smelifaro	export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1));
1761272840Smelifaro	IPFW_UH_RUNLOCK(ch);
1762272840Smelifaro
1763200590Sluigi	return (0);
1764200590Sluigi}
1765200590Sluigi
1766272840Smelifaro/*
1767272840Smelifaro * Modifies existing table.
1768272840Smelifaro * Data layout (v0)(current):
1769272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info ]
1770272840Smelifaro *
1771272840Smelifaro * Returns 0 on success
1772272840Smelifaro */
1773272840Smelifarostatic int
1774272840Smelifaromodify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1775272840Smelifaro    struct sockopt_data *sd)
1776232865Smelifaro{
1777272840Smelifaro	struct _ipfw_obj_header *oh;
1778272840Smelifaro	ipfw_xtable_info *i;
1779272840Smelifaro	char *tname;
1780272840Smelifaro	struct tid_info ti;
1781272840Smelifaro	struct namedobj_instance *ni;
1782272840Smelifaro	struct table_config *tc;
1783232865Smelifaro
1784272840Smelifaro	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
1785272840Smelifaro		return (EINVAL);
1786232865Smelifaro
1787272840Smelifaro	oh = (struct _ipfw_obj_header *)sd->kbuf;
1788272840Smelifaro	i = (ipfw_xtable_info *)(oh + 1);
1789232865Smelifaro
1790272840Smelifaro	/*
1791272840Smelifaro	 * Verify user-supplied strings.
1792272840Smelifaro	 * Check for null-terminated/zero-length strings/
1793272840Smelifaro	 */
1794272840Smelifaro	tname = oh->ntlv.name;
1795272840Smelifaro	if (ipfw_check_table_name(tname) != 0)
1796272840Smelifaro		return (EINVAL);
1797232865Smelifaro
1798272840Smelifaro	objheader_to_ti(oh, &ti);
1799272840Smelifaro	ti.type = i->type;
1800272840Smelifaro
1801272840Smelifaro	IPFW_UH_WLOCK(ch);
1802272840Smelifaro	ni = CHAIN_TO_NI(ch);
1803272840Smelifaro	if ((tc = find_table(ni, &ti)) == NULL) {
1804272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1805272840Smelifaro		return (ESRCH);
1806232865Smelifaro	}
1807232865Smelifaro
1808272840Smelifaro	/* Do not support any modifications for readonly tables */
1809272840Smelifaro	if ((tc->ta->flags & TA_FLAG_READONLY) != 0) {
1810272840Smelifaro		IPFW_UH_WUNLOCK(ch);
1811272840Smelifaro		return (EACCES);
1812232865Smelifaro	}
1813272840Smelifaro
1814272840Smelifaro	if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0)
1815272840Smelifaro		tc->limit = i->limit;
1816272840Smelifaro	if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0)
1817272840Smelifaro		tc->locked = ((i->flags & IPFW_TGFLAGS_LOCKED) != 0);
1818272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1819272840Smelifaro
1820232865Smelifaro	return (0);
1821232865Smelifaro}
1822232865Smelifaro
1823272840Smelifaro/*
1824272840Smelifaro * Creates new table.
1825272840Smelifaro * Data layout (v0)(current):
1826272840Smelifaro * Request: [ ipfw_obj_header ipfw_xtable_info ]
1827272840Smelifaro *
1828272840Smelifaro * Returns 0 on success
1829272840Smelifaro */
1830200590Sluigistatic int
1831272840Smelifarocreate_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
1832272840Smelifaro    struct sockopt_data *sd)
1833200590Sluigi{
1834272840Smelifaro	struct _ipfw_obj_header *oh;
1835272840Smelifaro	ipfw_xtable_info *i;
1836272840Smelifaro	char *tname, *aname;
1837272840Smelifaro	struct tid_info ti;
1838272840Smelifaro	struct namedobj_instance *ni;
1839200590Sluigi
1840272840Smelifaro	if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
1841272840Smelifaro		return (EINVAL);
1842272840Smelifaro
1843272840Smelifaro	oh = (struct _ipfw_obj_header *)sd->kbuf;
1844272840Smelifaro	i = (ipfw_xtable_info *)(oh + 1);
1845272840Smelifaro
1846272840Smelifaro	/*
1847272840Smelifaro	 * Verify user-supplied strings.
1848272840Smelifaro	 * Check for null-terminated/zero-length strings/
1849272840Smelifaro	 */
1850272840Smelifaro	tname = oh->ntlv.name;
1851272840Smelifaro	aname = i->algoname;
1852272840Smelifaro	if (ipfw_check_table_name(tname) != 0 ||
1853272840Smelifaro	    strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
1854272840Smelifaro		return (EINVAL);
1855272840Smelifaro
1856272840Smelifaro	if (aname[0] == '\0') {
1857272840Smelifaro		/* Use default algorithm */
1858272840Smelifaro		aname = NULL;
1859272840Smelifaro	}
1860272840Smelifaro
1861272840Smelifaro	objheader_to_ti(oh, &ti);
1862272840Smelifaro	ti.type = i->type;
1863272840Smelifaro
1864272840Smelifaro	ni = CHAIN_TO_NI(ch);
1865272840Smelifaro
1866272840Smelifaro	IPFW_UH_RLOCK(ch);
1867274087Smelifaro	if (find_table(ni, &ti) != NULL) {
1868272840Smelifaro		IPFW_UH_RUNLOCK(ch);
1869272840Smelifaro		return (EEXIST);
1870272840Smelifaro	}
1871272840Smelifaro	IPFW_UH_RUNLOCK(ch);
1872272840Smelifaro
1873272840Smelifaro	return (create_table_internal(ch, &ti, aname, i, NULL, 0));
1874272840Smelifaro}
1875272840Smelifaro
1876272840Smelifaro/*
1877272840Smelifaro * Creates new table based on @ti and @aname.
1878272840Smelifaro *
1879272840Smelifaro * Relies on table name checking inside find_name_tlv()
1880272840Smelifaro * Assume @aname to be checked and valid.
1881272840Smelifaro * Stores allocated table kidx inside @pkidx (if non-NULL).
1882272840Smelifaro * Reference created table if @compat is non-zero.
1883272840Smelifaro *
1884272840Smelifaro * Returns 0 on success.
1885272840Smelifaro */
1886272840Smelifarostatic int
1887272840Smelifarocreate_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
1888272840Smelifaro    char *aname, ipfw_xtable_info *i, uint16_t *pkidx, int compat)
1889272840Smelifaro{
1890272840Smelifaro	struct namedobj_instance *ni;
1891272840Smelifaro	struct table_config *tc, *tc_new, *tmp;
1892272840Smelifaro	struct table_algo *ta;
1893272840Smelifaro	uint16_t kidx;
1894272840Smelifaro
1895272840Smelifaro	ni = CHAIN_TO_NI(ch);
1896272840Smelifaro
1897272840Smelifaro	ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname);
1898272840Smelifaro	if (ta == NULL)
1899272840Smelifaro		return (ENOTSUP);
1900272840Smelifaro
1901272840Smelifaro	tc = alloc_table_config(ch, ti, ta, aname, i->tflags);
1902272840Smelifaro	if (tc == NULL)
1903272840Smelifaro		return (ENOMEM);
1904272840Smelifaro
1905272840Smelifaro	tc->vmask = i->vmask;
1906272840Smelifaro	tc->limit = i->limit;
1907272840Smelifaro	if (ta->flags & TA_FLAG_READONLY)
1908272840Smelifaro		tc->locked = 1;
1909272840Smelifaro	else
1910272840Smelifaro		tc->locked = (i->flags & IPFW_TGFLAGS_LOCKED) != 0;
1911272840Smelifaro
1912272840Smelifaro	IPFW_UH_WLOCK(ch);
1913272840Smelifaro
1914272840Smelifaro	/* Check if table has been already created */
1915272840Smelifaro	tc_new = find_table(ni, ti);
1916272840Smelifaro	if (tc_new != NULL) {
1917272840Smelifaro
1918272840Smelifaro		/*
1919272840Smelifaro		 * Compat: do not fail if we're
1920272840Smelifaro		 * requesting to create existing table
1921272840Smelifaro		 * which has the same type
1922272840Smelifaro		 */
1923272840Smelifaro		if (compat == 0 || tc_new->no.type != tc->no.type) {
1924272840Smelifaro			IPFW_UH_WUNLOCK(ch);
1925272840Smelifaro			free_table_config(ni, tc);
1926272840Smelifaro			return (EEXIST);
1927272840Smelifaro		}
1928272840Smelifaro
1929272840Smelifaro		/* Exchange tc and tc_new for proper refcounting & freeing */
1930272840Smelifaro		tmp = tc;
1931272840Smelifaro		tc = tc_new;
1932272840Smelifaro		tc_new = tmp;
1933272840Smelifaro	} else {
1934272840Smelifaro		/* New table */
1935272840Smelifaro		if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
1936272840Smelifaro			IPFW_UH_WUNLOCK(ch);
1937272840Smelifaro			printf("Unable to allocate table index."
1938272840Smelifaro			    " Consider increasing net.inet.ip.fw.tables_max");
1939272840Smelifaro			free_table_config(ni, tc);
1940272840Smelifaro			return (EBUSY);
1941272840Smelifaro		}
1942272840Smelifaro		tc->no.kidx = kidx;
1943272840Smelifaro
1944272840Smelifaro		IPFW_WLOCK(ch);
1945272840Smelifaro		link_table(ch, tc);
1946272840Smelifaro		IPFW_WUNLOCK(ch);
1947272840Smelifaro	}
1948272840Smelifaro
1949272840Smelifaro	if (compat != 0)
1950272840Smelifaro		tc->no.refcnt++;
1951272840Smelifaro	if (pkidx != NULL)
1952272840Smelifaro		*pkidx = tc->no.kidx;
1953272840Smelifaro
1954272840Smelifaro	IPFW_UH_WUNLOCK(ch);
1955272840Smelifaro
1956272840Smelifaro	if (tc_new != NULL)
1957272840Smelifaro		free_table_config(ni, tc_new);
1958272840Smelifaro
1959200590Sluigi	return (0);
1960200590Sluigi}
1961200590Sluigi
1962272840Smelifarostatic void
1963272840Smelifarontlv_to_ti(ipfw_obj_ntlv *ntlv, struct tid_info *ti)
1964272840Smelifaro{
1965272840Smelifaro
1966272840Smelifaro	memset(ti, 0, sizeof(struct tid_info));
1967272840Smelifaro	ti->set = ntlv->set;
1968272840Smelifaro	ti->uidx = ntlv->idx;
1969272840Smelifaro	ti->tlvs = ntlv;
1970272840Smelifaro	ti->tlen = ntlv->head.length;
1971272840Smelifaro}
1972272840Smelifaro
1973272840Smelifarostatic void
1974272840Smelifaroobjheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti)
1975272840Smelifaro{
1976272840Smelifaro
1977272840Smelifaro	ntlv_to_ti(&oh->ntlv, ti);
1978272840Smelifaro}
1979272840Smelifaro
1980272840Smelifaro/*
1981272840Smelifaro * Exports basic table info as name TLV.
1982272840Smelifaro * Used inside dump_static_rules() to provide info
1983272840Smelifaro * about all tables referenced by current ruleset.
1984272840Smelifaro *
1985272840Smelifaro * Returns 0 on success.
1986272840Smelifaro */
1987200590Sluigiint
1988272840Smelifaroipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
1989272840Smelifaro    struct sockopt_data *sd)
1990200590Sluigi{
1991272840Smelifaro	struct namedobj_instance *ni;
1992272840Smelifaro	struct named_object *no;
1993272840Smelifaro	ipfw_obj_ntlv *ntlv;
1994200590Sluigi
1995272840Smelifaro	ni = CHAIN_TO_NI(ch);
1996272840Smelifaro
1997272840Smelifaro	no = ipfw_objhash_lookup_kidx(ni, kidx);
1998272840Smelifaro	KASSERT(no != NULL, ("invalid table kidx passed"));
1999272840Smelifaro
2000272840Smelifaro	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
2001272840Smelifaro	if (ntlv == NULL)
2002272840Smelifaro		return (ENOMEM);
2003272840Smelifaro
2004272840Smelifaro	ntlv->head.type = IPFW_TLV_TBL_NAME;
2005272840Smelifaro	ntlv->head.length = sizeof(*ntlv);
2006272840Smelifaro	ntlv->idx = no->kidx;
2007272840Smelifaro	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
2008272840Smelifaro
2009272840Smelifaro	return (0);
2010272840Smelifaro}
2011272840Smelifaro
2012272840Smelifaro/*
2013272840Smelifaro * Marks every table kidx used in @rule with bit in @bmask.
2014272840Smelifaro * Used to generate bitmask of referenced tables for given ruleset.
2015272840Smelifaro *
2016272840Smelifaro * Returns number of newly-referenced tables.
2017272840Smelifaro */
2018272840Smelifaroint
2019272840Smelifaroipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
2020272840Smelifaro    uint32_t *bmask)
2021272840Smelifaro{
2022272840Smelifaro	int cmdlen, l, count;
2023272840Smelifaro	ipfw_insn *cmd;
2024272840Smelifaro	uint16_t kidx;
2025272840Smelifaro	uint8_t type;
2026272840Smelifaro
2027272840Smelifaro	l = rule->cmd_len;
2028272840Smelifaro	cmd = rule->cmd;
2029272840Smelifaro	cmdlen = 0;
2030272840Smelifaro	count = 0;
2031272840Smelifaro	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2032272840Smelifaro		cmdlen = F_LEN(cmd);
2033272840Smelifaro
2034272840Smelifaro		if (classify_table_opcode(cmd, &kidx, &type) != 0)
2035272840Smelifaro			continue;
2036272840Smelifaro
2037272840Smelifaro		if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0)
2038272840Smelifaro			count++;
2039272840Smelifaro
2040272840Smelifaro		bmask[kidx / 32] |= 1 << (kidx % 32);
2041272840Smelifaro	}
2042272840Smelifaro
2043272840Smelifaro	return (count);
2044272840Smelifaro}
2045272840Smelifaro
2046272840Smelifarostruct dump_args {
2047272840Smelifaro	struct ip_fw_chain *ch;
2048272840Smelifaro	struct table_info *ti;
2049272840Smelifaro	struct table_config *tc;
2050272840Smelifaro	struct sockopt_data *sd;
2051272840Smelifaro	uint32_t cnt;
2052272840Smelifaro	uint16_t uidx;
2053272840Smelifaro	int error;
2054272840Smelifaro	uint32_t size;
2055272840Smelifaro	ipfw_table_entry *ent;
2056272840Smelifaro	ta_foreach_f *f;
2057272840Smelifaro	void *farg;
2058272840Smelifaro	ipfw_obj_tentry tent;
2059272840Smelifaro};
2060272840Smelifaro
2061272840Smelifarostatic int
2062272840Smelifarocount_ext_entries(void *e, void *arg)
2063272840Smelifaro{
2064272840Smelifaro	struct dump_args *da;
2065272840Smelifaro
2066272840Smelifaro	da = (struct dump_args *)arg;
2067272840Smelifaro	da->cnt++;
2068272840Smelifaro
2069272840Smelifaro	return (0);
2070272840Smelifaro}
2071272840Smelifaro
2072272840Smelifaro/*
2073272840Smelifaro * Gets number of items from table either using
2074272840Smelifaro * internal counter or calling algo callback for
2075272840Smelifaro * externally-managed tables.
2076272840Smelifaro *
2077272840Smelifaro * Returns number of records.
2078272840Smelifaro */
2079272840Smelifarostatic uint32_t
2080272840Smelifarotable_get_count(struct ip_fw_chain *ch, struct table_config *tc)
2081272840Smelifaro{
2082272840Smelifaro	struct table_info *ti;
2083272840Smelifaro	struct table_algo *ta;
2084272840Smelifaro	struct dump_args da;
2085272840Smelifaro
2086272840Smelifaro	ti = KIDX_TO_TI(ch, tc->no.kidx);
2087272840Smelifaro	ta = tc->ta;
2088272840Smelifaro
2089272840Smelifaro	/* Use internal counter for self-managed tables */
2090272840Smelifaro	if ((ta->flags & TA_FLAG_READONLY) == 0)
2091272840Smelifaro		return (tc->count);
2092272840Smelifaro
2093272840Smelifaro	/* Use callback to quickly get number of items */
2094272840Smelifaro	if ((ta->flags & TA_FLAG_EXTCOUNTER) != 0)
2095272840Smelifaro		return (ta->get_count(tc->astate, ti));
2096272840Smelifaro
2097272840Smelifaro	/* Count number of iterms ourselves */
2098272840Smelifaro	memset(&da, 0, sizeof(da));
2099272840Smelifaro	ta->foreach(tc->astate, ti, count_ext_entries, &da);
2100272840Smelifaro
2101272840Smelifaro	return (da.cnt);
2102272840Smelifaro}
2103272840Smelifaro
2104272840Smelifaro/*
2105272840Smelifaro * Exports table @tc info into standard ipfw_xtable_info format.
2106272840Smelifaro */
2107272840Smelifarostatic void
2108272840Smelifaroexport_table_info(struct ip_fw_chain *ch, struct table_config *tc,
2109272840Smelifaro    ipfw_xtable_info *i)
2110272840Smelifaro{
2111272840Smelifaro	struct table_info *ti;
2112272840Smelifaro	struct table_algo *ta;
2113272840Smelifaro
2114272840Smelifaro	i->type = tc->no.type;
2115272840Smelifaro	i->tflags = tc->tflags;
2116272840Smelifaro	i->vmask = tc->vmask;
2117272840Smelifaro	i->set = tc->no.set;
2118272840Smelifaro	i->kidx = tc->no.kidx;
2119272840Smelifaro	i->refcnt = tc->no.refcnt;
2120272840Smelifaro	i->count = table_get_count(ch, tc);
2121272840Smelifaro	i->limit = tc->limit;
2122272840Smelifaro	i->flags |= (tc->locked != 0) ? IPFW_TGFLAGS_LOCKED : 0;
2123272840Smelifaro	i->size = tc->count * sizeof(ipfw_obj_tentry);
2124272840Smelifaro	i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
2125272840Smelifaro	strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
2126272840Smelifaro	ti = KIDX_TO_TI(ch, tc->no.kidx);
2127272840Smelifaro	ta = tc->ta;
2128272840Smelifaro	if (ta->print_config != NULL) {
2129272840Smelifaro		/* Use algo function to print table config to string */
2130272840Smelifaro		ta->print_config(tc->astate, ti, i->algoname,
2131272840Smelifaro		    sizeof(i->algoname));
2132272840Smelifaro	} else
2133272840Smelifaro		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
2134272840Smelifaro	/* Dump algo-specific data, if possible */
2135272840Smelifaro	if (ta->dump_tinfo != NULL) {
2136272840Smelifaro		ta->dump_tinfo(tc->astate, ti, &i->ta_info);
2137272840Smelifaro		i->ta_info.flags |= IPFW_TATFLAGS_DATA;
2138272840Smelifaro	}
2139272840Smelifaro}
2140272840Smelifaro
2141272840Smelifarostruct dump_table_args {
2142272840Smelifaro	struct ip_fw_chain *ch;
2143272840Smelifaro	struct sockopt_data *sd;
2144272840Smelifaro};
2145272840Smelifaro
2146272840Smelifarostatic void
2147272840Smelifaroexport_table_internal(struct namedobj_instance *ni, struct named_object *no,
2148272840Smelifaro    void *arg)
2149272840Smelifaro{
2150272840Smelifaro	ipfw_xtable_info *i;
2151272840Smelifaro	struct dump_table_args *dta;
2152272840Smelifaro
2153272840Smelifaro	dta = (struct dump_table_args *)arg;
2154272840Smelifaro
2155272840Smelifaro	i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
2156272840Smelifaro	KASSERT(i != 0, ("previously checked buffer is not enough"));
2157272840Smelifaro
2158272840Smelifaro	export_table_info(dta->ch, (struct table_config *)no, i);
2159272840Smelifaro}
2160272840Smelifaro
2161272840Smelifaro/*
2162272840Smelifaro * Export all tables as ipfw_xtable_info structures to
2163272840Smelifaro * storage provided by @sd.
2164272840Smelifaro *
2165272840Smelifaro * If supplied buffer is too small, fills in required size
2166272840Smelifaro * and returns ENOMEM.
2167272840Smelifaro * Returns 0 on success.
2168272840Smelifaro */
2169272840Smelifarostatic int
2170272840Smelifaroexport_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
2171272840Smelifaro    struct sockopt_data *sd)
2172272840Smelifaro{
2173272840Smelifaro	uint32_t size;
2174272840Smelifaro	uint32_t count;
2175272840Smelifaro	struct dump_table_args dta;
2176272840Smelifaro
2177272840Smelifaro	count = ipfw_objhash_count(CHAIN_TO_NI(ch));
2178272840Smelifaro	size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
2179272840Smelifaro
2180272840Smelifaro	/* Fill in header regadless of buffer size */
2181272840Smelifaro	olh->count = count;
2182272840Smelifaro	olh->objsize = sizeof(ipfw_xtable_info);
2183272840Smelifaro
2184272840Smelifaro	if (size > olh->size) {
2185272840Smelifaro		olh->size = size;
2186272840Smelifaro		return (ENOMEM);
2187272840Smelifaro	}
2188272840Smelifaro
2189272840Smelifaro	olh->size = size;
2190272840Smelifaro
2191272840Smelifaro	dta.ch = ch;
2192272840Smelifaro	dta.sd = sd;
2193272840Smelifaro
2194272840Smelifaro	ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta);
2195272840Smelifaro
2196272840Smelifaro	return (0);
2197272840Smelifaro}
2198272840Smelifaro
2199272840Smelifaro/*
2200272840Smelifaro * Dumps all table data
2201272840Smelifaro * Data layout (v1)(current):
2202272840Smelifaro * Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
2203272840Smelifaro * Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_obj_tentry x N ]
2204272840Smelifaro *
2205272840Smelifaro * Returns 0 on success
2206272840Smelifaro */
2207272840Smelifarostatic int
2208272840Smelifarodump_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
2209272840Smelifaro    struct sockopt_data *sd)
2210272840Smelifaro{
2211272840Smelifaro	struct _ipfw_obj_header *oh;
2212272840Smelifaro	ipfw_xtable_info *i;
2213272840Smelifaro	struct tid_info ti;
2214272840Smelifaro	struct table_config *tc;
2215272840Smelifaro	struct table_algo *ta;
2216272840Smelifaro	struct dump_args da;
2217272840Smelifaro	uint32_t sz;
2218272840Smelifaro
2219272840Smelifaro	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
2220272840Smelifaro	oh = (struct _ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
2221272840Smelifaro	if (oh == NULL)
2222200590Sluigi		return (EINVAL);
2223272840Smelifaro
2224272840Smelifaro	i = (ipfw_xtable_info *)(oh + 1);
2225272840Smelifaro	objheader_to_ti(oh, &ti);
2226272840Smelifaro
2227272840Smelifaro	IPFW_UH_RLOCK(ch);
2228272840Smelifaro	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
2229272840Smelifaro		IPFW_UH_RUNLOCK(ch);
2230272840Smelifaro		return (ESRCH);
2231272840Smelifaro	}
2232272840Smelifaro	export_table_info(ch, tc, i);
2233272840Smelifaro
2234272840Smelifaro	if (sd->valsize < i->size) {
2235272840Smelifaro
2236272840Smelifaro		/*
2237272840Smelifaro		 * Submitted buffer size is not enough.
2238272840Smelifaro		 * WE've already filled in @i structure with
2239272840Smelifaro		 * relevant table info including size, so we
2240272840Smelifaro		 * can return. Buffer will be flushed automatically.
2241272840Smelifaro		 */
2242272840Smelifaro		IPFW_UH_RUNLOCK(ch);
2243272840Smelifaro		return (ENOMEM);
2244272840Smelifaro	}
2245272840Smelifaro
2246272840Smelifaro	/*
2247272840Smelifaro	 * Do the actual dump in eXtended format
2248272840Smelifaro	 */
2249272840Smelifaro	memset(&da, 0, sizeof(da));
2250272840Smelifaro	da.ch = ch;
2251272840Smelifaro	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2252272840Smelifaro	da.tc = tc;
2253272840Smelifaro	da.sd = sd;
2254272840Smelifaro
2255272840Smelifaro	ta = tc->ta;
2256272840Smelifaro
2257272840Smelifaro	ta->foreach(tc->astate, da.ti, dump_table_tentry, &da);
2258272840Smelifaro	IPFW_UH_RUNLOCK(ch);
2259272840Smelifaro
2260272840Smelifaro	return (da.error);
2261272840Smelifaro}
2262272840Smelifaro
2263272840Smelifaro/*
2264272840Smelifaro * Dumps all table data
2265272840Smelifaro * Data layout (version 0)(legacy):
2266272840Smelifaro * Request: [ ipfw_xtable ], size = IP_FW_TABLE_XGETSIZE()
2267272840Smelifaro * Reply: [ ipfw_xtable ipfw_table_xentry x N ]
2268272840Smelifaro *
2269272840Smelifaro * Returns 0 on success
2270272840Smelifaro */
2271272840Smelifarostatic int
2272272840Smelifarodump_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
2273272840Smelifaro    struct sockopt_data *sd)
2274272840Smelifaro{
2275272840Smelifaro	ipfw_xtable *xtbl;
2276272840Smelifaro	struct tid_info ti;
2277272840Smelifaro	struct table_config *tc;
2278272840Smelifaro	struct table_algo *ta;
2279272840Smelifaro	struct dump_args da;
2280272840Smelifaro	size_t sz, count;
2281272840Smelifaro
2282272840Smelifaro	xtbl = (ipfw_xtable *)ipfw_get_sopt_header(sd, sizeof(ipfw_xtable));
2283272840Smelifaro	if (xtbl == NULL)
2284272840Smelifaro		return (EINVAL);
2285272840Smelifaro
2286272840Smelifaro	memset(&ti, 0, sizeof(ti));
2287272840Smelifaro	ti.uidx = xtbl->tbl;
2288272840Smelifaro
2289272840Smelifaro	IPFW_UH_RLOCK(ch);
2290272840Smelifaro	if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
2291272840Smelifaro		IPFW_UH_RUNLOCK(ch);
2292232865Smelifaro		return (0);
2293272840Smelifaro	}
2294272840Smelifaro	count = table_get_count(ch, tc);
2295272840Smelifaro	sz = count * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable);
2296272840Smelifaro
2297272840Smelifaro	xtbl->cnt = count;
2298272840Smelifaro	xtbl->size = sz;
2299272840Smelifaro	xtbl->type = tc->no.type;
2300272840Smelifaro	xtbl->tbl = ti.uidx;
2301272840Smelifaro
2302272840Smelifaro	if (sd->valsize < sz) {
2303272840Smelifaro
2304272840Smelifaro		/*
2305272840Smelifaro		 * Submitted buffer size is not enough.
2306272840Smelifaro		 * WE've already filled in @i structure with
2307272840Smelifaro		 * relevant table info including size, so we
2308272840Smelifaro		 * can return. Buffer will be flushed automatically.
2309272840Smelifaro		 */
2310272840Smelifaro		IPFW_UH_RUNLOCK(ch);
2311272840Smelifaro		return (ENOMEM);
2312272840Smelifaro	}
2313272840Smelifaro
2314272840Smelifaro	/* Do the actual dump in eXtended format */
2315272840Smelifaro	memset(&da, 0, sizeof(da));
2316272840Smelifaro	da.ch = ch;
2317272840Smelifaro	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2318272840Smelifaro	da.tc = tc;
2319272840Smelifaro	da.sd = sd;
2320272840Smelifaro
2321272840Smelifaro	ta = tc->ta;
2322272840Smelifaro
2323272840Smelifaro	ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
2324272840Smelifaro	IPFW_UH_RUNLOCK(ch);
2325272840Smelifaro
2326200590Sluigi	return (0);
2327200590Sluigi}
2328200590Sluigi
2329272840Smelifaro/*
2330272840Smelifaro * Legacy function to retrieve number of items in table.
2331272840Smelifaro */
2332200590Sluigistatic int
2333272840Smelifaroget_table_size(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
2334272840Smelifaro    struct sockopt_data *sd)
2335200590Sluigi{
2336272840Smelifaro	uint32_t *tbl;
2337272840Smelifaro	struct tid_info ti;
2338272840Smelifaro	size_t sz;
2339272840Smelifaro	int error;
2340200590Sluigi
2341272840Smelifaro	sz = sizeof(*op3) + sizeof(uint32_t);
2342272840Smelifaro	op3 = (ip_fw3_opheader *)ipfw_get_sopt_header(sd, sz);
2343272840Smelifaro	if (op3 == NULL)
2344272840Smelifaro		return (EINVAL);
2345272840Smelifaro
2346272840Smelifaro	tbl = (uint32_t *)(op3 + 1);
2347272840Smelifaro	memset(&ti, 0, sizeof(ti));
2348272840Smelifaro	ti.uidx = *tbl;
2349272840Smelifaro	IPFW_UH_RLOCK(ch);
2350272840Smelifaro	error = ipfw_count_xtable(ch, &ti, tbl);
2351272840Smelifaro	IPFW_UH_RUNLOCK(ch);
2352272840Smelifaro	return (error);
2353272840Smelifaro}
2354272840Smelifaro
2355272840Smelifaro/*
2356272840Smelifaro * Legacy IP_FW_TABLE_GETSIZE handler
2357272840Smelifaro */
2358272840Smelifaroint
2359272840Smelifaroipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
2360272840Smelifaro{
2361272840Smelifaro	struct table_config *tc;
2362272840Smelifaro
2363272840Smelifaro	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
2364272840Smelifaro		return (ESRCH);
2365272840Smelifaro	*cnt = table_get_count(ch, tc);
2366200590Sluigi	return (0);
2367200590Sluigi}
2368200590Sluigi
2369272840Smelifaro/*
2370272840Smelifaro * Legacy IP_FW_TABLE_XGETSIZE handler
2371272840Smelifaro */
2372200590Sluigiint
2373272840Smelifaroipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
2374200590Sluigi{
2375272840Smelifaro	struct table_config *tc;
2376272840Smelifaro	uint32_t count;
2377200590Sluigi
2378272840Smelifaro	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL) {
2379272840Smelifaro		*cnt = 0;
2380272840Smelifaro		return (0); /* 'table all list' requires success */
2381272840Smelifaro	}
2382272840Smelifaro
2383272840Smelifaro	count = table_get_count(ch, tc);
2384272840Smelifaro	*cnt = count * sizeof(ipfw_table_xentry);
2385272840Smelifaro	if (count > 0)
2386272840Smelifaro		*cnt += sizeof(ipfw_xtable);
2387200590Sluigi	return (0);
2388200590Sluigi}
2389232865Smelifaro
2390232865Smelifarostatic int
2391272840Smelifarodump_table_entry(void *e, void *arg)
2392232865Smelifaro{
2393272840Smelifaro	struct dump_args *da;
2394272840Smelifaro	struct table_config *tc;
2395272840Smelifaro	struct table_algo *ta;
2396272840Smelifaro	ipfw_table_entry *ent;
2397272840Smelifaro	struct table_value *pval;
2398272840Smelifaro	int error;
2399232865Smelifaro
2400272840Smelifaro	da = (struct dump_args *)arg;
2401272840Smelifaro
2402272840Smelifaro	tc = da->tc;
2403272840Smelifaro	ta = tc->ta;
2404272840Smelifaro
2405272840Smelifaro	/* Out of memory, returning */
2406272840Smelifaro	if (da->cnt == da->size)
2407272840Smelifaro		return (1);
2408272840Smelifaro	ent = da->ent++;
2409272840Smelifaro	ent->tbl = da->uidx;
2410272840Smelifaro	da->cnt++;
2411272840Smelifaro
2412272840Smelifaro	error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent);
2413272840Smelifaro	if (error != 0)
2414272840Smelifaro		return (error);
2415272840Smelifaro
2416272840Smelifaro	ent->addr = da->tent.k.addr.s_addr;
2417272840Smelifaro	ent->masklen = da->tent.masklen;
2418272840Smelifaro	pval = get_table_value(da->ch, da->tc, da->tent.v.kidx);
2419272840Smelifaro	ent->value = ipfw_export_table_value_legacy(pval);
2420272840Smelifaro
2421232865Smelifaro	return (0);
2422232865Smelifaro}
2423232865Smelifaro
2424272840Smelifaro/*
2425272840Smelifaro * Dumps table in pre-8.1 legacy format.
2426272840Smelifaro */
2427232865Smelifaroint
2428272840Smelifaroipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
2429272840Smelifaro    ipfw_table *tbl)
2430232865Smelifaro{
2431272840Smelifaro	struct table_config *tc;
2432272840Smelifaro	struct table_algo *ta;
2433272840Smelifaro	struct dump_args da;
2434232865Smelifaro
2435272840Smelifaro	tbl->cnt = 0;
2436272840Smelifaro
2437272840Smelifaro	if ((tc = find_table(CHAIN_TO_NI(ch), ti)) == NULL)
2438272840Smelifaro		return (0);	/* XXX: We should return ESRCH */
2439272840Smelifaro
2440272840Smelifaro	ta = tc->ta;
2441272840Smelifaro
2442272840Smelifaro	/* This dump format supports IPv4 only */
2443272840Smelifaro	if (tc->no.type != IPFW_TABLE_ADDR)
2444272840Smelifaro		return (0);
2445272840Smelifaro
2446272840Smelifaro	memset(&da, 0, sizeof(da));
2447272840Smelifaro	da.ch = ch;
2448272840Smelifaro	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2449272840Smelifaro	da.tc = tc;
2450272840Smelifaro	da.ent = &tbl->ent[0];
2451272840Smelifaro	da.size = tbl->size;
2452272840Smelifaro
2453272840Smelifaro	tbl->cnt = 0;
2454272840Smelifaro	ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
2455272840Smelifaro	tbl->cnt = da.cnt;
2456272840Smelifaro
2457232865Smelifaro	return (0);
2458232865Smelifaro}
2459232865Smelifaro
2460272840Smelifaro/*
2461272840Smelifaro * Dumps table entry in eXtended format (v1)(current).
2462272840Smelifaro */
2463232865Smelifarostatic int
2464272840Smelifarodump_table_tentry(void *e, void *arg)
2465232865Smelifaro{
2466272840Smelifaro	struct dump_args *da;
2467272840Smelifaro	struct table_config *tc;
2468272840Smelifaro	struct table_algo *ta;
2469272840Smelifaro	struct table_value *pval;
2470272840Smelifaro	ipfw_obj_tentry *tent;
2471272840Smelifaro	int error;
2472232865Smelifaro
2473272840Smelifaro	da = (struct dump_args *)arg;
2474272840Smelifaro
2475272840Smelifaro	tc = da->tc;
2476272840Smelifaro	ta = tc->ta;
2477272840Smelifaro
2478272840Smelifaro	tent = (ipfw_obj_tentry *)ipfw_get_sopt_space(da->sd, sizeof(*tent));
2479232865Smelifaro	/* Out of memory, returning */
2480272840Smelifaro	if (tent == NULL) {
2481272840Smelifaro		da->error = ENOMEM;
2482232865Smelifaro		return (1);
2483272840Smelifaro	}
2484272840Smelifaro	tent->head.length = sizeof(ipfw_obj_tentry);
2485272840Smelifaro	tent->idx = da->uidx;
2486272840Smelifaro
2487272840Smelifaro	error = ta->dump_tentry(tc->astate, da->ti, e, tent);
2488272840Smelifaro	if (error != 0)
2489272840Smelifaro		return (error);
2490272840Smelifaro
2491272840Smelifaro	pval = get_table_value(da->ch, da->tc, tent->v.kidx);
2492272840Smelifaro	ipfw_export_table_value_v1(pval, &tent->v.value);
2493272840Smelifaro
2494232865Smelifaro	return (0);
2495232865Smelifaro}
2496232865Smelifaro
2497272840Smelifaro/*
2498272840Smelifaro * Dumps table entry in eXtended format (v0).
2499272840Smelifaro */
2500232865Smelifarostatic int
2501272840Smelifarodump_table_xentry(void *e, void *arg)
2502232865Smelifaro{
2503272840Smelifaro	struct dump_args *da;
2504272840Smelifaro	struct table_config *tc;
2505272840Smelifaro	struct table_algo *ta;
2506232865Smelifaro	ipfw_table_xentry *xent;
2507272840Smelifaro	ipfw_obj_tentry *tent;
2508272840Smelifaro	struct table_value *pval;
2509272840Smelifaro	int error;
2510272840Smelifaro
2511272840Smelifaro	da = (struct dump_args *)arg;
2512272840Smelifaro
2513272840Smelifaro	tc = da->tc;
2514272840Smelifaro	ta = tc->ta;
2515272840Smelifaro
2516272840Smelifaro	xent = (ipfw_table_xentry *)ipfw_get_sopt_space(da->sd, sizeof(*xent));
2517232865Smelifaro	/* Out of memory, returning */
2518272840Smelifaro	if (xent == NULL)
2519232865Smelifaro		return (1);
2520232865Smelifaro	xent->len = sizeof(ipfw_table_xentry);
2521272840Smelifaro	xent->tbl = da->uidx;
2522232865Smelifaro
2523272840Smelifaro	memset(&da->tent, 0, sizeof(da->tent));
2524272840Smelifaro	tent = &da->tent;
2525272840Smelifaro	error = ta->dump_tentry(tc->astate, da->ti, e, tent);
2526272840Smelifaro	if (error != 0)
2527272840Smelifaro		return (error);
2528272840Smelifaro
2529272840Smelifaro	/* Convert current format to previous one */
2530272840Smelifaro	xent->masklen = tent->masklen;
2531272840Smelifaro	pval = get_table_value(da->ch, da->tc, da->tent.v.kidx);
2532272840Smelifaro	xent->value = ipfw_export_table_value_legacy(pval);
2533272840Smelifaro	/* Apply some hacks */
2534272840Smelifaro	if (tc->no.type == IPFW_TABLE_ADDR && tent->subtype == AF_INET) {
2535272840Smelifaro		xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr;
2536272840Smelifaro		xent->flags = IPFW_TCF_INET;
2537272840Smelifaro	} else
2538272840Smelifaro		memcpy(&xent->k, &tent->k, sizeof(xent->k));
2539272840Smelifaro
2540272840Smelifaro	return (0);
2541272840Smelifaro}
2542272840Smelifaro
2543272840Smelifaro/*
2544272840Smelifaro * Helper function to export table algo data
2545272840Smelifaro * to tentry format before calling user function.
2546272840Smelifaro *
2547272840Smelifaro * Returns 0 on success.
2548272840Smelifaro */
2549272840Smelifarostatic int
2550272840Smelifaroprepare_table_tentry(void *e, void *arg)
2551272840Smelifaro{
2552272840Smelifaro	struct dump_args *da;
2553272840Smelifaro	struct table_config *tc;
2554272840Smelifaro	struct table_algo *ta;
2555272840Smelifaro	int error;
2556272840Smelifaro
2557272840Smelifaro	da = (struct dump_args *)arg;
2558272840Smelifaro
2559272840Smelifaro	tc = da->tc;
2560272840Smelifaro	ta = tc->ta;
2561272840Smelifaro
2562272840Smelifaro	error = ta->dump_tentry(tc->astate, da->ti, e, &da->tent);
2563272840Smelifaro	if (error != 0)
2564272840Smelifaro		return (error);
2565272840Smelifaro
2566272840Smelifaro	da->f(&da->tent, da->farg);
2567272840Smelifaro
2568272840Smelifaro	return (0);
2569272840Smelifaro}
2570272840Smelifaro
2571272840Smelifaro/*
2572272840Smelifaro * Allow external consumers to read table entries in standard format.
2573272840Smelifaro */
2574272840Smelifaroint
2575272840Smelifaroipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx,
2576272840Smelifaro    ta_foreach_f *f, void *arg)
2577272840Smelifaro{
2578272840Smelifaro	struct namedobj_instance *ni;
2579272840Smelifaro	struct table_config *tc;
2580272840Smelifaro	struct table_algo *ta;
2581272840Smelifaro	struct dump_args da;
2582272840Smelifaro
2583272840Smelifaro	ni = CHAIN_TO_NI(ch);
2584272840Smelifaro
2585272840Smelifaro	tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, kidx);
2586272840Smelifaro	if (tc == NULL)
2587272840Smelifaro		return (ESRCH);
2588272840Smelifaro
2589272840Smelifaro	ta = tc->ta;
2590272840Smelifaro
2591272840Smelifaro	memset(&da, 0, sizeof(da));
2592272840Smelifaro	da.ch = ch;
2593272840Smelifaro	da.ti = KIDX_TO_TI(ch, tc->no.kidx);
2594272840Smelifaro	da.tc = tc;
2595272840Smelifaro	da.f = f;
2596272840Smelifaro	da.farg = arg;
2597272840Smelifaro
2598272840Smelifaro	ta->foreach(tc->astate, da.ti, prepare_table_tentry, &da);
2599272840Smelifaro
2600272840Smelifaro	return (0);
2601272840Smelifaro}
2602272840Smelifaro
2603272840Smelifaro/*
2604272840Smelifaro * Table algorithms
2605272840Smelifaro */
2606272840Smelifaro
2607272840Smelifaro/*
2608272840Smelifaro * Finds algoritm by index, table type or supplied name.
2609272840Smelifaro *
2610272840Smelifaro * Returns pointer to algo or NULL.
2611272840Smelifaro */
2612272840Smelifarostatic struct table_algo *
2613272840Smelifarofind_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
2614272840Smelifaro{
2615272840Smelifaro	int i, l;
2616272840Smelifaro	struct table_algo *ta;
2617272840Smelifaro
2618272840Smelifaro	if (ti->type > IPFW_TABLE_MAXTYPE)
2619272840Smelifaro		return (NULL);
2620272840Smelifaro
2621272840Smelifaro	/* Search by index */
2622272840Smelifaro	if (ti->atype != 0) {
2623272840Smelifaro		if (ti->atype > tcfg->algo_count)
2624272840Smelifaro			return (NULL);
2625272840Smelifaro		return (tcfg->algo[ti->atype]);
2626272840Smelifaro	}
2627272840Smelifaro
2628272840Smelifaro	if (name == NULL) {
2629272840Smelifaro		/* Return default algorithm for given type if set */
2630272840Smelifaro		return (tcfg->def_algo[ti->type]);
2631272840Smelifaro	}
2632272840Smelifaro
2633272840Smelifaro	/* Search by name */
2634272840Smelifaro	/* TODO: better search */
2635272840Smelifaro	for (i = 1; i <= tcfg->algo_count; i++) {
2636272840Smelifaro		ta = tcfg->algo[i];
2637272840Smelifaro
2638272840Smelifaro		/*
2639272840Smelifaro		 * One can supply additional algorithm
2640272840Smelifaro		 * parameters so we compare only the first word
2641272840Smelifaro		 * of supplied name:
2642272840Smelifaro		 * 'addr:chash hsize=32'
2643272840Smelifaro		 * '^^^^^^^^^'
2644272840Smelifaro		 *
2645272840Smelifaro		 */
2646272840Smelifaro		l = strlen(ta->name);
2647272840Smelifaro		if (strncmp(name, ta->name, l) != 0)
2648272840Smelifaro			continue;
2649272840Smelifaro		if (name[l] != '\0' && name[l] != ' ')
2650272840Smelifaro			continue;
2651272840Smelifaro		/* Check if we're requesting proper table type */
2652272840Smelifaro		if (ti->type != 0 && ti->type != ta->type)
2653272840Smelifaro			return (NULL);
2654272840Smelifaro		return (ta);
2655272840Smelifaro	}
2656272840Smelifaro
2657272840Smelifaro	return (NULL);
2658272840Smelifaro}
2659272840Smelifaro
2660272840Smelifaro/*
2661272840Smelifaro * Register new table algo @ta.
2662272840Smelifaro * Stores algo id inside @idx.
2663272840Smelifaro *
2664272840Smelifaro * Returns 0 on success.
2665272840Smelifaro */
2666272840Smelifaroint
2667272840Smelifaroipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta, size_t size,
2668272840Smelifaro    int *idx)
2669272840Smelifaro{
2670272840Smelifaro	struct tables_config *tcfg;
2671272840Smelifaro	struct table_algo *ta_new;
2672272840Smelifaro	size_t sz;
2673272840Smelifaro
2674272840Smelifaro	if (size > sizeof(struct table_algo))
2675272840Smelifaro		return (EINVAL);
2676272840Smelifaro
2677272840Smelifaro	/* Check for the required on-stack size for add/del */
2678272840Smelifaro	sz = roundup2(ta->ta_buf_size, sizeof(void *));
2679272840Smelifaro	if (sz > TA_BUF_SZ)
2680272840Smelifaro		return (EINVAL);
2681272840Smelifaro
2682272840Smelifaro	KASSERT(ta->type <= IPFW_TABLE_MAXTYPE,("Increase IPFW_TABLE_MAXTYPE"));
2683272840Smelifaro
2684272840Smelifaro	/* Copy algorithm data to stable storage. */
2685272840Smelifaro	ta_new = malloc(sizeof(struct table_algo), M_IPFW, M_WAITOK | M_ZERO);
2686272840Smelifaro	memcpy(ta_new, ta, size);
2687272840Smelifaro
2688272840Smelifaro	tcfg = CHAIN_TO_TCFG(ch);
2689272840Smelifaro
2690272840Smelifaro	KASSERT(tcfg->algo_count < 255, ("Increase algo array size"));
2691272840Smelifaro
2692272840Smelifaro	tcfg->algo[++tcfg->algo_count] = ta_new;
2693272840Smelifaro	ta_new->idx = tcfg->algo_count;
2694272840Smelifaro
2695272840Smelifaro	/* Set algorithm as default one for given type */
2696272840Smelifaro	if ((ta_new->flags & TA_FLAG_DEFAULT) != 0 &&
2697272840Smelifaro	    tcfg->def_algo[ta_new->type] == NULL)
2698272840Smelifaro		tcfg->def_algo[ta_new->type] = ta_new;
2699272840Smelifaro
2700272840Smelifaro	*idx = ta_new->idx;
2701232865Smelifaro
2702272840Smelifaro	return (0);
2703272840Smelifaro}
2704272840Smelifaro
2705272840Smelifaro/*
2706272840Smelifaro * Unregisters table algo using @idx as id.
2707272840Smelifaro * XXX: It is NOT safe to call this function in any place
2708272840Smelifaro * other than ipfw instance destroy handler.
2709272840Smelifaro */
2710272840Smelifarovoid
2711272840Smelifaroipfw_del_table_algo(struct ip_fw_chain *ch, int idx)
2712272840Smelifaro{
2713272840Smelifaro	struct tables_config *tcfg;
2714272840Smelifaro	struct table_algo *ta;
2715272840Smelifaro
2716272840Smelifaro	tcfg = CHAIN_TO_TCFG(ch);
2717272840Smelifaro
2718272840Smelifaro	KASSERT(idx <= tcfg->algo_count, ("algo idx %d out of range 1..%d",
2719272840Smelifaro	    idx, tcfg->algo_count));
2720272840Smelifaro
2721272840Smelifaro	ta = tcfg->algo[idx];
2722272840Smelifaro	KASSERT(ta != NULL, ("algo idx %d is NULL", idx));
2723272840Smelifaro
2724272840Smelifaro	if (tcfg->def_algo[ta->type] == ta)
2725272840Smelifaro		tcfg->def_algo[ta->type] = NULL;
2726272840Smelifaro
2727272840Smelifaro	free(ta, M_IPFW);
2728272840Smelifaro}
2729272840Smelifaro
2730272840Smelifaro/*
2731272840Smelifaro * Lists all table algorithms currently available.
2732272840Smelifaro * Data layout (v0)(current):
2733272840Smelifaro * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
2734272840Smelifaro * Reply: [ ipfw_obj_lheader ipfw_ta_info x N ]
2735272840Smelifaro *
2736272840Smelifaro * Returns 0 on success
2737272840Smelifaro */
2738272840Smelifarostatic int
2739272840Smelifarolist_table_algo(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
2740272840Smelifaro    struct sockopt_data *sd)
2741272840Smelifaro{
2742272840Smelifaro	struct _ipfw_obj_lheader *olh;
2743272840Smelifaro	struct tables_config *tcfg;
2744272840Smelifaro	ipfw_ta_info *i;
2745272840Smelifaro	struct table_algo *ta;
2746272840Smelifaro	uint32_t count, n, size;
2747272840Smelifaro
2748272840Smelifaro	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
2749272840Smelifaro	if (olh == NULL)
2750272840Smelifaro		return (EINVAL);
2751272840Smelifaro	if (sd->valsize < olh->size)
2752272840Smelifaro		return (EINVAL);
2753272840Smelifaro
2754272840Smelifaro	IPFW_UH_RLOCK(ch);
2755272840Smelifaro	tcfg = CHAIN_TO_TCFG(ch);
2756272840Smelifaro	count = tcfg->algo_count;
2757272840Smelifaro	size = count * sizeof(ipfw_ta_info) + sizeof(ipfw_obj_lheader);
2758272840Smelifaro
2759272840Smelifaro	/* Fill in header regadless of buffer size */
2760272840Smelifaro	olh->count = count;
2761272840Smelifaro	olh->objsize = sizeof(ipfw_ta_info);
2762272840Smelifaro
2763272840Smelifaro	if (size > olh->size) {
2764272840Smelifaro		olh->size = size;
2765272840Smelifaro		IPFW_UH_RUNLOCK(ch);
2766272840Smelifaro		return (ENOMEM);
2767232865Smelifaro	}
2768272840Smelifaro	olh->size = size;
2769232865Smelifaro
2770272840Smelifaro	for (n = 1; n <= count; n++) {
2771272840Smelifaro		i = (ipfw_ta_info *)ipfw_get_sopt_space(sd, sizeof(*i));
2772272840Smelifaro		KASSERT(i != 0, ("previously checked buffer is not enough"));
2773272840Smelifaro		ta = tcfg->algo[n];
2774272840Smelifaro		strlcpy(i->algoname, ta->name, sizeof(i->algoname));
2775272840Smelifaro		i->type = ta->type;
2776272840Smelifaro		i->refcnt = ta->refcnt;
2777272840Smelifaro	}
2778272840Smelifaro
2779272840Smelifaro	IPFW_UH_RUNLOCK(ch);
2780272840Smelifaro
2781232865Smelifaro	return (0);
2782232865Smelifaro}
2783232865Smelifaro
2784272840Smelifaro/*
2785272840Smelifaro * Tables rewriting code
2786272840Smelifaro */
2787272840Smelifaro
2788272840Smelifaro/*
2789272840Smelifaro * Determine table number and lookup type for @cmd.
2790272840Smelifaro * Fill @tbl and @type with appropriate values.
2791272840Smelifaro * Returns 0 for relevant opcodes, 1 otherwise.
2792272840Smelifaro */
2793272840Smelifarostatic int
2794272840Smelifaroclassify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
2795272840Smelifaro{
2796272840Smelifaro	ipfw_insn_if *cmdif;
2797272840Smelifaro	int skip;
2798272840Smelifaro	uint16_t v;
2799272840Smelifaro
2800272840Smelifaro	skip = 1;
2801272840Smelifaro
2802272840Smelifaro	switch (cmd->opcode) {
2803272840Smelifaro	case O_IP_SRC_LOOKUP:
2804272840Smelifaro	case O_IP_DST_LOOKUP:
2805272840Smelifaro		/* Basic IPv4/IPv6 or u32 lookups */
2806272840Smelifaro		*puidx = cmd->arg1;
2807272840Smelifaro		/* Assume ADDR by default */
2808272840Smelifaro		*ptype = IPFW_TABLE_ADDR;
2809272840Smelifaro		skip = 0;
2810272840Smelifaro
2811272840Smelifaro		if (F_LEN(cmd) > F_INSN_SIZE(ipfw_insn_u32)) {
2812272840Smelifaro			/*
2813272840Smelifaro			 * generic lookup. The key must be
2814272840Smelifaro			 * in 32bit big-endian format.
2815272840Smelifaro			 */
2816272840Smelifaro			v = ((ipfw_insn_u32 *)cmd)->d[1];
2817272840Smelifaro			switch (v) {
2818272840Smelifaro			case 0:
2819272840Smelifaro			case 1:
2820272840Smelifaro				/* IPv4 src/dst */
2821272840Smelifaro				break;
2822272840Smelifaro			case 2:
2823272840Smelifaro			case 3:
2824272840Smelifaro				/* src/dst port */
2825272840Smelifaro				*ptype = IPFW_TABLE_NUMBER;
2826272840Smelifaro				break;
2827272840Smelifaro			case 4:
2828272840Smelifaro				/* uid/gid */
2829272840Smelifaro				*ptype = IPFW_TABLE_NUMBER;
2830272840Smelifaro				break;
2831272840Smelifaro			case 5:
2832272840Smelifaro				/* jid */
2833272840Smelifaro				*ptype = IPFW_TABLE_NUMBER;
2834272840Smelifaro				break;
2835272840Smelifaro			case 6:
2836272840Smelifaro				/* dscp */
2837272840Smelifaro				*ptype = IPFW_TABLE_NUMBER;
2838272840Smelifaro				break;
2839272840Smelifaro			}
2840272840Smelifaro		}
2841272840Smelifaro		break;
2842272840Smelifaro	case O_XMIT:
2843272840Smelifaro	case O_RECV:
2844272840Smelifaro	case O_VIA:
2845272840Smelifaro		/* Interface table, possibly */
2846272840Smelifaro		cmdif = (ipfw_insn_if *)cmd;
2847272840Smelifaro		if (cmdif->name[0] != '\1')
2848272840Smelifaro			break;
2849272840Smelifaro
2850272840Smelifaro		*ptype = IPFW_TABLE_INTERFACE;
2851272840Smelifaro		*puidx = cmdif->p.kidx;
2852272840Smelifaro		skip = 0;
2853272840Smelifaro		break;
2854272840Smelifaro	case O_IP_FLOW_LOOKUP:
2855272840Smelifaro		*puidx = cmd->arg1;
2856272840Smelifaro		*ptype = IPFW_TABLE_FLOW;
2857272840Smelifaro		skip = 0;
2858272840Smelifaro		break;
2859272840Smelifaro	}
2860272840Smelifaro
2861272840Smelifaro	return (skip);
2862272840Smelifaro}
2863272840Smelifaro
2864272840Smelifaro/*
2865272840Smelifaro * Sets new table value for given opcode.
2866272840Smelifaro * Assume the same opcodes as classify_table_opcode()
2867272840Smelifaro */
2868272840Smelifarostatic void
2869272840Smelifaroupdate_table_opcode(ipfw_insn *cmd, uint16_t idx)
2870272840Smelifaro{
2871272840Smelifaro	ipfw_insn_if *cmdif;
2872272840Smelifaro
2873272840Smelifaro	switch (cmd->opcode) {
2874272840Smelifaro	case O_IP_SRC_LOOKUP:
2875272840Smelifaro	case O_IP_DST_LOOKUP:
2876272840Smelifaro		/* Basic IPv4/IPv6 or u32 lookups */
2877272840Smelifaro		cmd->arg1 = idx;
2878272840Smelifaro		break;
2879272840Smelifaro	case O_XMIT:
2880272840Smelifaro	case O_RECV:
2881272840Smelifaro	case O_VIA:
2882272840Smelifaro		/* Interface table, possibly */
2883272840Smelifaro		cmdif = (ipfw_insn_if *)cmd;
2884272840Smelifaro		cmdif->p.kidx = idx;
2885272840Smelifaro		break;
2886272840Smelifaro	case O_IP_FLOW_LOOKUP:
2887272840Smelifaro		cmd->arg1 = idx;
2888272840Smelifaro		break;
2889272840Smelifaro	}
2890272840Smelifaro}
2891272840Smelifaro
2892272840Smelifaro/*
2893272840Smelifaro * Checks table name for validity.
2894272840Smelifaro * Enforce basic length checks, the rest
2895272840Smelifaro * should be done in userland.
2896272840Smelifaro *
2897272840Smelifaro * Returns 0 if name is considered valid.
2898272840Smelifaro */
2899232865Smelifaroint
2900272840Smelifaroipfw_check_table_name(char *name)
2901232865Smelifaro{
2902272840Smelifaro	int nsize;
2903272840Smelifaro	ipfw_obj_ntlv *ntlv = NULL;
2904232865Smelifaro
2905272840Smelifaro	nsize = sizeof(ntlv->name);
2906272840Smelifaro
2907272840Smelifaro	if (strnlen(name, nsize) == nsize)
2908232865Smelifaro		return (EINVAL);
2909272840Smelifaro
2910272840Smelifaro	if (name[0] == '\0')
2911272840Smelifaro		return (EINVAL);
2912272840Smelifaro
2913272840Smelifaro	/*
2914272840Smelifaro	 * TODO: do some more complicated checks
2915272840Smelifaro	 */
2916272840Smelifaro
2917232865Smelifaro	return (0);
2918232865Smelifaro}
2919232865Smelifaro
2920272840Smelifaro/*
2921272840Smelifaro * Find tablename TLV by @uid.
2922272840Smelifaro * Check @tlvs for valid data inside.
2923272840Smelifaro *
2924272840Smelifaro * Returns pointer to found TLV or NULL.
2925272840Smelifaro */
2926272840Smelifarostatic ipfw_obj_ntlv *
2927272840Smelifarofind_name_tlv(void *tlvs, int len, uint16_t uidx)
2928272840Smelifaro{
2929272840Smelifaro	ipfw_obj_ntlv *ntlv;
2930272840Smelifaro	uintptr_t pa, pe;
2931272840Smelifaro	int l;
2932272840Smelifaro
2933272840Smelifaro	pa = (uintptr_t)tlvs;
2934272840Smelifaro	pe = pa + len;
2935272840Smelifaro	l = 0;
2936272840Smelifaro	for (; pa < pe; pa += l) {
2937272840Smelifaro		ntlv = (ipfw_obj_ntlv *)pa;
2938272840Smelifaro		l = ntlv->head.length;
2939272840Smelifaro
2940272840Smelifaro		if (l != sizeof(*ntlv))
2941272840Smelifaro			return (NULL);
2942272840Smelifaro
2943272840Smelifaro		if (ntlv->head.type != IPFW_TLV_TBL_NAME)
2944272840Smelifaro			continue;
2945272840Smelifaro
2946272840Smelifaro		if (ntlv->idx != uidx)
2947272840Smelifaro			continue;
2948272840Smelifaro
2949272840Smelifaro		if (ipfw_check_table_name(ntlv->name) != 0)
2950272840Smelifaro			return (NULL);
2951272840Smelifaro
2952272840Smelifaro		return (ntlv);
2953272840Smelifaro	}
2954272840Smelifaro
2955272840Smelifaro	return (NULL);
2956272840Smelifaro}
2957272840Smelifaro
2958272840Smelifaro/*
2959272840Smelifaro * Finds table config based on either legacy index
2960272840Smelifaro * or name in ntlv.
2961272840Smelifaro * Note @ti structure contains unchecked data from userland.
2962272840Smelifaro *
2963272840Smelifaro * Returns pointer to table_config or NULL.
2964272840Smelifaro */
2965272840Smelifarostatic struct table_config *
2966272840Smelifarofind_table(struct namedobj_instance *ni, struct tid_info *ti)
2967272840Smelifaro{
2968272840Smelifaro	char *name, bname[16];
2969272840Smelifaro	struct named_object *no;
2970272840Smelifaro	ipfw_obj_ntlv *ntlv;
2971272840Smelifaro	uint32_t set;
2972272840Smelifaro
2973272840Smelifaro	if (ti->tlvs != NULL) {
2974272840Smelifaro		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
2975272840Smelifaro		if (ntlv == NULL)
2976272840Smelifaro			return (NULL);
2977272840Smelifaro		name = ntlv->name;
2978272840Smelifaro
2979272840Smelifaro		/*
2980272840Smelifaro		 * Use set provided by @ti instead of @ntlv one.
2981272840Smelifaro		 * This is needed due to different sets behavior
2982272840Smelifaro		 * controlled by V_fw_tables_sets.
2983272840Smelifaro		 */
2984272840Smelifaro		set = ti->set;
2985272840Smelifaro	} else {
2986272840Smelifaro		snprintf(bname, sizeof(bname), "%d", ti->uidx);
2987272840Smelifaro		name = bname;
2988272840Smelifaro		set = 0;
2989272840Smelifaro	}
2990272840Smelifaro
2991272840Smelifaro	no = ipfw_objhash_lookup_name(ni, set, name);
2992272840Smelifaro
2993272840Smelifaro	return ((struct table_config *)no);
2994272840Smelifaro}
2995272840Smelifaro
2996272840Smelifaro/*
2997272840Smelifaro * Allocate new table config structure using
2998272840Smelifaro * specified @algo and @aname.
2999272840Smelifaro *
3000272840Smelifaro * Returns pointer to config or NULL.
3001272840Smelifaro */
3002272840Smelifarostatic struct table_config *
3003272840Smelifaroalloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti,
3004272840Smelifaro    struct table_algo *ta, char *aname, uint8_t tflags)
3005272840Smelifaro{
3006272840Smelifaro	char *name, bname[16];
3007272840Smelifaro	struct table_config *tc;
3008272840Smelifaro	int error;
3009272840Smelifaro	ipfw_obj_ntlv *ntlv;
3010272840Smelifaro	uint32_t set;
3011272840Smelifaro
3012272840Smelifaro	if (ti->tlvs != NULL) {
3013272840Smelifaro		ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
3014272840Smelifaro		if (ntlv == NULL)
3015272840Smelifaro			return (NULL);
3016272840Smelifaro		name = ntlv->name;
3017272840Smelifaro		set = ntlv->set;
3018272840Smelifaro	} else {
3019272840Smelifaro		snprintf(bname, sizeof(bname), "%d", ti->uidx);
3020272840Smelifaro		name = bname;
3021272840Smelifaro		set = 0;
3022272840Smelifaro	}
3023272840Smelifaro
3024272840Smelifaro	tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
3025272840Smelifaro	tc->no.name = tc->tablename;
3026272840Smelifaro	tc->no.type = ta->type;
3027272840Smelifaro	tc->no.set = set;
3028272840Smelifaro	tc->tflags = tflags;
3029272840Smelifaro	tc->ta = ta;
3030272840Smelifaro	strlcpy(tc->tablename, name, sizeof(tc->tablename));
3031272840Smelifaro	/* Set "shared" value type by default */
3032272840Smelifaro	tc->vshared = 1;
3033272840Smelifaro
3034272840Smelifaro	if (ti->tlvs == NULL) {
3035272840Smelifaro		tc->no.compat = 1;
3036272840Smelifaro		tc->no.uidx = ti->uidx;
3037272840Smelifaro	}
3038272840Smelifaro
3039272840Smelifaro	/* Preallocate data structures for new tables */
3040272840Smelifaro	error = ta->init(ch, &tc->astate, &tc->ti_copy, aname, tflags);
3041272840Smelifaro	if (error != 0) {
3042272840Smelifaro		free(tc, M_IPFW);
3043272840Smelifaro		return (NULL);
3044272840Smelifaro	}
3045272840Smelifaro
3046272840Smelifaro	return (tc);
3047272840Smelifaro}
3048272840Smelifaro
3049272840Smelifaro/*
3050272840Smelifaro * Destroys table state and config.
3051272840Smelifaro */
3052272840Smelifarostatic void
3053272840Smelifarofree_table_config(struct namedobj_instance *ni, struct table_config *tc)
3054272840Smelifaro{
3055272840Smelifaro
3056272840Smelifaro	KASSERT(tc->linked == 0, ("free() on linked config"));
3057278259Smelifaro	/* UH lock MUST NOT be held */
3058272840Smelifaro
3059272840Smelifaro	/*
3060272840Smelifaro	 * We're using ta without any locking/referencing.
3061272840Smelifaro	 * TODO: fix this if we're going to use unloadable algos.
3062272840Smelifaro	 */
3063272840Smelifaro	tc->ta->destroy(tc->astate, &tc->ti_copy);
3064272840Smelifaro	free(tc, M_IPFW);
3065272840Smelifaro}
3066272840Smelifaro
3067272840Smelifaro/*
3068272840Smelifaro * Links @tc to @chain table named instance.
3069272840Smelifaro * Sets appropriate type/states in @chain table info.
3070272840Smelifaro */
3071272840Smelifarostatic void
3072272840Smelifarolink_table(struct ip_fw_chain *ch, struct table_config *tc)
3073272840Smelifaro{
3074272840Smelifaro	struct namedobj_instance *ni;
3075272840Smelifaro	struct table_info *ti;
3076272840Smelifaro	uint16_t kidx;
3077272840Smelifaro
3078272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
3079272840Smelifaro	IPFW_WLOCK_ASSERT(ch);
3080272840Smelifaro
3081272840Smelifaro	ni = CHAIN_TO_NI(ch);
3082272840Smelifaro	kidx = tc->no.kidx;
3083272840Smelifaro
3084272840Smelifaro	ipfw_objhash_add(ni, &tc->no);
3085272840Smelifaro
3086272840Smelifaro	ti = KIDX_TO_TI(ch, kidx);
3087272840Smelifaro	*ti = tc->ti_copy;
3088272840Smelifaro
3089272840Smelifaro	/* Notify algo on real @ti address */
3090272840Smelifaro	if (tc->ta->change_ti != NULL)
3091272840Smelifaro		tc->ta->change_ti(tc->astate, ti);
3092272840Smelifaro
3093272840Smelifaro	tc->linked = 1;
3094272840Smelifaro	tc->ta->refcnt++;
3095272840Smelifaro}
3096272840Smelifaro
3097272840Smelifaro/*
3098272840Smelifaro * Unlinks @tc from @chain table named instance.
3099272840Smelifaro * Zeroes states in @chain and stores them in @tc.
3100272840Smelifaro */
3101272840Smelifarostatic void
3102272840Smelifarounlink_table(struct ip_fw_chain *ch, struct table_config *tc)
3103272840Smelifaro{
3104272840Smelifaro	struct namedobj_instance *ni;
3105272840Smelifaro	struct table_info *ti;
3106272840Smelifaro	uint16_t kidx;
3107272840Smelifaro
3108272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
3109272840Smelifaro	IPFW_WLOCK_ASSERT(ch);
3110272840Smelifaro
3111272840Smelifaro	ni = CHAIN_TO_NI(ch);
3112272840Smelifaro	kidx = tc->no.kidx;
3113272840Smelifaro
3114272840Smelifaro	/* Clear state. @ti copy is already saved inside @tc */
3115272840Smelifaro	ipfw_objhash_del(ni, &tc->no);
3116272840Smelifaro	ti = KIDX_TO_TI(ch, kidx);
3117272840Smelifaro	memset(ti, 0, sizeof(struct table_info));
3118272840Smelifaro	tc->linked = 0;
3119272840Smelifaro	tc->ta->refcnt--;
3120272840Smelifaro
3121272840Smelifaro	/* Notify algo on real @ti address */
3122272840Smelifaro	if (tc->ta->change_ti != NULL)
3123272840Smelifaro		tc->ta->change_ti(tc->astate, NULL);
3124272840Smelifaro}
3125272840Smelifaro
3126272840Smelifarostruct swap_table_args {
3127272840Smelifaro	int set;
3128272840Smelifaro	int new_set;
3129272840Smelifaro	int mv;
3130272840Smelifaro};
3131272840Smelifaro
3132272840Smelifaro/*
3133272840Smelifaro * Change set for each matching table.
3134272840Smelifaro *
3135272840Smelifaro * Ensure we dispatch each table once by setting/checking ochange
3136272840Smelifaro * fields.
3137272840Smelifaro */
3138272840Smelifarostatic void
3139272840Smelifaroswap_table_set(struct namedobj_instance *ni, struct named_object *no,
3140272840Smelifaro    void *arg)
3141272840Smelifaro{
3142272840Smelifaro	struct table_config *tc;
3143272840Smelifaro	struct swap_table_args *sta;
3144272840Smelifaro
3145272840Smelifaro	tc = (struct table_config *)no;
3146272840Smelifaro	sta = (struct swap_table_args *)arg;
3147272840Smelifaro
3148272840Smelifaro	if (no->set != sta->set && (no->set != sta->new_set || sta->mv != 0))
3149272840Smelifaro		return;
3150272840Smelifaro
3151272840Smelifaro	if (tc->ochanged != 0)
3152272840Smelifaro		return;
3153272840Smelifaro
3154272840Smelifaro	tc->ochanged = 1;
3155272840Smelifaro	ipfw_objhash_del(ni, no);
3156272840Smelifaro	if (no->set == sta->set)
3157272840Smelifaro		no->set = sta->new_set;
3158272840Smelifaro	else
3159272840Smelifaro		no->set = sta->set;
3160272840Smelifaro	ipfw_objhash_add(ni, no);
3161272840Smelifaro}
3162272840Smelifaro
3163272840Smelifaro/*
3164272840Smelifaro * Cleans up ochange field for all tables.
3165272840Smelifaro */
3166272840Smelifarostatic void
3167272840Smelifaroclean_table_set_data(struct namedobj_instance *ni, struct named_object *no,
3168272840Smelifaro    void *arg)
3169272840Smelifaro{
3170272840Smelifaro	struct table_config *tc;
3171272840Smelifaro	struct swap_table_args *sta;
3172272840Smelifaro
3173272840Smelifaro	tc = (struct table_config *)no;
3174272840Smelifaro	sta = (struct swap_table_args *)arg;
3175272840Smelifaro
3176272840Smelifaro	tc->ochanged = 0;
3177272840Smelifaro}
3178272840Smelifaro
3179272840Smelifaro/*
3180272840Smelifaro * Swaps tables within two sets.
3181272840Smelifaro */
3182272840Smelifarovoid
3183272840Smelifaroipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t set,
3184272840Smelifaro    uint32_t new_set, int mv)
3185272840Smelifaro{
3186272840Smelifaro	struct swap_table_args sta;
3187272840Smelifaro
3188272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
3189272840Smelifaro
3190272840Smelifaro	sta.set = set;
3191272840Smelifaro	sta.new_set = new_set;
3192272840Smelifaro	sta.mv = mv;
3193272840Smelifaro
3194272840Smelifaro	ipfw_objhash_foreach(CHAIN_TO_NI(ch), swap_table_set, &sta);
3195272840Smelifaro	ipfw_objhash_foreach(CHAIN_TO_NI(ch), clean_table_set_data, &sta);
3196272840Smelifaro}
3197272840Smelifaro
3198272840Smelifaro/*
3199272840Smelifaro * Move all tables which are reference by rules in @rr to set @new_set.
3200272840Smelifaro * Makes sure that all relevant tables are referenced ONLLY by given rules.
3201272840Smelifaro *
3202272840Smelifaro * Retuns 0 on success,
3203272840Smelifaro */
3204272840Smelifaroint
3205272840Smelifaroipfw_move_tables_sets(struct ip_fw_chain *ch, ipfw_range_tlv *rt,
3206272840Smelifaro    uint32_t new_set)
3207272840Smelifaro{
3208272840Smelifaro	struct ip_fw *rule;
3209272840Smelifaro	struct table_config *tc;
3210272840Smelifaro	struct named_object *no;
3211272840Smelifaro	struct namedobj_instance *ni;
3212272840Smelifaro	int bad, i, l, cmdlen;
3213272840Smelifaro	uint16_t kidx;
3214272840Smelifaro	uint8_t type;
3215272840Smelifaro	ipfw_insn *cmd;
3216272840Smelifaro
3217272840Smelifaro	IPFW_UH_WLOCK_ASSERT(ch);
3218272840Smelifaro
3219272840Smelifaro	ni = CHAIN_TO_NI(ch);
3220272840Smelifaro
3221272840Smelifaro	/* Stage 1: count number of references by given rules */
3222272840Smelifaro	for (i = 0; i < ch->n_rules - 1; i++) {
3223272840Smelifaro		rule = ch->map[i];
3224272840Smelifaro		if (ipfw_match_range(rule, rt) == 0)
3225272840Smelifaro			continue;
3226272840Smelifaro
3227272840Smelifaro		l = rule->cmd_len;
3228272840Smelifaro		cmd = rule->cmd;
3229272840Smelifaro		cmdlen = 0;
3230272840Smelifaro		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3231272840Smelifaro			cmdlen = F_LEN(cmd);
3232272840Smelifaro			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3233272840Smelifaro				continue;
3234272840Smelifaro			no = ipfw_objhash_lookup_kidx(ni, kidx);
3235272840Smelifaro			KASSERT(no != NULL,
3236272840Smelifaro			    ("objhash lookup failed on index %d", kidx));
3237272840Smelifaro			tc = (struct table_config *)no;
3238272840Smelifaro			tc->ocount++;
3239272840Smelifaro		}
3240272840Smelifaro
3241272840Smelifaro	}
3242272840Smelifaro
3243272840Smelifaro	/* Stage 2: verify "ownership" */
3244272840Smelifaro	bad = 0;
3245272840Smelifaro	for (i = 0; i < ch->n_rules - 1; i++) {
3246272840Smelifaro		rule = ch->map[i];
3247272840Smelifaro		if (ipfw_match_range(rule, rt) == 0)
3248272840Smelifaro			continue;
3249272840Smelifaro
3250272840Smelifaro		l = rule->cmd_len;
3251272840Smelifaro		cmd = rule->cmd;
3252272840Smelifaro		cmdlen = 0;
3253272840Smelifaro		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3254272840Smelifaro			cmdlen = F_LEN(cmd);
3255272840Smelifaro			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3256272840Smelifaro				continue;
3257272840Smelifaro			no = ipfw_objhash_lookup_kidx(ni, kidx);
3258272840Smelifaro			KASSERT(no != NULL,
3259272840Smelifaro			    ("objhash lookup failed on index %d", kidx));
3260272840Smelifaro			tc = (struct table_config *)no;
3261272840Smelifaro			if (tc->no.refcnt != tc->ocount) {
3262272840Smelifaro
3263272840Smelifaro				/*
3264272840Smelifaro				 * Number of references differ:
3265272840Smelifaro				 * Other rule(s) are holding reference to given
3266272840Smelifaro				 * table, so it is not possible to change its set.
3267272840Smelifaro				 *
3268272840Smelifaro				 * Note that refcnt may account
3269272840Smelifaro				 * references to some going-to-be-added rules.
3270272840Smelifaro				 * Since we don't know their numbers (and event
3271272840Smelifaro				 * if they will be added) it is perfectly OK
3272272840Smelifaro				 * to return error here.
3273272840Smelifaro				 */
3274272840Smelifaro				bad = 1;
3275272840Smelifaro				break;
3276272840Smelifaro			}
3277272840Smelifaro		}
3278272840Smelifaro
3279272840Smelifaro		if (bad != 0)
3280272840Smelifaro			break;
3281272840Smelifaro	}
3282272840Smelifaro
3283272840Smelifaro	/* Stage 3: change set or cleanup */
3284272840Smelifaro	for (i = 0; i < ch->n_rules - 1; i++) {
3285272840Smelifaro		rule = ch->map[i];
3286272840Smelifaro		if (ipfw_match_range(rule, rt) == 0)
3287272840Smelifaro			continue;
3288272840Smelifaro
3289272840Smelifaro		l = rule->cmd_len;
3290272840Smelifaro		cmd = rule->cmd;
3291272840Smelifaro		cmdlen = 0;
3292272840Smelifaro		for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3293272840Smelifaro			cmdlen = F_LEN(cmd);
3294272840Smelifaro			if (classify_table_opcode(cmd, &kidx, &type) != 0)
3295272840Smelifaro				continue;
3296272840Smelifaro			no = ipfw_objhash_lookup_kidx(ni, kidx);
3297272840Smelifaro			KASSERT(no != NULL,
3298272840Smelifaro			    ("objhash lookup failed on index %d", kidx));
3299272840Smelifaro			tc = (struct table_config *)no;
3300272840Smelifaro
3301272840Smelifaro			tc->ocount = 0;
3302272840Smelifaro			if (bad != 0)
3303272840Smelifaro				continue;
3304272840Smelifaro
3305272840Smelifaro			/* Actually change set. */
3306272840Smelifaro			ipfw_objhash_del(ni, no);
3307272840Smelifaro			no->set = new_set;
3308272840Smelifaro			ipfw_objhash_add(ni, no);
3309272840Smelifaro		}
3310272840Smelifaro	}
3311272840Smelifaro
3312272840Smelifaro	return (bad);
3313272840Smelifaro}
3314272840Smelifaro
3315272840Smelifaro/*
3316272840Smelifaro * Finds and bumps refcount for tables referenced by given @rule.
3317272840Smelifaro * Auto-creates non-existing tables.
3318272840Smelifaro * Fills in @oib array with userland/kernel indexes.
3319272840Smelifaro * First free oidx pointer is saved back in @oib.
3320272840Smelifaro *
3321272840Smelifaro * Returns 0 on success.
3322272840Smelifaro */
3323272840Smelifarostatic int
3324272840Smelifarofind_ref_rule_tables(struct ip_fw_chain *ch, struct ip_fw *rule,
3325272840Smelifaro    struct rule_check_info *ci, struct obj_idx **oib, struct tid_info *ti)
3326272840Smelifaro{
3327272840Smelifaro	struct table_config *tc;
3328272840Smelifaro	struct namedobj_instance *ni;
3329272840Smelifaro	struct named_object *no;
3330272840Smelifaro	int cmdlen, error, l, numnew;
3331272840Smelifaro	uint16_t kidx;
3332272840Smelifaro	ipfw_insn *cmd;
3333272840Smelifaro	struct obj_idx *pidx, *pidx_first, *p;
3334272840Smelifaro
3335272840Smelifaro	pidx_first = *oib;
3336272840Smelifaro	pidx = pidx_first;
3337272840Smelifaro	l = rule->cmd_len;
3338272840Smelifaro	cmd = rule->cmd;
3339272840Smelifaro	cmdlen = 0;
3340272840Smelifaro	error = 0;
3341272840Smelifaro	numnew = 0;
3342272840Smelifaro
3343272840Smelifaro	IPFW_UH_WLOCK(ch);
3344272840Smelifaro	ni = CHAIN_TO_NI(ch);
3345272840Smelifaro
3346272840Smelifaro	/* Increase refcount on each existing referenced table. */
3347272840Smelifaro	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3348272840Smelifaro		cmdlen = F_LEN(cmd);
3349272840Smelifaro
3350272840Smelifaro		if (classify_table_opcode(cmd, &ti->uidx, &ti->type) != 0)
3351272840Smelifaro			continue;
3352272840Smelifaro
3353272840Smelifaro		pidx->uidx = ti->uidx;
3354272840Smelifaro		pidx->type = ti->type;
3355272840Smelifaro
3356272840Smelifaro		if ((tc = find_table(ni, ti)) != NULL) {
3357272840Smelifaro			if (tc->no.type != ti->type) {
3358272840Smelifaro				/* Incompatible types */
3359272840Smelifaro				error = EINVAL;
3360272840Smelifaro				break;
3361272840Smelifaro			}
3362272840Smelifaro
3363272840Smelifaro			/* Reference found table and save kidx */
3364272840Smelifaro			tc->no.refcnt++;
3365272840Smelifaro			pidx->kidx = tc->no.kidx;
3366272840Smelifaro			pidx++;
3367272840Smelifaro			continue;
3368272840Smelifaro		}
3369272840Smelifaro
3370272840Smelifaro		/*
3371272840Smelifaro		 * Compability stuff for old clients:
3372272840Smelifaro		 * prepare to manually create non-existing tables.
3373272840Smelifaro		 */
3374272840Smelifaro		pidx++;
3375272840Smelifaro		numnew++;
3376272840Smelifaro	}
3377272840Smelifaro
3378272840Smelifaro	if (error != 0) {
3379272840Smelifaro		/* Unref everything we have already done */
3380272840Smelifaro		for (p = *oib; p < pidx; p++) {
3381272840Smelifaro			if (p->kidx == 0)
3382272840Smelifaro				continue;
3383272840Smelifaro
3384272840Smelifaro			/* Find & unref by existing idx */
3385272840Smelifaro			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
3386272840Smelifaro			KASSERT(no != NULL, ("Ref'd table %d disappeared",
3387272840Smelifaro			    p->kidx));
3388272840Smelifaro
3389272840Smelifaro			no->refcnt--;
3390272840Smelifaro		}
3391272840Smelifaro	}
3392272840Smelifaro
3393272840Smelifaro	IPFW_UH_WUNLOCK(ch);
3394272840Smelifaro
3395272840Smelifaro	if (numnew == 0) {
3396272840Smelifaro		*oib = pidx;
3397272840Smelifaro		return (error);
3398272840Smelifaro	}
3399272840Smelifaro
3400272840Smelifaro	/*
3401272840Smelifaro	 * Compatibility stuff: do actual creation for non-existing,
3402272840Smelifaro	 * but referenced tables.
3403272840Smelifaro	 */
3404272840Smelifaro	for (p = pidx_first; p < pidx; p++) {
3405272840Smelifaro		if (p->kidx != 0)
3406272840Smelifaro			continue;
3407272840Smelifaro
3408272840Smelifaro		ti->uidx = p->uidx;
3409272840Smelifaro		ti->type = p->type;
3410272840Smelifaro		ti->atype = 0;
3411272840Smelifaro
3412272840Smelifaro		error = create_table_compat(ch, ti, &kidx);
3413272840Smelifaro		if (error == 0) {
3414272840Smelifaro			p->kidx = kidx;
3415272840Smelifaro			continue;
3416272840Smelifaro		}
3417272840Smelifaro
3418272840Smelifaro		/* Error. We have to drop references */
3419272840Smelifaro		IPFW_UH_WLOCK(ch);
3420272840Smelifaro		for (p = pidx_first; p < pidx; p++) {
3421272840Smelifaro			if (p->kidx == 0)
3422272840Smelifaro				continue;
3423272840Smelifaro
3424272840Smelifaro			/* Find & unref by existing idx */
3425272840Smelifaro			no = ipfw_objhash_lookup_kidx(ni, p->kidx);
3426272840Smelifaro			KASSERT(no != NULL, ("Ref'd table %d disappeared",
3427272840Smelifaro			    p->kidx));
3428272840Smelifaro
3429272840Smelifaro			no->refcnt--;
3430272840Smelifaro		}
3431272840Smelifaro		IPFW_UH_WUNLOCK(ch);
3432272840Smelifaro
3433272840Smelifaro		return (error);
3434272840Smelifaro	}
3435272840Smelifaro
3436272840Smelifaro	*oib = pidx;
3437272840Smelifaro
3438272840Smelifaro	return (error);
3439272840Smelifaro}
3440272840Smelifaro
3441272840Smelifaro/*
3442272840Smelifaro * Remove references from every table used in @rule.
3443272840Smelifaro */
3444272840Smelifarovoid
3445272840Smelifaroipfw_unref_rule_tables(struct ip_fw_chain *chain, struct ip_fw *rule)
3446272840Smelifaro{
3447272840Smelifaro	int cmdlen, l;
3448272840Smelifaro	ipfw_insn *cmd;
3449272840Smelifaro	struct namedobj_instance *ni;
3450272840Smelifaro	struct named_object *no;
3451272840Smelifaro	uint16_t kidx;
3452272840Smelifaro	uint8_t type;
3453272840Smelifaro
3454272840Smelifaro	IPFW_UH_WLOCK_ASSERT(chain);
3455272840Smelifaro	ni = CHAIN_TO_NI(chain);
3456272840Smelifaro
3457272840Smelifaro	l = rule->cmd_len;
3458272840Smelifaro	cmd = rule->cmd;
3459272840Smelifaro	cmdlen = 0;
3460272840Smelifaro	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3461272840Smelifaro		cmdlen = F_LEN(cmd);
3462272840Smelifaro
3463272840Smelifaro		if (classify_table_opcode(cmd, &kidx, &type) != 0)
3464272840Smelifaro			continue;
3465272840Smelifaro
3466272840Smelifaro		no = ipfw_objhash_lookup_kidx(ni, kidx);
3467272840Smelifaro
3468272840Smelifaro		KASSERT(no != NULL, ("table id %d not found", kidx));
3469272840Smelifaro		KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",
3470272840Smelifaro		    no->type, type, kidx));
3471272840Smelifaro		KASSERT(no->refcnt > 0, ("refcount for table %d is %d",
3472272840Smelifaro		    kidx, no->refcnt));
3473272840Smelifaro
3474272840Smelifaro		no->refcnt--;
3475272840Smelifaro	}
3476272840Smelifaro}
3477272840Smelifaro
3478272840Smelifaro/*
3479272840Smelifaro * Compatibility function for old ipfw(8) binaries.
3480272840Smelifaro * Rewrites table kernel indices with userland ones.
3481272840Smelifaro * Convert tables matching '/^\d+$/' to their atoi() value.
3482272840Smelifaro * Use number 65535 for other tables.
3483272840Smelifaro *
3484272840Smelifaro * Returns 0 on success.
3485272840Smelifaro */
3486272840Smelifaroint
3487272840Smelifaroipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule)
3488272840Smelifaro{
3489272840Smelifaro	int cmdlen, error, l;
3490272840Smelifaro	ipfw_insn *cmd;
3491272840Smelifaro	uint16_t kidx, uidx;
3492272840Smelifaro	uint8_t type;
3493272840Smelifaro	struct named_object *no;
3494272840Smelifaro	struct namedobj_instance *ni;
3495272840Smelifaro
3496272840Smelifaro	ni = CHAIN_TO_NI(chain);
3497272840Smelifaro	error = 0;
3498272840Smelifaro
3499272840Smelifaro	l = rule->cmd_len;
3500272840Smelifaro	cmd = rule->cmd;
3501272840Smelifaro	cmdlen = 0;
3502272840Smelifaro	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3503272840Smelifaro		cmdlen = F_LEN(cmd);
3504272840Smelifaro
3505272840Smelifaro		if (classify_table_opcode(cmd, &kidx, &type) != 0)
3506272840Smelifaro			continue;
3507272840Smelifaro
3508272840Smelifaro		if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL)
3509272840Smelifaro			return (1);
3510272840Smelifaro
3511272840Smelifaro		uidx = no->uidx;
3512272840Smelifaro		if (no->compat == 0) {
3513272840Smelifaro
3514272840Smelifaro			/*
3515272840Smelifaro			 * We are called via legacy opcode.
3516272840Smelifaro			 * Save error and show table as fake number
3517272840Smelifaro			 * not to make ipfw(8) hang.
3518272840Smelifaro			 */
3519272840Smelifaro			uidx = 65535;
3520272840Smelifaro			error = 2;
3521272840Smelifaro		}
3522272840Smelifaro
3523272840Smelifaro		update_table_opcode(cmd, uidx);
3524272840Smelifaro	}
3525272840Smelifaro
3526272840Smelifaro	return (error);
3527272840Smelifaro}
3528272840Smelifaro
3529272840Smelifaro/*
3530272840Smelifaro * Checks is opcode is referencing table of appropriate type.
3531272840Smelifaro * Adds reference count for found table if true.
3532272840Smelifaro * Rewrites user-supplied opcode values with kernel ones.
3533272840Smelifaro *
3534272840Smelifaro * Returns 0 on success and appropriate error code otherwise.
3535272840Smelifaro */
3536272840Smelifaroint
3537272840Smelifaroipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
3538272840Smelifaro    struct rule_check_info *ci)
3539272840Smelifaro{
3540272840Smelifaro	int cmdlen, error, l;
3541272840Smelifaro	ipfw_insn *cmd;
3542272840Smelifaro	uint16_t uidx;
3543272840Smelifaro	uint8_t type;
3544272840Smelifaro	struct namedobj_instance *ni;
3545272840Smelifaro	struct obj_idx *p, *pidx_first, *pidx_last;
3546272840Smelifaro	struct tid_info ti;
3547272840Smelifaro
3548272840Smelifaro	ni = CHAIN_TO_NI(chain);
3549272840Smelifaro
3550272840Smelifaro	/*
3551272840Smelifaro	 * Prepare an array for storing opcode indices.
3552272840Smelifaro	 * Use stack allocation by default.
3553272840Smelifaro	 */
3554272840Smelifaro	if (ci->table_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
3555272840Smelifaro		/* Stack */
3556272840Smelifaro		pidx_first = ci->obuf;
3557272840Smelifaro	} else
3558272840Smelifaro		pidx_first = malloc(ci->table_opcodes * sizeof(struct obj_idx),
3559272840Smelifaro		    M_IPFW, M_WAITOK | M_ZERO);
3560272840Smelifaro
3561272840Smelifaro	pidx_last = pidx_first;
3562272840Smelifaro	error = 0;
3563272840Smelifaro	type = 0;
3564272840Smelifaro	memset(&ti, 0, sizeof(ti));
3565272840Smelifaro
3566272840Smelifaro	/*
3567272840Smelifaro	 * Use default set for looking up tables (old way) or
3568272840Smelifaro	 * use set rule is assigned to (new way).
3569272840Smelifaro	 */
3570272840Smelifaro	ti.set = (V_fw_tables_sets != 0) ? ci->krule->set : 0;
3571272840Smelifaro	if (ci->ctlv != NULL) {
3572272840Smelifaro		ti.tlvs = (void *)(ci->ctlv + 1);
3573272840Smelifaro		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
3574272840Smelifaro	}
3575272840Smelifaro
3576272840Smelifaro	/* Reference all used tables */
3577272840Smelifaro	error = find_ref_rule_tables(chain, ci->krule, ci, &pidx_last, &ti);
3578272840Smelifaro	if (error != 0)
3579272840Smelifaro		goto free;
3580272840Smelifaro
3581272840Smelifaro	IPFW_UH_WLOCK(chain);
3582272840Smelifaro
3583272840Smelifaro	/* Perform rule rewrite */
3584272840Smelifaro	l = ci->krule->cmd_len;
3585272840Smelifaro	cmd = ci->krule->cmd;
3586272840Smelifaro	cmdlen = 0;
3587272840Smelifaro	p = pidx_first;
3588272840Smelifaro	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
3589272840Smelifaro		cmdlen = F_LEN(cmd);
3590272840Smelifaro		if (classify_table_opcode(cmd, &uidx, &type) != 0)
3591272840Smelifaro			continue;
3592272840Smelifaro		update_table_opcode(cmd, p->kidx);
3593272840Smelifaro		p++;
3594272840Smelifaro	}
3595272840Smelifaro
3596272840Smelifaro	IPFW_UH_WUNLOCK(chain);
3597272840Smelifaro
3598272840Smelifarofree:
3599272840Smelifaro	if (pidx_first != ci->obuf)
3600272840Smelifaro		free(pidx_first, M_IPFW);
3601272840Smelifaro
3602272840Smelifaro	return (error);
3603272840Smelifaro}
3604272840Smelifaro
3605272840Smelifarostatic struct ipfw_sopt_handler	scodes[] = {
3606272840Smelifaro	{ IP_FW_TABLE_XCREATE,	0,	HDIR_SET,	create_table },
3607272840Smelifaro	{ IP_FW_TABLE_XDESTROY,	0,	HDIR_SET,	flush_table_v0 },
3608272840Smelifaro	{ IP_FW_TABLE_XFLUSH,	0,	HDIR_SET,	flush_table_v0 },
3609272840Smelifaro	{ IP_FW_TABLE_XMODIFY,	0,	HDIR_BOTH,	modify_table },
3610272840Smelifaro	{ IP_FW_TABLE_XINFO,	0,	HDIR_GET,	describe_table },
3611272840Smelifaro	{ IP_FW_TABLES_XLIST,	0,	HDIR_GET,	list_tables },
3612272840Smelifaro	{ IP_FW_TABLE_XLIST,	0,	HDIR_GET,	dump_table_v0 },
3613272840Smelifaro	{ IP_FW_TABLE_XLIST,	1,	HDIR_GET,	dump_table_v1 },
3614272840Smelifaro	{ IP_FW_TABLE_XADD,	0,	HDIR_BOTH,	manage_table_ent_v0 },
3615272840Smelifaro	{ IP_FW_TABLE_XADD,	1,	HDIR_BOTH,	manage_table_ent_v1 },
3616272840Smelifaro	{ IP_FW_TABLE_XDEL,	0,	HDIR_BOTH,	manage_table_ent_v0 },
3617272840Smelifaro	{ IP_FW_TABLE_XDEL,	1,	HDIR_BOTH,	manage_table_ent_v1 },
3618272840Smelifaro	{ IP_FW_TABLE_XFIND,	0,	HDIR_GET,	find_table_entry },
3619272840Smelifaro	{ IP_FW_TABLE_XSWAP,	0,	HDIR_SET,	swap_table },
3620272840Smelifaro	{ IP_FW_TABLES_ALIST,	0,	HDIR_GET,	list_table_algo },
3621272840Smelifaro	{ IP_FW_TABLE_XGETSIZE,	0,	HDIR_GET,	get_table_size },
3622272840Smelifaro};
3623272840Smelifaro
3624272840Smelifarostatic void
3625272840Smelifarodestroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
3626272840Smelifaro    void *arg)
3627272840Smelifaro{
3628272840Smelifaro
3629272840Smelifaro	unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
3630272840Smelifaro	if (ipfw_objhash_free_idx(ni, no->kidx) != 0)
3631272840Smelifaro		printf("Error unlinking kidx %d from table %s\n",
3632272840Smelifaro		    no->kidx, no->name);
3633272840Smelifaro	free_table_config(ni, (struct table_config *)no);
3634272840Smelifaro}
3635272840Smelifaro
3636272840Smelifaro/*
3637272840Smelifaro * Shuts tables module down.
3638272840Smelifaro */
3639272840Smelifarovoid
3640272840Smelifaroipfw_destroy_tables(struct ip_fw_chain *ch, int last)
3641272840Smelifaro{
3642272840Smelifaro
3643272840Smelifaro	IPFW_DEL_SOPT_HANDLER(last, scodes);
3644272840Smelifaro
3645272840Smelifaro	/* Remove all tables from working set */
3646272840Smelifaro	IPFW_UH_WLOCK(ch);
3647272840Smelifaro	IPFW_WLOCK(ch);
3648272840Smelifaro	ipfw_objhash_foreach(CHAIN_TO_NI(ch), destroy_table_locked, ch);
3649272840Smelifaro	IPFW_WUNLOCK(ch);
3650272840Smelifaro	IPFW_UH_WUNLOCK(ch);
3651272840Smelifaro
3652272840Smelifaro	/* Free pointers itself */
3653272840Smelifaro	free(ch->tablestate, M_IPFW);
3654272840Smelifaro
3655272840Smelifaro	ipfw_table_value_destroy(ch, last);
3656272840Smelifaro	ipfw_table_algo_destroy(ch);
3657272840Smelifaro
3658272840Smelifaro	ipfw_objhash_destroy(CHAIN_TO_NI(ch));
3659272840Smelifaro	free(CHAIN_TO_TCFG(ch), M_IPFW);
3660272840Smelifaro}
3661272840Smelifaro
3662272840Smelifaro/*
3663272840Smelifaro * Starts tables module.
3664272840Smelifaro */
3665272840Smelifaroint
3666272840Smelifaroipfw_init_tables(struct ip_fw_chain *ch, int first)
3667272840Smelifaro{
3668272840Smelifaro	struct tables_config *tcfg;
3669272840Smelifaro
3670272840Smelifaro	/* Allocate pointers */
3671272840Smelifaro	ch->tablestate = malloc(V_fw_tables_max * sizeof(struct table_info),
3672272840Smelifaro	    M_IPFW, M_WAITOK | M_ZERO);
3673272840Smelifaro
3674272840Smelifaro	tcfg = malloc(sizeof(struct tables_config), M_IPFW, M_WAITOK | M_ZERO);
3675272840Smelifaro	tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
3676272840Smelifaro	ch->tblcfg = tcfg;
3677272840Smelifaro
3678272840Smelifaro	ipfw_table_value_init(ch, first);
3679272840Smelifaro	ipfw_table_algo_init(ch);
3680272840Smelifaro
3681272840Smelifaro	IPFW_ADD_SOPT_HANDLER(first, scodes);
3682272840Smelifaro	return (0);
3683272840Smelifaro}
3684272840Smelifaro
3685272840Smelifaro
3686272840Smelifaro
3687