ip_fw_sockopt.c revision 201732
1/*-
2 * Copyright (c) 2002-2009 Luigi Rizzo, Universita` di Pisa
3 *
4 * Supported by: Valeria Paoli
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/netinet/ipfw/ip_fw_sockopt.c 201732 2010-01-07 10:08:05Z luigi $");
30
31/*
32 * Sockopt support for ipfw. The routines here implement
33 * the upper half of the ipfw code.
34 */
35
36#if !defined(KLD_MODULE)
37#include "opt_ipfw.h"
38#include "opt_ipdivert.h"
39#include "opt_ipdn.h"
40#include "opt_inet.h"
41#ifndef INET
42#error IPFIREWALL requires INET.
43#endif /* INET */
44#endif
45#include "opt_inet6.h"
46#include "opt_ipsec.h"
47
48#include <sys/param.h>
49#include <sys/systm.h>
50#include <sys/malloc.h>
51#include <sys/mbuf.h>	/* struct m_tag used by nested headers */
52#include <sys/kernel.h>
53#include <sys/lock.h>
54#include <sys/priv.h>
55#include <sys/proc.h>
56#include <sys/rwlock.h>
57#include <sys/socket.h>
58#include <sys/socketvar.h>
59#include <sys/sysctl.h>
60#include <sys/syslog.h>
61#include <net/if.h>
62#include <net/route.h>
63#include <net/vnet.h>
64
65#include <netinet/in.h>
66#include <netinet/ip_var.h> /* hooks */
67#include <netinet/ip_fw.h>
68#include <netinet/ipfw/ip_fw_private.h>
69
70#ifdef MAC
71#include <security/mac/mac_framework.h>
72#endif
73
74MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
75
76/*
77 * static variables followed by global ones (none in this file)
78 */
79
80/*
81 * Find the smallest rule >= key, id.
82 * We could use bsearch but it is so simple that we code it directly
83 */
84int
85ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id)
86{
87	int i, lo, hi;
88	struct ip_fw *r;
89
90  	for (lo = 0, hi = chain->n_rules - 1; lo < hi;) {
91		i = (lo + hi) / 2;
92		r = chain->map[i];
93		if (r->rulenum < key)
94			lo = i + 1;	/* continue from the next one */
95		else if (r->rulenum > key)
96			hi = i;		/* this might be good */
97		else if (r->id < id)
98			lo = i + 1;	/* continue from the next one */
99		else /* r->id >= id */
100			hi = i;		/* this might be good */
101	};
102	return hi;
103}
104
105/*
106 * allocate a new map, returns the chain locked. extra is the number
107 * of entries to add or delete.
108 */
109static struct ip_fw **
110get_map(struct ip_fw_chain *chain, int extra, int locked)
111{
112
113	for (;;) {
114		struct ip_fw **map;
115		int i;
116
117		i = chain->n_rules + extra;
118		map = malloc(i * sizeof(struct ip_fw *), M_IPFW, M_WAITOK);
119		if (map == NULL) {
120			printf("%s: cannot allocate map\n", __FUNCTION__);
121			return NULL;
122		}
123		if (!locked)
124			IPFW_UH_WLOCK(chain);
125		if (i >= chain->n_rules + extra) /* good */
126			return map;
127		/* otherwise we lost the race, free and retry */
128		if (!locked)
129			IPFW_UH_WUNLOCK(chain);
130		free(map, M_IPFW);
131	}
132}
133
134/*
135 * swap the maps. It is supposed to be called with IPFW_UH_WLOCK
136 */
137static struct ip_fw **
138swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
139{
140	struct ip_fw **old_map;
141
142	IPFW_WLOCK(chain);
143	chain->id++;
144	chain->n_rules = new_len;
145	old_map = chain->map;
146	chain->map = new_map;
147	IPFW_WUNLOCK(chain);
148	return old_map;
149}
150
151/*
152 * Add a new rule to the list. Copy the rule into a malloc'ed area, then
153 * possibly create a rule number and add the rule to the list.
154 * Update the rule_number in the input struct so the caller knows it as well.
155 * XXX DO NOT USE FOR THE DEFAULT RULE.
156 * Must be called without IPFW_UH held
157 */
158int
159ipfw_add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule)
160{
161	struct ip_fw *rule;
162	int i, l, insert_before;
163	struct ip_fw **map;	/* the new array of pointers */
164
165	if (chain->rules == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE-1)
166		return (EINVAL);
167
168	l = RULESIZE(input_rule);
169	rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO);
170	if (rule == NULL)
171		return (ENOSPC);
172	/* get_map returns with IPFW_UH_WLOCK if successful */
173	map = get_map(chain, 1, 0 /* not locked */);
174	if (map == NULL) {
175		free(rule, M_IPFW);
176		return ENOSPC;
177	}
178
179	bcopy(input_rule, rule, l);
180	/* clear fields not settable from userland */
181	rule->x_next = NULL;
182	rule->next_rule = NULL;
183	rule->pcnt = 0;
184	rule->bcnt = 0;
185	rule->timestamp = 0;
186
187	if (V_autoinc_step < 1)
188		V_autoinc_step = 1;
189	else if (V_autoinc_step > 1000)
190		V_autoinc_step = 1000;
191	/* find the insertion point, we will insert before */
192	insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE;
193	i = ipfw_find_rule(chain, insert_before, 0);
194	/* duplicate first part */
195	if (i > 0)
196		bcopy(chain->map, map, i * sizeof(struct ip_fw *));
197	map[i] = rule;
198	/* duplicate remaining part, we always have the default rule */
199	bcopy(chain->map + i, map + i + 1,
200		sizeof(struct ip_fw *) *(chain->n_rules - i));
201	if (rule->rulenum == 0) {
202		/* write back the number */
203		rule->rulenum = i > 0 ? map[i-1]->rulenum : 0;
204		if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
205			rule->rulenum += V_autoinc_step;
206		input_rule->rulenum = rule->rulenum;
207	}
208
209	rule->id = chain->id + 1;
210	map = swap_map(chain, map, chain->n_rules + 1);
211	chain->static_len += l;
212	IPFW_UH_WUNLOCK(chain);
213	if (map)
214		free(map, M_IPFW);
215	return (0);
216}
217
218/*
219 * Reclaim storage associated with a list of rules.  This is
220 * typically the list created using remove_rule.
221 * A NULL pointer on input is handled correctly.
222 */
223void
224ipfw_reap_rules(struct ip_fw *head)
225{
226	struct ip_fw *rule;
227
228	while ((rule = head) != NULL) {
229		head = head->x_next;
230		free(rule, M_IPFW);
231	}
232}
233
234/**
235 * Remove all rules with given number, and also do set manipulation.
236 * Assumes chain != NULL && *chain != NULL.
237 *
238 * The argument is an u_int32_t. The low 16 bit are the rule or set number,
239 * the next 8 bits are the new set, the top 8 bits are the command:
240 *
241 *	0	delete rules with given number
242 *	1	delete rules with given set number
243 *	2	move rules with given number to new set
244 *	3	move rules with given set number to new set
245 *	4	swap sets with given numbers
246 *	5	delete rules with given number and with given set number
247 */
248static int
249del_entry(struct ip_fw_chain *chain, u_int32_t arg)
250{
251	struct ip_fw *rule;
252	uint32_t rulenum;	/* rule or old_set */
253	uint8_t cmd, new_set;
254	int start, end = 0, i, ofs, n;
255	struct ip_fw **map = NULL;
256	int error = 0;
257
258	rulenum = arg & 0xffff;
259	cmd = (arg >> 24) & 0xff;
260	new_set = (arg >> 16) & 0xff;
261
262	if (cmd > 5 || new_set > RESVD_SET)
263		return EINVAL;
264	if (cmd == 0 || cmd == 2 || cmd == 5) {
265		if (rulenum >= IPFW_DEFAULT_RULE)
266			return EINVAL;
267	} else {
268		if (rulenum > RESVD_SET)	/* old_set */
269			return EINVAL;
270	}
271
272	IPFW_UH_WLOCK(chain); /* prevent conflicts among the writers */
273	chain->reap = NULL;	/* prepare for deletions */
274
275	switch (cmd) {
276	case 0:	/* delete rules with given number (0 is special means all) */
277	case 1:	/* delete all rules with given set number, rule->set == rulenum */
278	case 5: /* delete rules with given number and with given set number.
279		 * rulenum - given rule number;
280		 * new_set - given set number.
281		 */
282		/* locate first rule to delete (start), the one after the
283		 * last one (end), and count how many rules to delete (n)
284		 */
285		n = 0;
286		if (cmd == 1) { /* look for a specific set, must scan all */
287			for (start = -1, i = 0; i < chain->n_rules; i++) {
288				if (chain->map[start]->set != rulenum)
289					continue;
290				if (start < 0)
291					start = i;
292				end = i;
293				n++;
294			}
295			end++;	/* first non-matching */
296		} else {
297			start = ipfw_find_rule(chain, rulenum, 0);
298			for (end = start; end < chain->n_rules; end++) {
299				rule = chain->map[end];
300				if (rulenum > 0 && rule->rulenum != rulenum)
301					break;
302				if (rule->set != RESVD_SET &&
303				    (cmd == 0 || rule->set == new_set) )
304					n++;
305			}
306		}
307		if (n == 0 && arg == 0)
308			break; /* special case, flush on empty ruleset */
309		/* allocate the map, if needed */
310		if (n > 0)
311			map = get_map(chain, -n, 1 /* locked */);
312		if (n == 0 || map == NULL) {
313			error = EINVAL;
314			break;
315		}
316		/* copy the initial part of the map */
317		if (start > 0)
318			bcopy(chain->map, map, start * sizeof(struct ip_fw *));
319		/* copy active rules between start and end */
320		for (i = ofs = start; i < end; i++) {
321			rule = chain->map[i];
322			if (!(rule->set != RESVD_SET &&
323			    (cmd == 0 || rule->set == new_set) ))
324				map[ofs++] = chain->map[i];
325		}
326		/* finally the tail */
327		bcopy(chain->map + end, map + ofs,
328			(chain->n_rules - end) * sizeof(struct ip_fw *));
329		map = swap_map(chain, map, chain->n_rules - n);
330		/* now remove the rules deleted */
331		for (i = start; i < end; i++) {
332			rule = map[i];
333			if (rule->set != RESVD_SET &&
334			    (cmd == 0 || rule->set == new_set) ) {
335				int l = RULESIZE(rule);
336
337				chain->static_len -= l;
338				ipfw_remove_dyn_children(rule);
339				rule->x_next = chain->reap;
340				chain->reap = rule;
341			}
342		}
343		break;
344
345	case 2:	/* move rules with given number to new set */
346		IPFW_UH_WLOCK(chain);
347		for (i = 0; i < chain->n_rules; i++) {
348			rule = chain->map[i];
349			if (rule->rulenum == rulenum)
350				rule->set = new_set;
351		}
352		IPFW_UH_WUNLOCK(chain);
353		break;
354
355	case 3: /* move rules with given set number to new set */
356		IPFW_UH_WLOCK(chain);
357		for (i = 0; i < chain->n_rules; i++) {
358			rule = chain->map[i];
359			if (rule->set == rulenum)
360				rule->set = new_set;
361		}
362		IPFW_UH_WUNLOCK(chain);
363		break;
364
365	case 4: /* swap two sets */
366		IPFW_UH_WLOCK(chain);
367		for (i = 0; i < chain->n_rules; i++) {
368			rule = chain->map[i];
369			if (rule->set == rulenum)
370				rule->set = new_set;
371			else if (rule->set == new_set)
372				rule->set = rulenum;
373		}
374		IPFW_UH_WUNLOCK(chain);
375		break;
376	}
377	rule = chain->reap;
378	chain->reap = NULL;
379	IPFW_UH_WUNLOCK(chain);
380	ipfw_reap_rules(rule);
381	if (map)
382		free(map, M_IPFW);
383	return error;
384}
385
386/*
387 * Clear counters for a specific rule.
388 * Normally run under IPFW_UH_RLOCK, but these are idempotent ops
389 * so we only care that rules do not disappear.
390 */
391static void
392clear_counters(struct ip_fw *rule, int log_only)
393{
394	ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
395
396	if (log_only == 0) {
397		rule->bcnt = rule->pcnt = 0;
398		rule->timestamp = 0;
399	}
400	if (l->o.opcode == O_LOG)
401		l->log_left = l->max_log;
402}
403
404/**
405 * Reset some or all counters on firewall rules.
406 * The argument `arg' is an u_int32_t. The low 16 bit are the rule number,
407 * the next 8 bits are the set number, the top 8 bits are the command:
408 *	0	work with rules from all set's;
409 *	1	work with rules only from specified set.
410 * Specified rule number is zero if we want to clear all entries.
411 * log_only is 1 if we only want to reset logs, zero otherwise.
412 */
413static int
414zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only)
415{
416	struct ip_fw *rule;
417	char *msg;
418	int i;
419
420	uint16_t rulenum = arg & 0xffff;
421	uint8_t set = (arg >> 16) & 0xff;
422	uint8_t cmd = (arg >> 24) & 0xff;
423
424	if (cmd > 1)
425		return (EINVAL);
426	if (cmd == 1 && set > RESVD_SET)
427		return (EINVAL);
428
429	IPFW_UH_RLOCK(chain);
430	if (rulenum == 0) {
431		V_norule_counter = 0;
432		for (i = 0; i < chain->n_rules; i++) {
433			rule = chain->map[i];
434			/* Skip rules not in our set. */
435			if (cmd == 1 && rule->set != set)
436				continue;
437			clear_counters(rule, log_only);
438		}
439		msg = log_only ? "All logging counts reset" :
440		    "Accounting cleared";
441	} else {
442		int cleared = 0;
443		for (i = 0; i < chain->n_rules; i++) {
444			rule = chain->map[i];
445			if (rule->rulenum == rulenum) {
446				if (cmd == 0 || rule->set == set)
447					clear_counters(rule, log_only);
448				cleared = 1;
449			}
450			if (rule->rulenum > rulenum)
451				break;
452		}
453		if (!cleared) {	/* we did not find any matching rules */
454			IPFW_WUNLOCK(chain);
455			return (EINVAL);
456		}
457		msg = log_only ? "logging count reset" : "cleared";
458	}
459	IPFW_UH_RUNLOCK(chain);
460
461	if (V_fw_verbose) {
462		int lev = LOG_SECURITY | LOG_NOTICE;
463
464		if (rulenum)
465			log(lev, "ipfw: Entry %d %s.\n", rulenum, msg);
466		else
467			log(lev, "ipfw: %s.\n", msg);
468	}
469	return (0);
470}
471
472/*
473 * Check validity of the structure before insert.
474 * Rules are simple, so this mostly need to check rule sizes.
475 */
476static int
477check_ipfw_struct(struct ip_fw *rule, int size)
478{
479	int l, cmdlen = 0;
480	int have_action=0;
481	ipfw_insn *cmd;
482
483	if (size < sizeof(*rule)) {
484		printf("ipfw: rule too short\n");
485		return (EINVAL);
486	}
487	/* first, check for valid size */
488	l = RULESIZE(rule);
489	if (l != size) {
490		printf("ipfw: size mismatch (have %d want %d)\n", size, l);
491		return (EINVAL);
492	}
493	if (rule->act_ofs >= rule->cmd_len) {
494		printf("ipfw: bogus action offset (%u > %u)\n",
495		    rule->act_ofs, rule->cmd_len - 1);
496		return (EINVAL);
497	}
498	/*
499	 * Now go for the individual checks. Very simple ones, basically only
500	 * instruction sizes.
501	 */
502	for (l = rule->cmd_len, cmd = rule->cmd ;
503			l > 0 ; l -= cmdlen, cmd += cmdlen) {
504		cmdlen = F_LEN(cmd);
505		if (cmdlen > l) {
506			printf("ipfw: opcode %d size truncated\n",
507			    cmd->opcode);
508			return EINVAL;
509		}
510		switch (cmd->opcode) {
511		case O_PROBE_STATE:
512		case O_KEEP_STATE:
513		case O_PROTO:
514		case O_IP_SRC_ME:
515		case O_IP_DST_ME:
516		case O_LAYER2:
517		case O_IN:
518		case O_FRAG:
519		case O_DIVERTED:
520		case O_IPOPT:
521		case O_IPTOS:
522		case O_IPPRECEDENCE:
523		case O_IPVER:
524		case O_TCPWIN:
525		case O_TCPFLAGS:
526		case O_TCPOPTS:
527		case O_ESTAB:
528		case O_VERREVPATH:
529		case O_VERSRCREACH:
530		case O_ANTISPOOF:
531		case O_IPSEC:
532#ifdef INET6
533		case O_IP6_SRC_ME:
534		case O_IP6_DST_ME:
535		case O_EXT_HDR:
536		case O_IP6:
537#endif
538		case O_IP4:
539		case O_TAG:
540			if (cmdlen != F_INSN_SIZE(ipfw_insn))
541				goto bad_size;
542			break;
543
544		case O_FIB:
545			if (cmdlen != F_INSN_SIZE(ipfw_insn))
546				goto bad_size;
547			if (cmd->arg1 >= rt_numfibs) {
548				printf("ipfw: invalid fib number %d\n",
549					cmd->arg1);
550				return EINVAL;
551			}
552			break;
553
554		case O_SETFIB:
555			if (cmdlen != F_INSN_SIZE(ipfw_insn))
556				goto bad_size;
557			if (cmd->arg1 >= rt_numfibs) {
558				printf("ipfw: invalid fib number %d\n",
559					cmd->arg1);
560				return EINVAL;
561			}
562			goto check_action;
563
564		case O_UID:
565		case O_GID:
566		case O_JAIL:
567		case O_IP_SRC:
568		case O_IP_DST:
569		case O_TCPSEQ:
570		case O_TCPACK:
571		case O_PROB:
572		case O_ICMPTYPE:
573			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
574				goto bad_size;
575			break;
576
577		case O_LIMIT:
578			if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
579				goto bad_size;
580			break;
581
582		case O_LOG:
583			if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
584				goto bad_size;
585
586			((ipfw_insn_log *)cmd)->log_left =
587			    ((ipfw_insn_log *)cmd)->max_log;
588
589			break;
590
591		case O_IP_SRC_MASK:
592		case O_IP_DST_MASK:
593			/* only odd command lengths */
594			if ( !(cmdlen & 1) || cmdlen > 31)
595				goto bad_size;
596			break;
597
598		case O_IP_SRC_SET:
599		case O_IP_DST_SET:
600			if (cmd->arg1 == 0 || cmd->arg1 > 256) {
601				printf("ipfw: invalid set size %d\n",
602					cmd->arg1);
603				return EINVAL;
604			}
605			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
606			    (cmd->arg1+31)/32 )
607				goto bad_size;
608			break;
609
610		case O_IP_SRC_LOOKUP:
611		case O_IP_DST_LOOKUP:
612			if (cmd->arg1 >= IPFW_TABLES_MAX) {
613				printf("ipfw: invalid table number %d\n",
614				    cmd->arg1);
615				return (EINVAL);
616			}
617			if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
618			    cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
619			    cmdlen != F_INSN_SIZE(ipfw_insn_u32))
620				goto bad_size;
621			break;
622
623		case O_MACADDR2:
624			if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
625				goto bad_size;
626			break;
627
628		case O_NOP:
629		case O_IPID:
630		case O_IPTTL:
631		case O_IPLEN:
632		case O_TCPDATALEN:
633		case O_TAGGED:
634			if (cmdlen < 1 || cmdlen > 31)
635				goto bad_size;
636			break;
637
638		case O_MAC_TYPE:
639		case O_IP_SRCPORT:
640		case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
641			if (cmdlen < 2 || cmdlen > 31)
642				goto bad_size;
643			break;
644
645		case O_RECV:
646		case O_XMIT:
647		case O_VIA:
648			if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
649				goto bad_size;
650			break;
651
652		case O_ALTQ:
653			if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
654				goto bad_size;
655			break;
656
657		case O_PIPE:
658		case O_QUEUE:
659			if (cmdlen != F_INSN_SIZE(ipfw_insn))
660				goto bad_size;
661			goto check_action;
662
663		case O_FORWARD_IP:
664#ifdef	IPFIREWALL_FORWARD
665			if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
666				goto bad_size;
667			goto check_action;
668#else
669			return EINVAL;
670#endif
671
672		case O_DIVERT:
673		case O_TEE:
674			if (ip_divert_ptr == NULL)
675				return EINVAL;
676			else
677				goto check_size;
678		case O_NETGRAPH:
679		case O_NGTEE:
680			if (ng_ipfw_input_p == NULL)
681				return EINVAL;
682			else
683				goto check_size;
684		case O_NAT:
685			if (!IPFW_NAT_LOADED)
686				return EINVAL;
687			if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
688 				goto bad_size;
689 			goto check_action;
690		case O_FORWARD_MAC: /* XXX not implemented yet */
691		case O_CHECK_STATE:
692		case O_COUNT:
693		case O_ACCEPT:
694		case O_DENY:
695		case O_REJECT:
696#ifdef INET6
697		case O_UNREACH6:
698#endif
699		case O_SKIPTO:
700		case O_REASS:
701check_size:
702			if (cmdlen != F_INSN_SIZE(ipfw_insn))
703				goto bad_size;
704check_action:
705			if (have_action) {
706				printf("ipfw: opcode %d, multiple actions"
707					" not allowed\n",
708					cmd->opcode);
709				return EINVAL;
710			}
711			have_action = 1;
712			if (l != cmdlen) {
713				printf("ipfw: opcode %d, action must be"
714					" last opcode\n",
715					cmd->opcode);
716				return EINVAL;
717			}
718			break;
719#ifdef INET6
720		case O_IP6_SRC:
721		case O_IP6_DST:
722			if (cmdlen != F_INSN_SIZE(struct in6_addr) +
723			    F_INSN_SIZE(ipfw_insn))
724				goto bad_size;
725			break;
726
727		case O_FLOW6ID:
728			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
729			    ((ipfw_insn_u32 *)cmd)->o.arg1)
730				goto bad_size;
731			break;
732
733		case O_IP6_SRC_MASK:
734		case O_IP6_DST_MASK:
735			if ( !(cmdlen & 1) || cmdlen > 127)
736				goto bad_size;
737			break;
738		case O_ICMP6TYPE:
739			if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
740				goto bad_size;
741			break;
742#endif
743
744		default:
745			switch (cmd->opcode) {
746#ifndef INET6
747			case O_IP6_SRC_ME:
748			case O_IP6_DST_ME:
749			case O_EXT_HDR:
750			case O_IP6:
751			case O_UNREACH6:
752			case O_IP6_SRC:
753			case O_IP6_DST:
754			case O_FLOW6ID:
755			case O_IP6_SRC_MASK:
756			case O_IP6_DST_MASK:
757			case O_ICMP6TYPE:
758				printf("ipfw: no IPv6 support in kernel\n");
759				return EPROTONOSUPPORT;
760#endif
761			default:
762				printf("ipfw: opcode %d, unknown opcode\n",
763					cmd->opcode);
764				return EINVAL;
765			}
766		}
767	}
768	if (have_action == 0) {
769		printf("ipfw: missing action\n");
770		return EINVAL;
771	}
772	return 0;
773
774bad_size:
775	printf("ipfw: opcode %d size %d wrong\n",
776		cmd->opcode, cmdlen);
777	return EINVAL;
778}
779
780/*
781 * Copy the static and dynamic rules to the supplied buffer
782 * and return the amount of space actually used.
783 * Must be run under IPFW_UH_RLOCK
784 */
785static size_t
786ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
787{
788	char *bp = buf;
789	char *ep = bp + space;
790	struct ip_fw *rule, *dst;
791	int l, i;
792	time_t	boot_seconds;
793
794        boot_seconds = boottime.tv_sec;
795	for (i = 0; i < chain->n_rules; i++) {
796		rule = chain->map[i];
797		l = RULESIZE(rule);
798		if (bp + l > ep) { /* should not happen */
799			printf("overflow dumping static rules\n");
800			break;
801		}
802		dst = (struct ip_fw *)bp;
803		bcopy(rule, dst, l);
804		/*
805		 * XXX HACK. Store the disable mask in the "next"
806		 * pointer in a wild attempt to keep the ABI the same.
807		 * Why do we do this on EVERY rule?
808		 */
809		bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable));
810		if (dst->timestamp)
811			dst->timestamp += boot_seconds;
812		bp += l;
813	}
814	ipfw_get_dynamic(&bp, ep); /* protected by the dynamic lock */
815	return (bp - (char *)buf);
816}
817
818
819/**
820 * {set|get}sockopt parser.
821 */
822int
823ipfw_ctl(struct sockopt *sopt)
824{
825#define	RULE_MAXSIZE	(256*sizeof(u_int32_t))
826	int error;
827	size_t size;
828	struct ip_fw *buf, *rule;
829	struct ip_fw_chain *chain;
830	u_int32_t rulenum[2];
831
832	error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
833	if (error)
834		return (error);
835
836	/*
837	 * Disallow modifications in really-really secure mode, but still allow
838	 * the logging counters to be reset.
839	 */
840	if (sopt->sopt_name == IP_FW_ADD ||
841	    (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) {
842		error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
843		if (error)
844			return (error);
845	}
846
847	chain = &V_layer3_chain;
848	error = 0;
849
850	switch (sopt->sopt_name) {
851	case IP_FW_GET:
852		/*
853		 * pass up a copy of the current rules. Static rules
854		 * come first (the last of which has number IPFW_DEFAULT_RULE),
855		 * followed by a possibly empty list of dynamic rule.
856		 * The last dynamic rule has NULL in the "next" field.
857		 *
858		 * Note that the calculated size is used to bound the
859		 * amount of data returned to the user.  The rule set may
860		 * change between calculating the size and returning the
861		 * data in which case we'll just return what fits.
862		 */
863		for (;;) {
864			int len = 0, want;
865
866			size = chain->static_len;
867			size += ipfw_dyn_len();
868			if (size >= sopt->sopt_valsize)
869				break;
870			buf = malloc(size, M_TEMP, M_WAITOK);
871			if (buf == NULL)
872				break;
873			IPFW_UH_RLOCK(chain);
874			/* check again how much space we need */
875			want = chain->static_len + ipfw_dyn_len();
876			if (size >= want)
877				len = ipfw_getrules(chain, buf, size);
878			IPFW_UH_RUNLOCK(chain);
879			if (size >= want)
880				error = sooptcopyout(sopt, buf, len);
881			free(buf, M_TEMP);
882			if (size >= want)
883				break;
884		}
885		break;
886
887	case IP_FW_FLUSH:
888		/* locking is done within del_entry() */
889		error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */
890		break;
891
892	case IP_FW_ADD:
893		rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK);
894		error = sooptcopyin(sopt, rule, RULE_MAXSIZE,
895			sizeof(struct ip_fw) );
896		if (error == 0)
897			error = check_ipfw_struct(rule, sopt->sopt_valsize);
898		if (error == 0) {
899			/* locking is done within ipfw_add_rule() */
900			error = ipfw_add_rule(chain, rule);
901			size = RULESIZE(rule);
902			if (!error && sopt->sopt_dir == SOPT_GET)
903				error = sooptcopyout(sopt, rule, size);
904		}
905		free(rule, M_TEMP);
906		break;
907
908	case IP_FW_DEL:
909		/*
910		 * IP_FW_DEL is used for deleting single rules or sets,
911		 * and (ab)used to atomically manipulate sets. Argument size
912		 * is used to distinguish between the two:
913		 *    sizeof(u_int32_t)
914		 *	delete single rule or set of rules,
915		 *	or reassign rules (or sets) to a different set.
916		 *    2*sizeof(u_int32_t)
917		 *	atomic disable/enable sets.
918		 *	first u_int32_t contains sets to be disabled,
919		 *	second u_int32_t contains sets to be enabled.
920		 */
921		error = sooptcopyin(sopt, rulenum,
922			2*sizeof(u_int32_t), sizeof(u_int32_t));
923		if (error)
924			break;
925		size = sopt->sopt_valsize;
926		if (size == sizeof(u_int32_t) && rulenum[0] != 0) {
927			/* delete or reassign, locking done in del_entry() */
928			error = del_entry(chain, rulenum[0]);
929		} else if (size == 2*sizeof(u_int32_t)) { /* set enable/disable */
930			IPFW_UH_WLOCK(chain);
931			V_set_disable =
932			    (V_set_disable | rulenum[0]) & ~rulenum[1] &
933			    ~(1<<RESVD_SET); /* set RESVD_SET always enabled */
934			IPFW_UH_WUNLOCK(chain);
935		} else
936			error = EINVAL;
937		break;
938
939	case IP_FW_ZERO:
940	case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */
941		rulenum[0] = 0;
942		if (sopt->sopt_val != 0) {
943		    error = sooptcopyin(sopt, rulenum,
944			    sizeof(u_int32_t), sizeof(u_int32_t));
945		    if (error)
946			break;
947		}
948		error = zero_entry(chain, rulenum[0],
949			sopt->sopt_name == IP_FW_RESETLOG);
950		break;
951
952	/*--- TABLE manipulations are protected by the IPFW_LOCK ---*/
953	case IP_FW_TABLE_ADD:
954		{
955			ipfw_table_entry ent;
956
957			error = sooptcopyin(sopt, &ent,
958			    sizeof(ent), sizeof(ent));
959			if (error)
960				break;
961			error = ipfw_add_table_entry(chain, ent.tbl,
962			    ent.addr, ent.masklen, ent.value);
963		}
964		break;
965
966	case IP_FW_TABLE_DEL:
967		{
968			ipfw_table_entry ent;
969
970			error = sooptcopyin(sopt, &ent,
971			    sizeof(ent), sizeof(ent));
972			if (error)
973				break;
974			error = ipfw_del_table_entry(chain, ent.tbl,
975			    ent.addr, ent.masklen);
976		}
977		break;
978
979	case IP_FW_TABLE_FLUSH:
980		{
981			u_int16_t tbl;
982
983			error = sooptcopyin(sopt, &tbl,
984			    sizeof(tbl), sizeof(tbl));
985			if (error)
986				break;
987			IPFW_WLOCK(chain);
988			error = ipfw_flush_table(chain, tbl);
989			IPFW_WUNLOCK(chain);
990		}
991		break;
992
993	case IP_FW_TABLE_GETSIZE:
994		{
995			u_int32_t tbl, cnt;
996
997			if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl),
998			    sizeof(tbl))))
999				break;
1000			IPFW_RLOCK(chain);
1001			error = ipfw_count_table(chain, tbl, &cnt);
1002			IPFW_RUNLOCK(chain);
1003			if (error)
1004				break;
1005			error = sooptcopyout(sopt, &cnt, sizeof(cnt));
1006		}
1007		break;
1008
1009	case IP_FW_TABLE_LIST:
1010		{
1011			ipfw_table *tbl;
1012
1013			if (sopt->sopt_valsize < sizeof(*tbl)) {
1014				error = EINVAL;
1015				break;
1016			}
1017			size = sopt->sopt_valsize;
1018			tbl = malloc(size, M_TEMP, M_WAITOK);
1019			error = sooptcopyin(sopt, tbl, size, sizeof(*tbl));
1020			if (error) {
1021				free(tbl, M_TEMP);
1022				break;
1023			}
1024			tbl->size = (size - sizeof(*tbl)) /
1025			    sizeof(ipfw_table_entry);
1026			IPFW_RLOCK(chain);
1027			error = ipfw_dump_table(chain, tbl);
1028			IPFW_RUNLOCK(chain);
1029			if (error) {
1030				free(tbl, M_TEMP);
1031				break;
1032			}
1033			error = sooptcopyout(sopt, tbl, size);
1034			free(tbl, M_TEMP);
1035		}
1036		break;
1037
1038	/*--- NAT operations are protected by the IPFW_LOCK ---*/
1039	case IP_FW_NAT_CFG:
1040		if (IPFW_NAT_LOADED)
1041			error = ipfw_nat_cfg_ptr(sopt);
1042		else {
1043			printf("IP_FW_NAT_CFG: %s\n",
1044			    "ipfw_nat not present, please load it");
1045			error = EINVAL;
1046		}
1047		break;
1048
1049	case IP_FW_NAT_DEL:
1050		if (IPFW_NAT_LOADED)
1051			error = ipfw_nat_del_ptr(sopt);
1052		else {
1053			printf("IP_FW_NAT_DEL: %s\n",
1054			    "ipfw_nat not present, please load it");
1055			error = EINVAL;
1056		}
1057		break;
1058
1059	case IP_FW_NAT_GET_CONFIG:
1060		if (IPFW_NAT_LOADED)
1061			error = ipfw_nat_get_cfg_ptr(sopt);
1062		else {
1063			printf("IP_FW_NAT_GET_CFG: %s\n",
1064			    "ipfw_nat not present, please load it");
1065			error = EINVAL;
1066		}
1067		break;
1068
1069	case IP_FW_NAT_GET_LOG:
1070		if (IPFW_NAT_LOADED)
1071			error = ipfw_nat_get_log_ptr(sopt);
1072		else {
1073			printf("IP_FW_NAT_GET_LOG: %s\n",
1074			    "ipfw_nat not present, please load it");
1075			error = EINVAL;
1076		}
1077		break;
1078
1079	default:
1080		printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
1081		error = EINVAL;
1082	}
1083
1084	return (error);
1085#undef RULE_MAXSIZE
1086}
1087/* end of file */
1088