ip_fw_sockopt.c revision 317043
1/*-
2 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
3 * Copyright (c) 2014 Yandex LLC
4 * Copyright (c) 2014 Alexander V. Chernikov
5 *
6 * Supported by: Valeria Paoli
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/ip_fw_sockopt.c 317043 2017-04-17 09:36:35Z ae $");
32
33/*
34 * Control socket and rule management routines for ipfw.
35 * Control is currently implemented via IP_FW3 setsockopt() code.
36 */
37
38#include "opt_ipfw.h"
39#include "opt_inet.h"
40#ifndef INET
41#error IPFIREWALL requires INET.
42#endif /* INET */
43#include "opt_inet6.h"
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/malloc.h>
48#include <sys/mbuf.h>	/* struct m_tag used by nested headers */
49#include <sys/kernel.h>
50#include <sys/lock.h>
51#include <sys/priv.h>
52#include <sys/proc.h>
53#include <sys/rwlock.h>
54#include <sys/rmlock.h>
55#include <sys/socket.h>
56#include <sys/socketvar.h>
57#include <sys/sysctl.h>
58#include <sys/syslog.h>
59#include <sys/fnv_hash.h>
60#include <net/if.h>
61#include <net/pfil.h>
62#include <net/route.h>
63#include <net/vnet.h>
64#include <vm/vm.h>
65#include <vm/vm_extern.h>
66
67#include <netinet/in.h>
68#include <netinet/ip_var.h> /* hooks */
69#include <netinet/ip_fw.h>
70
71#include <netpfil/ipfw/ip_fw_private.h>
72#include <netpfil/ipfw/ip_fw_table.h>
73
74#ifdef MAC
75#include <security/mac/mac_framework.h>
76#endif
77
78static int ipfw_ctl(struct sockopt *sopt);
79static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len,
80    struct rule_check_info *ci);
81static int check_ipfw_rule1(struct ip_fw_rule *rule, int size,
82    struct rule_check_info *ci);
83static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size,
84    struct rule_check_info *ci);
85static int rewrite_rule_uidx(struct ip_fw_chain *chain,
86    struct rule_check_info *ci);
87
88#define	NAMEDOBJ_HASH_SIZE	32
89
90struct namedobj_instance {
91	struct namedobjects_head	*names;
92	struct namedobjects_head	*values;
93	uint32_t nn_size;		/* names hash size */
94	uint32_t nv_size;		/* number hash size */
95	u_long *idx_mask;		/* used items bitmask */
96	uint32_t max_blocks;		/* number of "long" blocks in bitmask */
97	uint32_t count;			/* number of items */
98	uint16_t free_off[IPFW_MAX_SETS];	/* first possible free offset */
99	objhash_hash_f	*hash_f;
100	objhash_cmp_f	*cmp_f;
101};
102#define	BLOCK_ITEMS	(8 * sizeof(u_long))	/* Number of items for ffsl() */
103
104static uint32_t objhash_hash_name(struct namedobj_instance *ni,
105    const void *key, uint32_t kopt);
106static uint32_t objhash_hash_idx(struct namedobj_instance *ni, uint32_t val);
107static int objhash_cmp_name(struct named_object *no, const void *name,
108    uint32_t set);
109
110MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
111
112static int dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
113    struct sockopt_data *sd);
114static int add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
115    struct sockopt_data *sd);
116static int del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
117    struct sockopt_data *sd);
118static int clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
119    struct sockopt_data *sd);
120static int move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
121    struct sockopt_data *sd);
122static int manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
123    struct sockopt_data *sd);
124static int dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
125    struct sockopt_data *sd);
126static int dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
127    struct sockopt_data *sd);
128
129/* ctl3 handler data */
130struct mtx ctl3_lock;
131#define	CTL3_LOCK_INIT()	mtx_init(&ctl3_lock, "ctl3_lock", NULL, MTX_DEF)
132#define	CTL3_LOCK_DESTROY()	mtx_destroy(&ctl3_lock)
133#define	CTL3_LOCK()		mtx_lock(&ctl3_lock)
134#define	CTL3_UNLOCK()		mtx_unlock(&ctl3_lock)
135
136static struct ipfw_sopt_handler *ctl3_handlers;
137static size_t ctl3_hsize;
138static uint64_t ctl3_refct, ctl3_gencnt;
139#define	CTL3_SMALLBUF	4096			/* small page-size write buffer */
140#define	CTL3_LARGEBUF	16 * 1024 * 1024	/* handle large rulesets */
141
142static int ipfw_flush_sopt_data(struct sockopt_data *sd);
143
144static struct ipfw_sopt_handler	scodes[] = {
145	{ IP_FW_XGET,		0,	HDIR_GET,	dump_config },
146	{ IP_FW_XADD,		0,	HDIR_BOTH,	add_rules },
147	{ IP_FW_XDEL,		0,	HDIR_BOTH,	del_rules },
148	{ IP_FW_XZERO,		0,	HDIR_SET,	clear_rules },
149	{ IP_FW_XRESETLOG,	0,	HDIR_SET,	clear_rules },
150	{ IP_FW_XMOVE,		0,	HDIR_SET,	move_rules },
151	{ IP_FW_SET_SWAP,	0,	HDIR_SET,	manage_sets },
152	{ IP_FW_SET_MOVE,	0,	HDIR_SET,	manage_sets },
153	{ IP_FW_SET_ENABLE,	0,	HDIR_SET,	manage_sets },
154	{ IP_FW_DUMP_SOPTCODES,	0,	HDIR_GET,	dump_soptcodes },
155	{ IP_FW_DUMP_SRVOBJECTS,0,	HDIR_GET,	dump_srvobjects },
156};
157
158static int
159set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule);
160static struct opcode_obj_rewrite *find_op_rw(ipfw_insn *cmd,
161    uint16_t *puidx, uint8_t *ptype);
162static int mark_object_kidx(struct ip_fw_chain *ch, struct ip_fw *rule,
163    uint32_t *bmask);
164static int ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
165    struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti);
166static int ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd,
167    struct tid_info *ti, struct obj_idx *pidx, int *unresolved);
168static void unref_rule_objects(struct ip_fw_chain *chain, struct ip_fw *rule);
169static void unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd,
170    struct obj_idx *oib, struct obj_idx *end);
171static int export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
172    struct sockopt_data *sd);
173
174/*
175 * Opcode object rewriter variables
176 */
177struct opcode_obj_rewrite *ctl3_rewriters;
178static size_t ctl3_rsize;
179
180/*
181 * static variables followed by global ones
182 */
183
184static VNET_DEFINE(uma_zone_t, ipfw_cntr_zone);
185#define	V_ipfw_cntr_zone		VNET(ipfw_cntr_zone)
186
187void
188ipfw_init_counters()
189{
190
191	V_ipfw_cntr_zone = uma_zcreate("IPFW counters",
192	    IPFW_RULE_CNTR_SIZE, NULL, NULL, NULL, NULL,
193	    UMA_ALIGN_PTR, UMA_ZONE_PCPU);
194}
195
196void
197ipfw_destroy_counters()
198{
199
200	uma_zdestroy(V_ipfw_cntr_zone);
201}
202
203struct ip_fw *
204ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize)
205{
206	struct ip_fw *rule;
207
208	rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO);
209	rule->cntr = uma_zalloc(V_ipfw_cntr_zone, M_WAITOK | M_ZERO);
210
211	return (rule);
212}
213
214static void
215free_rule(struct ip_fw *rule)
216{
217
218	uma_zfree(V_ipfw_cntr_zone, rule->cntr);
219	free(rule, M_IPFW);
220}
221
222
223/*
224 * Find the smallest rule >= key, id.
225 * We could use bsearch but it is so simple that we code it directly
226 */
227int
228ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id)
229{
230	int i, lo, hi;
231	struct ip_fw *r;
232
233  	for (lo = 0, hi = chain->n_rules - 1; lo < hi;) {
234		i = (lo + hi) / 2;
235		r = chain->map[i];
236		if (r->rulenum < key)
237			lo = i + 1;	/* continue from the next one */
238		else if (r->rulenum > key)
239			hi = i;		/* this might be good */
240		else if (r->id < id)
241			lo = i + 1;	/* continue from the next one */
242		else /* r->id >= id */
243			hi = i;		/* this might be good */
244	}
245	return hi;
246}
247
248/*
249 * Builds skipto cache on rule set @map.
250 */
251static void
252update_skipto_cache(struct ip_fw_chain *chain, struct ip_fw **map)
253{
254	int *smap, rulenum;
255	int i, mi;
256
257	IPFW_UH_WLOCK_ASSERT(chain);
258
259	mi = 0;
260	rulenum = map[mi]->rulenum;
261	smap = chain->idxmap_back;
262
263	if (smap == NULL)
264		return;
265
266	for (i = 0; i < 65536; i++) {
267		smap[i] = mi;
268		/* Use the same rule index until i < rulenum */
269		if (i != rulenum || i == 65535)
270			continue;
271		/* Find next rule with num > i */
272		rulenum = map[++mi]->rulenum;
273		while (rulenum == i)
274			rulenum = map[++mi]->rulenum;
275	}
276}
277
278/*
279 * Swaps prepared (backup) index with current one.
280 */
281static void
282swap_skipto_cache(struct ip_fw_chain *chain)
283{
284	int *map;
285
286	IPFW_UH_WLOCK_ASSERT(chain);
287	IPFW_WLOCK_ASSERT(chain);
288
289	map = chain->idxmap;
290	chain->idxmap = chain->idxmap_back;
291	chain->idxmap_back = map;
292}
293
294/*
295 * Allocate and initialize skipto cache.
296 */
297void
298ipfw_init_skipto_cache(struct ip_fw_chain *chain)
299{
300	int *idxmap, *idxmap_back;
301
302	idxmap = malloc(65536 * sizeof(uint32_t *), M_IPFW,
303	    M_WAITOK | M_ZERO);
304	idxmap_back = malloc(65536 * sizeof(uint32_t *), M_IPFW,
305	    M_WAITOK | M_ZERO);
306
307	/*
308	 * Note we may be called at any time after initialization,
309	 * for example, on first skipto rule, so we need to
310	 * provide valid chain->idxmap on return
311	 */
312
313	IPFW_UH_WLOCK(chain);
314	if (chain->idxmap != NULL) {
315		IPFW_UH_WUNLOCK(chain);
316		free(idxmap, M_IPFW);
317		free(idxmap_back, M_IPFW);
318		return;
319	}
320
321	/* Set backup pointer first to permit building cache */
322	chain->idxmap_back = idxmap_back;
323	update_skipto_cache(chain, chain->map);
324	IPFW_WLOCK(chain);
325	/* It is now safe to set chain->idxmap ptr */
326	chain->idxmap = idxmap;
327	swap_skipto_cache(chain);
328	IPFW_WUNLOCK(chain);
329	IPFW_UH_WUNLOCK(chain);
330}
331
332/*
333 * Destroys skipto cache.
334 */
335void
336ipfw_destroy_skipto_cache(struct ip_fw_chain *chain)
337{
338
339	if (chain->idxmap != NULL)
340		free(chain->idxmap, M_IPFW);
341	if (chain->idxmap != NULL)
342		free(chain->idxmap_back, M_IPFW);
343}
344
345
346/*
347 * allocate a new map, returns the chain locked. extra is the number
348 * of entries to add or delete.
349 */
350static struct ip_fw **
351get_map(struct ip_fw_chain *chain, int extra, int locked)
352{
353
354	for (;;) {
355		struct ip_fw **map;
356		int i, mflags;
357
358		mflags = M_ZERO | ((locked != 0) ? M_NOWAIT : M_WAITOK);
359
360		i = chain->n_rules + extra;
361		map = malloc(i * sizeof(struct ip_fw *), M_IPFW, mflags);
362		if (map == NULL) {
363			printf("%s: cannot allocate map\n", __FUNCTION__);
364			return NULL;
365		}
366		if (!locked)
367			IPFW_UH_WLOCK(chain);
368		if (i >= chain->n_rules + extra) /* good */
369			return map;
370		/* otherwise we lost the race, free and retry */
371		if (!locked)
372			IPFW_UH_WUNLOCK(chain);
373		free(map, M_IPFW);
374	}
375}
376
377/*
378 * swap the maps. It is supposed to be called with IPFW_UH_WLOCK
379 */
380static struct ip_fw **
381swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
382{
383	struct ip_fw **old_map;
384
385	IPFW_WLOCK(chain);
386	chain->id++;
387	chain->n_rules = new_len;
388	old_map = chain->map;
389	chain->map = new_map;
390	swap_skipto_cache(chain);
391	IPFW_WUNLOCK(chain);
392	return old_map;
393}
394
395
396static void
397export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr)
398{
399	struct timeval boottime;
400
401	cntr->size = sizeof(*cntr);
402
403	if (krule->cntr != NULL) {
404		cntr->pcnt = counter_u64_fetch(krule->cntr);
405		cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
406		cntr->timestamp = krule->timestamp;
407	}
408	if (cntr->timestamp > 0) {
409		getboottime(&boottime);
410		cntr->timestamp += boottime.tv_sec;
411	}
412}
413
414static void
415export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr)
416{
417	struct timeval boottime;
418
419	if (krule->cntr != NULL) {
420		cntr->pcnt = counter_u64_fetch(krule->cntr);
421		cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
422		cntr->timestamp = krule->timestamp;
423	}
424	if (cntr->timestamp > 0) {
425		getboottime(&boottime);
426		cntr->timestamp += boottime.tv_sec;
427	}
428}
429
430/*
431 * Copies rule @urule from v1 userland format (current).
432 * to kernel @krule.
433 * Assume @krule is zeroed.
434 */
435static void
436import_rule1(struct rule_check_info *ci)
437{
438	struct ip_fw_rule *urule;
439	struct ip_fw *krule;
440
441	urule = (struct ip_fw_rule *)ci->urule;
442	krule = (struct ip_fw *)ci->krule;
443
444	/* copy header */
445	krule->act_ofs = urule->act_ofs;
446	krule->cmd_len = urule->cmd_len;
447	krule->rulenum = urule->rulenum;
448	krule->set = urule->set;
449	krule->flags = urule->flags;
450
451	/* Save rulenum offset */
452	ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum);
453
454	/* Copy opcodes */
455	memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
456}
457
458/*
459 * Export rule into v1 format (Current).
460 * Layout:
461 * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT)
462 *     [ ip_fw_rule ] OR
463 *     [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs).
464 * ]
465 * Assume @data is zeroed.
466 */
467static void
468export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
469{
470	struct ip_fw_bcounter *cntr;
471	struct ip_fw_rule *urule;
472	ipfw_obj_tlv *tlv;
473
474	/* Fill in TLV header */
475	tlv = (ipfw_obj_tlv *)data;
476	tlv->type = IPFW_TLV_RULE_ENT;
477	tlv->length = len;
478
479	if (rcntrs != 0) {
480		/* Copy counters */
481		cntr = (struct ip_fw_bcounter *)(tlv + 1);
482		urule = (struct ip_fw_rule *)(cntr + 1);
483		export_cntr1_base(krule, cntr);
484	} else
485		urule = (struct ip_fw_rule *)(tlv + 1);
486
487	/* copy header */
488	urule->act_ofs = krule->act_ofs;
489	urule->cmd_len = krule->cmd_len;
490	urule->rulenum = krule->rulenum;
491	urule->set = krule->set;
492	urule->flags = krule->flags;
493	urule->id = krule->id;
494
495	/* Copy opcodes */
496	memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
497}
498
499
500/*
501 * Copies rule @urule from FreeBSD8 userland format (v0)
502 * to kernel @krule.
503 * Assume @krule is zeroed.
504 */
505static void
506import_rule0(struct rule_check_info *ci)
507{
508	struct ip_fw_rule0 *urule;
509	struct ip_fw *krule;
510	int cmdlen, l;
511	ipfw_insn *cmd;
512	ipfw_insn_limit *lcmd;
513	ipfw_insn_if *cmdif;
514
515	urule = (struct ip_fw_rule0 *)ci->urule;
516	krule = (struct ip_fw *)ci->krule;
517
518	/* copy header */
519	krule->act_ofs = urule->act_ofs;
520	krule->cmd_len = urule->cmd_len;
521	krule->rulenum = urule->rulenum;
522	krule->set = urule->set;
523	if ((urule->_pad & 1) != 0)
524		krule->flags |= IPFW_RULE_NOOPT;
525
526	/* Save rulenum offset */
527	ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum);
528
529	/* Copy opcodes */
530	memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
531
532	/*
533	 * Alter opcodes:
534	 * 1) convert tablearg value from 65535 to 0
535	 * 2) Add high bit to O_SETFIB/O_SETDSCP values (to make room
536	 *    for targ).
537	 * 3) convert table number in iface opcodes to u16
538	 * 4) convert old `nat global` into new 65535
539	 */
540	l = krule->cmd_len;
541	cmd = krule->cmd;
542	cmdlen = 0;
543
544	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
545		cmdlen = F_LEN(cmd);
546
547		switch (cmd->opcode) {
548		/* Opcodes supporting tablearg */
549		case O_TAG:
550		case O_TAGGED:
551		case O_PIPE:
552		case O_QUEUE:
553		case O_DIVERT:
554		case O_TEE:
555		case O_SKIPTO:
556		case O_CALLRETURN:
557		case O_NETGRAPH:
558		case O_NGTEE:
559		case O_NAT:
560			if (cmd->arg1 == IP_FW_TABLEARG)
561				cmd->arg1 = IP_FW_TARG;
562			else if (cmd->arg1 == 0)
563				cmd->arg1 = IP_FW_NAT44_GLOBAL;
564			break;
565		case O_SETFIB:
566		case O_SETDSCP:
567			if (cmd->arg1 == IP_FW_TABLEARG)
568				cmd->arg1 = IP_FW_TARG;
569			else
570				cmd->arg1 |= 0x8000;
571			break;
572		case O_LIMIT:
573			lcmd = (ipfw_insn_limit *)cmd;
574			if (lcmd->conn_limit == IP_FW_TABLEARG)
575				lcmd->conn_limit = IP_FW_TARG;
576			break;
577		/* Interface tables */
578		case O_XMIT:
579		case O_RECV:
580		case O_VIA:
581			/* Interface table, possibly */
582			cmdif = (ipfw_insn_if *)cmd;
583			if (cmdif->name[0] != '\1')
584				break;
585
586			cmdif->p.kidx = (uint16_t)cmdif->p.glob;
587			break;
588		}
589	}
590}
591
592/*
593 * Copies rule @krule from kernel to FreeBSD8 userland format (v0)
594 */
595static void
596export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len)
597{
598	int cmdlen, l;
599	ipfw_insn *cmd;
600	ipfw_insn_limit *lcmd;
601	ipfw_insn_if *cmdif;
602
603	/* copy header */
604	memset(urule, 0, len);
605	urule->act_ofs = krule->act_ofs;
606	urule->cmd_len = krule->cmd_len;
607	urule->rulenum = krule->rulenum;
608	urule->set = krule->set;
609	if ((krule->flags & IPFW_RULE_NOOPT) != 0)
610		urule->_pad |= 1;
611
612	/* Copy opcodes */
613	memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
614
615	/* Export counters */
616	export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt);
617
618	/*
619	 * Alter opcodes:
620	 * 1) convert tablearg value from 0 to 65535
621	 * 2) Remove highest bit from O_SETFIB/O_SETDSCP values.
622	 * 3) convert table number in iface opcodes to int
623	 */
624	l = urule->cmd_len;
625	cmd = urule->cmd;
626	cmdlen = 0;
627
628	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
629		cmdlen = F_LEN(cmd);
630
631		switch (cmd->opcode) {
632		/* Opcodes supporting tablearg */
633		case O_TAG:
634		case O_TAGGED:
635		case O_PIPE:
636		case O_QUEUE:
637		case O_DIVERT:
638		case O_TEE:
639		case O_SKIPTO:
640		case O_CALLRETURN:
641		case O_NETGRAPH:
642		case O_NGTEE:
643		case O_NAT:
644			if (cmd->arg1 == IP_FW_TARG)
645				cmd->arg1 = IP_FW_TABLEARG;
646			else if (cmd->arg1 == IP_FW_NAT44_GLOBAL)
647				cmd->arg1 = 0;
648			break;
649		case O_SETFIB:
650		case O_SETDSCP:
651			if (cmd->arg1 == IP_FW_TARG)
652				cmd->arg1 = IP_FW_TABLEARG;
653			else
654				cmd->arg1 &= ~0x8000;
655			break;
656		case O_LIMIT:
657			lcmd = (ipfw_insn_limit *)cmd;
658			if (lcmd->conn_limit == IP_FW_TARG)
659				lcmd->conn_limit = IP_FW_TABLEARG;
660			break;
661		/* Interface tables */
662		case O_XMIT:
663		case O_RECV:
664		case O_VIA:
665			/* Interface table, possibly */
666			cmdif = (ipfw_insn_if *)cmd;
667			if (cmdif->name[0] != '\1')
668				break;
669
670			cmdif->p.glob = cmdif->p.kidx;
671			break;
672		}
673	}
674}
675
676/*
677 * Add new rule(s) to the list possibly creating rule number for each.
678 * Update the rule_number in the input struct so the caller knows it as well.
679 * Must be called without IPFW_UH held
680 */
681static int
682commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count)
683{
684	int error, i, insert_before, tcount;
685	uint16_t rulenum, *pnum;
686	struct rule_check_info *ci;
687	struct ip_fw *krule;
688	struct ip_fw **map;	/* the new array of pointers */
689
690	/* Check if we need to do table/obj index remap */
691	tcount = 0;
692	for (ci = rci, i = 0; i < count; ci++, i++) {
693		if (ci->object_opcodes == 0)
694			continue;
695
696		/*
697		 * Rule has some object opcodes.
698		 * We need to find (and create non-existing)
699		 * kernel objects, and reference existing ones.
700		 */
701		error = rewrite_rule_uidx(chain, ci);
702		if (error != 0) {
703
704			/*
705			 * rewrite failed, state for current rule
706			 * has been reverted. Check if we need to
707			 * revert more.
708			 */
709			if (tcount > 0) {
710
711				/*
712				 * We have some more table rules
713				 * we need to rollback.
714				 */
715
716				IPFW_UH_WLOCK(chain);
717				while (ci != rci) {
718					ci--;
719					if (ci->object_opcodes == 0)
720						continue;
721					unref_rule_objects(chain,ci->krule);
722
723				}
724				IPFW_UH_WUNLOCK(chain);
725
726			}
727
728			return (error);
729		}
730
731		tcount++;
732	}
733
734	/* get_map returns with IPFW_UH_WLOCK if successful */
735	map = get_map(chain, count, 0 /* not locked */);
736	if (map == NULL) {
737		if (tcount > 0) {
738			/* Unbind tables */
739			IPFW_UH_WLOCK(chain);
740			for (ci = rci, i = 0; i < count; ci++, i++) {
741				if (ci->object_opcodes == 0)
742					continue;
743
744				unref_rule_objects(chain, ci->krule);
745			}
746			IPFW_UH_WUNLOCK(chain);
747		}
748
749		return (ENOSPC);
750	}
751
752	if (V_autoinc_step < 1)
753		V_autoinc_step = 1;
754	else if (V_autoinc_step > 1000)
755		V_autoinc_step = 1000;
756
757	/* FIXME: Handle count > 1 */
758	ci = rci;
759	krule = ci->krule;
760	rulenum = krule->rulenum;
761
762	/* find the insertion point, we will insert before */
763	insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE;
764	i = ipfw_find_rule(chain, insert_before, 0);
765	/* duplicate first part */
766	if (i > 0)
767		bcopy(chain->map, map, i * sizeof(struct ip_fw *));
768	map[i] = krule;
769	/* duplicate remaining part, we always have the default rule */
770	bcopy(chain->map + i, map + i + 1,
771		sizeof(struct ip_fw *) *(chain->n_rules - i));
772	if (rulenum == 0) {
773		/* Compute rule number and write it back */
774		rulenum = i > 0 ? map[i-1]->rulenum : 0;
775		if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
776			rulenum += V_autoinc_step;
777		krule->rulenum = rulenum;
778		/* Save number to userland rule */
779		pnum = (uint16_t *)((caddr_t)ci->urule + ci->urule_numoff);
780		*pnum = rulenum;
781	}
782
783	krule->id = chain->id + 1;
784	update_skipto_cache(chain, map);
785	map = swap_map(chain, map, chain->n_rules + 1);
786	chain->static_len += RULEUSIZE0(krule);
787	IPFW_UH_WUNLOCK(chain);
788	if (map)
789		free(map, M_IPFW);
790	return (0);
791}
792
793/*
794 * Adds @rule to the list of rules to reap
795 */
796void
797ipfw_reap_add(struct ip_fw_chain *chain, struct ip_fw **head,
798    struct ip_fw *rule)
799{
800
801	IPFW_UH_WLOCK_ASSERT(chain);
802
803	/* Unlink rule from everywhere */
804	unref_rule_objects(chain, rule);
805
806	*((struct ip_fw **)rule) = *head;
807	*head = rule;
808}
809
810/*
811 * Reclaim storage associated with a list of rules.  This is
812 * typically the list created using remove_rule.
813 * A NULL pointer on input is handled correctly.
814 */
815void
816ipfw_reap_rules(struct ip_fw *head)
817{
818	struct ip_fw *rule;
819
820	while ((rule = head) != NULL) {
821		head = *((struct ip_fw **)head);
822		free_rule(rule);
823	}
824}
825
826/*
827 * Rules to keep are
828 *	(default || reserved || !match_set || !match_number)
829 * where
830 *   default ::= (rule->rulenum == IPFW_DEFAULT_RULE)
831 *	// the default rule is always protected
832 *
833 *   reserved ::= (cmd == 0 && n == 0 && rule->set == RESVD_SET)
834 *	// RESVD_SET is protected only if cmd == 0 and n == 0 ("ipfw flush")
835 *
836 *   match_set ::= (cmd == 0 || rule->set == set)
837 *	// set number is ignored for cmd == 0
838 *
839 *   match_number ::= (cmd == 1 || n == 0 || n == rule->rulenum)
840 *	// number is ignored for cmd == 1 or n == 0
841 *
842 */
843int
844ipfw_match_range(struct ip_fw *rule, ipfw_range_tlv *rt)
845{
846
847	/* Don't match default rule for modification queries */
848	if (rule->rulenum == IPFW_DEFAULT_RULE &&
849	    (rt->flags & IPFW_RCFLAG_DEFAULT) == 0)
850		return (0);
851
852	/* Don't match rules in reserved set for flush requests */
853	if ((rt->flags & IPFW_RCFLAG_ALL) != 0 && rule->set == RESVD_SET)
854		return (0);
855
856	/* If we're filtering by set, don't match other sets */
857	if ((rt->flags & IPFW_RCFLAG_SET) != 0 && rule->set != rt->set)
858		return (0);
859
860	if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 &&
861	    (rule->rulenum < rt->start_rule || rule->rulenum > rt->end_rule))
862		return (0);
863
864	return (1);
865}
866
867struct manage_sets_args {
868	uint16_t	set;
869	uint8_t		new_set;
870};
871
872static int
873swap_sets_cb(struct namedobj_instance *ni, struct named_object *no,
874    void *arg)
875{
876	struct manage_sets_args *args;
877
878	args = (struct manage_sets_args *)arg;
879	if (no->set == (uint8_t)args->set)
880		no->set = args->new_set;
881	else if (no->set == args->new_set)
882		no->set = (uint8_t)args->set;
883	return (0);
884}
885
886static int
887move_sets_cb(struct namedobj_instance *ni, struct named_object *no,
888    void *arg)
889{
890	struct manage_sets_args *args;
891
892	args = (struct manage_sets_args *)arg;
893	if (no->set == (uint8_t)args->set)
894		no->set = args->new_set;
895	return (0);
896}
897
898static int
899test_sets_cb(struct namedobj_instance *ni, struct named_object *no,
900    void *arg)
901{
902	struct manage_sets_args *args;
903
904	args = (struct manage_sets_args *)arg;
905	if (no->set != (uint8_t)args->set)
906		return (0);
907	if (ipfw_objhash_lookup_name_type(ni, args->new_set,
908	    no->etlv, no->name) != NULL)
909		return (EEXIST);
910	return (0);
911}
912
913/*
914 * Generic function to handler moving and swapping sets.
915 */
916int
917ipfw_obj_manage_sets(struct namedobj_instance *ni, uint16_t type,
918    uint16_t set, uint8_t new_set, enum ipfw_sets_cmd cmd)
919{
920	struct manage_sets_args args;
921	struct named_object *no;
922
923	args.set = set;
924	args.new_set = new_set;
925	switch (cmd) {
926	case SWAP_ALL:
927		return (ipfw_objhash_foreach_type(ni, swap_sets_cb,
928		    &args, type));
929	case TEST_ALL:
930		return (ipfw_objhash_foreach_type(ni, test_sets_cb,
931		    &args, type));
932	case MOVE_ALL:
933		return (ipfw_objhash_foreach_type(ni, move_sets_cb,
934		    &args, type));
935	case COUNT_ONE:
936		/*
937		 * @set used to pass kidx.
938		 * When @new_set is zero - reset object counter,
939		 * otherwise increment it.
940		 */
941		no = ipfw_objhash_lookup_kidx(ni, set);
942		if (new_set != 0)
943			no->ocnt++;
944		else
945			no->ocnt = 0;
946		return (0);
947	case TEST_ONE:
948		/* @set used to pass kidx */
949		no = ipfw_objhash_lookup_kidx(ni, set);
950		/*
951		 * First check number of references:
952		 * when it differs, this mean other rules are holding
953		 * reference to given object, so it is not possible to
954		 * change its set. Note that refcnt may account references
955		 * to some going-to-be-added rules. Since we don't know
956		 * their numbers (and even if they will be added) it is
957		 * perfectly OK to return error here.
958		 */
959		if (no->ocnt != no->refcnt)
960			return (EBUSY);
961		if (ipfw_objhash_lookup_name_type(ni, new_set, type,
962		    no->name) != NULL)
963			return (EEXIST);
964		return (0);
965	case MOVE_ONE:
966		/* @set used to pass kidx */
967		no = ipfw_objhash_lookup_kidx(ni, set);
968		no->set = new_set;
969		return (0);
970	}
971	return (EINVAL);
972}
973
974/*
975 * Delete rules matching range @rt.
976 * Saves number of deleted rules in @ndel.
977 *
978 * Returns 0 on success.
979 */
980static int
981delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
982{
983	struct ip_fw *reap, *rule, **map;
984	int end, start;
985	int i, n, ndyn, ofs;
986
987	reap = NULL;
988	IPFW_UH_WLOCK(chain);	/* arbitrate writers */
989
990	/*
991	 * Stage 1: Determine range to inspect.
992	 * Range is half-inclusive, e.g [start, end).
993	 */
994	start = 0;
995	end = chain->n_rules - 1;
996
997	if ((rt->flags & IPFW_RCFLAG_RANGE) != 0) {
998		start = ipfw_find_rule(chain, rt->start_rule, 0);
999
1000		end = ipfw_find_rule(chain, rt->end_rule, 0);
1001		if (rt->end_rule != IPFW_DEFAULT_RULE)
1002			while (chain->map[end]->rulenum == rt->end_rule)
1003				end++;
1004	}
1005
1006	/* Allocate new map of the same size */
1007	map = get_map(chain, 0, 1 /* locked */);
1008	if (map == NULL) {
1009		IPFW_UH_WUNLOCK(chain);
1010		return (ENOMEM);
1011	}
1012
1013	n = 0;
1014	ndyn = 0;
1015	ofs = start;
1016	/* 1. bcopy the initial part of the map */
1017	if (start > 0)
1018		bcopy(chain->map, map, start * sizeof(struct ip_fw *));
1019	/* 2. copy active rules between start and end */
1020	for (i = start; i < end; i++) {
1021		rule = chain->map[i];
1022		if (ipfw_match_range(rule, rt) == 0) {
1023			map[ofs++] = rule;
1024			continue;
1025		}
1026
1027		n++;
1028		if (ipfw_is_dyn_rule(rule) != 0)
1029			ndyn++;
1030	}
1031	/* 3. copy the final part of the map */
1032	bcopy(chain->map + end, map + ofs,
1033		(chain->n_rules - end) * sizeof(struct ip_fw *));
1034	/* 4. recalculate skipto cache */
1035	update_skipto_cache(chain, map);
1036	/* 5. swap the maps (under UH_WLOCK + WHLOCK) */
1037	map = swap_map(chain, map, chain->n_rules - n);
1038	/* 6. Remove all dynamic states originated by deleted rules */
1039	if (ndyn > 0)
1040		ipfw_expire_dyn_rules(chain, rt);
1041	/* 7. now remove the rules deleted from the old map */
1042	for (i = start; i < end; i++) {
1043		rule = map[i];
1044		if (ipfw_match_range(rule, rt) == 0)
1045			continue;
1046		chain->static_len -= RULEUSIZE0(rule);
1047		ipfw_reap_add(chain, &reap, rule);
1048	}
1049	IPFW_UH_WUNLOCK(chain);
1050
1051	ipfw_reap_rules(reap);
1052	if (map != NULL)
1053		free(map, M_IPFW);
1054	*ndel = n;
1055	return (0);
1056}
1057
1058static int
1059move_objects(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
1060{
1061	struct opcode_obj_rewrite *rw;
1062	struct ip_fw *rule;
1063	ipfw_insn *cmd;
1064	int cmdlen, i, l, c;
1065	uint16_t kidx;
1066
1067	IPFW_UH_WLOCK_ASSERT(ch);
1068
1069	/* Stage 1: count number of references by given rules */
1070	for (c = 0, i = 0; i < ch->n_rules - 1; i++) {
1071		rule = ch->map[i];
1072		if (ipfw_match_range(rule, rt) == 0)
1073			continue;
1074		if (rule->set == rt->new_set) /* nothing to do */
1075			continue;
1076		/* Search opcodes with named objects */
1077		for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
1078		    l > 0; l -= cmdlen, cmd += cmdlen) {
1079			cmdlen = F_LEN(cmd);
1080			rw = find_op_rw(cmd, &kidx, NULL);
1081			if (rw == NULL || rw->manage_sets == NULL)
1082				continue;
1083			/*
1084			 * When manage_sets() returns non-zero value to
1085			 * COUNT_ONE command, consider this as an object
1086			 * doesn't support sets (e.g. disabled with sysctl).
1087			 * So, skip checks for this object.
1088			 */
1089			if (rw->manage_sets(ch, kidx, 1, COUNT_ONE) != 0)
1090				continue;
1091			c++;
1092		}
1093	}
1094	if (c == 0) /* No objects found */
1095		return (0);
1096	/* Stage 2: verify "ownership" */
1097	for (c = 0, i = 0; (i < ch->n_rules - 1) && c == 0; i++) {
1098		rule = ch->map[i];
1099		if (ipfw_match_range(rule, rt) == 0)
1100			continue;
1101		if (rule->set == rt->new_set) /* nothing to do */
1102			continue;
1103		/* Search opcodes with named objects */
1104		for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
1105		    l > 0 && c == 0; l -= cmdlen, cmd += cmdlen) {
1106			cmdlen = F_LEN(cmd);
1107			rw = find_op_rw(cmd, &kidx, NULL);
1108			if (rw == NULL || rw->manage_sets == NULL)
1109				continue;
1110			/* Test for ownership and conflicting names */
1111			c = rw->manage_sets(ch, kidx,
1112			    (uint8_t)rt->new_set, TEST_ONE);
1113		}
1114	}
1115	/* Stage 3: change set and cleanup */
1116	for (i = 0; i < ch->n_rules - 1; i++) {
1117		rule = ch->map[i];
1118		if (ipfw_match_range(rule, rt) == 0)
1119			continue;
1120		if (rule->set == rt->new_set) /* nothing to do */
1121			continue;
1122		/* Search opcodes with named objects */
1123		for (l = rule->cmd_len, cmdlen = 0, cmd = rule->cmd;
1124		    l > 0; l -= cmdlen, cmd += cmdlen) {
1125			cmdlen = F_LEN(cmd);
1126			rw = find_op_rw(cmd, &kidx, NULL);
1127			if (rw == NULL || rw->manage_sets == NULL)
1128				continue;
1129			/* cleanup object counter */
1130			rw->manage_sets(ch, kidx,
1131			    0 /* reset counter */, COUNT_ONE);
1132			if (c != 0)
1133				continue;
1134			/* change set */
1135			rw->manage_sets(ch, kidx,
1136			    (uint8_t)rt->new_set, MOVE_ONE);
1137		}
1138	}
1139	return (c);
1140}/*
1141 * Changes set of given rule rannge @rt
1142 * with each other.
1143 *
1144 * Returns 0 on success.
1145 */
1146static int
1147move_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1148{
1149	struct ip_fw *rule;
1150	int i;
1151
1152	IPFW_UH_WLOCK(chain);
1153
1154	/*
1155	 * Move rules with matching paramenerts to a new set.
1156	 * This one is much more complex. We have to ensure
1157	 * that all referenced tables (if any) are referenced
1158	 * by given rule subset only. Otherwise, we can't move
1159	 * them to new set and have to return error.
1160	 */
1161	if ((i = move_objects(chain, rt)) != 0) {
1162		IPFW_UH_WUNLOCK(chain);
1163		return (i);
1164	}
1165
1166	/* XXX: We have to do swap holding WLOCK */
1167	for (i = 0; i < chain->n_rules; i++) {
1168		rule = chain->map[i];
1169		if (ipfw_match_range(rule, rt) == 0)
1170			continue;
1171		rule->set = rt->new_set;
1172	}
1173
1174	IPFW_UH_WUNLOCK(chain);
1175
1176	return (0);
1177}
1178
1179/*
1180 * Clear counters for a specific rule.
1181 * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
1182 * so we only care that rules do not disappear.
1183 */
1184static void
1185clear_counters(struct ip_fw *rule, int log_only)
1186{
1187	ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
1188
1189	if (log_only == 0)
1190		IPFW_ZERO_RULE_COUNTER(rule);
1191	if (l->o.opcode == O_LOG)
1192		l->log_left = l->max_log;
1193}
1194
1195/*
1196 * Flushes rules counters and/or log values on matching range.
1197 *
1198 * Returns number of items cleared.
1199 */
1200static int
1201clear_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int log_only)
1202{
1203	struct ip_fw *rule;
1204	int num;
1205	int i;
1206
1207	num = 0;
1208	rt->flags |= IPFW_RCFLAG_DEFAULT;
1209
1210	IPFW_UH_WLOCK(chain);	/* arbitrate writers */
1211	for (i = 0; i < chain->n_rules; i++) {
1212		rule = chain->map[i];
1213		if (ipfw_match_range(rule, rt) == 0)
1214			continue;
1215		clear_counters(rule, log_only);
1216		num++;
1217	}
1218	IPFW_UH_WUNLOCK(chain);
1219
1220	return (num);
1221}
1222
1223static int
1224check_range_tlv(ipfw_range_tlv *rt)
1225{
1226
1227	if (rt->head.length != sizeof(*rt))
1228		return (1);
1229	if (rt->start_rule > rt->end_rule)
1230		return (1);
1231	if (rt->set >= IPFW_MAX_SETS || rt->new_set >= IPFW_MAX_SETS)
1232		return (1);
1233
1234	if ((rt->flags & IPFW_RCFLAG_USER) != rt->flags)
1235		return (1);
1236
1237	return (0);
1238}
1239
1240/*
1241 * Delete rules matching specified parameters
1242 * Data layout (v0)(current):
1243 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1244 * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1245 *
1246 * Saves number of deleted rules in ipfw_range_tlv->new_set.
1247 *
1248 * Returns 0 on success.
1249 */
1250static int
1251del_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1252    struct sockopt_data *sd)
1253{
1254	ipfw_range_header *rh;
1255	int error, ndel;
1256
1257	if (sd->valsize != sizeof(*rh))
1258		return (EINVAL);
1259
1260	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1261
1262	if (check_range_tlv(&rh->range) != 0)
1263		return (EINVAL);
1264
1265	ndel = 0;
1266	if ((error = delete_range(chain, &rh->range, &ndel)) != 0)
1267		return (error);
1268
1269	/* Save number of rules deleted */
1270	rh->range.new_set = ndel;
1271	return (0);
1272}
1273
1274/*
1275 * Move rules/sets matching specified parameters
1276 * Data layout (v0)(current):
1277 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1278 *
1279 * Returns 0 on success.
1280 */
1281static int
1282move_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1283    struct sockopt_data *sd)
1284{
1285	ipfw_range_header *rh;
1286
1287	if (sd->valsize != sizeof(*rh))
1288		return (EINVAL);
1289
1290	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1291
1292	if (check_range_tlv(&rh->range) != 0)
1293		return (EINVAL);
1294
1295	return (move_range(chain, &rh->range));
1296}
1297
1298/*
1299 * Clear rule accounting data matching specified parameters
1300 * Data layout (v0)(current):
1301 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1302 * Reply: [ ipfw_obj_header ipfw_range_tlv ]
1303 *
1304 * Saves number of cleared rules in ipfw_range_tlv->new_set.
1305 *
1306 * Returns 0 on success.
1307 */
1308static int
1309clear_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1310    struct sockopt_data *sd)
1311{
1312	ipfw_range_header *rh;
1313	int log_only, num;
1314	char *msg;
1315
1316	if (sd->valsize != sizeof(*rh))
1317		return (EINVAL);
1318
1319	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1320
1321	if (check_range_tlv(&rh->range) != 0)
1322		return (EINVAL);
1323
1324	log_only = (op3->opcode == IP_FW_XRESETLOG);
1325
1326	num = clear_range(chain, &rh->range, log_only);
1327
1328	if (rh->range.flags & IPFW_RCFLAG_ALL)
1329		msg = log_only ? "All logging counts reset" :
1330		    "Accounting cleared";
1331	else
1332		msg = log_only ? "logging count reset" : "cleared";
1333
1334	if (V_fw_verbose) {
1335		int lev = LOG_SECURITY | LOG_NOTICE;
1336		log(lev, "ipfw: %s.\n", msg);
1337	}
1338
1339	/* Save number of rules cleared */
1340	rh->range.new_set = num;
1341	return (0);
1342}
1343
1344static void
1345enable_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt)
1346{
1347	uint32_t v_set;
1348
1349	IPFW_UH_WLOCK_ASSERT(chain);
1350
1351	/* Change enabled/disabled sets mask */
1352	v_set = (V_set_disable | rt->set) & ~rt->new_set;
1353	v_set &= ~(1 << RESVD_SET); /* set RESVD_SET always enabled */
1354	IPFW_WLOCK(chain);
1355	V_set_disable = v_set;
1356	IPFW_WUNLOCK(chain);
1357}
1358
1359static int
1360swap_sets(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int mv)
1361{
1362	struct opcode_obj_rewrite *rw;
1363	struct ip_fw *rule;
1364	int i;
1365
1366	IPFW_UH_WLOCK_ASSERT(chain);
1367
1368	if (rt->set == rt->new_set) /* nothing to do */
1369		return (0);
1370
1371	if (mv != 0) {
1372		/*
1373		 * Berfore moving the rules we need to check that
1374		 * there aren't any conflicting named objects.
1375		 */
1376		for (rw = ctl3_rewriters;
1377		    rw < ctl3_rewriters + ctl3_rsize; rw++) {
1378			if (rw->manage_sets == NULL)
1379				continue;
1380			i = rw->manage_sets(chain, (uint8_t)rt->set,
1381			    (uint8_t)rt->new_set, TEST_ALL);
1382			if (i != 0)
1383				return (EEXIST);
1384		}
1385	}
1386	/* Swap or move two sets */
1387	for (i = 0; i < chain->n_rules - 1; i++) {
1388		rule = chain->map[i];
1389		if (rule->set == (uint8_t)rt->set)
1390			rule->set = (uint8_t)rt->new_set;
1391		else if (rule->set == (uint8_t)rt->new_set && mv == 0)
1392			rule->set = (uint8_t)rt->set;
1393	}
1394	for (rw = ctl3_rewriters; rw < ctl3_rewriters + ctl3_rsize; rw++) {
1395		if (rw->manage_sets == NULL)
1396			continue;
1397		rw->manage_sets(chain, (uint8_t)rt->set,
1398		    (uint8_t)rt->new_set, mv != 0 ? MOVE_ALL: SWAP_ALL);
1399	}
1400	return (0);
1401}
1402
1403/*
1404 * Swaps or moves set
1405 * Data layout (v0)(current):
1406 * Request: [ ipfw_obj_header ipfw_range_tlv ]
1407 *
1408 * Returns 0 on success.
1409 */
1410static int
1411manage_sets(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
1412    struct sockopt_data *sd)
1413{
1414	ipfw_range_header *rh;
1415	int ret;
1416
1417	if (sd->valsize != sizeof(*rh))
1418		return (EINVAL);
1419
1420	rh = (ipfw_range_header *)ipfw_get_sopt_space(sd, sd->valsize);
1421
1422	if (rh->range.head.length != sizeof(ipfw_range_tlv))
1423		return (1);
1424	/* enable_sets() expects bitmasks. */
1425	if (op3->opcode != IP_FW_SET_ENABLE &&
1426	    (rh->range.set >= IPFW_MAX_SETS ||
1427	    rh->range.new_set >= IPFW_MAX_SETS))
1428		return (EINVAL);
1429
1430	ret = 0;
1431	IPFW_UH_WLOCK(chain);
1432	switch (op3->opcode) {
1433	case IP_FW_SET_SWAP:
1434	case IP_FW_SET_MOVE:
1435		ret = swap_sets(chain, &rh->range,
1436		    op3->opcode == IP_FW_SET_MOVE);
1437		break;
1438	case IP_FW_SET_ENABLE:
1439		enable_sets(chain, &rh->range);
1440		break;
1441	}
1442	IPFW_UH_WUNLOCK(chain);
1443
1444	return (ret);
1445}
1446
1447/**
1448 * Remove all rules with given number, or do set manipulation.
1449 * Assumes chain != NULL && *chain != NULL.
1450 *
1451 * The argument is an uint32_t. The low 16 bit are the rule or set number;
1452 * the next 8 bits are the new set; the top 8 bits indicate the command:
1453 *
1454 *	0	delete rules numbered "rulenum"
1455 *	1	delete rules in set "rulenum"
1456 *	2	move rules "rulenum" to set "new_set"
1457 *	3	move rules from set "rulenum" to set "new_set"
1458 *	4	swap sets "rulenum" and "new_set"
1459 *	5	delete rules "rulenum" and set "new_set"
1460 */
1461static int
1462del_entry(struct ip_fw_chain *chain, uint32_t arg)
1463{
1464	uint32_t num;	/* rule number or old_set */
1465	uint8_t cmd, new_set;
1466	int do_del, ndel;
1467	int error = 0;
1468	ipfw_range_tlv rt;
1469
1470	num = arg & 0xffff;
1471	cmd = (arg >> 24) & 0xff;
1472	new_set = (arg >> 16) & 0xff;
1473
1474	if (cmd > 5 || new_set > RESVD_SET)
1475		return EINVAL;
1476	if (cmd == 0 || cmd == 2 || cmd == 5) {
1477		if (num >= IPFW_DEFAULT_RULE)
1478			return EINVAL;
1479	} else {
1480		if (num > RESVD_SET)	/* old_set */
1481			return EINVAL;
1482	}
1483
1484	/* Convert old requests into new representation */
1485	memset(&rt, 0, sizeof(rt));
1486	rt.start_rule = num;
1487	rt.end_rule = num;
1488	rt.set = num;
1489	rt.new_set = new_set;
1490	do_del = 0;
1491
1492	switch (cmd) {
1493	case 0: /* delete rules numbered "rulenum" */
1494		if (num == 0)
1495			rt.flags |= IPFW_RCFLAG_ALL;
1496		else
1497			rt.flags |= IPFW_RCFLAG_RANGE;
1498		do_del = 1;
1499		break;
1500	case 1: /* delete rules in set "rulenum" */
1501		rt.flags |= IPFW_RCFLAG_SET;
1502		do_del = 1;
1503		break;
1504	case 5: /* delete rules "rulenum" and set "new_set" */
1505		rt.flags |= IPFW_RCFLAG_RANGE | IPFW_RCFLAG_SET;
1506		rt.set = new_set;
1507		rt.new_set = 0;
1508		do_del = 1;
1509		break;
1510	case 2: /* move rules "rulenum" to set "new_set" */
1511		rt.flags |= IPFW_RCFLAG_RANGE;
1512		break;
1513	case 3: /* move rules from set "rulenum" to set "new_set" */
1514		IPFW_UH_WLOCK(chain);
1515		error = swap_sets(chain, &rt, 1);
1516		IPFW_UH_WUNLOCK(chain);
1517		return (error);
1518	case 4: /* swap sets "rulenum" and "new_set" */
1519		IPFW_UH_WLOCK(chain);
1520		error = swap_sets(chain, &rt, 0);
1521		IPFW_UH_WUNLOCK(chain);
1522		return (error);
1523	default:
1524		return (ENOTSUP);
1525	}
1526
1527	if (do_del != 0) {
1528		if ((error = delete_range(chain, &rt, &ndel)) != 0)
1529			return (error);
1530
1531		if (ndel == 0 && (cmd != 1 && num != 0))
1532			return (EINVAL);
1533
1534		return (0);
1535	}
1536
1537	return (move_range(chain, &rt));
1538}
1539
1540/**
1541 * Reset some or all counters on firewall rules.
1542 * The argument `arg' is an u_int32_t. The low 16 bit are the rule number,
1543 * the next 8 bits are the set number, the top 8 bits are the command:
1544 *	0	work with rules from all set's;
1545 *	1	work with rules only from specified set.
1546 * Specified rule number is zero if we want to clear all entries.
1547 * log_only is 1 if we only want to reset logs, zero otherwise.
1548 */
1549static int
1550zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only)
1551{
1552	struct ip_fw *rule;
1553	char *msg;
1554	int i;
1555
1556	uint16_t rulenum = arg & 0xffff;
1557	uint8_t set = (arg >> 16) & 0xff;
1558	uint8_t cmd = (arg >> 24) & 0xff;
1559
1560	if (cmd > 1)
1561		return (EINVAL);
1562	if (cmd == 1 && set > RESVD_SET)
1563		return (EINVAL);
1564
1565	IPFW_UH_RLOCK(chain);
1566	if (rulenum == 0) {
1567		V_norule_counter = 0;
1568		for (i = 0; i < chain->n_rules; i++) {
1569			rule = chain->map[i];
1570			/* Skip rules not in our set. */
1571			if (cmd == 1 && rule->set != set)
1572				continue;
1573			clear_counters(rule, log_only);
1574		}
1575		msg = log_only ? "All logging counts reset" :
1576		    "Accounting cleared";
1577	} else {
1578		int cleared = 0;
1579		for (i = 0; i < chain->n_rules; i++) {
1580			rule = chain->map[i];
1581			if (rule->rulenum == rulenum) {
1582				if (cmd == 0 || rule->set == set)
1583					clear_counters(rule, log_only);
1584				cleared = 1;
1585			}
1586			if (rule->rulenum > rulenum)
1587				break;
1588		}
1589		if (!cleared) {	/* we did not find any matching rules */
1590			IPFW_UH_RUNLOCK(chain);
1591			return (EINVAL);
1592		}
1593		msg = log_only ? "logging count reset" : "cleared";
1594	}
1595	IPFW_UH_RUNLOCK(chain);
1596
1597	if (V_fw_verbose) {
1598		int lev = LOG_SECURITY | LOG_NOTICE;
1599
1600		if (rulenum)
1601			log(lev, "ipfw: Entry %d %s.\n", rulenum, msg);
1602		else
1603			log(lev, "ipfw: %s.\n", msg);
1604	}
1605	return (0);
1606}
1607
1608
1609/*
1610 * Check rule head in FreeBSD11 format
1611 *
1612 */
1613static int
1614check_ipfw_rule1(struct ip_fw_rule *rule, int size,
1615    struct rule_check_info *ci)
1616{
1617	int l;
1618
1619	if (size < sizeof(*rule)) {
1620		printf("ipfw: rule too short\n");
1621		return (EINVAL);
1622	}
1623
1624	/* Check for valid cmd_len */
1625	l = roundup2(RULESIZE(rule), sizeof(uint64_t));
1626	if (l != size) {
1627		printf("ipfw: size mismatch (have %d want %d)\n", size, l);
1628		return (EINVAL);
1629	}
1630	if (rule->act_ofs >= rule->cmd_len) {
1631		printf("ipfw: bogus action offset (%u > %u)\n",
1632		    rule->act_ofs, rule->cmd_len - 1);
1633		return (EINVAL);
1634	}
1635
1636	if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
1637		return (EINVAL);
1638
1639	return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
1640}
1641
1642/*
1643 * Check rule head in FreeBSD8 format
1644 *
1645 */
1646static int
1647check_ipfw_rule0(struct ip_fw_rule0 *rule, int size,
1648    struct rule_check_info *ci)
1649{
1650	int l;
1651
1652	if (size < sizeof(*rule)) {
1653		printf("ipfw: rule too short\n");
1654		return (EINVAL);
1655	}
1656
1657	/* Check for valid cmd_len */
1658	l = sizeof(*rule) + rule->cmd_len * 4 - 4;
1659	if (l != size) {
1660		printf("ipfw: size mismatch (have %d want %d)\n", size, l);
1661		return (EINVAL);
1662	}
1663	if (rule->act_ofs >= rule->cmd_len) {
1664		printf("ipfw: bogus action offset (%u > %u)\n",
1665		    rule->act_ofs, rule->cmd_len - 1);
1666		return (EINVAL);
1667	}
1668
1669	if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
1670		return (EINVAL);
1671
1672	return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci));
1673}
1674
1675static int
1676check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
1677{
1678	int cmdlen, l;
1679	int have_action;
1680
1681	have_action = 0;
1682
1683	/*
1684	 * Now go for the individual checks. Very simple ones, basically only
1685	 * instruction sizes.
1686	 */
1687	for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) {
1688		cmdlen = F_LEN(cmd);
1689		if (cmdlen > l) {
1690			printf("ipfw: opcode %d size truncated\n",
1691			    cmd->opcode);
1692			return EINVAL;
1693		}
1694		switch (cmd->opcode) {
1695		case O_PROBE_STATE:
1696		case O_KEEP_STATE:
1697			if (cmdlen != F_INSN_SIZE(ipfw_insn))
1698				goto bad_size;
1699			ci->object_opcodes++;
1700			break;
1701		case O_PROTO:
1702		case O_IP_SRC_ME:
1703		case O_IP_DST_ME:
1704		case O_LAYER2:
1705		case O_IN:
1706		case O_FRAG:
1707		case O_DIVERTED:
1708		case O_IPOPT:
1709		case O_IPTOS:
1710		case O_IPPRECEDENCE:
1711		case O_IPVER:
1712		case O_SOCKARG:
1713		case O_TCPFLAGS:
1714		case O_TCPOPTS:
1715		case O_ESTAB:
1716		case O_VERREVPATH:
1717		case O_VERSRCREACH:
1718		case O_ANTISPOOF:
1719		case O_IPSEC:
1720#ifdef INET6
1721		case O_IP6_SRC_ME:
1722		case O_IP6_DST_ME:
1723		case O_EXT_HDR:
1724		case O_IP6:
1725#endif
1726		case O_IP4:
1727		case O_TAG:
1728			if (cmdlen != F_INSN_SIZE(ipfw_insn))
1729				goto bad_size;
1730			break;
1731
1732		case O_EXTERNAL_ACTION:
1733			if (cmd->arg1 == 0 ||
1734			    cmdlen != F_INSN_SIZE(ipfw_insn)) {
1735				printf("ipfw: invalid external "
1736				    "action opcode\n");
1737				return (EINVAL);
1738			}
1739			ci->object_opcodes++;
1740			/*
1741			 * Do we have O_EXTERNAL_INSTANCE or O_EXTERNAL_DATA
1742			 * opcode?
1743			 */
1744			if (l != cmdlen) {
1745				l -= cmdlen;
1746				cmd += cmdlen;
1747				cmdlen = F_LEN(cmd);
1748				if (cmd->opcode == O_EXTERNAL_DATA)
1749					goto check_action;
1750				if (cmd->opcode != O_EXTERNAL_INSTANCE) {
1751					printf("ipfw: invalid opcode "
1752					    "next to external action %u\n",
1753					    cmd->opcode);
1754					return (EINVAL);
1755				}
1756				if (cmd->arg1 == 0 ||
1757				    cmdlen != F_INSN_SIZE(ipfw_insn)) {
1758					printf("ipfw: invalid external "
1759					    "action instance opcode\n");
1760					return (EINVAL);
1761				}
1762				ci->object_opcodes++;
1763			}
1764			goto check_action;
1765
1766		case O_FIB:
1767			if (cmdlen != F_INSN_SIZE(ipfw_insn))
1768				goto bad_size;
1769			if (cmd->arg1 >= rt_numfibs) {
1770				printf("ipfw: invalid fib number %d\n",
1771					cmd->arg1);
1772				return EINVAL;
1773			}
1774			break;
1775
1776		case O_SETFIB:
1777			if (cmdlen != F_INSN_SIZE(ipfw_insn))
1778				goto bad_size;
1779			if ((cmd->arg1 != IP_FW_TARG) &&
1780			    ((cmd->arg1 & 0x7FFF) >= rt_numfibs)) {
1781				printf("ipfw: invalid fib number %d\n",
1782					cmd->arg1 & 0x7FFF);
1783				return EINVAL;
1784			}
1785			goto check_action;
1786
1787		case O_UID:
1788		case O_GID:
1789		case O_JAIL:
1790		case O_IP_SRC:
1791		case O_IP_DST:
1792		case O_TCPSEQ:
1793		case O_TCPACK:
1794		case O_PROB:
1795		case O_ICMPTYPE:
1796			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1797				goto bad_size;
1798			break;
1799
1800		case O_LIMIT:
1801			if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
1802				goto bad_size;
1803			ci->object_opcodes++;
1804			break;
1805
1806		case O_LOG:
1807			if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
1808				goto bad_size;
1809
1810			((ipfw_insn_log *)cmd)->log_left =
1811			    ((ipfw_insn_log *)cmd)->max_log;
1812
1813			break;
1814
1815		case O_IP_SRC_MASK:
1816		case O_IP_DST_MASK:
1817			/* only odd command lengths */
1818			if ((cmdlen & 1) == 0)
1819				goto bad_size;
1820			break;
1821
1822		case O_IP_SRC_SET:
1823		case O_IP_DST_SET:
1824			if (cmd->arg1 == 0 || cmd->arg1 > 256) {
1825				printf("ipfw: invalid set size %d\n",
1826					cmd->arg1);
1827				return EINVAL;
1828			}
1829			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
1830			    (cmd->arg1+31)/32 )
1831				goto bad_size;
1832			break;
1833
1834		case O_IP_SRC_LOOKUP:
1835			if (cmdlen > F_INSN_SIZE(ipfw_insn_u32))
1836				goto bad_size;
1837		case O_IP_DST_LOOKUP:
1838			if (cmd->arg1 >= V_fw_tables_max) {
1839				printf("ipfw: invalid table number %d\n",
1840				    cmd->arg1);
1841				return (EINVAL);
1842			}
1843			if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
1844			    cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
1845			    cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1846				goto bad_size;
1847			ci->object_opcodes++;
1848			break;
1849		case O_IP_FLOW_LOOKUP:
1850			if (cmd->arg1 >= V_fw_tables_max) {
1851				printf("ipfw: invalid table number %d\n",
1852				    cmd->arg1);
1853				return (EINVAL);
1854			}
1855			if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
1856			    cmdlen != F_INSN_SIZE(ipfw_insn_u32))
1857				goto bad_size;
1858			ci->object_opcodes++;
1859			break;
1860		case O_MACADDR2:
1861			if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
1862				goto bad_size;
1863			break;
1864
1865		case O_NOP:
1866		case O_IPID:
1867		case O_IPTTL:
1868		case O_IPLEN:
1869		case O_TCPDATALEN:
1870		case O_TCPWIN:
1871		case O_TAGGED:
1872			if (cmdlen < 1 || cmdlen > 31)
1873				goto bad_size;
1874			break;
1875
1876		case O_DSCP:
1877			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1)
1878				goto bad_size;
1879			break;
1880
1881		case O_MAC_TYPE:
1882		case O_IP_SRCPORT:
1883		case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
1884			if (cmdlen < 2 || cmdlen > 31)
1885				goto bad_size;
1886			break;
1887
1888		case O_RECV:
1889		case O_XMIT:
1890		case O_VIA:
1891			if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
1892				goto bad_size;
1893			ci->object_opcodes++;
1894			break;
1895
1896		case O_ALTQ:
1897			if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
1898				goto bad_size;
1899			break;
1900
1901		case O_PIPE:
1902		case O_QUEUE:
1903			if (cmdlen != F_INSN_SIZE(ipfw_insn))
1904				goto bad_size;
1905			goto check_action;
1906
1907		case O_FORWARD_IP:
1908			if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
1909				goto bad_size;
1910			goto check_action;
1911#ifdef INET6
1912		case O_FORWARD_IP6:
1913			if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6))
1914				goto bad_size;
1915			goto check_action;
1916#endif /* INET6 */
1917
1918		case O_DIVERT:
1919		case O_TEE:
1920			if (ip_divert_ptr == NULL)
1921				return EINVAL;
1922			else
1923				goto check_size;
1924		case O_NETGRAPH:
1925		case O_NGTEE:
1926			if (ng_ipfw_input_p == NULL)
1927				return EINVAL;
1928			else
1929				goto check_size;
1930		case O_NAT:
1931			if (!IPFW_NAT_LOADED)
1932				return EINVAL;
1933			if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
1934 				goto bad_size;
1935 			goto check_action;
1936		case O_CHECK_STATE:
1937			ci->object_opcodes++;
1938			/* FALLTHROUGH */
1939		case O_FORWARD_MAC: /* XXX not implemented yet */
1940		case O_COUNT:
1941		case O_ACCEPT:
1942		case O_DENY:
1943		case O_REJECT:
1944		case O_SETDSCP:
1945#ifdef INET6
1946		case O_UNREACH6:
1947#endif
1948		case O_SKIPTO:
1949		case O_REASS:
1950		case O_CALLRETURN:
1951check_size:
1952			if (cmdlen != F_INSN_SIZE(ipfw_insn))
1953				goto bad_size;
1954check_action:
1955			if (have_action) {
1956				printf("ipfw: opcode %d, multiple actions"
1957					" not allowed\n",
1958					cmd->opcode);
1959				return (EINVAL);
1960			}
1961			have_action = 1;
1962			if (l != cmdlen) {
1963				printf("ipfw: opcode %d, action must be"
1964					" last opcode\n",
1965					cmd->opcode);
1966				return (EINVAL);
1967			}
1968			break;
1969#ifdef INET6
1970		case O_IP6_SRC:
1971		case O_IP6_DST:
1972			if (cmdlen != F_INSN_SIZE(struct in6_addr) +
1973			    F_INSN_SIZE(ipfw_insn))
1974				goto bad_size;
1975			break;
1976
1977		case O_FLOW6ID:
1978			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
1979			    ((ipfw_insn_u32 *)cmd)->o.arg1)
1980				goto bad_size;
1981			break;
1982
1983		case O_IP6_SRC_MASK:
1984		case O_IP6_DST_MASK:
1985			if ( !(cmdlen & 1) || cmdlen > 127)
1986				goto bad_size;
1987			break;
1988		case O_ICMP6TYPE:
1989			if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
1990				goto bad_size;
1991			break;
1992#endif
1993
1994		default:
1995			switch (cmd->opcode) {
1996#ifndef INET6
1997			case O_IP6_SRC_ME:
1998			case O_IP6_DST_ME:
1999			case O_EXT_HDR:
2000			case O_IP6:
2001			case O_UNREACH6:
2002			case O_IP6_SRC:
2003			case O_IP6_DST:
2004			case O_FLOW6ID:
2005			case O_IP6_SRC_MASK:
2006			case O_IP6_DST_MASK:
2007			case O_ICMP6TYPE:
2008				printf("ipfw: no IPv6 support in kernel\n");
2009				return (EPROTONOSUPPORT);
2010#endif
2011			default:
2012				printf("ipfw: opcode %d, unknown opcode\n",
2013					cmd->opcode);
2014				return (EINVAL);
2015			}
2016		}
2017	}
2018	if (have_action == 0) {
2019		printf("ipfw: missing action\n");
2020		return (EINVAL);
2021	}
2022	return 0;
2023
2024bad_size:
2025	printf("ipfw: opcode %d size %d wrong\n",
2026		cmd->opcode, cmdlen);
2027	return (EINVAL);
2028}
2029
2030
2031/*
2032 * Translation of requests for compatibility with FreeBSD 7.2/8.
2033 * a static variable tells us if we have an old client from userland,
2034 * and if necessary we translate requests and responses between the
2035 * two formats.
2036 */
2037static int is7 = 0;
2038
2039struct ip_fw7 {
2040	struct ip_fw7	*next;		/* linked list of rules     */
2041	struct ip_fw7	*next_rule;	/* ptr to next [skipto] rule    */
2042	/* 'next_rule' is used to pass up 'set_disable' status      */
2043
2044	uint16_t	act_ofs;	/* offset of action in 32-bit units */
2045	uint16_t	cmd_len;	/* # of 32-bit words in cmd */
2046	uint16_t	rulenum;	/* rule number          */
2047	uint8_t		set;		/* rule set (0..31)     */
2048	// #define RESVD_SET   31  /* set for default and persistent rules */
2049	uint8_t		_pad;		/* padding          */
2050	// uint32_t        id;             /* rule id, only in v.8 */
2051	/* These fields are present in all rules.           */
2052	uint64_t	pcnt;		/* Packet counter       */
2053	uint64_t	bcnt;		/* Byte counter         */
2054	uint32_t	timestamp;	/* tv_sec of last match     */
2055
2056	ipfw_insn	cmd[1];		/* storage for commands     */
2057};
2058
2059static int convert_rule_to_7(struct ip_fw_rule0 *rule);
2060static int convert_rule_to_8(struct ip_fw_rule0 *rule);
2061
2062#ifndef RULESIZE7
2063#define RULESIZE7(rule)  (sizeof(struct ip_fw7) + \
2064	((struct ip_fw7 *)(rule))->cmd_len * 4 - 4)
2065#endif
2066
2067
2068/*
2069 * Copy the static and dynamic rules to the supplied buffer
2070 * and return the amount of space actually used.
2071 * Must be run under IPFW_UH_RLOCK
2072 */
2073static size_t
2074ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
2075{
2076	char *bp = buf;
2077	char *ep = bp + space;
2078	struct ip_fw *rule;
2079	struct ip_fw_rule0 *dst;
2080	struct timeval boottime;
2081	int error, i, l, warnflag;
2082	time_t	boot_seconds;
2083
2084	warnflag = 0;
2085
2086	getboottime(&boottime);
2087        boot_seconds = boottime.tv_sec;
2088	for (i = 0; i < chain->n_rules; i++) {
2089		rule = chain->map[i];
2090
2091		if (is7) {
2092		    /* Convert rule to FreeBSd 7.2 format */
2093		    l = RULESIZE7(rule);
2094		    if (bp + l + sizeof(uint32_t) <= ep) {
2095			bcopy(rule, bp, l + sizeof(uint32_t));
2096			error = set_legacy_obj_kidx(chain,
2097			    (struct ip_fw_rule0 *)bp);
2098			if (error != 0)
2099				return (0);
2100			error = convert_rule_to_7((struct ip_fw_rule0 *) bp);
2101			if (error)
2102				return 0; /*XXX correct? */
2103			/*
2104			 * XXX HACK. Store the disable mask in the "next"
2105			 * pointer in a wild attempt to keep the ABI the same.
2106			 * Why do we do this on EVERY rule?
2107			 */
2108			bcopy(&V_set_disable,
2109				&(((struct ip_fw7 *)bp)->next_rule),
2110				sizeof(V_set_disable));
2111			if (((struct ip_fw7 *)bp)->timestamp)
2112			    ((struct ip_fw7 *)bp)->timestamp += boot_seconds;
2113			bp += l;
2114		    }
2115		    continue; /* go to next rule */
2116		}
2117
2118		l = RULEUSIZE0(rule);
2119		if (bp + l > ep) { /* should not happen */
2120			printf("overflow dumping static rules\n");
2121			break;
2122		}
2123		dst = (struct ip_fw_rule0 *)bp;
2124		export_rule0(rule, dst, l);
2125		error = set_legacy_obj_kidx(chain, dst);
2126
2127		/*
2128		 * XXX HACK. Store the disable mask in the "next"
2129		 * pointer in a wild attempt to keep the ABI the same.
2130		 * Why do we do this on EVERY rule?
2131		 *
2132		 * XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask
2133		 * so we need to fail _after_ saving at least one mask.
2134		 */
2135		bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable));
2136		if (dst->timestamp)
2137			dst->timestamp += boot_seconds;
2138		bp += l;
2139
2140		if (error != 0) {
2141			if (error == 2) {
2142				/* Non-fatal table rewrite error. */
2143				warnflag = 1;
2144				continue;
2145			}
2146			printf("Stop on rule %d. Fail to convert table\n",
2147			    rule->rulenum);
2148			break;
2149		}
2150	}
2151	if (warnflag != 0)
2152		printf("ipfw: process %s is using legacy interfaces,"
2153		    " consider rebuilding\n", "");
2154	ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */
2155	return (bp - (char *)buf);
2156}
2157
2158
2159struct dump_args {
2160	uint32_t	b;	/* start rule */
2161	uint32_t	e;	/* end rule */
2162	uint32_t	rcount;	/* number of rules */
2163	uint32_t	rsize;	/* rules size */
2164	uint32_t	tcount;	/* number of tables */
2165	int		rcounters;	/* counters */
2166};
2167
2168void
2169ipfw_export_obj_ntlv(struct named_object *no, ipfw_obj_ntlv *ntlv)
2170{
2171
2172	ntlv->head.type = no->etlv;
2173	ntlv->head.length = sizeof(*ntlv);
2174	ntlv->idx = no->kidx;
2175	strlcpy(ntlv->name, no->name, sizeof(ntlv->name));
2176}
2177
2178/*
2179 * Export named object info in instance @ni, identified by @kidx
2180 * to ipfw_obj_ntlv. TLV is allocated from @sd space.
2181 *
2182 * Returns 0 on success.
2183 */
2184static int
2185export_objhash_ntlv(struct namedobj_instance *ni, uint16_t kidx,
2186    struct sockopt_data *sd)
2187{
2188	struct named_object *no;
2189	ipfw_obj_ntlv *ntlv;
2190
2191	no = ipfw_objhash_lookup_kidx(ni, kidx);
2192	KASSERT(no != NULL, ("invalid object kernel index passed"));
2193
2194	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
2195	if (ntlv == NULL)
2196		return (ENOMEM);
2197
2198	ipfw_export_obj_ntlv(no, ntlv);
2199	return (0);
2200}
2201
2202/*
2203 * Dumps static rules with table TLVs in buffer @sd.
2204 *
2205 * Returns 0 on success.
2206 */
2207static int
2208dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da,
2209    uint32_t *bmask, struct sockopt_data *sd)
2210{
2211	int error;
2212	int i, l;
2213	uint32_t tcount;
2214	ipfw_obj_ctlv *ctlv;
2215	struct ip_fw *krule;
2216	struct namedobj_instance *ni;
2217	caddr_t dst;
2218
2219	/* Dump table names first (if any) */
2220	if (da->tcount > 0) {
2221		/* Header first */
2222		ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
2223		if (ctlv == NULL)
2224			return (ENOMEM);
2225		ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
2226		ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) +
2227		    sizeof(*ctlv);
2228		ctlv->count = da->tcount;
2229		ctlv->objsize = sizeof(ipfw_obj_ntlv);
2230	}
2231
2232	i = 0;
2233	tcount = da->tcount;
2234	ni = ipfw_get_table_objhash(chain);
2235	while (tcount > 0) {
2236		if ((bmask[i / 32] & (1 << (i % 32))) == 0) {
2237			i++;
2238			continue;
2239		}
2240
2241		/* Jump to shared named object bitmask */
2242		if (i >= IPFW_TABLES_MAX) {
2243			ni = CHAIN_TO_SRV(chain);
2244			i -= IPFW_TABLES_MAX;
2245			bmask += IPFW_TABLES_MAX / 32;
2246		}
2247
2248		if ((error = export_objhash_ntlv(ni, i, sd)) != 0)
2249			return (error);
2250
2251		i++;
2252		tcount--;
2253	}
2254
2255	/* Dump rules */
2256	ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv));
2257	if (ctlv == NULL)
2258		return (ENOMEM);
2259	ctlv->head.type = IPFW_TLV_RULE_LIST;
2260	ctlv->head.length = da->rsize + sizeof(*ctlv);
2261	ctlv->count = da->rcount;
2262
2263	for (i = da->b; i < da->e; i++) {
2264		krule = chain->map[i];
2265
2266		l = RULEUSIZE1(krule) + sizeof(ipfw_obj_tlv);
2267		if (da->rcounters != 0)
2268			l += sizeof(struct ip_fw_bcounter);
2269		dst = (caddr_t)ipfw_get_sopt_space(sd, l);
2270		if (dst == NULL)
2271			return (ENOMEM);
2272
2273		export_rule1(krule, dst, l, da->rcounters);
2274	}
2275
2276	return (0);
2277}
2278
2279/*
2280 * Marks every object index used in @rule with bit in @bmask.
2281 * Used to generate bitmask of referenced tables/objects for given ruleset
2282 * or its part.
2283 *
2284 * Returns number of newly-referenced objects.
2285 */
2286static int
2287mark_object_kidx(struct ip_fw_chain *ch, struct ip_fw *rule,
2288    uint32_t *bmask)
2289{
2290	struct opcode_obj_rewrite *rw;
2291	ipfw_insn *cmd;
2292	int bidx, cmdlen, l, count;
2293	uint16_t kidx;
2294	uint8_t subtype;
2295
2296	l = rule->cmd_len;
2297	cmd = rule->cmd;
2298	cmdlen = 0;
2299	count = 0;
2300	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2301		cmdlen = F_LEN(cmd);
2302
2303		rw = find_op_rw(cmd, &kidx, &subtype);
2304		if (rw == NULL)
2305			continue;
2306
2307		bidx = kidx / 32;
2308		/*
2309		 * Maintain separate bitmasks for table and
2310		 * non-table objects.
2311		 */
2312		if (rw->etlv != IPFW_TLV_TBL_NAME)
2313			bidx += IPFW_TABLES_MAX / 32;
2314
2315		if ((bmask[bidx] & (1 << (kidx % 32))) == 0)
2316			count++;
2317
2318		bmask[bidx] |= 1 << (kidx % 32);
2319	}
2320
2321	return (count);
2322}
2323
2324/*
2325 * Dumps requested objects data
2326 * Data layout (version 0)(current):
2327 * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags
2328 *   size = ipfw_cfg_lheader.size
2329 * Reply: [ ipfw_cfg_lheader
2330 *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
2331 *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST)
2332 *     ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ]
2333 *   ] (optional)
2334 *   [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_obj_dyntlv x N ] (optional)
2335 * ]
2336 * * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize.
2337 * The rest (size, count) are set to zero and needs to be ignored.
2338 *
2339 * Returns 0 on success.
2340 */
2341static int
2342dump_config(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2343    struct sockopt_data *sd)
2344{
2345	ipfw_cfg_lheader *hdr;
2346	struct ip_fw *rule;
2347	size_t sz, rnum;
2348	uint32_t hdr_flags;
2349	int error, i;
2350	struct dump_args da;
2351	uint32_t *bmask;
2352
2353	hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
2354	if (hdr == NULL)
2355		return (EINVAL);
2356
2357	error = 0;
2358	bmask = NULL;
2359	/* Allocate needed state. Note we allocate 2xspace mask, for table&srv  */
2360	if (hdr->flags & IPFW_CFG_GET_STATIC)
2361		bmask = malloc(IPFW_TABLES_MAX / 4, M_TEMP, M_WAITOK | M_ZERO);
2362
2363	IPFW_UH_RLOCK(chain);
2364
2365	/*
2366	 * STAGE 1: Determine size/count for objects in range.
2367	 * Prepare used tables bitmask.
2368	 */
2369	sz = sizeof(ipfw_cfg_lheader);
2370	memset(&da, 0, sizeof(da));
2371
2372	da.b = 0;
2373	da.e = chain->n_rules;
2374
2375	if (hdr->end_rule != 0) {
2376		/* Handle custom range */
2377		if ((rnum = hdr->start_rule) > IPFW_DEFAULT_RULE)
2378			rnum = IPFW_DEFAULT_RULE;
2379		da.b = ipfw_find_rule(chain, rnum, 0);
2380		rnum = hdr->end_rule;
2381		rnum = (rnum < IPFW_DEFAULT_RULE) ? rnum+1 : IPFW_DEFAULT_RULE;
2382		da.e = ipfw_find_rule(chain, rnum, 0) + 1;
2383	}
2384
2385	if (hdr->flags & IPFW_CFG_GET_STATIC) {
2386		for (i = da.b; i < da.e; i++) {
2387			rule = chain->map[i];
2388			da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv);
2389			da.rcount++;
2390			/* Update bitmask of used objects for given range */
2391			da.tcount += mark_object_kidx(chain, rule, bmask);
2392		}
2393		/* Add counters if requested */
2394		if (hdr->flags & IPFW_CFG_GET_COUNTERS) {
2395			da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount;
2396			da.rcounters = 1;
2397		}
2398
2399		if (da.tcount > 0)
2400			sz += da.tcount * sizeof(ipfw_obj_ntlv) +
2401			    sizeof(ipfw_obj_ctlv);
2402		sz += da.rsize + sizeof(ipfw_obj_ctlv);
2403	}
2404
2405	if (hdr->flags & IPFW_CFG_GET_STATES)
2406		sz += ipfw_dyn_get_count() * sizeof(ipfw_obj_dyntlv) +
2407		     sizeof(ipfw_obj_ctlv);
2408
2409
2410	/*
2411	 * Fill header anyway.
2412	 * Note we have to save header fields to stable storage
2413	 * buffer inside @sd can be flushed after dumping rules
2414	 */
2415	hdr->size = sz;
2416	hdr->set_mask = ~V_set_disable;
2417	hdr_flags = hdr->flags;
2418	hdr = NULL;
2419
2420	if (sd->valsize < sz) {
2421		error = ENOMEM;
2422		goto cleanup;
2423	}
2424
2425	/* STAGE2: Store actual data */
2426	if (hdr_flags & IPFW_CFG_GET_STATIC) {
2427		error = dump_static_rules(chain, &da, bmask, sd);
2428		if (error != 0)
2429			goto cleanup;
2430	}
2431
2432	if (hdr_flags & IPFW_CFG_GET_STATES)
2433		error = ipfw_dump_states(chain, sd);
2434
2435cleanup:
2436	IPFW_UH_RUNLOCK(chain);
2437
2438	if (bmask != NULL)
2439		free(bmask, M_TEMP);
2440
2441	return (error);
2442}
2443
2444int
2445ipfw_check_object_name_generic(const char *name)
2446{
2447	int nsize;
2448
2449	nsize = sizeof(((ipfw_obj_ntlv *)0)->name);
2450	if (strnlen(name, nsize) == nsize)
2451		return (EINVAL);
2452	if (name[0] == '\0')
2453		return (EINVAL);
2454	return (0);
2455}
2456
2457/*
2458 * Creates non-existent objects referenced by rule.
2459 *
2460 * Return 0 on success.
2461 */
2462int
2463create_objects_compat(struct ip_fw_chain *ch, ipfw_insn *cmd,
2464    struct obj_idx *oib, struct obj_idx *pidx, struct tid_info *ti)
2465{
2466	struct opcode_obj_rewrite *rw;
2467	struct obj_idx *p;
2468	uint16_t kidx;
2469	int error;
2470
2471	/*
2472	 * Compatibility stuff: do actual creation for non-existing,
2473	 * but referenced objects.
2474	 */
2475	for (p = oib; p < pidx; p++) {
2476		if (p->kidx != 0)
2477			continue;
2478
2479		ti->uidx = p->uidx;
2480		ti->type = p->type;
2481		ti->atype = 0;
2482
2483		rw = find_op_rw(cmd + p->off, NULL, NULL);
2484		KASSERT(rw != NULL, ("Unable to find handler for op %d",
2485		    (cmd + p->off)->opcode));
2486
2487		if (rw->create_object == NULL)
2488			error = EOPNOTSUPP;
2489		else
2490			error = rw->create_object(ch, ti, &kidx);
2491		if (error == 0) {
2492			p->kidx = kidx;
2493			continue;
2494		}
2495
2496		/*
2497		 * Error happened. We have to rollback everything.
2498		 * Drop all already acquired references.
2499		 */
2500		IPFW_UH_WLOCK(ch);
2501		unref_oib_objects(ch, cmd, oib, pidx);
2502		IPFW_UH_WUNLOCK(ch);
2503
2504		return (error);
2505	}
2506
2507	return (0);
2508}
2509
2510/*
2511 * Compatibility function for old ipfw(8) binaries.
2512 * Rewrites table/nat kernel indices with userland ones.
2513 * Convert tables matching '/^\d+$/' to their atoi() value.
2514 * Use number 65535 for other tables.
2515 *
2516 * Returns 0 on success.
2517 */
2518static int
2519set_legacy_obj_kidx(struct ip_fw_chain *ch, struct ip_fw_rule0 *rule)
2520{
2521	struct opcode_obj_rewrite *rw;
2522	struct named_object *no;
2523	ipfw_insn *cmd;
2524	char *end;
2525	long val;
2526	int cmdlen, error, l;
2527	uint16_t kidx, uidx;
2528	uint8_t subtype;
2529
2530	error = 0;
2531
2532	l = rule->cmd_len;
2533	cmd = rule->cmd;
2534	cmdlen = 0;
2535	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2536		cmdlen = F_LEN(cmd);
2537
2538		/* Check if is index in given opcode */
2539		rw = find_op_rw(cmd, &kidx, &subtype);
2540		if (rw == NULL)
2541			continue;
2542
2543		/* Try to find referenced kernel object */
2544		no = rw->find_bykidx(ch, kidx);
2545		if (no == NULL)
2546			continue;
2547
2548		val = strtol(no->name, &end, 10);
2549		if (*end == '\0' && val < 65535) {
2550			uidx = val;
2551		} else {
2552
2553			/*
2554			 * We are called via legacy opcode.
2555			 * Save error and show table as fake number
2556			 * not to make ipfw(8) hang.
2557			 */
2558			uidx = 65535;
2559			error = 2;
2560		}
2561
2562		rw->update(cmd, uidx);
2563	}
2564
2565	return (error);
2566}
2567
2568
2569/*
2570 * Unreferences all already-referenced objects in given @cmd rule,
2571 * using information in @oib.
2572 *
2573 * Used to rollback partially converted rule on error.
2574 */
2575static void
2576unref_oib_objects(struct ip_fw_chain *ch, ipfw_insn *cmd, struct obj_idx *oib,
2577    struct obj_idx *end)
2578{
2579	struct opcode_obj_rewrite *rw;
2580	struct named_object *no;
2581	struct obj_idx *p;
2582
2583	IPFW_UH_WLOCK_ASSERT(ch);
2584
2585	for (p = oib; p < end; p++) {
2586		if (p->kidx == 0)
2587			continue;
2588
2589		rw = find_op_rw(cmd + p->off, NULL, NULL);
2590		KASSERT(rw != NULL, ("Unable to find handler for op %d",
2591		    (cmd + p->off)->opcode));
2592
2593		/* Find & unref by existing idx */
2594		no = rw->find_bykidx(ch, p->kidx);
2595		KASSERT(no != NULL, ("Ref'd object %d disappeared", p->kidx));
2596		no->refcnt--;
2597	}
2598}
2599
2600/*
2601 * Remove references from every object used in @rule.
2602 * Used at rule removal code.
2603 */
2604static void
2605unref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule)
2606{
2607	struct opcode_obj_rewrite *rw;
2608	struct named_object *no;
2609	ipfw_insn *cmd;
2610	int cmdlen, l;
2611	uint16_t kidx;
2612	uint8_t subtype;
2613
2614	IPFW_UH_WLOCK_ASSERT(ch);
2615
2616	l = rule->cmd_len;
2617	cmd = rule->cmd;
2618	cmdlen = 0;
2619	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2620		cmdlen = F_LEN(cmd);
2621
2622		rw = find_op_rw(cmd, &kidx, &subtype);
2623		if (rw == NULL)
2624			continue;
2625		no = rw->find_bykidx(ch, kidx);
2626
2627		KASSERT(no != NULL, ("object id %d not found", kidx));
2628		KASSERT(no->subtype == subtype,
2629		    ("wrong type %d (%d) for object id %d",
2630		    no->subtype, subtype, kidx));
2631		KASSERT(no->refcnt > 0, ("refcount for object %d is %d",
2632		    kidx, no->refcnt));
2633
2634		if (no->refcnt == 1 && rw->destroy_object != NULL)
2635			rw->destroy_object(ch, no);
2636		else
2637			no->refcnt--;
2638	}
2639}
2640
2641
2642/*
2643 * Find and reference object (if any) stored in instruction @cmd.
2644 *
2645 * Saves object info in @pidx, sets
2646 *  - @unresolved to 1 if object should exists but not found
2647 *
2648 * Returns non-zero value in case of error.
2649 */
2650static int
2651ref_opcode_object(struct ip_fw_chain *ch, ipfw_insn *cmd, struct tid_info *ti,
2652    struct obj_idx *pidx, int *unresolved)
2653{
2654	struct named_object *no;
2655	struct opcode_obj_rewrite *rw;
2656	int error;
2657
2658	/* Check if this opcode is candidate for rewrite */
2659	rw = find_op_rw(cmd, &ti->uidx, &ti->type);
2660	if (rw == NULL)
2661		return (0);
2662
2663	/* Need to rewrite. Save necessary fields */
2664	pidx->uidx = ti->uidx;
2665	pidx->type = ti->type;
2666
2667	/* Try to find referenced kernel object */
2668	error = rw->find_byname(ch, ti, &no);
2669	if (error != 0)
2670		return (error);
2671	if (no == NULL) {
2672		/*
2673		 * Report about unresolved object for automaic
2674		 * creation.
2675		 */
2676		*unresolved = 1;
2677		return (0);
2678	}
2679
2680	/*
2681	 * Object is already exist.
2682	 * Its subtype should match with expected value.
2683	 */
2684	if (ti->type != no->subtype)
2685		return (EINVAL);
2686
2687	/* Bump refcount and update kidx. */
2688	no->refcnt++;
2689	rw->update(cmd, no->kidx);
2690	return (0);
2691}
2692
2693/*
2694 * Finds and bumps refcount for objects referenced by given @rule.
2695 * Auto-creates non-existing tables.
2696 * Fills in @oib array with userland/kernel indexes.
2697 *
2698 * Returns 0 on success.
2699 */
2700static int
2701ref_rule_objects(struct ip_fw_chain *ch, struct ip_fw *rule,
2702    struct rule_check_info *ci, struct obj_idx *oib, struct tid_info *ti)
2703{
2704	struct obj_idx *pidx;
2705	ipfw_insn *cmd;
2706	int cmdlen, error, l, unresolved;
2707
2708	pidx = oib;
2709	l = rule->cmd_len;
2710	cmd = rule->cmd;
2711	cmdlen = 0;
2712	error = 0;
2713
2714	IPFW_UH_WLOCK(ch);
2715
2716	/* Increase refcount on each existing referenced table. */
2717	for ( ;	l > 0 ; l -= cmdlen, cmd += cmdlen) {
2718		cmdlen = F_LEN(cmd);
2719		unresolved = 0;
2720
2721		error = ref_opcode_object(ch, cmd, ti, pidx, &unresolved);
2722		if (error != 0)
2723			break;
2724		/*
2725		 * Compatibility stuff for old clients:
2726		 * prepare to automaitcally create non-existing objects.
2727		 */
2728		if (unresolved != 0) {
2729			pidx->off = rule->cmd_len - l;
2730			pidx++;
2731		}
2732	}
2733
2734	if (error != 0) {
2735		/* Unref everything we have already done */
2736		unref_oib_objects(ch, rule->cmd, oib, pidx);
2737		IPFW_UH_WUNLOCK(ch);
2738		return (error);
2739	}
2740	IPFW_UH_WUNLOCK(ch);
2741
2742	/* Perform auto-creation for non-existing objects */
2743	if (pidx != oib)
2744		error = create_objects_compat(ch, rule->cmd, oib, pidx, ti);
2745
2746	/* Calculate real number of dynamic objects */
2747	ci->object_opcodes = (uint16_t)(pidx - oib);
2748
2749	return (error);
2750}
2751
2752/*
2753 * Checks is opcode is referencing table of appropriate type.
2754 * Adds reference count for found table if true.
2755 * Rewrites user-supplied opcode values with kernel ones.
2756 *
2757 * Returns 0 on success and appropriate error code otherwise.
2758 */
2759static int
2760rewrite_rule_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci)
2761{
2762	int error;
2763	ipfw_insn *cmd;
2764	uint8_t type;
2765	struct obj_idx *p, *pidx_first, *pidx_last;
2766	struct tid_info ti;
2767
2768	/*
2769	 * Prepare an array for storing opcode indices.
2770	 * Use stack allocation by default.
2771	 */
2772	if (ci->object_opcodes <= (sizeof(ci->obuf)/sizeof(ci->obuf[0]))) {
2773		/* Stack */
2774		pidx_first = ci->obuf;
2775	} else
2776		pidx_first = malloc(
2777		    ci->object_opcodes * sizeof(struct obj_idx),
2778		    M_IPFW, M_WAITOK | M_ZERO);
2779
2780	error = 0;
2781	type = 0;
2782	memset(&ti, 0, sizeof(ti));
2783
2784	/* Use set rule is assigned to. */
2785	ti.set = ci->krule->set;
2786	if (ci->ctlv != NULL) {
2787		ti.tlvs = (void *)(ci->ctlv + 1);
2788		ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
2789	}
2790
2791	/* Reference all used tables and other objects */
2792	error = ref_rule_objects(chain, ci->krule, ci, pidx_first, &ti);
2793	if (error != 0)
2794		goto free;
2795	/*
2796	 * Note that ref_rule_objects() might have updated ci->object_opcodes
2797	 * to reflect actual number of object opcodes.
2798	 */
2799
2800	/* Perform rewrite of remaining opcodes */
2801	p = pidx_first;
2802	pidx_last = pidx_first + ci->object_opcodes;
2803	for (p = pidx_first; p < pidx_last; p++) {
2804		cmd = ci->krule->cmd + p->off;
2805		update_opcode_kidx(cmd, p->kidx);
2806	}
2807
2808free:
2809	if (pidx_first != ci->obuf)
2810		free(pidx_first, M_IPFW);
2811
2812	return (error);
2813}
2814
2815/*
2816 * Adds one or more rules to ipfw @chain.
2817 * Data layout (version 0)(current):
2818 * Request:
2819 * [
2820 *   ip_fw3_opheader
2821 *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
2822 *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3)
2823 * ]
2824 * Reply:
2825 * [
2826 *   ip_fw3_opheader
2827 *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
2828 *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ]
2829 * ]
2830 *
2831 * Rules in reply are modified to store their actual ruleset number.
2832 *
2833 * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
2834 * according to their idx field and there has to be no duplicates.
2835 * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
2836 * (*3) Each ip_fw structure needs to be aligned to u64 boundary.
2837 *
2838 * Returns 0 on success.
2839 */
2840static int
2841add_rules(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
2842    struct sockopt_data *sd)
2843{
2844	ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
2845	ipfw_obj_ntlv *ntlv;
2846	int clen, error, idx;
2847	uint32_t count, read;
2848	struct ip_fw_rule *r;
2849	struct rule_check_info rci, *ci, *cbuf;
2850	int i, rsize;
2851
2852	op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
2853	ctlv = (ipfw_obj_ctlv *)(op3 + 1);
2854
2855	read = sizeof(ip_fw3_opheader);
2856	rtlv = NULL;
2857	tstate = NULL;
2858	cbuf = NULL;
2859	memset(&rci, 0, sizeof(struct rule_check_info));
2860
2861	if (read + sizeof(*ctlv) > sd->valsize)
2862		return (EINVAL);
2863
2864	if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
2865		clen = ctlv->head.length;
2866		/* Check size and alignment */
2867		if (clen > sd->valsize || clen < sizeof(*ctlv))
2868			return (EINVAL);
2869		if ((clen % sizeof(uint64_t)) != 0)
2870			return (EINVAL);
2871
2872		/*
2873		 * Some table names or other named objects.
2874		 * Check for validness.
2875		 */
2876		count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
2877		if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
2878			return (EINVAL);
2879
2880		/*
2881		 * Check each TLV.
2882		 * Ensure TLVs are sorted ascending and
2883		 * there are no duplicates.
2884		 */
2885		idx = -1;
2886		ntlv = (ipfw_obj_ntlv *)(ctlv + 1);
2887		while (count > 0) {
2888			if (ntlv->head.length != sizeof(ipfw_obj_ntlv))
2889				return (EINVAL);
2890
2891			error = ipfw_check_object_name_generic(ntlv->name);
2892			if (error != 0)
2893				return (error);
2894
2895			if (ntlv->idx <= idx)
2896				return (EINVAL);
2897
2898			idx = ntlv->idx;
2899			count--;
2900			ntlv++;
2901		}
2902
2903		tstate = ctlv;
2904		read += ctlv->head.length;
2905		ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
2906	}
2907
2908	if (read + sizeof(*ctlv) > sd->valsize)
2909		return (EINVAL);
2910
2911	if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
2912		clen = ctlv->head.length;
2913		if (clen + read > sd->valsize || clen < sizeof(*ctlv))
2914			return (EINVAL);
2915		if ((clen % sizeof(uint64_t)) != 0)
2916			return (EINVAL);
2917
2918		/*
2919		 * TODO: Permit adding multiple rules at once
2920		 */
2921		if (ctlv->count != 1)
2922			return (ENOTSUP);
2923
2924		clen -= sizeof(*ctlv);
2925
2926		if (ctlv->count > clen / sizeof(struct ip_fw_rule))
2927			return (EINVAL);
2928
2929		/* Allocate state for each rule or use stack */
2930		if (ctlv->count == 1) {
2931			memset(&rci, 0, sizeof(struct rule_check_info));
2932			cbuf = &rci;
2933		} else
2934			cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP,
2935			    M_WAITOK | M_ZERO);
2936		ci = cbuf;
2937
2938		/*
2939		 * Check each rule for validness.
2940		 * Ensure numbered rules are sorted ascending
2941		 * and properly aligned
2942		 */
2943		idx = 0;
2944		r = (struct ip_fw_rule *)(ctlv + 1);
2945		count = 0;
2946		error = 0;
2947		while (clen > 0) {
2948			rsize = roundup2(RULESIZE(r), sizeof(uint64_t));
2949			if (rsize > clen || ctlv->count <= count) {
2950				error = EINVAL;
2951				break;
2952			}
2953
2954			ci->ctlv = tstate;
2955			error = check_ipfw_rule1(r, rsize, ci);
2956			if (error != 0)
2957				break;
2958
2959			/* Check sorting */
2960			if (r->rulenum != 0 && r->rulenum < idx) {
2961				printf("rulenum %d idx %d\n", r->rulenum, idx);
2962				error = EINVAL;
2963				break;
2964			}
2965			idx = r->rulenum;
2966
2967			ci->urule = (caddr_t)r;
2968
2969			rsize = roundup2(rsize, sizeof(uint64_t));
2970			clen -= rsize;
2971			r = (struct ip_fw_rule *)((caddr_t)r + rsize);
2972			count++;
2973			ci++;
2974		}
2975
2976		if (ctlv->count != count || error != 0) {
2977			if (cbuf != &rci)
2978				free(cbuf, M_TEMP);
2979			return (EINVAL);
2980		}
2981
2982		rtlv = ctlv;
2983		read += ctlv->head.length;
2984		ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
2985	}
2986
2987	if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) {
2988		if (cbuf != NULL && cbuf != &rci)
2989			free(cbuf, M_TEMP);
2990		return (EINVAL);
2991	}
2992
2993	/*
2994	 * Passed rules seems to be valid.
2995	 * Allocate storage and try to add them to chain.
2996	 */
2997	for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) {
2998		clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule);
2999		ci->krule = ipfw_alloc_rule(chain, clen);
3000		import_rule1(ci);
3001	}
3002
3003	if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) {
3004		/* Free allocate krules */
3005		for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++)
3006			free_rule(ci->krule);
3007	}
3008
3009	if (cbuf != NULL && cbuf != &rci)
3010		free(cbuf, M_TEMP);
3011
3012	return (error);
3013}
3014
3015/*
3016 * Lists all sopts currently registered.
3017 * Data layout (v0)(current):
3018 * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
3019 * Reply: [ ipfw_obj_lheader ipfw_sopt_info x N ]
3020 *
3021 * Returns 0 on success
3022 */
3023static int
3024dump_soptcodes(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
3025    struct sockopt_data *sd)
3026{
3027	struct _ipfw_obj_lheader *olh;
3028	ipfw_sopt_info *i;
3029	struct ipfw_sopt_handler *sh;
3030	uint32_t count, n, size;
3031
3032	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
3033	if (olh == NULL)
3034		return (EINVAL);
3035	if (sd->valsize < olh->size)
3036		return (EINVAL);
3037
3038	CTL3_LOCK();
3039	count = ctl3_hsize;
3040	size = count * sizeof(ipfw_sopt_info) + sizeof(ipfw_obj_lheader);
3041
3042	/* Fill in header regadless of buffer size */
3043	olh->count = count;
3044	olh->objsize = sizeof(ipfw_sopt_info);
3045
3046	if (size > olh->size) {
3047		olh->size = size;
3048		CTL3_UNLOCK();
3049		return (ENOMEM);
3050	}
3051	olh->size = size;
3052
3053	for (n = 1; n <= count; n++) {
3054		i = (ipfw_sopt_info *)ipfw_get_sopt_space(sd, sizeof(*i));
3055		KASSERT(i != NULL, ("previously checked buffer is not enough"));
3056		sh = &ctl3_handlers[n];
3057		i->opcode = sh->opcode;
3058		i->version = sh->version;
3059		i->refcnt = sh->refcnt;
3060	}
3061	CTL3_UNLOCK();
3062
3063	return (0);
3064}
3065
3066/*
3067 * Compares two opcodes.
3068 * Used both in qsort() and bsearch().
3069 *
3070 * Returns 0 if match is found.
3071 */
3072static int
3073compare_opcodes(const void *_a, const void *_b)
3074{
3075	const struct opcode_obj_rewrite *a, *b;
3076
3077	a = (const struct opcode_obj_rewrite *)_a;
3078	b = (const struct opcode_obj_rewrite *)_b;
3079
3080	if (a->opcode < b->opcode)
3081		return (-1);
3082	else if (a->opcode > b->opcode)
3083		return (1);
3084
3085	return (0);
3086}
3087
3088/*
3089 * XXX: Rewrite bsearch()
3090 */
3091static int
3092find_op_rw_range(uint16_t op, struct opcode_obj_rewrite **plo,
3093    struct opcode_obj_rewrite **phi)
3094{
3095	struct opcode_obj_rewrite *ctl3_max, *lo, *hi, h, *rw;
3096
3097	memset(&h, 0, sizeof(h));
3098	h.opcode = op;
3099
3100	rw = (struct opcode_obj_rewrite *)bsearch(&h, ctl3_rewriters,
3101	    ctl3_rsize, sizeof(h), compare_opcodes);
3102	if (rw == NULL)
3103		return (1);
3104
3105	/* Find the first element matching the same opcode */
3106	lo = rw;
3107	for ( ; lo > ctl3_rewriters && (lo - 1)->opcode == op; lo--)
3108		;
3109
3110	/* Find the last element matching the same opcode */
3111	hi = rw;
3112	ctl3_max = ctl3_rewriters + ctl3_rsize;
3113	for ( ; (hi + 1) < ctl3_max && (hi + 1)->opcode == op; hi++)
3114		;
3115
3116	*plo = lo;
3117	*phi = hi;
3118
3119	return (0);
3120}
3121
3122/*
3123 * Finds opcode object rewriter based on @code.
3124 *
3125 * Returns pointer to handler or NULL.
3126 */
3127static struct opcode_obj_rewrite *
3128find_op_rw(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
3129{
3130	struct opcode_obj_rewrite *rw, *lo, *hi;
3131	uint16_t uidx;
3132	uint8_t subtype;
3133
3134	if (find_op_rw_range(cmd->opcode, &lo, &hi) != 0)
3135		return (NULL);
3136
3137	for (rw = lo; rw <= hi; rw++) {
3138		if (rw->classifier(cmd, &uidx, &subtype) == 0) {
3139			if (puidx != NULL)
3140				*puidx = uidx;
3141			if (ptype != NULL)
3142				*ptype = subtype;
3143			return (rw);
3144		}
3145	}
3146
3147	return (NULL);
3148}
3149int
3150classify_opcode_kidx(ipfw_insn *cmd, uint16_t *puidx)
3151{
3152
3153	if (find_op_rw(cmd, puidx, NULL) == NULL)
3154		return (1);
3155	return (0);
3156}
3157
3158void
3159update_opcode_kidx(ipfw_insn *cmd, uint16_t idx)
3160{
3161	struct opcode_obj_rewrite *rw;
3162
3163	rw = find_op_rw(cmd, NULL, NULL);
3164	KASSERT(rw != NULL, ("No handler to update opcode %d", cmd->opcode));
3165	rw->update(cmd, idx);
3166}
3167
3168void
3169ipfw_init_obj_rewriter()
3170{
3171
3172	ctl3_rewriters = NULL;
3173	ctl3_rsize = 0;
3174}
3175
3176void
3177ipfw_destroy_obj_rewriter()
3178{
3179
3180	if (ctl3_rewriters != NULL)
3181		free(ctl3_rewriters, M_IPFW);
3182	ctl3_rewriters = NULL;
3183	ctl3_rsize = 0;
3184}
3185
3186/*
3187 * Adds one or more opcode object rewrite handlers to the global array.
3188 * Function may sleep.
3189 */
3190void
3191ipfw_add_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
3192{
3193	size_t sz;
3194	struct opcode_obj_rewrite *tmp;
3195
3196	CTL3_LOCK();
3197
3198	for (;;) {
3199		sz = ctl3_rsize + count;
3200		CTL3_UNLOCK();
3201		tmp = malloc(sizeof(*rw) * sz, M_IPFW, M_WAITOK | M_ZERO);
3202		CTL3_LOCK();
3203		if (ctl3_rsize + count <= sz)
3204			break;
3205
3206		/* Retry */
3207		free(tmp, M_IPFW);
3208	}
3209
3210	/* Merge old & new arrays */
3211	sz = ctl3_rsize + count;
3212	memcpy(tmp, ctl3_rewriters, ctl3_rsize * sizeof(*rw));
3213	memcpy(&tmp[ctl3_rsize], rw, count * sizeof(*rw));
3214	qsort(tmp, sz, sizeof(*rw), compare_opcodes);
3215	/* Switch new and free old */
3216	if (ctl3_rewriters != NULL)
3217		free(ctl3_rewriters, M_IPFW);
3218	ctl3_rewriters = tmp;
3219	ctl3_rsize = sz;
3220
3221	CTL3_UNLOCK();
3222}
3223
3224/*
3225 * Removes one or more object rewrite handlers from the global array.
3226 */
3227int
3228ipfw_del_obj_rewriter(struct opcode_obj_rewrite *rw, size_t count)
3229{
3230	size_t sz;
3231	struct opcode_obj_rewrite *ctl3_max, *ktmp, *lo, *hi;
3232	int i;
3233
3234	CTL3_LOCK();
3235
3236	for (i = 0; i < count; i++) {
3237		if (find_op_rw_range(rw[i].opcode, &lo, &hi) != 0)
3238			continue;
3239
3240		for (ktmp = lo; ktmp <= hi; ktmp++) {
3241			if (ktmp->classifier != rw[i].classifier)
3242				continue;
3243
3244			ctl3_max = ctl3_rewriters + ctl3_rsize;
3245			sz = (ctl3_max - (ktmp + 1)) * sizeof(*ktmp);
3246			memmove(ktmp, ktmp + 1, sz);
3247			ctl3_rsize--;
3248			break;
3249		}
3250
3251	}
3252
3253	if (ctl3_rsize == 0) {
3254		if (ctl3_rewriters != NULL)
3255			free(ctl3_rewriters, M_IPFW);
3256		ctl3_rewriters = NULL;
3257	}
3258
3259	CTL3_UNLOCK();
3260
3261	return (0);
3262}
3263
3264static int
3265export_objhash_ntlv_internal(struct namedobj_instance *ni,
3266    struct named_object *no, void *arg)
3267{
3268	struct sockopt_data *sd;
3269	ipfw_obj_ntlv *ntlv;
3270
3271	sd = (struct sockopt_data *)arg;
3272	ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
3273	if (ntlv == NULL)
3274		return (ENOMEM);
3275	ipfw_export_obj_ntlv(no, ntlv);
3276	return (0);
3277}
3278
3279/*
3280 * Lists all service objects.
3281 * Data layout (v0)(current):
3282 * Request: [ ipfw_obj_lheader ] size = ipfw_obj_lheader.size
3283 * Reply: [ ipfw_obj_lheader [ ipfw_obj_ntlv x N ] (optional) ]
3284 * Returns 0 on success
3285 */
3286static int
3287dump_srvobjects(struct ip_fw_chain *chain, ip_fw3_opheader *op3,
3288    struct sockopt_data *sd)
3289{
3290	ipfw_obj_lheader *hdr;
3291	int count;
3292
3293	hdr = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr));
3294	if (hdr == NULL)
3295		return (EINVAL);
3296
3297	IPFW_UH_RLOCK(chain);
3298	count = ipfw_objhash_count(CHAIN_TO_SRV(chain));
3299	hdr->size = sizeof(ipfw_obj_lheader) + count * sizeof(ipfw_obj_ntlv);
3300	if (sd->valsize < hdr->size) {
3301		IPFW_UH_RUNLOCK(chain);
3302		return (ENOMEM);
3303	}
3304	hdr->count = count;
3305	hdr->objsize = sizeof(ipfw_obj_ntlv);
3306	if (count > 0)
3307		ipfw_objhash_foreach(CHAIN_TO_SRV(chain),
3308		    export_objhash_ntlv_internal, sd);
3309	IPFW_UH_RUNLOCK(chain);
3310	return (0);
3311}
3312
3313/*
3314 * Compares two sopt handlers (code, version and handler ptr).
3315 * Used both as qsort() and bsearch().
3316 * Does not compare handler for latter case.
3317 *
3318 * Returns 0 if match is found.
3319 */
3320static int
3321compare_sh(const void *_a, const void *_b)
3322{
3323	const struct ipfw_sopt_handler *a, *b;
3324
3325	a = (const struct ipfw_sopt_handler *)_a;
3326	b = (const struct ipfw_sopt_handler *)_b;
3327
3328	if (a->opcode < b->opcode)
3329		return (-1);
3330	else if (a->opcode > b->opcode)
3331		return (1);
3332
3333	if (a->version < b->version)
3334		return (-1);
3335	else if (a->version > b->version)
3336		return (1);
3337
3338	/* bsearch helper */
3339	if (a->handler == NULL)
3340		return (0);
3341
3342	if ((uintptr_t)a->handler < (uintptr_t)b->handler)
3343		return (-1);
3344	else if ((uintptr_t)a->handler > (uintptr_t)b->handler)
3345		return (1);
3346
3347	return (0);
3348}
3349
3350/*
3351 * Finds sopt handler based on @code and @version.
3352 *
3353 * Returns pointer to handler or NULL.
3354 */
3355static struct ipfw_sopt_handler *
3356find_sh(uint16_t code, uint8_t version, sopt_handler_f *handler)
3357{
3358	struct ipfw_sopt_handler *sh, h;
3359
3360	memset(&h, 0, sizeof(h));
3361	h.opcode = code;
3362	h.version = version;
3363	h.handler = handler;
3364
3365	sh = (struct ipfw_sopt_handler *)bsearch(&h, ctl3_handlers,
3366	    ctl3_hsize, sizeof(h), compare_sh);
3367
3368	return (sh);
3369}
3370
3371static int
3372find_ref_sh(uint16_t opcode, uint8_t version, struct ipfw_sopt_handler *psh)
3373{
3374	struct ipfw_sopt_handler *sh;
3375
3376	CTL3_LOCK();
3377	if ((sh = find_sh(opcode, version, NULL)) == NULL) {
3378		CTL3_UNLOCK();
3379		printf("ipfw: ipfw_ctl3 invalid option %d""v""%d\n",
3380		    opcode, version);
3381		return (EINVAL);
3382	}
3383	sh->refcnt++;
3384	ctl3_refct++;
3385	/* Copy handler data to requested buffer */
3386	*psh = *sh;
3387	CTL3_UNLOCK();
3388
3389	return (0);
3390}
3391
3392static void
3393find_unref_sh(struct ipfw_sopt_handler *psh)
3394{
3395	struct ipfw_sopt_handler *sh;
3396
3397	CTL3_LOCK();
3398	sh = find_sh(psh->opcode, psh->version, NULL);
3399	KASSERT(sh != NULL, ("ctl3 handler disappeared"));
3400	sh->refcnt--;
3401	ctl3_refct--;
3402	CTL3_UNLOCK();
3403}
3404
3405void
3406ipfw_init_sopt_handler()
3407{
3408
3409	CTL3_LOCK_INIT();
3410	IPFW_ADD_SOPT_HANDLER(1, scodes);
3411}
3412
3413void
3414ipfw_destroy_sopt_handler()
3415{
3416
3417	IPFW_DEL_SOPT_HANDLER(1, scodes);
3418	CTL3_LOCK_DESTROY();
3419}
3420
3421/*
3422 * Adds one or more sockopt handlers to the global array.
3423 * Function may sleep.
3424 */
3425void
3426ipfw_add_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
3427{
3428	size_t sz;
3429	struct ipfw_sopt_handler *tmp;
3430
3431	CTL3_LOCK();
3432
3433	for (;;) {
3434		sz = ctl3_hsize + count;
3435		CTL3_UNLOCK();
3436		tmp = malloc(sizeof(*sh) * sz, M_IPFW, M_WAITOK | M_ZERO);
3437		CTL3_LOCK();
3438		if (ctl3_hsize + count <= sz)
3439			break;
3440
3441		/* Retry */
3442		free(tmp, M_IPFW);
3443	}
3444
3445	/* Merge old & new arrays */
3446	sz = ctl3_hsize + count;
3447	memcpy(tmp, ctl3_handlers, ctl3_hsize * sizeof(*sh));
3448	memcpy(&tmp[ctl3_hsize], sh, count * sizeof(*sh));
3449	qsort(tmp, sz, sizeof(*sh), compare_sh);
3450	/* Switch new and free old */
3451	if (ctl3_handlers != NULL)
3452		free(ctl3_handlers, M_IPFW);
3453	ctl3_handlers = tmp;
3454	ctl3_hsize = sz;
3455	ctl3_gencnt++;
3456
3457	CTL3_UNLOCK();
3458}
3459
3460/*
3461 * Removes one or more sockopt handlers from the global array.
3462 */
3463int
3464ipfw_del_sopt_handler(struct ipfw_sopt_handler *sh, size_t count)
3465{
3466	size_t sz;
3467	struct ipfw_sopt_handler *tmp, *h;
3468	int i;
3469
3470	CTL3_LOCK();
3471
3472	for (i = 0; i < count; i++) {
3473		tmp = &sh[i];
3474		h = find_sh(tmp->opcode, tmp->version, tmp->handler);
3475		if (h == NULL)
3476			continue;
3477
3478		sz = (ctl3_handlers + ctl3_hsize - (h + 1)) * sizeof(*h);
3479		memmove(h, h + 1, sz);
3480		ctl3_hsize--;
3481	}
3482
3483	if (ctl3_hsize == 0) {
3484		if (ctl3_handlers != NULL)
3485			free(ctl3_handlers, M_IPFW);
3486		ctl3_handlers = NULL;
3487	}
3488
3489	ctl3_gencnt++;
3490
3491	CTL3_UNLOCK();
3492
3493	return (0);
3494}
3495
3496/*
3497 * Writes data accumulated in @sd to sockopt buffer.
3498 * Zeroes internal @sd buffer.
3499 */
3500static int
3501ipfw_flush_sopt_data(struct sockopt_data *sd)
3502{
3503	struct sockopt *sopt;
3504	int error;
3505	size_t sz;
3506
3507	sz = sd->koff;
3508	if (sz == 0)
3509		return (0);
3510
3511	sopt = sd->sopt;
3512
3513	if (sopt->sopt_dir == SOPT_GET) {
3514		error = copyout(sd->kbuf, sopt->sopt_val, sz);
3515		if (error != 0)
3516			return (error);
3517	}
3518
3519	memset(sd->kbuf, 0, sd->ksize);
3520	sd->ktotal += sz;
3521	sd->koff = 0;
3522	if (sd->ktotal + sd->ksize < sd->valsize)
3523		sd->kavail = sd->ksize;
3524	else
3525		sd->kavail = sd->valsize - sd->ktotal;
3526
3527	/* Update sopt buffer data */
3528	sopt->sopt_valsize = sd->ktotal;
3529	sopt->sopt_val = sd->sopt_val + sd->ktotal;
3530
3531	return (0);
3532}
3533
3534/*
3535 * Ensures that @sd buffer has contiguous @neeeded number of
3536 * bytes.
3537 *
3538 * Returns pointer to requested space or NULL.
3539 */
3540caddr_t
3541ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed)
3542{
3543	int error;
3544	caddr_t addr;
3545
3546	if (sd->kavail < needed) {
3547		/*
3548		 * Flush data and try another time.
3549		 */
3550		error = ipfw_flush_sopt_data(sd);
3551
3552		if (sd->kavail < needed || error != 0)
3553			return (NULL);
3554	}
3555
3556	addr = sd->kbuf + sd->koff;
3557	sd->koff += needed;
3558	sd->kavail -= needed;
3559	return (addr);
3560}
3561
3562/*
3563 * Requests @needed contiguous bytes from @sd buffer.
3564 * Function is used to notify subsystem that we are
3565 * interesed in first @needed bytes (request header)
3566 * and the rest buffer can be safely zeroed.
3567 *
3568 * Returns pointer to requested space or NULL.
3569 */
3570caddr_t
3571ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed)
3572{
3573	caddr_t addr;
3574
3575	if ((addr = ipfw_get_sopt_space(sd, needed)) == NULL)
3576		return (NULL);
3577
3578	if (sd->kavail > 0)
3579		memset(sd->kbuf + sd->koff, 0, sd->kavail);
3580
3581	return (addr);
3582}
3583
3584/*
3585 * New sockopt handler.
3586 */
3587int
3588ipfw_ctl3(struct sockopt *sopt)
3589{
3590	int error, locked;
3591	size_t size, valsize;
3592	struct ip_fw_chain *chain;
3593	char xbuf[256];
3594	struct sockopt_data sdata;
3595	struct ipfw_sopt_handler h;
3596	ip_fw3_opheader *op3 = NULL;
3597
3598	error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
3599	if (error != 0)
3600		return (error);
3601
3602	if (sopt->sopt_name != IP_FW3)
3603		return (ipfw_ctl(sopt));
3604
3605	chain = &V_layer3_chain;
3606	error = 0;
3607
3608	/* Save original valsize before it is altered via sooptcopyin() */
3609	valsize = sopt->sopt_valsize;
3610	memset(&sdata, 0, sizeof(sdata));
3611	/* Read op3 header first to determine actual operation */
3612	op3 = (ip_fw3_opheader *)xbuf;
3613	error = sooptcopyin(sopt, op3, sizeof(*op3), sizeof(*op3));
3614	if (error != 0)
3615		return (error);
3616	sopt->sopt_valsize = valsize;
3617
3618	/*
3619	 * Find and reference command.
3620	 */
3621	error = find_ref_sh(op3->opcode, op3->version, &h);
3622	if (error != 0)
3623		return (error);
3624
3625	/*
3626	 * Disallow modifications in really-really secure mode, but still allow
3627	 * the logging counters to be reset.
3628	 */
3629	if ((h.dir & HDIR_SET) != 0 && h.opcode != IP_FW_XRESETLOG) {
3630		error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
3631		if (error != 0) {
3632			find_unref_sh(&h);
3633			return (error);
3634		}
3635	}
3636
3637	/*
3638	 * Fill in sockopt_data structure that may be useful for
3639	 * IP_FW3 get requests.
3640	 */
3641	locked = 0;
3642	if (valsize <= sizeof(xbuf)) {
3643		/* use on-stack buffer */
3644		sdata.kbuf = xbuf;
3645		sdata.ksize = sizeof(xbuf);
3646		sdata.kavail = valsize;
3647	} else {
3648
3649		/*
3650		 * Determine opcode type/buffer size:
3651		 * allocate sliding-window buf for data export or
3652		 * contiguous buffer for special ops.
3653		 */
3654		if ((h.dir & HDIR_SET) != 0) {
3655			/* Set request. Allocate contigous buffer. */
3656			if (valsize > CTL3_LARGEBUF) {
3657				find_unref_sh(&h);
3658				return (EFBIG);
3659			}
3660
3661			size = valsize;
3662		} else {
3663			/* Get request. Allocate sliding window buffer */
3664			size = (valsize<CTL3_SMALLBUF) ? valsize:CTL3_SMALLBUF;
3665
3666			if (size < valsize) {
3667				/* We have to wire user buffer */
3668				error = vslock(sopt->sopt_val, valsize);
3669				if (error != 0)
3670					return (error);
3671				locked = 1;
3672			}
3673		}
3674
3675		sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
3676		sdata.ksize = size;
3677		sdata.kavail = size;
3678	}
3679
3680	sdata.sopt = sopt;
3681	sdata.sopt_val = sopt->sopt_val;
3682	sdata.valsize = valsize;
3683
3684	/*
3685	 * Copy either all request (if valsize < bsize_max)
3686	 * or first bsize_max bytes to guarantee most consumers
3687	 * that all necessary data has been copied).
3688	 * Anyway, copy not less than sizeof(ip_fw3_opheader).
3689	 */
3690	if ((error = sooptcopyin(sopt, sdata.kbuf, sdata.ksize,
3691	    sizeof(ip_fw3_opheader))) != 0)
3692		return (error);
3693	op3 = (ip_fw3_opheader *)sdata.kbuf;
3694
3695	/* Finally, run handler */
3696	error = h.handler(chain, op3, &sdata);
3697	find_unref_sh(&h);
3698
3699	/* Flush state and free buffers */
3700	if (error == 0)
3701		error = ipfw_flush_sopt_data(&sdata);
3702	else
3703		ipfw_flush_sopt_data(&sdata);
3704
3705	if (locked != 0)
3706		vsunlock(sdata.sopt_val, valsize);
3707
3708	/* Restore original pointer and set number of bytes written */
3709	sopt->sopt_val = sdata.sopt_val;
3710	sopt->sopt_valsize = sdata.ktotal;
3711	if (sdata.kbuf != xbuf)
3712		free(sdata.kbuf, M_TEMP);
3713
3714	return (error);
3715}
3716
3717/**
3718 * {set|get}sockopt parser.
3719 */
3720int
3721ipfw_ctl(struct sockopt *sopt)
3722{
3723#define	RULE_MAXSIZE	(512*sizeof(u_int32_t))
3724	int error;
3725	size_t size, valsize;
3726	struct ip_fw *buf;
3727	struct ip_fw_rule0 *rule;
3728	struct ip_fw_chain *chain;
3729	u_int32_t rulenum[2];
3730	uint32_t opt;
3731	struct rule_check_info ci;
3732	IPFW_RLOCK_TRACKER;
3733
3734	chain = &V_layer3_chain;
3735	error = 0;
3736
3737	/* Save original valsize before it is altered via sooptcopyin() */
3738	valsize = sopt->sopt_valsize;
3739	opt = sopt->sopt_name;
3740
3741	/*
3742	 * Disallow modifications in really-really secure mode, but still allow
3743	 * the logging counters to be reset.
3744	 */
3745	if (opt == IP_FW_ADD ||
3746	    (sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) {
3747		error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
3748		if (error != 0)
3749			return (error);
3750	}
3751
3752	switch (opt) {
3753	case IP_FW_GET:
3754		/*
3755		 * pass up a copy of the current rules. Static rules
3756		 * come first (the last of which has number IPFW_DEFAULT_RULE),
3757		 * followed by a possibly empty list of dynamic rule.
3758		 * The last dynamic rule has NULL in the "next" field.
3759		 *
3760		 * Note that the calculated size is used to bound the
3761		 * amount of data returned to the user.  The rule set may
3762		 * change between calculating the size and returning the
3763		 * data in which case we'll just return what fits.
3764		 */
3765		for (;;) {
3766			int len = 0, want;
3767
3768			size = chain->static_len;
3769			size += ipfw_dyn_len();
3770			if (size >= sopt->sopt_valsize)
3771				break;
3772			buf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
3773			IPFW_UH_RLOCK(chain);
3774			/* check again how much space we need */
3775			want = chain->static_len + ipfw_dyn_len();
3776			if (size >= want)
3777				len = ipfw_getrules(chain, buf, size);
3778			IPFW_UH_RUNLOCK(chain);
3779			if (size >= want)
3780				error = sooptcopyout(sopt, buf, len);
3781			free(buf, M_TEMP);
3782			if (size >= want)
3783				break;
3784		}
3785		break;
3786
3787	case IP_FW_FLUSH:
3788		/* locking is done within del_entry() */
3789		error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */
3790		break;
3791
3792	case IP_FW_ADD:
3793		rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK);
3794		error = sooptcopyin(sopt, rule, RULE_MAXSIZE,
3795			sizeof(struct ip_fw7) );
3796
3797		memset(&ci, 0, sizeof(struct rule_check_info));
3798
3799		/*
3800		 * If the size of commands equals RULESIZE7 then we assume
3801		 * a FreeBSD7.2 binary is talking to us (set is7=1).
3802		 * is7 is persistent so the next 'ipfw list' command
3803		 * will use this format.
3804		 * NOTE: If wrong version is guessed (this can happen if
3805		 *       the first ipfw command is 'ipfw [pipe] list')
3806		 *       the ipfw binary may crash or loop infinitly...
3807		 */
3808		size = sopt->sopt_valsize;
3809		if (size == RULESIZE7(rule)) {
3810		    is7 = 1;
3811		    error = convert_rule_to_8(rule);
3812		    if (error) {
3813			free(rule, M_TEMP);
3814			return error;
3815		    }
3816		    size = RULESIZE(rule);
3817		} else
3818		    is7 = 0;
3819		if (error == 0)
3820			error = check_ipfw_rule0(rule, size, &ci);
3821		if (error == 0) {
3822			/* locking is done within add_rule() */
3823			struct ip_fw *krule;
3824			krule = ipfw_alloc_rule(chain, RULEKSIZE0(rule));
3825			ci.urule = (caddr_t)rule;
3826			ci.krule = krule;
3827			import_rule0(&ci);
3828			error = commit_rules(chain, &ci, 1);
3829			if (error != 0)
3830				free_rule(ci.krule);
3831			else if (sopt->sopt_dir == SOPT_GET) {
3832				if (is7) {
3833					error = convert_rule_to_7(rule);
3834					size = RULESIZE7(rule);
3835					if (error) {
3836						free(rule, M_TEMP);
3837						return error;
3838					}
3839				}
3840				error = sooptcopyout(sopt, rule, size);
3841			}
3842		}
3843		free(rule, M_TEMP);
3844		break;
3845
3846	case IP_FW_DEL:
3847		/*
3848		 * IP_FW_DEL is used for deleting single rules or sets,
3849		 * and (ab)used to atomically manipulate sets. Argument size
3850		 * is used to distinguish between the two:
3851		 *    sizeof(u_int32_t)
3852		 *	delete single rule or set of rules,
3853		 *	or reassign rules (or sets) to a different set.
3854		 *    2*sizeof(u_int32_t)
3855		 *	atomic disable/enable sets.
3856		 *	first u_int32_t contains sets to be disabled,
3857		 *	second u_int32_t contains sets to be enabled.
3858		 */
3859		error = sooptcopyin(sopt, rulenum,
3860			2*sizeof(u_int32_t), sizeof(u_int32_t));
3861		if (error)
3862			break;
3863		size = sopt->sopt_valsize;
3864		if (size == sizeof(u_int32_t) && rulenum[0] != 0) {
3865			/* delete or reassign, locking done in del_entry() */
3866			error = del_entry(chain, rulenum[0]);
3867		} else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */
3868			IPFW_UH_WLOCK(chain);
3869			V_set_disable =
3870			    (V_set_disable | rulenum[0]) & ~rulenum[1] &
3871			    ~(1<<RESVD_SET); /* set RESVD_SET always enabled */
3872			IPFW_UH_WUNLOCK(chain);
3873		} else
3874			error = EINVAL;
3875		break;
3876
3877	case IP_FW_ZERO:
3878	case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */
3879		rulenum[0] = 0;
3880		if (sopt->sopt_val != 0) {
3881		    error = sooptcopyin(sopt, rulenum,
3882			    sizeof(u_int32_t), sizeof(u_int32_t));
3883		    if (error)
3884			break;
3885		}
3886		error = zero_entry(chain, rulenum[0],
3887			sopt->sopt_name == IP_FW_RESETLOG);
3888		break;
3889
3890	/*--- TABLE opcodes ---*/
3891	case IP_FW_TABLE_ADD:
3892	case IP_FW_TABLE_DEL:
3893		{
3894			ipfw_table_entry ent;
3895			struct tentry_info tei;
3896			struct tid_info ti;
3897			struct table_value v;
3898
3899			error = sooptcopyin(sopt, &ent,
3900			    sizeof(ent), sizeof(ent));
3901			if (error)
3902				break;
3903
3904			memset(&tei, 0, sizeof(tei));
3905			tei.paddr = &ent.addr;
3906			tei.subtype = AF_INET;
3907			tei.masklen = ent.masklen;
3908			ipfw_import_table_value_legacy(ent.value, &v);
3909			tei.pvalue = &v;
3910			memset(&ti, 0, sizeof(ti));
3911			ti.uidx = ent.tbl;
3912			ti.type = IPFW_TABLE_CIDR;
3913
3914			error = (opt == IP_FW_TABLE_ADD) ?
3915			    add_table_entry(chain, &ti, &tei, 0, 1) :
3916			    del_table_entry(chain, &ti, &tei, 0, 1);
3917		}
3918		break;
3919
3920
3921	case IP_FW_TABLE_FLUSH:
3922		{
3923			u_int16_t tbl;
3924			struct tid_info ti;
3925
3926			error = sooptcopyin(sopt, &tbl,
3927			    sizeof(tbl), sizeof(tbl));
3928			if (error)
3929				break;
3930			memset(&ti, 0, sizeof(ti));
3931			ti.uidx = tbl;
3932			error = flush_table(chain, &ti);
3933		}
3934		break;
3935
3936	case IP_FW_TABLE_GETSIZE:
3937		{
3938			u_int32_t tbl, cnt;
3939			struct tid_info ti;
3940
3941			if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl),
3942			    sizeof(tbl))))
3943				break;
3944			memset(&ti, 0, sizeof(ti));
3945			ti.uidx = tbl;
3946			IPFW_RLOCK(chain);
3947			error = ipfw_count_table(chain, &ti, &cnt);
3948			IPFW_RUNLOCK(chain);
3949			if (error)
3950				break;
3951			error = sooptcopyout(sopt, &cnt, sizeof(cnt));
3952		}
3953		break;
3954
3955	case IP_FW_TABLE_LIST:
3956		{
3957			ipfw_table *tbl;
3958			struct tid_info ti;
3959
3960			if (sopt->sopt_valsize < sizeof(*tbl)) {
3961				error = EINVAL;
3962				break;
3963			}
3964			size = sopt->sopt_valsize;
3965			tbl = malloc(size, M_TEMP, M_WAITOK);
3966			error = sooptcopyin(sopt, tbl, size, sizeof(*tbl));
3967			if (error) {
3968				free(tbl, M_TEMP);
3969				break;
3970			}
3971			tbl->size = (size - sizeof(*tbl)) /
3972			    sizeof(ipfw_table_entry);
3973			memset(&ti, 0, sizeof(ti));
3974			ti.uidx = tbl->tbl;
3975			IPFW_RLOCK(chain);
3976			error = ipfw_dump_table_legacy(chain, &ti, tbl);
3977			IPFW_RUNLOCK(chain);
3978			if (error) {
3979				free(tbl, M_TEMP);
3980				break;
3981			}
3982			error = sooptcopyout(sopt, tbl, size);
3983			free(tbl, M_TEMP);
3984		}
3985		break;
3986
3987	/*--- NAT operations are protected by the IPFW_LOCK ---*/
3988	case IP_FW_NAT_CFG:
3989		if (IPFW_NAT_LOADED)
3990			error = ipfw_nat_cfg_ptr(sopt);
3991		else {
3992			printf("IP_FW_NAT_CFG: %s\n",
3993			    "ipfw_nat not present, please load it");
3994			error = EINVAL;
3995		}
3996		break;
3997
3998	case IP_FW_NAT_DEL:
3999		if (IPFW_NAT_LOADED)
4000			error = ipfw_nat_del_ptr(sopt);
4001		else {
4002			printf("IP_FW_NAT_DEL: %s\n",
4003			    "ipfw_nat not present, please load it");
4004			error = EINVAL;
4005		}
4006		break;
4007
4008	case IP_FW_NAT_GET_CONFIG:
4009		if (IPFW_NAT_LOADED)
4010			error = ipfw_nat_get_cfg_ptr(sopt);
4011		else {
4012			printf("IP_FW_NAT_GET_CFG: %s\n",
4013			    "ipfw_nat not present, please load it");
4014			error = EINVAL;
4015		}
4016		break;
4017
4018	case IP_FW_NAT_GET_LOG:
4019		if (IPFW_NAT_LOADED)
4020			error = ipfw_nat_get_log_ptr(sopt);
4021		else {
4022			printf("IP_FW_NAT_GET_LOG: %s\n",
4023			    "ipfw_nat not present, please load it");
4024			error = EINVAL;
4025		}
4026		break;
4027
4028	default:
4029		printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
4030		error = EINVAL;
4031	}
4032
4033	return (error);
4034#undef RULE_MAXSIZE
4035}
4036#define	RULE_MAXSIZE	(256*sizeof(u_int32_t))
4037
4038/* Functions to convert rules 7.2 <==> 8.0 */
4039static int
4040convert_rule_to_7(struct ip_fw_rule0 *rule)
4041{
4042	/* Used to modify original rule */
4043	struct ip_fw7 *rule7 = (struct ip_fw7 *)rule;
4044	/* copy of original rule, version 8 */
4045	struct ip_fw_rule0 *tmp;
4046
4047	/* Used to copy commands */
4048	ipfw_insn *ccmd, *dst;
4049	int ll = 0, ccmdlen = 0;
4050
4051	tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO);
4052	if (tmp == NULL) {
4053		return 1; //XXX error
4054	}
4055	bcopy(rule, tmp, RULE_MAXSIZE);
4056
4057	/* Copy fields */
4058	//rule7->_pad = tmp->_pad;
4059	rule7->set = tmp->set;
4060	rule7->rulenum = tmp->rulenum;
4061	rule7->cmd_len = tmp->cmd_len;
4062	rule7->act_ofs = tmp->act_ofs;
4063	rule7->next_rule = (struct ip_fw7 *)tmp->next_rule;
4064	rule7->cmd_len = tmp->cmd_len;
4065	rule7->pcnt = tmp->pcnt;
4066	rule7->bcnt = tmp->bcnt;
4067	rule7->timestamp = tmp->timestamp;
4068
4069	/* Copy commands */
4070	for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ;
4071			ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) {
4072		ccmdlen = F_LEN(ccmd);
4073
4074		bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t));
4075
4076		if (dst->opcode > O_NAT)
4077			/* O_REASS doesn't exists in 7.2 version, so
4078			 * decrement opcode if it is after O_REASS
4079			 */
4080			dst->opcode--;
4081
4082		if (ccmdlen > ll) {
4083			printf("ipfw: opcode %d size truncated\n",
4084				ccmd->opcode);
4085			return EINVAL;
4086		}
4087	}
4088	free(tmp, M_TEMP);
4089
4090	return 0;
4091}
4092
4093static int
4094convert_rule_to_8(struct ip_fw_rule0 *rule)
4095{
4096	/* Used to modify original rule */
4097	struct ip_fw7 *rule7 = (struct ip_fw7 *) rule;
4098
4099	/* Used to copy commands */
4100	ipfw_insn *ccmd, *dst;
4101	int ll = 0, ccmdlen = 0;
4102
4103	/* Copy of original rule */
4104	struct ip_fw7 *tmp = malloc(RULE_MAXSIZE, M_TEMP, M_NOWAIT | M_ZERO);
4105	if (tmp == NULL) {
4106		return 1; //XXX error
4107	}
4108
4109	bcopy(rule7, tmp, RULE_MAXSIZE);
4110
4111	for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule->cmd ;
4112			ll > 0 ; ll -= ccmdlen, ccmd += ccmdlen, dst += ccmdlen) {
4113		ccmdlen = F_LEN(ccmd);
4114
4115		bcopy(ccmd, dst, F_LEN(ccmd)*sizeof(uint32_t));
4116
4117		if (dst->opcode > O_NAT)
4118			/* O_REASS doesn't exists in 7.2 version, so
4119			 * increment opcode if it is after O_REASS
4120			 */
4121			dst->opcode++;
4122
4123		if (ccmdlen > ll) {
4124			printf("ipfw: opcode %d size truncated\n",
4125			    ccmd->opcode);
4126			return EINVAL;
4127		}
4128	}
4129
4130	rule->_pad = tmp->_pad;
4131	rule->set = tmp->set;
4132	rule->rulenum = tmp->rulenum;
4133	rule->cmd_len = tmp->cmd_len;
4134	rule->act_ofs = tmp->act_ofs;
4135	rule->next_rule = (struct ip_fw *)tmp->next_rule;
4136	rule->cmd_len = tmp->cmd_len;
4137	rule->id = 0; /* XXX see if is ok = 0 */
4138	rule->pcnt = tmp->pcnt;
4139	rule->bcnt = tmp->bcnt;
4140	rule->timestamp = tmp->timestamp;
4141
4142	free (tmp, M_TEMP);
4143	return 0;
4144}
4145
4146/*
4147 * Named object api
4148 *
4149 */
4150
4151void
4152ipfw_init_srv(struct ip_fw_chain *ch)
4153{
4154
4155	ch->srvmap = ipfw_objhash_create(IPFW_OBJECTS_DEFAULT);
4156	ch->srvstate = malloc(sizeof(void *) * IPFW_OBJECTS_DEFAULT,
4157	    M_IPFW, M_WAITOK | M_ZERO);
4158}
4159
4160void
4161ipfw_destroy_srv(struct ip_fw_chain *ch)
4162{
4163
4164	free(ch->srvstate, M_IPFW);
4165	ipfw_objhash_destroy(ch->srvmap);
4166}
4167
4168/*
4169 * Allocate new bitmask which can be used to enlarge/shrink
4170 * named instance index.
4171 */
4172void
4173ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks)
4174{
4175	size_t size;
4176	int max_blocks;
4177	u_long *idx_mask;
4178
4179	KASSERT((items % BLOCK_ITEMS) == 0,
4180	   ("bitmask size needs to power of 2 and greater or equal to %zu",
4181	    BLOCK_ITEMS));
4182
4183	max_blocks = items / BLOCK_ITEMS;
4184	size = items / 8;
4185	idx_mask = malloc(size * IPFW_MAX_SETS, M_IPFW, M_WAITOK);
4186	/* Mark all as free */
4187	memset(idx_mask, 0xFF, size * IPFW_MAX_SETS);
4188	*idx_mask &= ~(u_long)1; /* Skip index 0 */
4189
4190	*idx = idx_mask;
4191	*pblocks = max_blocks;
4192}
4193
4194/*
4195 * Copy current bitmask index to new one.
4196 */
4197void
4198ipfw_objhash_bitmap_merge(struct namedobj_instance *ni, void **idx, int *blocks)
4199{
4200	int old_blocks, new_blocks;
4201	u_long *old_idx, *new_idx;
4202	int i;
4203
4204	old_idx = ni->idx_mask;
4205	old_blocks = ni->max_blocks;
4206	new_idx = *idx;
4207	new_blocks = *blocks;
4208
4209	for (i = 0; i < IPFW_MAX_SETS; i++) {
4210		memcpy(&new_idx[new_blocks * i], &old_idx[old_blocks * i],
4211		    old_blocks * sizeof(u_long));
4212	}
4213}
4214
4215/*
4216 * Swaps current @ni index with new one.
4217 */
4218void
4219ipfw_objhash_bitmap_swap(struct namedobj_instance *ni, void **idx, int *blocks)
4220{
4221	int old_blocks;
4222	u_long *old_idx;
4223
4224	old_idx = ni->idx_mask;
4225	old_blocks = ni->max_blocks;
4226
4227	ni->idx_mask = *idx;
4228	ni->max_blocks = *blocks;
4229
4230	/* Save old values */
4231	*idx = old_idx;
4232	*blocks = old_blocks;
4233}
4234
4235void
4236ipfw_objhash_bitmap_free(void *idx, int blocks)
4237{
4238
4239	free(idx, M_IPFW);
4240}
4241
4242/*
4243 * Creates named hash instance.
4244 * Must be called without holding any locks.
4245 * Return pointer to new instance.
4246 */
4247struct namedobj_instance *
4248ipfw_objhash_create(uint32_t items)
4249{
4250	struct namedobj_instance *ni;
4251	int i;
4252	size_t size;
4253
4254	size = sizeof(struct namedobj_instance) +
4255	    sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE +
4256	    sizeof(struct namedobjects_head) * NAMEDOBJ_HASH_SIZE;
4257
4258	ni = malloc(size, M_IPFW, M_WAITOK | M_ZERO);
4259	ni->nn_size = NAMEDOBJ_HASH_SIZE;
4260	ni->nv_size = NAMEDOBJ_HASH_SIZE;
4261
4262	ni->names = (struct namedobjects_head *)(ni +1);
4263	ni->values = &ni->names[ni->nn_size];
4264
4265	for (i = 0; i < ni->nn_size; i++)
4266		TAILQ_INIT(&ni->names[i]);
4267
4268	for (i = 0; i < ni->nv_size; i++)
4269		TAILQ_INIT(&ni->values[i]);
4270
4271	/* Set default hashing/comparison functions */
4272	ni->hash_f = objhash_hash_name;
4273	ni->cmp_f = objhash_cmp_name;
4274
4275	/* Allocate bitmask separately due to possible resize */
4276	ipfw_objhash_bitmap_alloc(items, (void*)&ni->idx_mask, &ni->max_blocks);
4277
4278	return (ni);
4279}
4280
4281void
4282ipfw_objhash_destroy(struct namedobj_instance *ni)
4283{
4284
4285	free(ni->idx_mask, M_IPFW);
4286	free(ni, M_IPFW);
4287}
4288
4289void
4290ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f,
4291    objhash_cmp_f *cmp_f)
4292{
4293
4294	ni->hash_f = hash_f;
4295	ni->cmp_f = cmp_f;
4296}
4297
4298static uint32_t
4299objhash_hash_name(struct namedobj_instance *ni, const void *name, uint32_t set)
4300{
4301
4302	return (fnv_32_str((const char *)name, FNV1_32_INIT));
4303}
4304
4305static int
4306objhash_cmp_name(struct named_object *no, const void *name, uint32_t set)
4307{
4308
4309	if ((strcmp(no->name, (const char *)name) == 0) && (no->set == set))
4310		return (0);
4311
4312	return (1);
4313}
4314
4315static uint32_t
4316objhash_hash_idx(struct namedobj_instance *ni, uint32_t val)
4317{
4318	uint32_t v;
4319
4320	v = val % (ni->nv_size - 1);
4321
4322	return (v);
4323}
4324
4325struct named_object *
4326ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set, char *name)
4327{
4328	struct named_object *no;
4329	uint32_t hash;
4330
4331	hash = ni->hash_f(ni, name, set) % ni->nn_size;
4332
4333	TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
4334		if (ni->cmp_f(no, name, set) == 0)
4335			return (no);
4336	}
4337
4338	return (NULL);
4339}
4340
4341/*
4342 * Find named object by @uid.
4343 * Check @tlvs for valid data inside.
4344 *
4345 * Returns pointer to found TLV or NULL.
4346 */
4347ipfw_obj_ntlv *
4348ipfw_find_name_tlv_type(void *tlvs, int len, uint16_t uidx, uint32_t etlv)
4349{
4350	ipfw_obj_ntlv *ntlv;
4351	uintptr_t pa, pe;
4352	int l;
4353
4354	pa = (uintptr_t)tlvs;
4355	pe = pa + len;
4356	l = 0;
4357	for (; pa < pe; pa += l) {
4358		ntlv = (ipfw_obj_ntlv *)pa;
4359		l = ntlv->head.length;
4360
4361		if (l != sizeof(*ntlv))
4362			return (NULL);
4363
4364		if (ntlv->idx != uidx)
4365			continue;
4366		/*
4367		 * When userland has specified zero TLV type, do
4368		 * not compare it with eltv. In some cases userland
4369		 * doesn't know what type should it have. Use only
4370		 * uidx and name for search named_object.
4371		 */
4372		if (ntlv->head.type != 0 &&
4373		    ntlv->head.type != (uint16_t)etlv)
4374			continue;
4375
4376		if (ipfw_check_object_name_generic(ntlv->name) != 0)
4377			return (NULL);
4378
4379		return (ntlv);
4380	}
4381
4382	return (NULL);
4383}
4384
4385/*
4386 * Finds object config based on either legacy index
4387 * or name in ntlv.
4388 * Note @ti structure contains unchecked data from userland.
4389 *
4390 * Returns 0 in success and fills in @pno with found config
4391 */
4392int
4393ipfw_objhash_find_type(struct namedobj_instance *ni, struct tid_info *ti,
4394    uint32_t etlv, struct named_object **pno)
4395{
4396	char *name;
4397	ipfw_obj_ntlv *ntlv;
4398	uint32_t set;
4399
4400	if (ti->tlvs == NULL)
4401		return (EINVAL);
4402
4403	ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, etlv);
4404	if (ntlv == NULL)
4405		return (EINVAL);
4406	name = ntlv->name;
4407
4408	/*
4409	 * Use set provided by @ti instead of @ntlv one.
4410	 * This is needed due to different sets behavior
4411	 * controlled by V_fw_tables_sets.
4412	 */
4413	set = ti->set;
4414	*pno = ipfw_objhash_lookup_name(ni, set, name);
4415	if (*pno == NULL)
4416		return (ESRCH);
4417	return (0);
4418}
4419
4420/*
4421 * Find named object by name, considering also its TLV type.
4422 */
4423struct named_object *
4424ipfw_objhash_lookup_name_type(struct namedobj_instance *ni, uint32_t set,
4425    uint32_t type, const char *name)
4426{
4427	struct named_object *no;
4428	uint32_t hash;
4429
4430	hash = ni->hash_f(ni, name, set) % ni->nn_size;
4431
4432	TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
4433		if (ni->cmp_f(no, name, set) == 0 &&
4434		    no->etlv == (uint16_t)type)
4435			return (no);
4436	}
4437
4438	return (NULL);
4439}
4440
4441struct named_object *
4442ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx)
4443{
4444	struct named_object *no;
4445	uint32_t hash;
4446
4447	hash = objhash_hash_idx(ni, kidx);
4448
4449	TAILQ_FOREACH(no, &ni->values[hash], nv_next) {
4450		if (no->kidx == kidx)
4451			return (no);
4452	}
4453
4454	return (NULL);
4455}
4456
4457int
4458ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
4459    struct named_object *b)
4460{
4461
4462	if ((strcmp(a->name, b->name) == 0) && a->set == b->set)
4463		return (1);
4464
4465	return (0);
4466}
4467
4468void
4469ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no)
4470{
4471	uint32_t hash;
4472
4473	hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
4474	TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next);
4475
4476	hash = objhash_hash_idx(ni, no->kidx);
4477	TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next);
4478
4479	ni->count++;
4480}
4481
4482void
4483ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no)
4484{
4485	uint32_t hash;
4486
4487	hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
4488	TAILQ_REMOVE(&ni->names[hash], no, nn_next);
4489
4490	hash = objhash_hash_idx(ni, no->kidx);
4491	TAILQ_REMOVE(&ni->values[hash], no, nv_next);
4492
4493	ni->count--;
4494}
4495
4496uint32_t
4497ipfw_objhash_count(struct namedobj_instance *ni)
4498{
4499
4500	return (ni->count);
4501}
4502
4503uint32_t
4504ipfw_objhash_count_type(struct namedobj_instance *ni, uint16_t type)
4505{
4506	struct named_object *no;
4507	uint32_t count;
4508	int i;
4509
4510	count = 0;
4511	for (i = 0; i < ni->nn_size; i++) {
4512		TAILQ_FOREACH(no, &ni->names[i], nn_next) {
4513			if (no->etlv == type)
4514				count++;
4515		}
4516	}
4517	return (count);
4518}
4519
4520/*
4521 * Runs @func for each found named object.
4522 * It is safe to delete objects from callback
4523 */
4524int
4525ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
4526{
4527	struct named_object *no, *no_tmp;
4528	int i, ret;
4529
4530	for (i = 0; i < ni->nn_size; i++) {
4531		TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
4532			ret = f(ni, no, arg);
4533			if (ret != 0)
4534				return (ret);
4535		}
4536	}
4537	return (0);
4538}
4539
4540/*
4541 * Runs @f for each found named object with type @type.
4542 * It is safe to delete objects from callback
4543 */
4544int
4545ipfw_objhash_foreach_type(struct namedobj_instance *ni, objhash_cb_t *f,
4546    void *arg, uint16_t type)
4547{
4548	struct named_object *no, *no_tmp;
4549	int i, ret;
4550
4551	for (i = 0; i < ni->nn_size; i++) {
4552		TAILQ_FOREACH_SAFE(no, &ni->names[i], nn_next, no_tmp) {
4553			if (no->etlv != type)
4554				continue;
4555			ret = f(ni, no, arg);
4556			if (ret != 0)
4557				return (ret);
4558		}
4559	}
4560	return (0);
4561}
4562
4563/*
4564 * Removes index from given set.
4565 * Returns 0 on success.
4566 */
4567int
4568ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx)
4569{
4570	u_long *mask;
4571	int i, v;
4572
4573	i = idx / BLOCK_ITEMS;
4574	v = idx % BLOCK_ITEMS;
4575
4576	if (i >= ni->max_blocks)
4577		return (1);
4578
4579	mask = &ni->idx_mask[i];
4580
4581	if ((*mask & ((u_long)1 << v)) != 0)
4582		return (1);
4583
4584	/* Mark as free */
4585	*mask |= (u_long)1 << v;
4586
4587	/* Update free offset */
4588	if (ni->free_off[0] > i)
4589		ni->free_off[0] = i;
4590
4591	return (0);
4592}
4593
4594/*
4595 * Allocate new index in given instance and stores in in @pidx.
4596 * Returns 0 on success.
4597 */
4598int
4599ipfw_objhash_alloc_idx(void *n, uint16_t *pidx)
4600{
4601	struct namedobj_instance *ni;
4602	u_long *mask;
4603	int i, off, v;
4604
4605	ni = (struct namedobj_instance *)n;
4606
4607	off = ni->free_off[0];
4608	mask = &ni->idx_mask[off];
4609
4610	for (i = off; i < ni->max_blocks; i++, mask++) {
4611		if ((v = ffsl(*mask)) == 0)
4612			continue;
4613
4614		/* Mark as busy */
4615		*mask &= ~ ((u_long)1 << (v - 1));
4616
4617		ni->free_off[0] = i;
4618
4619		v = BLOCK_ITEMS * i + v - 1;
4620
4621		*pidx = v;
4622		return (0);
4623	}
4624
4625	return (1);
4626}
4627
4628/* end of file */
4629