ip_fw2.c revision 175659
1139823Simp/*-
298943Sluigi * Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa
398943Sluigi *
498943Sluigi * Redistribution and use in source and binary forms, with or without
598943Sluigi * modification, are permitted provided that the following conditions
698943Sluigi * are met:
798943Sluigi * 1. Redistributions of source code must retain the above copyright
898943Sluigi *    notice, this list of conditions and the following disclaimer.
9116763Sluigi * 2. Redistributions in binary form must reproduce the above copyright
1098943Sluigi *    notice, this list of conditions and the following disclaimer in the
1198943Sluigi *    documentation and/or other materials provided with the distribution.
12105775Smaxim *
1398943Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1498943Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1598943Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1698943Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1798943Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1898943Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1998943Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2098943Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2198943Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2298943Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2398943Sluigi * SUCH DAMAGE.
2498943Sluigi */
2598943Sluigi
26172467Ssilby#include <sys/cdefs.h>
27172467Ssilby__FBSDID("$FreeBSD: head/sys/netinet/ip_fw2.c 175659 2008-01-25 14:38:27Z rwatson $");
28172467Ssilby
2998943Sluigi#define        DEB(x)
3098943Sluigi#define        DDB(x) x
3198943Sluigi
3298943Sluigi/*
3399622Sluigi * Implement IP packet firewall (new version)
3498943Sluigi */
3598943Sluigi
36134346Sru#if !defined(KLD_MODULE)
3798943Sluigi#include "opt_ipfw.h"
38166479Salc#include "opt_ipdivert.h"
3998943Sluigi#include "opt_ipdn.h"
4098943Sluigi#include "opt_inet.h"
4198943Sluigi#ifndef INET
4298943Sluigi#error IPFIREWALL requires INET.
4398943Sluigi#endif /* INET */
44134346Sru#endif
45152928Sume#include "opt_inet6.h"
46152928Sume#include "opt_ipsec.h"
47162238Scsjp#include "opt_mac.h"
4898943Sluigi
4998943Sluigi#include <sys/param.h>
5098943Sluigi#include <sys/systm.h>
51138642Scsjp#include <sys/condvar.h>
52165648Spiso#include <sys/eventhandler.h>
5398943Sluigi#include <sys/malloc.h>
5498943Sluigi#include <sys/mbuf.h>
5598943Sluigi#include <sys/kernel.h>
56155201Scsjp#include <sys/lock.h>
57133600Scsjp#include <sys/jail.h>
58129876Sphk#include <sys/module.h>
59164033Srwatson#include <sys/priv.h>
6098943Sluigi#include <sys/proc.h>
61155201Scsjp#include <sys/rwlock.h>
6298943Sluigi#include <sys/socket.h>
6398943Sluigi#include <sys/socketvar.h>
6498943Sluigi#include <sys/sysctl.h>
6598943Sluigi#include <sys/syslog.h>
6698943Sluigi#include <sys/ucred.h>
6798943Sluigi#include <net/if.h>
68130281Sru#include <net/radix.h>
6998943Sluigi#include <net/route.h>
70171173Smlaier#include <net/pf_mtag.h>
71175659Srwatson
72175659Srwatson#define	IPFW_INTERNAL	/* Access to protected data structures in ip_fw.h. */
73175659Srwatson
7498943Sluigi#include <netinet/in.h>
7598943Sluigi#include <netinet/in_systm.h>
7698943Sluigi#include <netinet/in_var.h>
7798943Sluigi#include <netinet/in_pcb.h>
7898943Sluigi#include <netinet/ip.h>
7998943Sluigi#include <netinet/ip_var.h>
8098943Sluigi#include <netinet/ip_icmp.h>
8198943Sluigi#include <netinet/ip_fw.h>
82126239Smlaier#include <netinet/ip_divert.h>
8398943Sluigi#include <netinet/ip_dummynet.h>
84163069Sbz#include <netinet/ip_carp.h>
85161767Sjhay#include <netinet/pim.h>
8698943Sluigi#include <netinet/tcp.h>
8798943Sluigi#include <netinet/tcp_timer.h>
8898943Sluigi#include <netinet/tcp_var.h>
8998943Sluigi#include <netinet/tcpip.h>
9098943Sluigi#include <netinet/udp.h>
9198943Sluigi#include <netinet/udp_var.h>
92164258Sbz#include <netinet/sctp.h>
93165750Spiso#ifdef IPFIREWALL_NAT
94165648Spiso#include <netinet/libalias/alias.h>
95165648Spiso#include <netinet/libalias/alias_local.h>
96165750Spiso#endif
97141351Sglebius#include <netgraph/ng_ipfw.h>
98141351Sglebius
99136071Sgreen#include <altq/if_altq.h>
10098943Sluigi
101145246Sbrooks#include <netinet/ip6.h>
102145246Sbrooks#include <netinet/icmp6.h>
103148414Sume#ifdef INET6
104148414Sume#include <netinet6/scope6_var.h>
105148414Sume#endif
106145246Sbrooks
10798943Sluigi#include <netinet/if_ether.h> /* XXX for ETHERTYPE_IP */
10898943Sluigi
10999475Sluigi#include <machine/in_cksum.h>	/* XXX for in_cksum */
11099475Sluigi
111163606Srwatson#include <security/mac/mac_framework.h>
112163606Srwatson
113101628Sluigi/*
114101628Sluigi * set_disable contains one bit per set value (0..31).
115101628Sluigi * If the bit is set, all rules with the corresponding set
116117654Sluigi * are disabled. Set RESVD_SET(31) is reserved for the default rule
117117654Sluigi * and rules that are not deleted by the flush command,
118101628Sluigi * and CANNOT be disabled.
119117654Sluigi * Rules in set RESVD_SET can only be deleted explicitly.
120101628Sluigi */
121101628Sluigistatic u_int32_t set_disable;
122101628Sluigi
12399622Sluigistatic int fw_verbose;
12499622Sluigistatic int verbose_limit;
12598943Sluigi
126120141Ssamstatic struct callout ipfw_timeout;
127141076Scsjpstatic uma_zone_t ipfw_dyn_rule_zone;
12898943Sluigi#define	IPFW_DEFAULT_RULE	65535
12998943Sluigi
130130363Scsjp/*
131130363Scsjp * Data structure to cache our ucred related
132130363Scsjp * information. This structure only gets used if
133130363Scsjp * the user specified UID/GID based constraints in
134130363Scsjp * a firewall rule.
135130363Scsjp */
136130363Scsjpstruct ip_fw_ugid {
137130363Scsjp	gid_t		fw_groups[NGROUPS];
138130363Scsjp	int		fw_ngroups;
139130363Scsjp	uid_t		fw_uid;
140133600Scsjp	int		fw_prid;
141130363Scsjp};
142130363Scsjp
143153163Sglebius#define	IPFW_TABLES_MAX		128
144120141Ssamstruct ip_fw_chain {
145120141Ssam	struct ip_fw	*rules;		/* list of rules */
146120141Ssam	struct ip_fw	*reap;		/* list of rules to reap */
147165648Spiso	LIST_HEAD(, cfg_nat) nat;       /* list of nat entries */
148153163Sglebius	struct radix_node_head *tables[IPFW_TABLES_MAX];
149155201Scsjp	struct rwlock	rwmtx;
150120141Ssam};
151120141Ssam#define	IPFW_LOCK_INIT(_chain) \
152155201Scsjp	rw_init(&(_chain)->rwmtx, "IPFW static rules")
153155201Scsjp#define	IPFW_LOCK_DESTROY(_chain)	rw_destroy(&(_chain)->rwmtx)
154171744Srwatson#define	IPFW_WLOCK_ASSERT(_chain)	rw_assert(&(_chain)->rwmtx, RA_WLOCKED)
155120141Ssam
156155201Scsjp#define IPFW_RLOCK(p) rw_rlock(&(p)->rwmtx)
157155201Scsjp#define IPFW_RUNLOCK(p) rw_runlock(&(p)->rwmtx)
158155201Scsjp#define IPFW_WLOCK(p) rw_wlock(&(p)->rwmtx)
159155201Scsjp#define IPFW_WUNLOCK(p) rw_wunlock(&(p)->rwmtx)
160138642Scsjp
16198943Sluigi/*
16298943Sluigi * list of rules for layer 3
16398943Sluigi */
164120141Ssamstatic struct ip_fw_chain layer3_chain;
16598943Sluigi
16698943SluigiMALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
167130281SruMALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
16898943Sluigi
169130281Srustruct table_entry {
170130281Sru	struct radix_node	rn[2];
171130281Sru	struct sockaddr_in	addr, mask;
172130281Sru	u_int32_t		value;
173130281Sru};
174130281Sru
17598943Sluigistatic int fw_debug = 1;
17698943Sluigistatic int autoinc_step = 100; /* bounded to 1..1000 in add_rule() */
17798943Sluigi
178158470Smlaierextern int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
179158470Smlaier
18098943Sluigi#ifdef SYSCTL_NODE
18198943SluigiSYSCTL_NODE(_net_inet_ip, OID_AUTO, fw, CTLFLAG_RW, 0, "Firewall");
182158470SmlaierSYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, enable,
183158470Smlaier    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &fw_enable, 0,
184158470Smlaier    ipfw_chg_hook, "I", "Enable ipfw");
18598943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, autoinc_step, CTLFLAG_RW,
18698943Sluigi    &autoinc_step, 0, "Rule number autincrement step");
187102397ScjcSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, one_pass,
188109246Sdillon    CTLFLAG_RW | CTLFLAG_SECURE3,
189105775Smaxim    &fw_one_pass, 0,
19098943Sluigi    "Only do a single pass through ipfw when using dummynet(4)");
191105775SmaximSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, debug, CTLFLAG_RW,
19298943Sluigi    &fw_debug, 0, "Enable printing of debug ip_fw statements");
193102397ScjcSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose,
194109246Sdillon    CTLFLAG_RW | CTLFLAG_SECURE3,
19598943Sluigi    &fw_verbose, 0, "Log matches to ipfw rules");
196105775SmaximSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW,
19798943Sluigi    &verbose_limit, 0, "Set upper limit of matches of ipfw rules logged");
19898943Sluigi
19998943Sluigi/*
20098943Sluigi * Description of dynamic rules.
20198943Sluigi *
20298943Sluigi * Dynamic rules are stored in lists accessed through a hash table
20398943Sluigi * (ipfw_dyn_v) whose size is curr_dyn_buckets. This value can
20498943Sluigi * be modified through the sysctl variable dyn_buckets which is
20598943Sluigi * updated when the table becomes empty.
20698943Sluigi *
20798943Sluigi * XXX currently there is only one list, ipfw_dyn.
20898943Sluigi *
20998943Sluigi * When a packet is received, its address fields are first masked
21098943Sluigi * with the mask defined for the rule, then hashed, then matched
21198943Sluigi * against the entries in the corresponding list.
21298943Sluigi * Dynamic rules can be used for different purposes:
21398943Sluigi *  + stateful rules;
21498943Sluigi *  + enforcing limits on the number of sessions;
21598943Sluigi *  + in-kernel NAT (not implemented yet)
21698943Sluigi *
21798943Sluigi * The lifetime of dynamic rules is regulated by dyn_*_lifetime,
21898943Sluigi * measured in seconds and depending on the flags.
21998943Sluigi *
22098943Sluigi * The total number of dynamic rules is stored in dyn_count.
22198943Sluigi * The max number of dynamic rules is dyn_max. When we reach
22298943Sluigi * the maximum number of rules we do not create anymore. This is
22398943Sluigi * done to avoid consuming too much memory, but also too much
22498943Sluigi * time when searching on each packet (ideally, we should try instead
22598943Sluigi * to put a limit on the length of the list on each bucket...).
22698943Sluigi *
22798943Sluigi * Each dynamic rule holds a pointer to the parent ipfw rule so
22898943Sluigi * we know what action to perform. Dynamic rules are removed when
22998943Sluigi * the parent rule is deleted. XXX we should make them survive.
23098943Sluigi *
23198943Sluigi * There are some limitations with dynamic rules -- we do not
23298943Sluigi * obey the 'randomized match', and we do not do multiple
23398943Sluigi * passes through the firewall. XXX check the latter!!!
23498943Sluigi */
23598943Sluigistatic ipfw_dyn_rule **ipfw_dyn_v = NULL;
23698943Sluigistatic u_int32_t dyn_buckets = 256; /* must be power of 2 */
23798943Sluigistatic u_int32_t curr_dyn_buckets = 256; /* must be power of 2 */
23898943Sluigi
239120141Ssamstatic struct mtx ipfw_dyn_mtx;		/* mutex guarding dynamic rules */
240120141Ssam#define	IPFW_DYN_LOCK_INIT() \
241120141Ssam	mtx_init(&ipfw_dyn_mtx, "IPFW dynamic rules", NULL, MTX_DEF)
242120141Ssam#define	IPFW_DYN_LOCK_DESTROY()	mtx_destroy(&ipfw_dyn_mtx)
243120141Ssam#define	IPFW_DYN_LOCK()		mtx_lock(&ipfw_dyn_mtx)
244120141Ssam#define	IPFW_DYN_UNLOCK()	mtx_unlock(&ipfw_dyn_mtx)
245120141Ssam#define	IPFW_DYN_LOCK_ASSERT()	mtx_assert(&ipfw_dyn_mtx, MA_OWNED)
246120141Ssam
24798943Sluigi/*
24898943Sluigi * Timeouts for various events in handing dynamic rules.
24998943Sluigi */
25098943Sluigistatic u_int32_t dyn_ack_lifetime = 300;
25198943Sluigistatic u_int32_t dyn_syn_lifetime = 20;
25298943Sluigistatic u_int32_t dyn_fin_lifetime = 1;
25398943Sluigistatic u_int32_t dyn_rst_lifetime = 1;
25498943Sluigistatic u_int32_t dyn_udp_lifetime = 10;
25598943Sluigistatic u_int32_t dyn_short_lifetime = 5;
25698943Sluigi
257101978Sluigi/*
258101978Sluigi * Keepalives are sent if dyn_keepalive is set. They are sent every
259101978Sluigi * dyn_keepalive_period seconds, in the last dyn_keepalive_interval
260101978Sluigi * seconds of lifetime of a rule.
261101978Sluigi * dyn_rst_lifetime and dyn_fin_lifetime should be strictly lower
262101978Sluigi * than dyn_keepalive_period.
263101978Sluigi */
264105775Smaxim
265101978Sluigistatic u_int32_t dyn_keepalive_interval = 20;
266101978Sluigistatic u_int32_t dyn_keepalive_period = 5;
267100004Sluigistatic u_int32_t dyn_keepalive = 1;	/* do send keepalives */
26898943Sluigi
26999622Sluigistatic u_int32_t static_count;	/* # of static rules */
27099622Sluigistatic u_int32_t static_len;	/* size in bytes of static rules */
27199622Sluigistatic u_int32_t dyn_count;		/* # of dynamic rules */
272101978Sluigistatic u_int32_t dyn_max = 4096;	/* max # of dynamic rules */
27398943Sluigi
27498943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, CTLFLAG_RW,
27598943Sluigi    &dyn_buckets, 0, "Number of dyn. buckets");
27698943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets, CTLFLAG_RD,
27798943Sluigi    &curr_dyn_buckets, 0, "Current Number of dyn. buckets");
27898943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_count, CTLFLAG_RD,
27998943Sluigi    &dyn_count, 0, "Number of dyn. rules");
28098943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_max, CTLFLAG_RW,
28198943Sluigi    &dyn_max, 0, "Max number of dyn. rules");
28298943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, static_count, CTLFLAG_RD,
28398943Sluigi    &static_count, 0, "Number of static rules");
28498943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime, CTLFLAG_RW,
28598943Sluigi    &dyn_ack_lifetime, 0, "Lifetime of dyn. rules for acks");
28698943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime, CTLFLAG_RW,
28798943Sluigi    &dyn_syn_lifetime, 0, "Lifetime of dyn. rules for syn");
28898943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime, CTLFLAG_RW,
28998943Sluigi    &dyn_fin_lifetime, 0, "Lifetime of dyn. rules for fin");
29098943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime, CTLFLAG_RW,
29198943Sluigi    &dyn_rst_lifetime, 0, "Lifetime of dyn. rules for rst");
29298943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime, CTLFLAG_RW,
29398943Sluigi    &dyn_udp_lifetime, 0, "Lifetime of dyn. rules for UDP");
29498943SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, CTLFLAG_RW,
29598943Sluigi    &dyn_short_lifetime, 0, "Lifetime of dyn. rules for other situations");
296100004SluigiSYSCTL_INT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, CTLFLAG_RW,
297100004Sluigi    &dyn_keepalive, 0, "Enable keepalives for dyn. rules");
29898943Sluigi
299149052Sbz#ifdef INET6
300149020Sbz/*
301149020Sbz * IPv6 specific variables
302149020Sbz */
303149020SbzSYSCTL_DECL(_net_inet6_ip6);
304149020Sbz
305149020Sbzstatic struct sysctl_ctx_list ip6_fw_sysctl_ctx;
306149020Sbzstatic struct sysctl_oid *ip6_fw_sysctl_tree;
307149052Sbz#endif /* INET6 */
308149052Sbz#endif /* SYSCTL_NODE */
309149020Sbz
310165750Spiso#ifdef IPFIREWALL_NAT
311165648SpisoMODULE_DEPEND(ipfw, libalias, 1, 1, 1);
312165750Spiso#endif
313149020Sbzstatic int fw_deny_unknown_exthdrs = 1;
31498943Sluigi
31598943Sluigi
31698943Sluigi/*
317145093Sbrooks * L3HDR maps an ipv4 pointer into a layer3 header pointer of type T
318145093Sbrooks * Other macros just cast void * into the appropriate type
31998943Sluigi */
320145093Sbrooks#define	L3HDR(T, ip)	((T *)((u_int32_t *)(ip) + (ip)->ip_hl))
321145093Sbrooks#define	TCP(p)		((struct tcphdr *)(p))
322164258Sbz#define	SCTP(p)		((struct sctphdr *)(p))
323145093Sbrooks#define	UDP(p)		((struct udphdr *)(p))
324145565Sbrooks#define	ICMP(p)		((struct icmphdr *)(p))
325145246Sbrooks#define	ICMP6(p)	((struct icmp6_hdr *)(p))
32698943Sluigi
32799622Sluigistatic __inline int
328145565Sbrooksicmptype_match(struct icmphdr *icmp, ipfw_insn_u32 *cmd)
32998943Sluigi{
330145093Sbrooks	int type = icmp->icmp_type;
33198943Sluigi
33298943Sluigi	return (type <= ICMP_MAXTYPE && (cmd->d[0] & (1<<type)) );
33398943Sluigi}
33498943Sluigi
33598943Sluigi#define TT	( (1 << ICMP_ECHO) | (1 << ICMP_ROUTERSOLICIT) | \
33698943Sluigi    (1 << ICMP_TSTAMP) | (1 << ICMP_IREQ) | (1 << ICMP_MASKREQ) )
33798943Sluigi
33898943Sluigistatic int
339145565Sbrooksis_icmp_query(struct icmphdr *icmp)
34098943Sluigi{
341145093Sbrooks	int type = icmp->icmp_type;
342145093Sbrooks
34398943Sluigi	return (type <= ICMP_MAXTYPE && (TT & (1<<type)) );
34498943Sluigi}
34598943Sluigi#undef TT
34698943Sluigi
34798943Sluigi/*
34898943Sluigi * The following checks use two arrays of 8 or 16 bits to store the
34998943Sluigi * bits that we want set or clear, respectively. They are in the
35098943Sluigi * low and high half of cmd->arg1 or cmd->d[0].
35198943Sluigi *
35298943Sluigi * We scan options and store the bits we find set. We succeed if
35398943Sluigi *
35498943Sluigi *	(want_set & ~bits) == 0 && (want_clear & ~bits) == want_clear
35598943Sluigi *
35698943Sluigi * The code is sometimes optimized not to store additional variables.
35798943Sluigi */
35898943Sluigi
35998943Sluigistatic int
36098943Sluigiflags_match(ipfw_insn *cmd, u_int8_t bits)
36198943Sluigi{
36298943Sluigi	u_char want_clear;
36398943Sluigi	bits = ~bits;
36498943Sluigi
36598943Sluigi	if ( ((cmd->arg1 & 0xff) & bits) != 0)
36698943Sluigi		return 0; /* some bits we want set were clear */
36798943Sluigi	want_clear = (cmd->arg1 >> 8) & 0xff;
36898943Sluigi	if ( (want_clear & bits) != want_clear)
36998943Sluigi		return 0; /* some bits we want clear were set */
37098943Sluigi	return 1;
37198943Sluigi}
37298943Sluigi
37398943Sluigistatic int
37498943Sluigiipopts_match(struct ip *ip, ipfw_insn *cmd)
37598943Sluigi{
37698943Sluigi	int optlen, bits = 0;
37798943Sluigi	u_char *cp = (u_char *)(ip + 1);
37898943Sluigi	int x = (ip->ip_hl << 2) - sizeof (struct ip);
37998943Sluigi
38098943Sluigi	for (; x > 0; x -= optlen, cp += optlen) {
38198943Sluigi		int opt = cp[IPOPT_OPTVAL];
38298943Sluigi
38398943Sluigi		if (opt == IPOPT_EOL)
38498943Sluigi			break;
38598943Sluigi		if (opt == IPOPT_NOP)
38698943Sluigi			optlen = 1;
38798943Sluigi		else {
38898943Sluigi			optlen = cp[IPOPT_OLEN];
38998943Sluigi			if (optlen <= 0 || optlen > x)
39098943Sluigi				return 0; /* invalid or truncated */
39198943Sluigi		}
39298943Sluigi		switch (opt) {
39398943Sluigi
39498943Sluigi		default:
39598943Sluigi			break;
39698943Sluigi
39798943Sluigi		case IPOPT_LSRR:
39898943Sluigi			bits |= IP_FW_IPOPT_LSRR;
39998943Sluigi			break;
40098943Sluigi
40198943Sluigi		case IPOPT_SSRR:
40298943Sluigi			bits |= IP_FW_IPOPT_SSRR;
40398943Sluigi			break;
40498943Sluigi
40598943Sluigi		case IPOPT_RR:
40698943Sluigi			bits |= IP_FW_IPOPT_RR;
40798943Sluigi			break;
40898943Sluigi
40998943Sluigi		case IPOPT_TS:
41098943Sluigi			bits |= IP_FW_IPOPT_TS;
41198943Sluigi			break;
41298943Sluigi		}
41398943Sluigi	}
41498943Sluigi	return (flags_match(cmd, bits));
41598943Sluigi}
41698943Sluigi
41798943Sluigistatic int
418145093Sbrookstcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd)
41998943Sluigi{
42098943Sluigi	int optlen, bits = 0;
42198943Sluigi	u_char *cp = (u_char *)(tcp + 1);
42298943Sluigi	int x = (tcp->th_off << 2) - sizeof(struct tcphdr);
42398943Sluigi
42498943Sluigi	for (; x > 0; x -= optlen, cp += optlen) {
42598943Sluigi		int opt = cp[0];
42698943Sluigi		if (opt == TCPOPT_EOL)
42798943Sluigi			break;
42898943Sluigi		if (opt == TCPOPT_NOP)
42998943Sluigi			optlen = 1;
43098943Sluigi		else {
43198943Sluigi			optlen = cp[1];
43298943Sluigi			if (optlen <= 0)
43398943Sluigi				break;
43498943Sluigi		}
43598943Sluigi
43698943Sluigi		switch (opt) {
43798943Sluigi
43898943Sluigi		default:
43998943Sluigi			break;
44098943Sluigi
44198943Sluigi		case TCPOPT_MAXSEG:
44298943Sluigi			bits |= IP_FW_TCPOPT_MSS;
44398943Sluigi			break;
44498943Sluigi
44598943Sluigi		case TCPOPT_WINDOW:
44698943Sluigi			bits |= IP_FW_TCPOPT_WINDOW;
44798943Sluigi			break;
44898943Sluigi
44998943Sluigi		case TCPOPT_SACK_PERMITTED:
45098943Sluigi		case TCPOPT_SACK:
45198943Sluigi			bits |= IP_FW_TCPOPT_SACK;
45298943Sluigi			break;
45398943Sluigi
45498943Sluigi		case TCPOPT_TIMESTAMP:
45598943Sluigi			bits |= IP_FW_TCPOPT_TS;
45698943Sluigi			break;
45798943Sluigi
45898943Sluigi		}
45998943Sluigi	}
46098943Sluigi	return (flags_match(cmd, bits));
46198943Sluigi}
46298943Sluigi
46398943Sluigistatic int
46498943Sluigiiface_match(struct ifnet *ifp, ipfw_insn_if *cmd)
46598943Sluigi{
46698943Sluigi	if (ifp == NULL)	/* no iface with this packet, match fails */
46798943Sluigi		return 0;
46898943Sluigi	/* Check by name or by IP address */
46999475Sluigi	if (cmd->name[0] != '\0') { /* match by name */
47098943Sluigi		/* Check name */
471121816Sbrooks		if (cmd->p.glob) {
472121816Sbrooks			if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
473121816Sbrooks				return(1);
474121816Sbrooks		} else {
475121816Sbrooks			if (strncmp(ifp->if_xname, cmd->name, IFNAMSIZ) == 0)
476121816Sbrooks				return(1);
477121816Sbrooks		}
47898943Sluigi	} else {
47998943Sluigi		struct ifaddr *ia;
48098943Sluigi
481120141Ssam		/* XXX lock? */
48298943Sluigi		TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) {
48398943Sluigi			if (ia->ifa_addr->sa_family != AF_INET)
48498943Sluigi				continue;
48598943Sluigi			if (cmd->p.ip.s_addr == ((struct sockaddr_in *)
48698943Sluigi			    (ia->ifa_addr))->sin_addr.s_addr)
48798943Sluigi				return(1);	/* match */
48898943Sluigi		}
48998943Sluigi	}
49098943Sluigi	return(0);	/* no match, fail ... */
49198943Sluigi}
49298943Sluigi
493112250Scjc/*
494128575Sandre * The verify_path function checks if a route to the src exists and
495128575Sandre * if it is reachable via ifp (when provided).
496128575Sandre *
497112250Scjc * The 'verrevpath' option checks that the interface that an IP packet
498116763Sluigi * arrives on is the same interface that traffic destined for the
499128575Sandre * packet's source address would be routed out of.  The 'versrcreach'
500128575Sandre * option just checks that the source address is reachable via any route
501128575Sandre * (except default) in the routing table.  These two are a measure to block
502128575Sandre * forged packets.  This is also commonly known as "anti-spoofing" or Unicast
503128575Sandre * Reverse Path Forwarding (Unicast RFP) in Cisco-ese. The name of the knobs
504128575Sandre * is purposely reminiscent of the Cisco IOS command,
505112250Scjc *
506112250Scjc *   ip verify unicast reverse-path
507128575Sandre *   ip verify unicast source reachable-via any
508112250Scjc *
509112250Scjc * which implements the same functionality. But note that syntax is
510112250Scjc * misleading. The check may be performed on all IP packets whether unicast,
511112250Scjc * multicast, or broadcast.
512112250Scjc */
513112250Scjcstatic int
514128575Sandreverify_path(struct in_addr src, struct ifnet *ifp)
515112250Scjc{
516123000Sandre	struct route ro;
517112250Scjc	struct sockaddr_in *dst;
518112250Scjc
519123000Sandre	bzero(&ro, sizeof(ro));
520123000Sandre
521112250Scjc	dst = (struct sockaddr_in *)&(ro.ro_dst);
522123000Sandre	dst->sin_family = AF_INET;
523123000Sandre	dst->sin_len = sizeof(*dst);
524123000Sandre	dst->sin_addr = src;
525123000Sandre	rtalloc_ign(&ro, RTF_CLONING);
526112250Scjc
527122922Sandre	if (ro.ro_rt == NULL)
528112250Scjc		return 0;
529128575Sandre
530154769Soleg	/*
531154769Soleg	 * If ifp is provided, check for equality with rtentry.
532154769Soleg	 * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp,
533154769Soleg	 * in order to pass packets injected back by if_simloop():
534154769Soleg	 * if useloopback == 1 routing entry (via lo0) for our own address
535154769Soleg	 * may exist, so we need to handle routing assymetry.
536154769Soleg	 */
537154769Soleg	if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) {
538122922Sandre		RTFREE(ro.ro_rt);
539122922Sandre		return 0;
540122922Sandre	}
541128575Sandre
542128575Sandre	/* if no ifp provided, check if rtentry is not default route */
543128575Sandre	if (ifp == NULL &&
544128575Sandre	     satosin(rt_key(ro.ro_rt))->sin_addr.s_addr == INADDR_ANY) {
545128575Sandre		RTFREE(ro.ro_rt);
546128575Sandre		return 0;
547128575Sandre	}
548128575Sandre
549132510Sandre	/* or if this is a blackhole/reject route */
550132510Sandre	if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
551132510Sandre		RTFREE(ro.ro_rt);
552132510Sandre		return 0;
553132510Sandre	}
554132510Sandre
555128575Sandre	/* found valid route */
556122922Sandre	RTFREE(ro.ro_rt);
557116981Sluigi	return 1;
558112250Scjc}
559112250Scjc
560145266Sphk#ifdef INET6
561145246Sbrooks/*
562145246Sbrooks * ipv6 specific rules here...
563145246Sbrooks */
564145246Sbrooksstatic __inline int
565145246Sbrooksicmp6type_match (int type, ipfw_insn_u32 *cmd)
566145246Sbrooks{
567147415Smlaier	return (type <= ICMP6_MAXTYPE && (cmd->d[type/32] & (1<<(type%32)) ) );
568145246Sbrooks}
569112250Scjc
570145246Sbrooksstatic int
571145246Sbrooksflow6id_match( int curr_flow, ipfw_insn_u32 *cmd )
572145246Sbrooks{
573147415Smlaier	int i;
574147415Smlaier	for (i=0; i <= cmd->o.arg1; ++i )
575147415Smlaier		if (curr_flow == cmd->d[i] )
576147415Smlaier			return 1;
577147415Smlaier	return 0;
578145246Sbrooks}
579145246Sbrooks
580145246Sbrooks/* support for IP6_*_ME opcodes */
581145246Sbrooksstatic int
582145246Sbrookssearch_ip6_addr_net (struct in6_addr * ip6_addr)
583145246Sbrooks{
584147415Smlaier	struct ifnet *mdc;
585147415Smlaier	struct ifaddr *mdc2;
586147415Smlaier	struct in6_ifaddr *fdm;
587147415Smlaier	struct in6_addr copia;
588145246Sbrooks
589147415Smlaier	TAILQ_FOREACH(mdc, &ifnet, if_link)
590160032Syar		TAILQ_FOREACH(mdc2, &mdc->if_addrlist, ifa_list) {
591147415Smlaier			if (mdc2->ifa_addr->sa_family == AF_INET6) {
592147415Smlaier				fdm = (struct in6_ifaddr *)mdc2;
593147415Smlaier				copia = fdm->ia_addr.sin6_addr;
594147415Smlaier				/* need for leaving scope_id in the sock_addr */
595147415Smlaier				in6_clearscope(&copia);
596147415Smlaier				if (IN6_ARE_ADDR_EQUAL(ip6_addr, &copia))
597147415Smlaier					return 1;
598147415Smlaier			}
599147415Smlaier		}
600147415Smlaier	return 0;
601145246Sbrooks}
602145246Sbrooks
603145246Sbrooksstatic int
604147418Smlaierverify_path6(struct in6_addr *src, struct ifnet *ifp)
605145246Sbrooks{
606147418Smlaier	struct route_in6 ro;
607147415Smlaier	struct sockaddr_in6 *dst;
608145246Sbrooks
609147418Smlaier	bzero(&ro, sizeof(ro));
610147418Smlaier
611147415Smlaier	dst = (struct sockaddr_in6 * )&(ro.ro_dst);
612147418Smlaier	dst->sin6_family = AF_INET6;
613147418Smlaier	dst->sin6_len = sizeof(*dst);
614147418Smlaier	dst->sin6_addr = *src;
615147418Smlaier	rtalloc_ign((struct route *)&ro, RTF_CLONING);
616145246Sbrooks
617147418Smlaier	if (ro.ro_rt == NULL)
618147418Smlaier		return 0;
619147418Smlaier
620152288Ssuz	/*
621152288Ssuz	 * if ifp is provided, check for equality with rtentry
622152288Ssuz	 * We should use rt->rt_ifa->ifa_ifp, instead of rt->rt_ifp,
623152288Ssuz	 * to support the case of sending packets to an address of our own.
624152288Ssuz	 * (where the former interface is the first argument of if_simloop()
625152288Ssuz	 *  (=ifp), the latter is lo0)
626152288Ssuz	 */
627152288Ssuz	if (ifp != NULL && ro.ro_rt->rt_ifa->ifa_ifp != ifp) {
628147418Smlaier		RTFREE(ro.ro_rt);
629147418Smlaier		return 0;
630147415Smlaier	}
631147418Smlaier
632147418Smlaier	/* if no ifp provided, check if rtentry is not default route */
633147418Smlaier	if (ifp == NULL &&
634147418Smlaier	    IN6_IS_ADDR_UNSPECIFIED(&satosin6(rt_key(ro.ro_rt))->sin6_addr)) {
635147418Smlaier		RTFREE(ro.ro_rt);
636147415Smlaier		return 0;
637147418Smlaier	}
638147418Smlaier
639147418Smlaier	/* or if this is a blackhole/reject route */
640147418Smlaier	if (ifp == NULL && ro.ro_rt->rt_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
641147418Smlaier		RTFREE(ro.ro_rt);
642147418Smlaier		return 0;
643147418Smlaier	}
644147418Smlaier
645147418Smlaier	/* found valid route */
646147418Smlaier	RTFREE(ro.ro_rt);
647147415Smlaier	return 1;
648147418Smlaier
649145246Sbrooks}
650145246Sbrooksstatic __inline int
651145246Sbrookshash_packet6(struct ipfw_flow_id *id)
652145246Sbrooks{
653147415Smlaier	u_int32_t i;
654158580Smlaier	i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^
655147415Smlaier	    (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^
656158580Smlaier	    (id->src_ip6.__u6_addr.__u6_addr32[2]) ^
657158580Smlaier	    (id->src_ip6.__u6_addr.__u6_addr32[3]) ^
658158580Smlaier	    (id->dst_port) ^ (id->src_port);
659147415Smlaier	return i;
660145246Sbrooks}
661145246Sbrooks
662149020Sbzstatic int
663149020Sbzis_icmp6_query(int icmp6_type)
664149020Sbz{
665149020Sbz	if ((icmp6_type <= ICMP6_MAXTYPE) &&
666149020Sbz	    (icmp6_type == ICMP6_ECHO_REQUEST ||
667149020Sbz	    icmp6_type == ICMP6_MEMBERSHIP_QUERY ||
668149020Sbz	    icmp6_type == ICMP6_WRUREQUEST ||
669149020Sbz	    icmp6_type == ICMP6_FQDN_QUERY ||
670149020Sbz	    icmp6_type == ICMP6_NI_QUERY))
671149020Sbz		return (1);
672149020Sbz
673149020Sbz	return (0);
674149020Sbz}
675149020Sbz
676149020Sbzstatic void
677165738Sjuliansend_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6)
678149020Sbz{
679165738Sjulian	struct mbuf *m;
680165738Sjulian
681165738Sjulian	m = args->m;
682160025Sbz	if (code == ICMP6_UNREACH_RST && args->f_id.proto == IPPROTO_TCP) {
683149020Sbz		struct tcphdr *tcp;
684149020Sbz		tcp_seq ack, seq;
685149020Sbz		int flags;
686149020Sbz		struct {
687149020Sbz			struct ip6_hdr ip6;
688149020Sbz			struct tcphdr th;
689149020Sbz		} ti;
690165738Sjulian		tcp = (struct tcphdr *)((char *)ip6 + hlen);
691149020Sbz
692149020Sbz		if ((tcp->th_flags & TH_RST) != 0) {
693165738Sjulian			m_freem(m);
694165738Sjulian			args->m = NULL;
695149020Sbz			return;
696149020Sbz		}
697149020Sbz
698149020Sbz		ti.ip6 = *ip6;
699149020Sbz		ti.th = *tcp;
700149020Sbz		ti.th.th_seq = ntohl(ti.th.th_seq);
701149020Sbz		ti.th.th_ack = ntohl(ti.th.th_ack);
702149020Sbz		ti.ip6.ip6_nxt = IPPROTO_TCP;
703149020Sbz
704149020Sbz		if (ti.th.th_flags & TH_ACK) {
705149020Sbz			ack = 0;
706149020Sbz			seq = ti.th.th_ack;
707149020Sbz			flags = TH_RST;
708149020Sbz		} else {
709149020Sbz			ack = ti.th.th_seq;
710165738Sjulian			if ((m->m_flags & M_PKTHDR) != 0) {
711165738Sjulian				/*
712165738Sjulian				 * total new data to ACK is:
713165738Sjulian				 * total packet length,
714165738Sjulian				 * minus the header length,
715165738Sjulian				 * minus the tcp header length.
716165738Sjulian				 */
717165738Sjulian				ack += m->m_pkthdr.len - hlen
718149020Sbz					- (ti.th.th_off << 2);
719149020Sbz			} else if (ip6->ip6_plen) {
720165738Sjulian				ack += ntohs(ip6->ip6_plen) + sizeof(*ip6) -
721165738Sjulian				    hlen - (ti.th.th_off << 2);
722149020Sbz			} else {
723165738Sjulian				m_freem(m);
724149020Sbz				return;
725149020Sbz			}
726149020Sbz			if (tcp->th_flags & TH_SYN)
727149020Sbz				ack++;
728149020Sbz			seq = 0;
729149020Sbz			flags = TH_RST|TH_ACK;
730149020Sbz		}
731149020Sbz		bcopy(&ti, ip6, sizeof(ti));
732165738Sjulian		/*
733165738Sjulian		 * m is only used to recycle the mbuf
734165738Sjulian		 * The data in it is never read so we don't need
735165738Sjulian		 * to correct the offsets or anything
736165738Sjulian		 */
737165738Sjulian		tcp_respond(NULL, ip6, tcp, m, ack, seq, flags);
738149020Sbz	} else if (code != ICMP6_UNREACH_RST) { /* Send an ICMPv6 unreach. */
739165738Sjulian#if 0
740165738Sjulian		/*
741165738Sjulian		 * Unlike above, the mbufs need to line up with the ip6 hdr,
742165738Sjulian		 * as the contents are read. We need to m_adj() the
743165738Sjulian		 * needed amount.
744165738Sjulian		 * The mbuf will however be thrown away so we can adjust it.
745165738Sjulian		 * Remember we did an m_pullup on it already so we
746165738Sjulian		 * can make some assumptions about contiguousness.
747165738Sjulian		 */
748165738Sjulian		if (args->L3offset)
749165738Sjulian			m_adj(m, args->L3offset);
750165738Sjulian#endif
751165738Sjulian		icmp6_error(m, ICMP6_DST_UNREACH, code, 0);
752149020Sbz	} else
753165738Sjulian		m_freem(m);
754149020Sbz
755149020Sbz	args->m = NULL;
756149020Sbz}
757149020Sbz
758145266Sphk#endif /* INET6 */
759145266Sphk
76098943Sluigistatic u_int64_t norule_counter;	/* counter for ipfw_log(NULL...) */
76198943Sluigi
76298943Sluigi#define SNPARGS(buf, len) buf + len, sizeof(buf) > len ? sizeof(buf) - len : 0
76399622Sluigi#define SNP(buf) buf, sizeof(buf)
764100004Sluigi
76598943Sluigi/*
76698943Sluigi * We enter here when we have a rule with O_LOG.
76799622Sluigi * XXX this function alone takes about 2Kbytes of code!
76898943Sluigi */
76998943Sluigistatic void
770149020Sbzipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
771169454Srwatson    struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
772169454Srwatson    struct ip *ip)
77398943Sluigi{
774149020Sbz	struct ether_header *eh = args->eh;
77598943Sluigi	char *action;
77698943Sluigi	int limit_reached = 0;
777149020Sbz	char action2[40], proto[128], fragment[32];
77898943Sluigi
77998943Sluigi	fragment[0] = '\0';
78098943Sluigi	proto[0] = '\0';
78198943Sluigi
78298943Sluigi	if (f == NULL) {	/* bogus pkt */
78398943Sluigi		if (verbose_limit != 0 && norule_counter >= verbose_limit)
78498943Sluigi			return;
78598943Sluigi		norule_counter++;
78698943Sluigi		if (norule_counter == verbose_limit)
78798943Sluigi			limit_reached = verbose_limit;
78898943Sluigi		action = "Refuse";
78998943Sluigi	} else {	/* O_LOG is the first action, find the real one */
79098943Sluigi		ipfw_insn *cmd = ACTION_PTR(f);
79198943Sluigi		ipfw_insn_log *l = (ipfw_insn_log *)cmd;
79298943Sluigi
79398943Sluigi		if (l->max_log != 0 && l->log_left == 0)
79498943Sluigi			return;
79598943Sluigi		l->log_left--;
79698943Sluigi		if (l->log_left == 0)
79798943Sluigi			limit_reached = l->max_log;
79898943Sluigi		cmd += F_LEN(cmd);	/* point to first action */
799136071Sgreen		if (cmd->opcode == O_ALTQ) {
800136071Sgreen			ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd;
801136071Sgreen
802136071Sgreen			snprintf(SNPARGS(action2, 0), "Altq %d",
803136071Sgreen				altq->qid);
804136071Sgreen			cmd += F_LEN(cmd);
805136071Sgreen		}
80698943Sluigi		if (cmd->opcode == O_PROB)
80798943Sluigi			cmd += F_LEN(cmd);
80898943Sluigi
809158879Soleg		if (cmd->opcode == O_TAG)
810158879Soleg			cmd += F_LEN(cmd);
811158879Soleg
81298943Sluigi		action = action2;
81398943Sluigi		switch (cmd->opcode) {
81498943Sluigi		case O_DENY:
81598943Sluigi			action = "Deny";
81698943Sluigi			break;
81799475Sluigi
81898943Sluigi		case O_REJECT:
81999475Sluigi			if (cmd->arg1==ICMP_REJECT_RST)
82099475Sluigi				action = "Reset";
82199475Sluigi			else if (cmd->arg1==ICMP_UNREACH_HOST)
82299475Sluigi				action = "Reject";
82399475Sluigi			else
82499475Sluigi				snprintf(SNPARGS(action2, 0), "Unreach %d",
82599475Sluigi					cmd->arg1);
82698943Sluigi			break;
82799475Sluigi
828149020Sbz		case O_UNREACH6:
829149020Sbz			if (cmd->arg1==ICMP6_UNREACH_RST)
830149020Sbz				action = "Reset";
831149020Sbz			else
832149020Sbz				snprintf(SNPARGS(action2, 0), "Unreach %d",
833149020Sbz					cmd->arg1);
834149020Sbz			break;
835149020Sbz
83698943Sluigi		case O_ACCEPT:
83798943Sluigi			action = "Accept";
83898943Sluigi			break;
83998943Sluigi		case O_COUNT:
84098943Sluigi			action = "Count";
84198943Sluigi			break;
84298943Sluigi		case O_DIVERT:
84398943Sluigi			snprintf(SNPARGS(action2, 0), "Divert %d",
84498943Sluigi				cmd->arg1);
84598943Sluigi			break;
84698943Sluigi		case O_TEE:
84798943Sluigi			snprintf(SNPARGS(action2, 0), "Tee %d",
84898943Sluigi				cmd->arg1);
84998943Sluigi			break;
85098943Sluigi		case O_SKIPTO:
85198943Sluigi			snprintf(SNPARGS(action2, 0), "SkipTo %d",
85298943Sluigi				cmd->arg1);
85398943Sluigi			break;
85498943Sluigi		case O_PIPE:
85598943Sluigi			snprintf(SNPARGS(action2, 0), "Pipe %d",
85698943Sluigi				cmd->arg1);
85798943Sluigi			break;
85898943Sluigi		case O_QUEUE:
85998943Sluigi			snprintf(SNPARGS(action2, 0), "Queue %d",
86098943Sluigi				cmd->arg1);
86198943Sluigi			break;
86298943Sluigi		case O_FORWARD_IP: {
86398943Sluigi			ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;
864100004Sluigi			int len;
865161424Sjulian			struct in_addr dummyaddr;
866161424Sjulian			if (sa->sa.sin_addr.s_addr == INADDR_ANY)
867161424Sjulian				dummyaddr.s_addr = htonl(tablearg);
868161424Sjulian			else
869161424Sjulian				dummyaddr.s_addr = sa->sa.sin_addr.s_addr;
87098943Sluigi
871100004Sluigi			len = snprintf(SNPARGS(action2, 0), "Forward to %s",
872161424Sjulian				inet_ntoa(dummyaddr));
873161424Sjulian
87498943Sluigi			if (sa->sa.sin_port)
875100004Sluigi				snprintf(SNPARGS(action2, len), ":%d",
876107897Smaxim				    sa->sa.sin_port);
87798943Sluigi			}
87898943Sluigi			break;
879141351Sglebius		case O_NETGRAPH:
880141351Sglebius			snprintf(SNPARGS(action2, 0), "Netgraph %d",
881141351Sglebius				cmd->arg1);
882141351Sglebius			break;
883141351Sglebius		case O_NGTEE:
884141351Sglebius			snprintf(SNPARGS(action2, 0), "Ngtee %d",
885141351Sglebius				cmd->arg1);
886141351Sglebius			break;
887165648Spiso		case O_NAT:
888165648Spiso			action = "Nat";
889165648Spiso 			break;
890105775Smaxim		default:
89198943Sluigi			action = "UNKNOWN";
89298943Sluigi			break;
89398943Sluigi		}
89498943Sluigi	}
89598943Sluigi
89698943Sluigi	if (hlen == 0) {	/* non-ip */
89798943Sluigi		snprintf(SNPARGS(proto, 0), "MAC");
898149020Sbz
89998943Sluigi	} else {
900149020Sbz		int len;
901149020Sbz		char src[48], dst[48];
902149020Sbz		struct icmphdr *icmp;
903149020Sbz		struct tcphdr *tcp;
904149020Sbz		struct udphdr *udp;
905149020Sbz#ifdef INET6
906149020Sbz		struct ip6_hdr *ip6 = NULL;
907149020Sbz		struct icmp6_hdr *icmp6;
908149020Sbz#endif
909149020Sbz		src[0] = '\0';
910149020Sbz		dst[0] = '\0';
911149020Sbz#ifdef INET6
912163237Smaxim		if (IS_IP6_FLOW_ID(&(args->f_id))) {
913165118Sbz			char ip6buf[INET6_ADDRSTRLEN];
914149020Sbz			snprintf(src, sizeof(src), "[%s]",
915165118Sbz			    ip6_sprintf(ip6buf, &args->f_id.src_ip6));
916149020Sbz			snprintf(dst, sizeof(dst), "[%s]",
917165118Sbz			    ip6_sprintf(ip6buf, &args->f_id.dst_ip6));
91898943Sluigi
919165738Sjulian			ip6 = (struct ip6_hdr *)ip;
920165738Sjulian			tcp = (struct tcphdr *)(((char *)ip) + hlen);
921165738Sjulian			udp = (struct udphdr *)(((char *)ip) + hlen);
922149020Sbz		} else
923149020Sbz#endif
924149020Sbz		{
925149020Sbz			tcp = L3HDR(struct tcphdr, ip);
926149020Sbz			udp = L3HDR(struct udphdr, ip);
92798943Sluigi
928149020Sbz			inet_ntoa_r(ip->ip_src, src);
929149020Sbz			inet_ntoa_r(ip->ip_dst, dst);
930149020Sbz		}
93198943Sluigi
932149020Sbz		switch (args->f_id.proto) {
93398943Sluigi		case IPPROTO_TCP:
934149020Sbz			len = snprintf(SNPARGS(proto, 0), "TCP %s", src);
93598943Sluigi			if (offset == 0)
936100004Sluigi				snprintf(SNPARGS(proto, len), ":%d %s:%d",
937100004Sluigi				    ntohs(tcp->th_sport),
938149020Sbz				    dst,
939100004Sluigi				    ntohs(tcp->th_dport));
94098943Sluigi			else
941149020Sbz				snprintf(SNPARGS(proto, len), " %s", dst);
94298943Sluigi			break;
94398943Sluigi
94498943Sluigi		case IPPROTO_UDP:
945149020Sbz			len = snprintf(SNPARGS(proto, 0), "UDP %s", src);
94698943Sluigi			if (offset == 0)
947100004Sluigi				snprintf(SNPARGS(proto, len), ":%d %s:%d",
948100004Sluigi				    ntohs(udp->uh_sport),
949149020Sbz				    dst,
950100004Sluigi				    ntohs(udp->uh_dport));
95198943Sluigi			else
952149020Sbz				snprintf(SNPARGS(proto, len), " %s", dst);
95398943Sluigi			break;
95498943Sluigi
95598943Sluigi		case IPPROTO_ICMP:
956149020Sbz			icmp = L3HDR(struct icmphdr, ip);
95798943Sluigi			if (offset == 0)
95898943Sluigi				len = snprintf(SNPARGS(proto, 0),
95998943Sluigi				    "ICMP:%u.%u ",
96098943Sluigi				    icmp->icmp_type, icmp->icmp_code);
96198943Sluigi			else
96298943Sluigi				len = snprintf(SNPARGS(proto, 0), "ICMP ");
963149020Sbz			len += snprintf(SNPARGS(proto, len), "%s", src);
964149020Sbz			snprintf(SNPARGS(proto, len), " %s", dst);
96598943Sluigi			break;
966149020Sbz#ifdef INET6
967149020Sbz		case IPPROTO_ICMPV6:
968165738Sjulian			icmp6 = (struct icmp6_hdr *)(((char *)ip) + hlen);
969149020Sbz			if (offset == 0)
970149020Sbz				len = snprintf(SNPARGS(proto, 0),
971149020Sbz				    "ICMPv6:%u.%u ",
972149020Sbz				    icmp6->icmp6_type, icmp6->icmp6_code);
973149020Sbz			else
974149020Sbz				len = snprintf(SNPARGS(proto, 0), "ICMPv6 ");
975149020Sbz			len += snprintf(SNPARGS(proto, len), "%s", src);
976149020Sbz			snprintf(SNPARGS(proto, len), " %s", dst);
977149020Sbz			break;
978149020Sbz#endif
97998943Sluigi		default:
980149020Sbz			len = snprintf(SNPARGS(proto, 0), "P:%d %s",
981149020Sbz			    args->f_id.proto, src);
982149020Sbz			snprintf(SNPARGS(proto, len), " %s", dst);
98398943Sluigi			break;
98498943Sluigi		}
98598943Sluigi
986149020Sbz#ifdef INET6
987163237Smaxim		if (IS_IP6_FLOW_ID(&(args->f_id))) {
988149020Sbz			if (offset & (IP6F_OFF_MASK | IP6F_MORE_FRAG))
989149020Sbz				snprintf(SNPARGS(fragment, 0),
990149020Sbz				    " (frag %08x:%d@%d%s)",
991149020Sbz				    args->f_id.frag_id6,
992149020Sbz				    ntohs(ip6->ip6_plen) - hlen,
993149020Sbz				    ntohs(offset & IP6F_OFF_MASK) << 3,
994149020Sbz				    (offset & IP6F_MORE_FRAG) ? "+" : "");
995149020Sbz		} else
996149020Sbz#endif
997149020Sbz		{
998149020Sbz			int ip_off, ip_len;
999149020Sbz			if (eh != NULL) { /* layer 2 packets are as on the wire */
1000149020Sbz				ip_off = ntohs(ip->ip_off);
1001149020Sbz				ip_len = ntohs(ip->ip_len);
1002149020Sbz			} else {
1003149020Sbz				ip_off = ip->ip_off;
1004149020Sbz				ip_len = ip->ip_len;
1005149020Sbz			}
1006149020Sbz			if (ip_off & (IP_MF | IP_OFFMASK))
1007149020Sbz				snprintf(SNPARGS(fragment, 0),
1008149020Sbz				    " (frag %d:%d@%d%s)",
1009149020Sbz				    ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2),
1010149020Sbz				    offset << 3,
1011149020Sbz				    (ip_off & IP_MF) ? "+" : "");
1012149020Sbz		}
101398943Sluigi	}
101498943Sluigi	if (oif || m->m_pkthdr.rcvif)
101598943Sluigi		log(LOG_SECURITY | LOG_INFO,
1016121816Sbrooks		    "ipfw: %d %s %s %s via %s%s\n",
101798943Sluigi		    f ? f->rulenum : -1,
101898943Sluigi		    action, proto, oif ? "out" : "in",
1019121816Sbrooks		    oif ? oif->if_xname : m->m_pkthdr.rcvif->if_xname,
102098943Sluigi		    fragment);
102198943Sluigi	else
102298943Sluigi		log(LOG_SECURITY | LOG_INFO,
102398943Sluigi		    "ipfw: %d %s %s [no if info]%s\n",
102498943Sluigi		    f ? f->rulenum : -1,
102598943Sluigi		    action, proto, fragment);
102698943Sluigi	if (limit_reached)
102798943Sluigi		log(LOG_SECURITY | LOG_NOTICE,
102898943Sluigi		    "ipfw: limit %d reached on entry %d\n",
102998943Sluigi		    limit_reached, f ? f->rulenum : -1);
103098943Sluigi}
103198943Sluigi
103298943Sluigi/*
103398943Sluigi * IMPORTANT: the hash function for dynamic rules must be commutative
103499475Sluigi * in source and destination (ip,port), because rules are bidirectional
103598943Sluigi * and we want to find both in the same bucket.
103698943Sluigi */
103798943Sluigistatic __inline int
103898943Sluigihash_packet(struct ipfw_flow_id *id)
103998943Sluigi{
104098943Sluigi	u_int32_t i;
104198943Sluigi
1042145266Sphk#ifdef INET6
1043145266Sphk	if (IS_IP6_FLOW_ID(id))
1044145267Sphk		i = hash_packet6(id);
1045145266Sphk	else
1046145266Sphk#endif /* INET6 */
1047145266Sphk	i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port);
104898943Sluigi	i &= (curr_dyn_buckets - 1);
104998943Sluigi	return i;
105098943Sluigi}
105198943Sluigi
105298943Sluigi/**
105398943Sluigi * unlink a dynamic rule from a chain. prev is a pointer to
105498943Sluigi * the previous one, q is a pointer to the rule to delete,
105598943Sluigi * head is a pointer to the head of the queue.
105698943Sluigi * Modifies q and potentially also head.
105798943Sluigi */
105898943Sluigi#define UNLINK_DYN_RULE(prev, head, q) {				\
105998943Sluigi	ipfw_dyn_rule *old_q = q;					\
106098943Sluigi									\
106198943Sluigi	/* remove a refcount to the parent */				\
106298943Sluigi	if (q->dyn_type == O_LIMIT)					\
106398943Sluigi		q->parent->count--;					\
1064108258Smaxim	DEB(printf("ipfw: unlink entry 0x%08x %d -> 0x%08x %d, %d left\n",\
106598943Sluigi		(q->id.src_ip), (q->id.src_port),			\
106698943Sluigi		(q->id.dst_ip), (q->id.dst_port), dyn_count-1 ); )	\
106798943Sluigi	if (prev != NULL)						\
106898943Sluigi		prev->next = q = q->next;				\
106998943Sluigi	else								\
107098943Sluigi		head = q = q->next;					\
107198943Sluigi	dyn_count--;							\
1072141076Scsjp	uma_zfree(ipfw_dyn_rule_zone, old_q); }
107398943Sluigi
107498943Sluigi#define TIME_LEQ(a,b)       ((int)((a)-(b)) <= 0)
107598943Sluigi
107698943Sluigi/**
107798943Sluigi * Remove dynamic rules pointing to "rule", or all of them if rule == NULL.
107898943Sluigi *
107998943Sluigi * If keep_me == NULL, rules are deleted even if not expired,
108098943Sluigi * otherwise only expired rules are removed.
108198943Sluigi *
108298943Sluigi * The value of the second parameter is also used to point to identify
108398943Sluigi * a rule we absolutely do not want to remove (e.g. because we are
108498943Sluigi * holding a reference to it -- this is the case with O_LIMIT_PARENT
108598943Sluigi * rules). The pointer is only used for comparison, so any non-null
108698943Sluigi * value will do.
108798943Sluigi */
108898943Sluigistatic void
108998943Sluigiremove_dyn_rule(struct ip_fw *rule, ipfw_dyn_rule *keep_me)
109098943Sluigi{
109198943Sluigi	static u_int32_t last_remove = 0;
109298943Sluigi
109398943Sluigi#define FORCE (keep_me == NULL)
109498943Sluigi
109598943Sluigi	ipfw_dyn_rule *prev, *q;
109698943Sluigi	int i, pass = 0, max_pass = 0;
109798943Sluigi
1098120141Ssam	IPFW_DYN_LOCK_ASSERT();
1099120141Ssam
110098943Sluigi	if (ipfw_dyn_v == NULL || dyn_count == 0)
110198943Sluigi		return;
110298943Sluigi	/* do not expire more than once per second, it is useless */
1103150350Sandre	if (!FORCE && last_remove == time_uptime)
110498943Sluigi		return;
1105150350Sandre	last_remove = time_uptime;
110698943Sluigi
110798943Sluigi	/*
110898943Sluigi	 * because O_LIMIT refer to parent rules, during the first pass only
110998943Sluigi	 * remove child and mark any pending LIMIT_PARENT, and remove
111098943Sluigi	 * them in a second pass.
111198943Sluigi	 */
111298943Sluiginext_pass:
111398943Sluigi	for (i = 0 ; i < curr_dyn_buckets ; i++) {
111498943Sluigi		for (prev=NULL, q = ipfw_dyn_v[i] ; q ; ) {
111598943Sluigi			/*
111698943Sluigi			 * Logic can become complex here, so we split tests.
111798943Sluigi			 */
111898943Sluigi			if (q == keep_me)
111998943Sluigi				goto next;
112098943Sluigi			if (rule != NULL && rule != q->rule)
112198943Sluigi				goto next; /* not the one we are looking for */
112298943Sluigi			if (q->dyn_type == O_LIMIT_PARENT) {
112398943Sluigi				/*
112498943Sluigi				 * handle parent in the second pass,
112598943Sluigi				 * record we need one.
112698943Sluigi				 */
112798943Sluigi				max_pass = 1;
112898943Sluigi				if (pass == 0)
112998943Sluigi					goto next;
113098943Sluigi				if (FORCE && q->count != 0 ) {
113198943Sluigi					/* XXX should not happen! */
1132108258Smaxim					printf("ipfw: OUCH! cannot remove rule,"
113398943Sluigi					     " count %d\n", q->count);
113498943Sluigi				}
113598943Sluigi			} else {
113698943Sluigi				if (!FORCE &&
1137150350Sandre				    !TIME_LEQ( q->expire, time_uptime ))
113898943Sluigi					goto next;
113998943Sluigi			}
1140121123Smckusick             if (q->dyn_type != O_LIMIT_PARENT || !q->count) {
1141121123Smckusick                     UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
1142121123Smckusick                     continue;
1143121123Smckusick             }
114498943Sluiginext:
114598943Sluigi			prev=q;
114698943Sluigi			q=q->next;
114798943Sluigi		}
114898943Sluigi	}
114998943Sluigi	if (pass++ < max_pass)
115098943Sluigi		goto next_pass;
115198943Sluigi}
115298943Sluigi
115398943Sluigi
115498943Sluigi/**
115598943Sluigi * lookup a dynamic rule.
115698943Sluigi */
115798943Sluigistatic ipfw_dyn_rule *
1158120141Ssamlookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction,
1159169454Srwatson    struct tcphdr *tcp)
116098943Sluigi{
116198943Sluigi	/*
116298943Sluigi	 * stateful ipfw extensions.
116398943Sluigi	 * Lookup into dynamic session queue
116498943Sluigi	 */
116598943Sluigi#define MATCH_REVERSE	0
116698943Sluigi#define MATCH_FORWARD	1
116798943Sluigi#define MATCH_NONE	2
116898943Sluigi#define MATCH_UNKNOWN	3
116998943Sluigi	int i, dir = MATCH_NONE;
117098943Sluigi	ipfw_dyn_rule *prev, *q=NULL;
117198943Sluigi
1172120141Ssam	IPFW_DYN_LOCK_ASSERT();
1173120141Ssam
117498943Sluigi	if (ipfw_dyn_v == NULL)
117598943Sluigi		goto done;	/* not found */
117698943Sluigi	i = hash_packet( pkt );
117798943Sluigi	for (prev=NULL, q = ipfw_dyn_v[i] ; q != NULL ; ) {
1178121123Smckusick		if (q->dyn_type == O_LIMIT_PARENT && q->count)
117998943Sluigi			goto next;
1180150350Sandre		if (TIME_LEQ( q->expire, time_uptime)) { /* expire entry */
118198943Sluigi			UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
118298943Sluigi			continue;
118398943Sluigi		}
1184121123Smckusick		if (pkt->proto == q->id.proto &&
1185121123Smckusick		    q->dyn_type != O_LIMIT_PARENT) {
1186145246Sbrooks			if (IS_IP6_FLOW_ID(pkt)) {
1187145246Sbrooks			    if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6),
1188145246Sbrooks				&(q->id.src_ip6)) &&
1189145246Sbrooks			    IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6),
1190145246Sbrooks				&(q->id.dst_ip6)) &&
119198943Sluigi			    pkt->src_port == q->id.src_port &&
119298943Sluigi			    pkt->dst_port == q->id.dst_port ) {
119398943Sluigi				dir = MATCH_FORWARD;
119498943Sluigi				break;
1195145246Sbrooks			    }
1196145246Sbrooks			    if (IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6),
1197145246Sbrooks				    &(q->id.dst_ip6)) &&
1198145246Sbrooks				IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6),
1199145246Sbrooks				    &(q->id.src_ip6)) &&
1200145246Sbrooks				pkt->src_port == q->id.dst_port &&
1201145246Sbrooks				pkt->dst_port == q->id.src_port ) {
1202145246Sbrooks				    dir = MATCH_REVERSE;
1203145246Sbrooks				    break;
1204145246Sbrooks			    }
1205145246Sbrooks			} else {
1206145246Sbrooks			    if (pkt->src_ip == q->id.src_ip &&
1207145246Sbrooks				pkt->dst_ip == q->id.dst_ip &&
1208145246Sbrooks				pkt->src_port == q->id.src_port &&
1209145246Sbrooks				pkt->dst_port == q->id.dst_port ) {
1210145246Sbrooks				    dir = MATCH_FORWARD;
1211145246Sbrooks				    break;
1212145246Sbrooks			    }
1213145246Sbrooks			    if (pkt->src_ip == q->id.dst_ip &&
1214145246Sbrooks				pkt->dst_ip == q->id.src_ip &&
1215145246Sbrooks				pkt->src_port == q->id.dst_port &&
1216145246Sbrooks				pkt->dst_port == q->id.src_port ) {
1217145246Sbrooks				    dir = MATCH_REVERSE;
1218145246Sbrooks				    break;
1219145246Sbrooks			    }
122098943Sluigi			}
122198943Sluigi		}
122298943Sluiginext:
122398943Sluigi		prev = q;
122498943Sluigi		q = q->next;
122598943Sluigi	}
122698943Sluigi	if (q == NULL)
122798943Sluigi		goto done; /* q = NULL, not found */
122898943Sluigi
122998943Sluigi	if ( prev != NULL) { /* found and not in front */
123098943Sluigi		prev->next = q->next;
123198943Sluigi		q->next = ipfw_dyn_v[i];
123298943Sluigi		ipfw_dyn_v[i] = q;
123398943Sluigi	}
123498943Sluigi	if (pkt->proto == IPPROTO_TCP) { /* update state according to flags */
123598943Sluigi		u_char flags = pkt->flags & (TH_FIN|TH_SYN|TH_RST);
123698943Sluigi
123798943Sluigi#define BOTH_SYN	(TH_SYN | (TH_SYN << 8))
123898943Sluigi#define BOTH_FIN	(TH_FIN | (TH_FIN << 8))
123998943Sluigi		q->state |= (dir == MATCH_FORWARD ) ? flags : (flags << 8);
124098943Sluigi		switch (q->state) {
124198943Sluigi		case TH_SYN:				/* opening */
1242150350Sandre			q->expire = time_uptime + dyn_syn_lifetime;
124398943Sluigi			break;
1244101978Sluigi
124598943Sluigi		case BOTH_SYN:			/* move to established */
124698943Sluigi		case BOTH_SYN | TH_FIN :	/* one side tries to close */
124798943Sluigi		case BOTH_SYN | (TH_FIN << 8) :
1248100004Sluigi 			if (tcp) {
1249100004Sluigi#define _SEQ_GE(a,b) ((int)(a) - (int)(b) >= 0)
1250100004Sluigi			    u_int32_t ack = ntohl(tcp->th_ack);
1251100004Sluigi			    if (dir == MATCH_FORWARD) {
1252100004Sluigi				if (q->ack_fwd == 0 || _SEQ_GE(ack, q->ack_fwd))
1253100004Sluigi				    q->ack_fwd = ack;
1254100004Sluigi				else { /* ignore out-of-sequence */
1255100004Sluigi				    break;
1256100004Sluigi				}
1257100004Sluigi			    } else {
1258100004Sluigi				if (q->ack_rev == 0 || _SEQ_GE(ack, q->ack_rev))
1259100004Sluigi				    q->ack_rev = ack;
1260100004Sluigi				else { /* ignore out-of-sequence */
1261100004Sluigi				    break;
1262100004Sluigi				}
1263100004Sluigi			    }
1264100004Sluigi			}
1265150350Sandre			q->expire = time_uptime + dyn_ack_lifetime;
126698943Sluigi			break;
1267101978Sluigi
126898943Sluigi		case BOTH_SYN | BOTH_FIN:	/* both sides closed */
1269101978Sluigi			if (dyn_fin_lifetime >= dyn_keepalive_period)
1270101978Sluigi				dyn_fin_lifetime = dyn_keepalive_period - 1;
1271150350Sandre			q->expire = time_uptime + dyn_fin_lifetime;
127298943Sluigi			break;
1273101978Sluigi
127498943Sluigi		default:
127598943Sluigi#if 0
127698943Sluigi			/*
127798943Sluigi			 * reset or some invalid combination, but can also
127898943Sluigi			 * occur if we use keep-state the wrong way.
127998943Sluigi			 */
128098943Sluigi			if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0)
128198943Sluigi				printf("invalid state: 0x%x\n", q->state);
128298943Sluigi#endif
1283101978Sluigi			if (dyn_rst_lifetime >= dyn_keepalive_period)
1284101978Sluigi				dyn_rst_lifetime = dyn_keepalive_period - 1;
1285150350Sandre			q->expire = time_uptime + dyn_rst_lifetime;
128698943Sluigi			break;
128798943Sluigi		}
128898943Sluigi	} else if (pkt->proto == IPPROTO_UDP) {
1289150350Sandre		q->expire = time_uptime + dyn_udp_lifetime;
129098943Sluigi	} else {
129198943Sluigi		/* other protocols */
1292150350Sandre		q->expire = time_uptime + dyn_short_lifetime;
129398943Sluigi	}
129498943Sluigidone:
129598943Sluigi	if (match_direction)
129698943Sluigi		*match_direction = dir;
129798943Sluigi	return q;
129898943Sluigi}
129998943Sluigi
1300120141Ssamstatic ipfw_dyn_rule *
1301120141Ssamlookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction,
1302169454Srwatson    struct tcphdr *tcp)
1303120141Ssam{
1304120141Ssam	ipfw_dyn_rule *q;
1305120141Ssam
1306120141Ssam	IPFW_DYN_LOCK();
1307120141Ssam	q = lookup_dyn_rule_locked(pkt, match_direction, tcp);
1308120141Ssam	if (q == NULL)
1309120141Ssam		IPFW_DYN_UNLOCK();
1310120141Ssam	/* NB: return table locked when q is not NULL */
1311120141Ssam	return q;
1312120141Ssam}
1313120141Ssam
131498943Sluigistatic void
131598943Sluigirealloc_dynamic_table(void)
131698943Sluigi{
1317120141Ssam	IPFW_DYN_LOCK_ASSERT();
1318120141Ssam
1319101978Sluigi	/*
1320101978Sluigi	 * Try reallocation, make sure we have a power of 2 and do
1321101978Sluigi	 * not allow more than 64k entries. In case of overflow,
1322101978Sluigi	 * default to 1024.
1323101978Sluigi	 */
132498943Sluigi
1325101978Sluigi	if (dyn_buckets > 65536)
1326101978Sluigi		dyn_buckets = 1024;
132798943Sluigi	if ((dyn_buckets & (dyn_buckets-1)) != 0) { /* not a power of 2 */
132898943Sluigi		dyn_buckets = curr_dyn_buckets; /* reset */
132998943Sluigi		return;
133098943Sluigi	}
133198943Sluigi	curr_dyn_buckets = dyn_buckets;
133298943Sluigi	if (ipfw_dyn_v != NULL)
133398943Sluigi		free(ipfw_dyn_v, M_IPFW);
1334101978Sluigi	for (;;) {
1335101978Sluigi		ipfw_dyn_v = malloc(curr_dyn_buckets * sizeof(ipfw_dyn_rule *),
1336105440Smux		       M_IPFW, M_NOWAIT | M_ZERO);
1337101978Sluigi		if (ipfw_dyn_v != NULL || curr_dyn_buckets <= 2)
1338101978Sluigi			break;
1339101978Sluigi		curr_dyn_buckets /= 2;
1340101978Sluigi	}
134198943Sluigi}
134298943Sluigi
134398943Sluigi/**
134498943Sluigi * Install state of type 'type' for a dynamic session.
134598943Sluigi * The hash table contains two type of rules:
134698943Sluigi * - regular rules (O_KEEP_STATE)
134798943Sluigi * - rules for sessions with limited number of sess per user
134898943Sluigi *   (O_LIMIT). When they are created, the parent is
134998943Sluigi *   increased by 1, and decreased on delete. In this case,
135098943Sluigi *   the third parameter is the parent rule and not the chain.
135198943Sluigi * - "parent" rules for the above (O_LIMIT_PARENT).
135298943Sluigi */
135398943Sluigistatic ipfw_dyn_rule *
135498943Sluigiadd_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
135598943Sluigi{
135698943Sluigi	ipfw_dyn_rule *r;
135798943Sluigi	int i;
135898943Sluigi
1359120141Ssam	IPFW_DYN_LOCK_ASSERT();
1360120141Ssam
136198943Sluigi	if (ipfw_dyn_v == NULL ||
136298943Sluigi	    (dyn_count == 0 && dyn_buckets != curr_dyn_buckets)) {
136398943Sluigi		realloc_dynamic_table();
136498943Sluigi		if (ipfw_dyn_v == NULL)
136598943Sluigi			return NULL; /* failed ! */
136698943Sluigi	}
136798943Sluigi	i = hash_packet(id);
136898943Sluigi
1369141076Scsjp	r = uma_zalloc(ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO);
137098943Sluigi	if (r == NULL) {
1371108258Smaxim		printf ("ipfw: sorry cannot allocate state\n");
137298943Sluigi		return NULL;
137398943Sluigi	}
137498943Sluigi
137598943Sluigi	/* increase refcount on parent, and set pointer */
137698943Sluigi	if (dyn_type == O_LIMIT) {
137798943Sluigi		ipfw_dyn_rule *parent = (ipfw_dyn_rule *)rule;
137898943Sluigi		if ( parent->dyn_type != O_LIMIT_PARENT)
137998943Sluigi			panic("invalid parent");
138098943Sluigi		parent->count++;
138198943Sluigi		r->parent = parent;
138298943Sluigi		rule = parent->rule;
138398943Sluigi	}
138498943Sluigi
138598943Sluigi	r->id = *id;
1386150350Sandre	r->expire = time_uptime + dyn_syn_lifetime;
138798943Sluigi	r->rule = rule;
138898943Sluigi	r->dyn_type = dyn_type;
138998943Sluigi	r->pcnt = r->bcnt = 0;
139098943Sluigi	r->count = 0;
139198943Sluigi
139298943Sluigi	r->bucket = i;
139398943Sluigi	r->next = ipfw_dyn_v[i];
139498943Sluigi	ipfw_dyn_v[i] = r;
139598943Sluigi	dyn_count++;
1396108258Smaxim	DEB(printf("ipfw: add dyn entry ty %d 0x%08x %d -> 0x%08x %d, total %d\n",
139798943Sluigi	   dyn_type,
139898943Sluigi	   (r->id.src_ip), (r->id.src_port),
139998943Sluigi	   (r->id.dst_ip), (r->id.dst_port),
140098943Sluigi	   dyn_count ); )
140198943Sluigi	return r;
140298943Sluigi}
140398943Sluigi
140498943Sluigi/**
140598943Sluigi * lookup dynamic parent rule using pkt and rule as search keys.
140698943Sluigi * If the lookup fails, then install one.
140798943Sluigi */
140898943Sluigistatic ipfw_dyn_rule *
140998943Sluigilookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
141098943Sluigi{
141198943Sluigi	ipfw_dyn_rule *q;
141298943Sluigi	int i;
141398943Sluigi
1414120141Ssam	IPFW_DYN_LOCK_ASSERT();
1415120141Ssam
141698943Sluigi	if (ipfw_dyn_v) {
1417145246Sbrooks		int is_v6 = IS_IP6_FLOW_ID(pkt);
141898943Sluigi		i = hash_packet( pkt );
141998943Sluigi		for (q = ipfw_dyn_v[i] ; q != NULL ; q=q->next)
142098943Sluigi			if (q->dyn_type == O_LIMIT_PARENT &&
142198943Sluigi			    rule== q->rule &&
142298943Sluigi			    pkt->proto == q->id.proto &&
142398943Sluigi			    pkt->src_port == q->id.src_port &&
1424145246Sbrooks			    pkt->dst_port == q->id.dst_port &&
1425145246Sbrooks			    (
1426145246Sbrooks				(is_v6 &&
1427145246Sbrooks				 IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6),
1428145246Sbrooks					&(q->id.src_ip6)) &&
1429145246Sbrooks				 IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6),
1430145246Sbrooks					&(q->id.dst_ip6))) ||
1431145246Sbrooks				(!is_v6 &&
1432145246Sbrooks				 pkt->src_ip == q->id.src_ip &&
1433145246Sbrooks				 pkt->dst_ip == q->id.dst_ip)
1434145246Sbrooks			    )
1435145246Sbrooks			) {
1436150350Sandre				q->expire = time_uptime + dyn_short_lifetime;
1437108258Smaxim				DEB(printf("ipfw: lookup_dyn_parent found 0x%p\n",q);)
143898943Sluigi				return q;
143998943Sluigi			}
144098943Sluigi	}
144198943Sluigi	return add_dyn_rule(pkt, O_LIMIT_PARENT, rule);
144298943Sluigi}
144398943Sluigi
144498943Sluigi/**
144598943Sluigi * Install dynamic state for rule type cmd->o.opcode
144698943Sluigi *
144798943Sluigi * Returns 1 (failure) if state is not installed because of errors or because
144898943Sluigi * session limitations are enforced.
144998943Sluigi */
145098943Sluigistatic int
145198943Sluigiinstall_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
1452159636Soleg    struct ip_fw_args *args, uint32_t tablearg)
145398943Sluigi{
145498943Sluigi	static int last_log;
145598943Sluigi	ipfw_dyn_rule *q;
1456163235Smaxim	struct in_addr da;
1457163235Smaxim	char src[48], dst[48];
145898943Sluigi
1459163235Smaxim	src[0] = '\0';
1460163235Smaxim	dst[0] = '\0';
1461163235Smaxim
1462159635Soleg	DEB(
1463159635Soleg	printf("ipfw: %s: type %d 0x%08x %u -> 0x%08x %u\n",
1464159635Soleg	    __func__, cmd->o.opcode,
146598943Sluigi	    (args->f_id.src_ip), (args->f_id.src_port),
1466159635Soleg	    (args->f_id.dst_ip), (args->f_id.dst_port));
1467159635Soleg	)
146898943Sluigi
1469120141Ssam	IPFW_DYN_LOCK();
147098943Sluigi
1471120141Ssam	q = lookup_dyn_rule_locked(&args->f_id, NULL, NULL);
1472120141Ssam
1473159635Soleg	if (q != NULL) {	/* should never occur */
1474150350Sandre		if (last_log != time_uptime) {
1475150350Sandre			last_log = time_uptime;
1476159635Soleg			printf("ipfw: %s: entry already present, done\n",
1477159635Soleg			    __func__);
147898943Sluigi		}
1479120141Ssam		IPFW_DYN_UNLOCK();
1480159635Soleg		return (0);
148198943Sluigi	}
148298943Sluigi
148398943Sluigi	if (dyn_count >= dyn_max)
1484159635Soleg		/* Run out of slots, try to remove any expired rule. */
148598943Sluigi		remove_dyn_rule(NULL, (ipfw_dyn_rule *)1);
148698943Sluigi
148798943Sluigi	if (dyn_count >= dyn_max) {
1488150350Sandre		if (last_log != time_uptime) {
1489150350Sandre			last_log = time_uptime;
1490159635Soleg			printf("ipfw: %s: Too many dynamic rules\n", __func__);
149198943Sluigi		}
1492120141Ssam		IPFW_DYN_UNLOCK();
1493159635Soleg		return (1);	/* cannot install, notify caller */
149498943Sluigi	}
149598943Sluigi
149698943Sluigi	switch (cmd->o.opcode) {
1497159635Soleg	case O_KEEP_STATE:	/* bidir rule */
149898943Sluigi		add_dyn_rule(&args->f_id, O_KEEP_STATE, rule);
149998943Sluigi		break;
150098943Sluigi
1501159635Soleg	case O_LIMIT: {		/* limit number of sessions */
150298943Sluigi		struct ipfw_flow_id id;
150398943Sluigi		ipfw_dyn_rule *parent;
1504159636Soleg		uint32_t conn_limit;
1505159635Soleg		uint16_t limit_mask = cmd->limit_mask;
150698943Sluigi
1507159636Soleg		conn_limit = (cmd->conn_limit == IP_FW_TABLEARG) ?
1508159636Soleg		    tablearg : cmd->conn_limit;
1509159636Soleg
1510159635Soleg		DEB(
1511159636Soleg		if (cmd->conn_limit == IP_FW_TABLEARG)
1512159636Soleg			printf("ipfw: %s: O_LIMIT rule, conn_limit: %u "
1513159636Soleg			    "(tablearg)\n", __func__, conn_limit);
1514159636Soleg		else
1515159636Soleg			printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n",
1516159636Soleg			    __func__, conn_limit);
1517159635Soleg		)
151898943Sluigi
1519159635Soleg		id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0;
152098943Sluigi		id.proto = args->f_id.proto;
1521159398Soleg		id.addr_type = args->f_id.addr_type;
152298943Sluigi
1523145246Sbrooks		if (IS_IP6_FLOW_ID (&(args->f_id))) {
1524145246Sbrooks			if (limit_mask & DYN_SRC_ADDR)
1525145246Sbrooks				id.src_ip6 = args->f_id.src_ip6;
1526145246Sbrooks			if (limit_mask & DYN_DST_ADDR)
1527145246Sbrooks				id.dst_ip6 = args->f_id.dst_ip6;
1528145246Sbrooks		} else {
1529145246Sbrooks			if (limit_mask & DYN_SRC_ADDR)
1530145246Sbrooks				id.src_ip = args->f_id.src_ip;
1531145246Sbrooks			if (limit_mask & DYN_DST_ADDR)
1532145246Sbrooks				id.dst_ip = args->f_id.dst_ip;
1533145246Sbrooks		}
153498943Sluigi		if (limit_mask & DYN_SRC_PORT)
153598943Sluigi			id.src_port = args->f_id.src_port;
153698943Sluigi		if (limit_mask & DYN_DST_PORT)
153798943Sluigi			id.dst_port = args->f_id.dst_port;
1538159635Soleg		if ((parent = lookup_dyn_parent(&id, rule)) == NULL) {
1539159635Soleg			printf("ipfw: %s: add parent failed\n", __func__);
1540149783Ssam			IPFW_DYN_UNLOCK();
1541159635Soleg			return (1);
154298943Sluigi		}
1543159635Soleg
1544159636Soleg		if (parent->count >= conn_limit) {
1545159635Soleg			/* See if we can remove some expired rule. */
154698943Sluigi			remove_dyn_rule(rule, parent);
1547159636Soleg			if (parent->count >= conn_limit) {
1548150350Sandre				if (fw_verbose && last_log != time_uptime) {
1549150350Sandre					last_log = time_uptime;
1550163235Smaxim#ifdef INET6
1551163235Smaxim					/*
1552163235Smaxim					 * XXX IPv6 flows are not
1553163235Smaxim					 * supported yet.
1554163236Smaxim					 */
1555163235Smaxim					if (IS_IP6_FLOW_ID(&(args->f_id))) {
1556165118Sbz						char ip6buf[INET6_ADDRSTRLEN];
1557163235Smaxim						snprintf(src, sizeof(src),
1558165118Sbz						    "[%s]", ip6_sprintf(ip6buf,
1559163235Smaxim							&args->f_id.src_ip6));
1560163235Smaxim						snprintf(dst, sizeof(dst),
1561165118Sbz						    "[%s]", ip6_sprintf(ip6buf,
1562163235Smaxim							&args->f_id.dst_ip6));
1563163235Smaxim					} else
1564163235Smaxim#endif
1565163235Smaxim					{
1566163235Smaxim						da.s_addr =
1567163235Smaxim						    htonl(args->f_id.src_ip);
1568163235Smaxim						inet_ntoa_r(da, src);
1569163235Smaxim						da.s_addr =
1570163235Smaxim						    htonl(args->f_id.dst_ip);
1571163235Smaxim						inet_ntoa_r(da, dst);
1572163235Smaxim					}
1573106118Smaxim					log(LOG_SECURITY | LOG_DEBUG,
1574172387Smaxim					    "ipfw: %d %s %s:%u -> %s:%u, %s\n",
1575172387Smaxim					    parent->rule->rulenum,
1576163235Smaxim					    "drop session",
1577163235Smaxim					    src, (args->f_id.src_port),
1578163235Smaxim					    dst, (args->f_id.dst_port),
1579163235Smaxim					    "too many entries");
158098943Sluigi				}
1581120141Ssam				IPFW_DYN_UNLOCK();
1582159635Soleg				return (1);
158398943Sluigi			}
158498943Sluigi		}
158598943Sluigi		add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent);
158698943Sluigi		break;
1587159635Soleg	}
158898943Sluigi	default:
1589159635Soleg		printf("ipfw: %s: unknown dynamic rule type %u\n",
1590159635Soleg		    __func__, cmd->o.opcode);
1591120141Ssam		IPFW_DYN_UNLOCK();
1592159635Soleg		return (1);
159398943Sluigi	}
1594159635Soleg
1595159635Soleg	/* XXX just set lifetime */
1596159635Soleg	lookup_dyn_rule_locked(&args->f_id, NULL, NULL);
1597159635Soleg
1598120141Ssam	IPFW_DYN_UNLOCK();
1599159635Soleg	return (0);
160098943Sluigi}
160198943Sluigi
1602100004Sluigi/*
1603147247Sgreen * Generate a TCP packet, containing either a RST or a keepalive.
1604100004Sluigi * When flags & TH_RST, we are sending a RST packet, because of a
1605100004Sluigi * "reset" action matched the packet.
1606100004Sluigi * Otherwise we are sending a keepalive, and flags & TH_
1607162238Scsjp * The 'replyto' mbuf is the mbuf being replied to, if any, and is required
1608162238Scsjp * so that MAC can label the reply appropriately.
1609100004Sluigi */
1610147247Sgreenstatic struct mbuf *
1611162238Scsjpsend_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq,
1612162238Scsjp    u_int32_t ack, int flags)
161399475Sluigi{
161499475Sluigi	struct mbuf *m;
1615100004Sluigi	struct ip *ip;
161699475Sluigi	struct tcphdr *tcp;
161799475Sluigi
1618151967Sandre	MGETHDR(m, M_DONTWAIT, MT_DATA);
1619105775Smaxim	if (m == 0)
1620147247Sgreen		return (NULL);
162199475Sluigi	m->m_pkthdr.rcvif = (struct ifnet *)0;
1622162238Scsjp
1623162238Scsjp#ifdef MAC
1624162238Scsjp	if (replyto != NULL)
1625173102Srwatson		mac_netinet_firewall_reply(replyto, m);
1626162238Scsjp	else
1627173018Srwatson		mac_netinet_firewall_send(m);
1628162238Scsjp#else
1629162238Scsjp	(void)replyto;		/* don't warn about unused arg */
1630162238Scsjp#endif
1631162238Scsjp
163299475Sluigi	m->m_pkthdr.len = m->m_len = sizeof(struct ip) + sizeof(struct tcphdr);
163399475Sluigi	m->m_data += max_linkhdr;
163499475Sluigi
1635100004Sluigi	ip = mtod(m, struct ip *);
1636100004Sluigi	bzero(ip, m->m_len);
1637100004Sluigi	tcp = (struct tcphdr *)(ip + 1); /* no IP options */
163899475Sluigi	ip->ip_p = IPPROTO_TCP;
1639100004Sluigi	tcp->th_off = 5;
1640100004Sluigi	/*
1641100004Sluigi	 * Assume we are sending a RST (or a keepalive in the reverse
1642100004Sluigi	 * direction), swap src and destination addresses and ports.
1643100004Sluigi	 */
1644100004Sluigi	ip->ip_src.s_addr = htonl(id->dst_ip);
1645100004Sluigi	ip->ip_dst.s_addr = htonl(id->src_ip);
1646100004Sluigi	tcp->th_sport = htons(id->dst_port);
1647100004Sluigi	tcp->th_dport = htons(id->src_port);
1648100004Sluigi	if (flags & TH_RST) {	/* we are sending a RST */
1649100004Sluigi		if (flags & TH_ACK) {
1650100004Sluigi			tcp->th_seq = htonl(ack);
1651100004Sluigi			tcp->th_ack = htonl(0);
1652100004Sluigi			tcp->th_flags = TH_RST;
1653100004Sluigi		} else {
1654100004Sluigi			if (flags & TH_SYN)
1655100004Sluigi				seq++;
1656100004Sluigi			tcp->th_seq = htonl(0);
1657100004Sluigi			tcp->th_ack = htonl(seq);
1658100004Sluigi			tcp->th_flags = TH_RST | TH_ACK;
1659100004Sluigi		}
166099475Sluigi	} else {
1661100004Sluigi		/*
1662100004Sluigi		 * We are sending a keepalive. flags & TH_SYN determines
1663100004Sluigi		 * the direction, forward if set, reverse if clear.
1664100004Sluigi		 * NOTE: seq and ack are always assumed to be correct
1665100004Sluigi		 * as set by the caller. This may be confusing...
1666100004Sluigi		 */
1667100004Sluigi		if (flags & TH_SYN) {
1668100004Sluigi			/*
1669100004Sluigi			 * we have to rewrite the correct addresses!
1670100004Sluigi			 */
1671100004Sluigi			ip->ip_dst.s_addr = htonl(id->dst_ip);
1672100004Sluigi			ip->ip_src.s_addr = htonl(id->src_ip);
1673100004Sluigi			tcp->th_dport = htons(id->dst_port);
1674100004Sluigi			tcp->th_sport = htons(id->src_port);
1675100004Sluigi		}
1676100004Sluigi		tcp->th_seq = htonl(seq);
1677100004Sluigi		tcp->th_ack = htonl(ack);
1678100004Sluigi		tcp->th_flags = TH_ACK;
167999475Sluigi	}
1680100004Sluigi	/*
1681100004Sluigi	 * set ip_len to the payload size so we can compute
1682100004Sluigi	 * the tcp checksum on the pseudoheader
1683100004Sluigi	 * XXX check this, could save a couple of words ?
1684100004Sluigi	 */
1685100004Sluigi	ip->ip_len = htons(sizeof(struct tcphdr));
168699475Sluigi	tcp->th_sum = in_cksum(m, m->m_pkthdr.len);
1687100004Sluigi	/*
1688100004Sluigi	 * now fill fields left out earlier
1689100004Sluigi	 */
169099475Sluigi	ip->ip_ttl = ip_defttl;
169199475Sluigi	ip->ip_len = m->m_pkthdr.len;
1692101978Sluigi	m->m_flags |= M_SKIP_FIREWALL;
1693147247Sgreen	return (m);
169499475Sluigi}
169599475Sluigi
169698943Sluigi/*
169798943Sluigi * sends a reject message, consuming the mbuf passed as an argument.
169898943Sluigi */
169998943Sluigistatic void
1700165738Sjuliansend_reject(struct ip_fw_args *args, int code, int ip_len, struct ip *ip)
170198943Sluigi{
1702101843Sphk
1703165738Sjulian#if 0
1704165738Sjulian	/* XXX When ip is not guaranteed to be at mtod() we will
1705165738Sjulian	 * need to account for this */
1706165738Sjulian	 * The mbuf will however be thrown away so we can adjust it.
1707165738Sjulian	 * Remember we did an m_pullup on it already so we
1708165738Sjulian	 * can make some assumptions about contiguousness.
1709165738Sjulian	 */
1710165738Sjulian	if (args->L3offset)
1711165738Sjulian		m_adj(m, args->L3offset);
1712165738Sjulian#endif
1713108327Siedowse	if (code != ICMP_REJECT_RST) { /* Send an ICMP unreach */
1714108327Siedowse		/* We need the IP header in host order for icmp_error(). */
1715108327Siedowse		if (args->eh != NULL) {
1716108327Siedowse			ip->ip_len = ntohs(ip->ip_len);
1717108327Siedowse			ip->ip_off = ntohs(ip->ip_off);
1718108327Siedowse		}
171999475Sluigi		icmp_error(args->m, ICMP_UNREACH, code, 0L, 0);
1720160025Sbz	} else if (args->f_id.proto == IPPROTO_TCP) {
172199475Sluigi		struct tcphdr *const tcp =
172299475Sluigi		    L3HDR(struct tcphdr, mtod(args->m, struct ip *));
1723147247Sgreen		if ( (tcp->th_flags & TH_RST) == 0) {
1724147247Sgreen			struct mbuf *m;
1725162238Scsjp			m = send_pkt(args->m, &(args->f_id),
1726162238Scsjp				ntohl(tcp->th_seq), ntohl(tcp->th_ack),
1727100004Sluigi				tcp->th_flags | TH_RST);
1728147247Sgreen			if (m != NULL)
1729147247Sgreen				ip_output(m, NULL, NULL, 0, NULL, NULL);
1730147247Sgreen		}
173199475Sluigi		m_freem(args->m);
173299475Sluigi	} else
173399475Sluigi		m_freem(args->m);
173499475Sluigi	args->m = NULL;
173598943Sluigi}
173698943Sluigi
173798943Sluigi/**
173898943Sluigi *
173998943Sluigi * Given an ip_fw *, lookup_next_rule will return a pointer
174098943Sluigi * to the next rule, which can be either the jump
174198943Sluigi * target (for skipto instructions) or the next one in the list (in
174298943Sluigi * all other cases including a missing jump target).
174398943Sluigi * The result is also written in the "next_rule" field of the rule.
174498943Sluigi * Backward jumps are not allowed, so start looking from the next
174598943Sluigi * rule...
174698943Sluigi *
174798943Sluigi * This never returns NULL -- in case we do not have an exact match,
174898943Sluigi * the next rule is returned. When the ruleset is changed,
174998943Sluigi * pointers are flushed so we are always correct.
1750105775Smaxim */
175198943Sluigi
175298943Sluigistatic struct ip_fw *
175398943Sluigilookup_next_rule(struct ip_fw *me)
175498943Sluigi{
175598943Sluigi	struct ip_fw *rule = NULL;
175698943Sluigi	ipfw_insn *cmd;
175798943Sluigi
175898943Sluigi	/* look for action, in case it is a skipto */
175998943Sluigi	cmd = ACTION_PTR(me);
1760109566Smaxim	if (cmd->opcode == O_LOG)
1761109566Smaxim		cmd += F_LEN(cmd);
1762136071Sgreen	if (cmd->opcode == O_ALTQ)
1763136071Sgreen		cmd += F_LEN(cmd);
1764158879Soleg	if (cmd->opcode == O_TAG)
1765158879Soleg		cmd += F_LEN(cmd);
176698943Sluigi	if ( cmd->opcode == O_SKIPTO )
176798943Sluigi		for (rule = me->next; rule ; rule = rule->next)
176898943Sluigi			if (rule->rulenum >= cmd->arg1)
176998943Sluigi				break;
177098943Sluigi	if (rule == NULL)			/* failure or not a skipto */
177198943Sluigi		rule = me->next;
177298943Sluigi	me->next_rule = rule;
177398943Sluigi	return rule;
177498943Sluigi}
177598943Sluigi
1776122265Ssamstatic int
1777153163Sglebiusadd_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
1778169454Srwatson    uint8_t mlen, uint32_t value)
1779130281Sru{
1780130281Sru	struct radix_node_head *rnh;
1781130281Sru	struct table_entry *ent;
1782130281Sru
1783130281Sru	if (tbl >= IPFW_TABLES_MAX)
1784130281Sru		return (EINVAL);
1785153163Sglebius	rnh = ch->tables[tbl];
1786130281Sru	ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO);
1787130281Sru	if (ent == NULL)
1788130281Sru		return (ENOMEM);
1789130281Sru	ent->value = value;
1790130281Sru	ent->addr.sin_len = ent->mask.sin_len = 8;
1791130281Sru	ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
1792130281Sru	ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
1793153163Sglebius	IPFW_WLOCK(&layer3_chain);
1794130281Sru	if (rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent) ==
1795130281Sru	    NULL) {
1796153163Sglebius		IPFW_WUNLOCK(&layer3_chain);
1797130281Sru		free(ent, M_IPFW_TBL);
1798130281Sru		return (EEXIST);
1799130281Sru	}
1800153163Sglebius	IPFW_WUNLOCK(&layer3_chain);
1801130281Sru	return (0);
1802130281Sru}
1803130281Sru
1804130281Srustatic int
1805153163Sglebiusdel_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
1806169454Srwatson    uint8_t mlen)
1807130281Sru{
1808130281Sru	struct radix_node_head *rnh;
1809130281Sru	struct table_entry *ent;
1810130281Sru	struct sockaddr_in sa, mask;
1811130281Sru
1812130281Sru	if (tbl >= IPFW_TABLES_MAX)
1813130281Sru		return (EINVAL);
1814153163Sglebius	rnh = ch->tables[tbl];
1815130281Sru	sa.sin_len = mask.sin_len = 8;
1816130281Sru	mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
1817130281Sru	sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
1818153163Sglebius	IPFW_WLOCK(ch);
1819130281Sru	ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh);
1820130281Sru	if (ent == NULL) {
1821153163Sglebius		IPFW_WUNLOCK(ch);
1822130281Sru		return (ESRCH);
1823130281Sru	}
1824153163Sglebius	IPFW_WUNLOCK(ch);
1825130281Sru	free(ent, M_IPFW_TBL);
1826130281Sru	return (0);
1827130281Sru}
1828130281Sru
1829130281Srustatic int
1830130281Sruflush_table_entry(struct radix_node *rn, void *arg)
1831130281Sru{
1832130281Sru	struct radix_node_head * const rnh = arg;
1833130281Sru	struct table_entry *ent;
1834130281Sru
1835130281Sru	ent = (struct table_entry *)
1836130281Sru	    rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
1837130281Sru	if (ent != NULL)
1838130281Sru		free(ent, M_IPFW_TBL);
1839130281Sru	return (0);
1840130281Sru}
1841130281Sru
1842130281Srustatic int
1843153163Sglebiusflush_table(struct ip_fw_chain *ch, uint16_t tbl)
1844130281Sru{
1845130281Sru	struct radix_node_head *rnh;
1846130281Sru
1847153163Sglebius	IPFW_WLOCK_ASSERT(ch);
1848153163Sglebius
1849130281Sru	if (tbl >= IPFW_TABLES_MAX)
1850130281Sru		return (EINVAL);
1851153163Sglebius	rnh = ch->tables[tbl];
1852154567Scsjp	KASSERT(rnh != NULL, ("NULL IPFW table"));
1853130281Sru	rnh->rnh_walktree(rnh, flush_table_entry, rnh);
1854130281Sru	return (0);
1855130281Sru}
1856130281Sru
1857130281Srustatic void
1858153163Sglebiusflush_tables(struct ip_fw_chain *ch)
1859130281Sru{
1860153163Sglebius	uint16_t tbl;
1861130281Sru
1862153163Sglebius	IPFW_WLOCK_ASSERT(ch);
1863153163Sglebius
1864130281Sru	for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++)
1865153163Sglebius		flush_table(ch, tbl);
1866130281Sru}
1867130281Sru
1868130281Srustatic int
1869154567Scsjpinit_tables(struct ip_fw_chain *ch)
1870154567Scsjp{
1871154567Scsjp	int i;
1872154567Scsjp	uint16_t j;
1873154567Scsjp
1874154567Scsjp	for (i = 0; i < IPFW_TABLES_MAX; i++) {
1875154567Scsjp		if (!rn_inithead((void **)&ch->tables[i], 32)) {
1876154567Scsjp			for (j = 0; j < i; j++) {
1877154567Scsjp				(void) flush_table(ch, j);
1878154567Scsjp			}
1879154567Scsjp			return (ENOMEM);
1880154567Scsjp		}
1881154567Scsjp	}
1882154567Scsjp	return (0);
1883154567Scsjp}
1884154567Scsjp
1885154567Scsjpstatic int
1886153163Sglebiuslookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
1887169454Srwatson    uint32_t *val)
1888130281Sru{
1889130281Sru	struct radix_node_head *rnh;
1890130281Sru	struct table_entry *ent;
1891130281Sru	struct sockaddr_in sa;
1892130281Sru
1893130281Sru	if (tbl >= IPFW_TABLES_MAX)
1894130281Sru		return (0);
1895153163Sglebius	rnh = ch->tables[tbl];
1896130281Sru	sa.sin_len = 8;
1897130281Sru	sa.sin_addr.s_addr = addr;
1898130281Sru	ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
1899130281Sru	if (ent != NULL) {
1900153163Sglebius		*val = ent->value;
1901130281Sru		return (1);
1902130281Sru	}
1903130281Sru	return (0);
1904130281Sru}
1905130281Sru
1906130281Srustatic int
1907130281Srucount_table_entry(struct radix_node *rn, void *arg)
1908130281Sru{
1909130281Sru	u_int32_t * const cnt = arg;
1910130281Sru
1911130281Sru	(*cnt)++;
1912130281Sru	return (0);
1913130281Sru}
1914130281Sru
1915130281Srustatic int
1916153163Sglebiuscount_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt)
1917130281Sru{
1918130281Sru	struct radix_node_head *rnh;
1919130281Sru
1920130281Sru	if (tbl >= IPFW_TABLES_MAX)
1921130281Sru		return (EINVAL);
1922153163Sglebius	rnh = ch->tables[tbl];
1923130281Sru	*cnt = 0;
1924130281Sru	rnh->rnh_walktree(rnh, count_table_entry, cnt);
1925130281Sru	return (0);
1926130281Sru}
1927130281Sru
1928130281Srustatic int
1929130281Srudump_table_entry(struct radix_node *rn, void *arg)
1930130281Sru{
1931130281Sru	struct table_entry * const n = (struct table_entry *)rn;
1932130281Sru	ipfw_table * const tbl = arg;
1933130281Sru	ipfw_table_entry *ent;
1934130281Sru
1935130281Sru	if (tbl->cnt == tbl->size)
1936130281Sru		return (1);
1937130281Sru	ent = &tbl->ent[tbl->cnt];
1938130281Sru	ent->tbl = tbl->tbl;
1939130281Sru	if (in_nullhost(n->mask.sin_addr))
1940130281Sru		ent->masklen = 0;
1941130281Sru	else
1942130281Sru		ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
1943130281Sru	ent->addr = n->addr.sin_addr.s_addr;
1944130281Sru	ent->value = n->value;
1945130281Sru	tbl->cnt++;
1946130281Sru	return (0);
1947130281Sru}
1948130281Sru
1949130281Srustatic int
1950153163Sglebiusdump_table(struct ip_fw_chain *ch, ipfw_table *tbl)
1951130281Sru{
1952130281Sru	struct radix_node_head *rnh;
1953130281Sru
1954130281Sru	if (tbl->tbl >= IPFW_TABLES_MAX)
1955130281Sru		return (EINVAL);
1956153163Sglebius	rnh = ch->tables[tbl->tbl];
1957130281Sru	tbl->cnt = 0;
1958130281Sru	rnh->rnh_walktree(rnh, dump_table_entry, tbl);
1959130281Sru	return (0);
1960130281Sru}
1961130281Sru
1962135920Smlaierstatic void
1963135920Smlaierfill_ugid_cache(struct inpcb *inp, struct ip_fw_ugid *ugp)
1964135920Smlaier{
1965135920Smlaier	struct ucred *cr;
1966135920Smlaier
1967135920Smlaier	if (inp->inp_socket != NULL) {
1968135920Smlaier		cr = inp->inp_socket->so_cred;
1969135920Smlaier		ugp->fw_prid = jailed(cr) ?
1970135920Smlaier		    cr->cr_prison->pr_id : -1;
1971135920Smlaier		ugp->fw_uid = cr->cr_uid;
1972135920Smlaier		ugp->fw_ngroups = cr->cr_ngroups;
1973135920Smlaier		bcopy(cr->cr_groups, ugp->fw_groups,
1974135920Smlaier		    sizeof(ugp->fw_groups));
1975135920Smlaier	}
1976135920Smlaier}
1977135920Smlaier
1978130281Srustatic int
1979169454Srwatsoncheck_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif,
1980169454Srwatson    struct in_addr dst_ip, u_int16_t dst_port, struct in_addr src_ip,
1981169454Srwatson    u_int16_t src_port, struct ip_fw_ugid *ugp, int *lookup,
1982169454Srwatson    struct inpcb *inp)
1983122265Ssam{
1984122265Ssam	struct inpcbinfo *pi;
1985122265Ssam	int wildcard;
1986122265Ssam	struct inpcb *pcb;
1987122265Ssam	int match;
1988130363Scsjp	gid_t *gp;
1989122265Ssam
1990130363Scsjp	/*
1991135920Smlaier	 * Check to see if the UDP or TCP stack supplied us with
1992135920Smlaier	 * the PCB. If so, rather then holding a lock and looking
1993135920Smlaier	 * up the PCB, we can use the one that was supplied.
1994135920Smlaier	 */
1995135920Smlaier	if (inp && *lookup == 0) {
1996135920Smlaier		INP_LOCK_ASSERT(inp);
1997135920Smlaier		if (inp->inp_socket != NULL) {
1998135920Smlaier			fill_ugid_cache(inp, ugp);
1999135920Smlaier			*lookup = 1;
2000135920Smlaier		}
2001135920Smlaier	}
2002135920Smlaier	/*
2003130363Scsjp	 * If we have already been here and the packet has no
2004130363Scsjp	 * PCB entry associated with it, then we can safely
2005130363Scsjp	 * assume that this is a no match.
2006130363Scsjp	 */
2007130363Scsjp	if (*lookup == -1)
2008130363Scsjp		return (0);
2009122265Ssam	if (proto == IPPROTO_TCP) {
2010122265Ssam		wildcard = 0;
2011122265Ssam		pi = &tcbinfo;
2012122265Ssam	} else if (proto == IPPROTO_UDP) {
2013160024Sbz		wildcard = INPLOOKUP_WILDCARD;
2014122265Ssam		pi = &udbinfo;
2015122265Ssam	} else
2016122265Ssam		return 0;
2017122265Ssam	match = 0;
2018130363Scsjp	if (*lookup == 0) {
2019135920Smlaier		INP_INFO_RLOCK(pi);
2020130363Scsjp		pcb =  (oif) ?
2021130363Scsjp			in_pcblookup_hash(pi,
2022130363Scsjp				dst_ip, htons(dst_port),
2023130363Scsjp				src_ip, htons(src_port),
2024130363Scsjp				wildcard, oif) :
2025130363Scsjp			in_pcblookup_hash(pi,
2026130363Scsjp				src_ip, htons(src_port),
2027130363Scsjp				dst_ip, htons(dst_port),
2028130363Scsjp				wildcard, NULL);
2029130363Scsjp		if (pcb != NULL) {
2030130363Scsjp			INP_LOCK(pcb);
2031130363Scsjp			if (pcb->inp_socket != NULL) {
2032135920Smlaier				fill_ugid_cache(pcb, ugp);
2033130363Scsjp				*lookup = 1;
2034122265Ssam			}
2035130363Scsjp			INP_UNLOCK(pcb);
2036122265Ssam		}
2037130363Scsjp		INP_INFO_RUNLOCK(pi);
2038130363Scsjp		if (*lookup == 0) {
2039130363Scsjp			/*
2040130363Scsjp			 * If the lookup did not yield any results, there
2041130363Scsjp			 * is no sense in coming back and trying again. So
2042130363Scsjp			 * we can set lookup to -1 and ensure that we wont
2043130363Scsjp			 * bother the pcb system again.
2044130363Scsjp			 */
2045130363Scsjp			*lookup = -1;
2046130363Scsjp			return (0);
2047130363Scsjp		}
2048130363Scsjp	}
2049130363Scsjp	if (insn->o.opcode == O_UID)
2050130363Scsjp		match = (ugp->fw_uid == (uid_t)insn->d[0]);
2051133600Scsjp	else if (insn->o.opcode == O_GID) {
2052130363Scsjp		for (gp = ugp->fw_groups;
2053130363Scsjp			gp < &ugp->fw_groups[ugp->fw_ngroups]; gp++)
2054130363Scsjp			if (*gp == (gid_t)insn->d[0]) {
2055130363Scsjp				match = 1;
2056130363Scsjp				break;
2057130363Scsjp			}
2058133600Scsjp	} else if (insn->o.opcode == O_JAIL)
2059133600Scsjp		match = (ugp->fw_prid == (int)insn->d[0]);
2060122265Ssam	return match;
2061122265Ssam}
2062122265Ssam
2063165750Spiso#ifdef IPFIREWALL_NAT
2064165648Spisostatic eventhandler_tag ifaddr_event_tag;
2065165648Spiso
2066165648Spisostatic void
2067169454Srwatsonifaddr_change(void *arg __unused, struct ifnet *ifp)
2068169454Srwatson{
2069165648Spiso	struct cfg_nat *ptr;
2070165648Spiso	struct ifaddr *ifa;
2071165648Spiso
2072165648Spiso	IPFW_WLOCK(&layer3_chain);
2073165648Spiso	/* Check every nat entry... */
2074165648Spiso	LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
2075165648Spiso		/* ...using nic 'ifp->if_xname' as dynamic alias address. */
2076165648Spiso		if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) == 0) {
2077165648Spiso			mtx_lock(&ifp->if_addr_mtx);
2078165648Spiso			TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
2079165648Spiso				if (ifa->ifa_addr == NULL)
2080165648Spiso					continue;
2081165648Spiso				if (ifa->ifa_addr->sa_family != AF_INET)
2082165648Spiso					continue;
2083165648Spiso				ptr->ip = ((struct sockaddr_in *)
2084165648Spiso				    (ifa->ifa_addr))->sin_addr;
2085165648Spiso				LibAliasSetAddress(ptr->lib, ptr->ip);
2086165648Spiso			}
2087165648Spiso			mtx_unlock(&ifp->if_addr_mtx);
2088165648Spiso		}
2089165648Spiso	}
2090165648Spiso	IPFW_WUNLOCK(&layer3_chain);
2091165648Spiso}
2092165648Spiso
2093165648Spisostatic void
2094169454Srwatsonflush_nat_ptrs(const int i)
2095169454Srwatson{
2096165648Spiso	struct ip_fw *rule;
2097165648Spiso
2098165648Spiso	IPFW_WLOCK_ASSERT(&layer3_chain);
2099165648Spiso	for (rule = layer3_chain.rules; rule; rule = rule->next) {
2100165648Spiso		ipfw_insn_nat *cmd = (ipfw_insn_nat *)ACTION_PTR(rule);
2101165648Spiso		if (cmd->o.opcode != O_NAT)
2102165648Spiso			continue;
2103165648Spiso		if (cmd->nat != NULL && cmd->nat->id == i)
2104165648Spiso			cmd->nat = NULL;
2105165648Spiso	}
2106165648Spiso}
2107165648Spiso
2108165648Spisostatic struct cfg_nat *
2109169454Srwatsonlookup_nat(const int i)
2110169454Srwatson{
2111165648Spiso	struct cfg_nat *ptr;
2112165648Spiso
2113165648Spiso	LIST_FOREACH(ptr, &layer3_chain.nat, _next)
2114165648Spiso		if (ptr->id == i)
2115165648Spiso			return(ptr);
2116165648Spiso	return (NULL);
2117165648Spiso}
2118165648Spiso
2119165648Spiso#define HOOK_NAT(b, p) do {                                     \
2120165648Spiso	IPFW_WLOCK_ASSERT(&layer3_chain);                       \
2121165648Spiso        LIST_INSERT_HEAD(b, p, _next);                          \
2122165648Spiso} while (0)
2123165648Spiso
2124165648Spiso#define UNHOOK_NAT(p) do {                                      \
2125165648Spiso	IPFW_WLOCK_ASSERT(&layer3_chain);                       \
2126165648Spiso        LIST_REMOVE(p, _next);                                  \
2127165648Spiso} while (0)
2128165648Spiso
2129165648Spiso#define HOOK_REDIR(b, p) do {                                   \
2130165648Spiso        LIST_INSERT_HEAD(b, p, _next);                          \
2131165648Spiso} while (0)
2132165648Spiso
2133165648Spiso#define HOOK_SPOOL(b, p) do {                                   \
2134165648Spiso        LIST_INSERT_HEAD(b, p, _next);                          \
2135165648Spiso} while (0)
2136165648Spiso
2137165648Spisostatic void
2138169454Srwatsondel_redir_spool_cfg(struct cfg_nat *n, struct redir_chain *head)
2139169454Srwatson{
2140165648Spiso	struct cfg_redir *r, *tmp_r;
2141165648Spiso	struct cfg_spool *s, *tmp_s;
2142165648Spiso	int i, num;
2143165648Spiso
2144165648Spiso	LIST_FOREACH_SAFE(r, head, _next, tmp_r) {
2145165648Spiso		num = 1; /* Number of alias_link to delete. */
2146165648Spiso		switch (r->mode) {
2147165648Spiso		case REDIR_PORT:
2148165648Spiso			num = r->pport_cnt;
2149165648Spiso			/* FALLTHROUGH */
2150165648Spiso		case REDIR_ADDR:
2151165648Spiso		case REDIR_PROTO:
2152165648Spiso			/* Delete all libalias redirect entry. */
2153165648Spiso			for (i = 0; i < num; i++)
2154165648Spiso				LibAliasRedirectDelete(n->lib, r->alink[i]);
2155165648Spiso			/* Del spool cfg if any. */
2156165648Spiso			LIST_FOREACH_SAFE(s, &r->spool_chain, _next, tmp_s) {
2157165648Spiso				LIST_REMOVE(s, _next);
2158165648Spiso				free(s, M_IPFW);
2159165648Spiso			}
2160165648Spiso			free(r->alink, M_IPFW);
2161165648Spiso			LIST_REMOVE(r, _next);
2162165648Spiso			free(r, M_IPFW);
2163165648Spiso			break;
2164165648Spiso		default:
2165165648Spiso			printf("unknown redirect mode: %u\n", r->mode);
2166165648Spiso			/* XXX - panic?!?!? */
2167165648Spiso			break;
2168165648Spiso		}
2169165648Spiso	}
2170165648Spiso}
2171165648Spiso
2172165648Spisostatic int
2173169454Srwatsonadd_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
2174169454Srwatson{
2175165648Spiso	struct cfg_redir *r, *ser_r;
2176165648Spiso	struct cfg_spool *s, *ser_s;
2177165648Spiso	int cnt, off, i;
2178165648Spiso	char *panic_err;
2179165648Spiso
2180165648Spiso	for (cnt = 0, off = 0; cnt < ptr->redir_cnt; cnt++) {
2181165648Spiso		ser_r = (struct cfg_redir *)&buf[off];
2182165648Spiso		r = malloc(SOF_REDIR, M_IPFW, M_WAITOK | M_ZERO);
2183165648Spiso		memcpy(r, ser_r, SOF_REDIR);
2184165648Spiso		LIST_INIT(&r->spool_chain);
2185165648Spiso		off += SOF_REDIR;
2186165648Spiso		r->alink = malloc(sizeof(struct alias_link *) * r->pport_cnt,
2187165648Spiso		    M_IPFW, M_WAITOK | M_ZERO);
2188165648Spiso		switch (r->mode) {
2189165648Spiso		case REDIR_ADDR:
2190165648Spiso			r->alink[0] = LibAliasRedirectAddr(ptr->lib, r->laddr,
2191165648Spiso			    r->paddr);
2192165648Spiso			break;
2193165648Spiso		case REDIR_PORT:
2194165648Spiso			for (i = 0 ; i < r->pport_cnt; i++) {
2195165648Spiso				/* If remotePort is all ports, set it to 0. */
2196165648Spiso				u_short remotePortCopy = r->rport + i;
2197165648Spiso				if (r->rport_cnt == 1 && r->rport == 0)
2198165648Spiso					remotePortCopy = 0;
2199165648Spiso				r->alink[i] = LibAliasRedirectPort(ptr->lib,
2200165648Spiso				    r->laddr, htons(r->lport + i), r->raddr,
2201165648Spiso				    htons(remotePortCopy), r->paddr,
2202165648Spiso				    htons(r->pport + i), r->proto);
2203165648Spiso				if (r->alink[i] == NULL) {
2204165648Spiso					r->alink[0] = NULL;
2205165648Spiso					break;
2206165648Spiso				}
2207165648Spiso			}
2208165648Spiso			break;
2209165648Spiso		case REDIR_PROTO:
2210165648Spiso			r->alink[0] = LibAliasRedirectProto(ptr->lib ,r->laddr,
2211165648Spiso			    r->raddr, r->paddr, r->proto);
2212165648Spiso			break;
2213165648Spiso		default:
2214165648Spiso			printf("unknown redirect mode: %u\n", r->mode);
2215165648Spiso			break;
2216165648Spiso		}
2217165648Spiso		if (r->alink[0] == NULL) {
2218165648Spiso			panic_err = "LibAliasRedirect* returned NULL";
2219165648Spiso			goto bad;
2220165648Spiso		} else /* LSNAT handling. */
2221165648Spiso			for (i = 0; i < r->spool_cnt; i++) {
2222165648Spiso				ser_s = (struct cfg_spool *)&buf[off];
2223165648Spiso				s = malloc(SOF_REDIR, M_IPFW,
2224165648Spiso				    M_WAITOK | M_ZERO);
2225165648Spiso				memcpy(s, ser_s, SOF_SPOOL);
2226165648Spiso				LibAliasAddServer(ptr->lib, r->alink[0],
2227165648Spiso				    s->addr, htons(s->port));
2228165648Spiso				off += SOF_SPOOL;
2229165648Spiso				/* Hook spool entry. */
2230165648Spiso				HOOK_SPOOL(&r->spool_chain, s);
2231165648Spiso			}
2232165648Spiso		/* And finally hook this redir entry. */
2233165648Spiso		HOOK_REDIR(&ptr->redir_chain, r);
2234165648Spiso	}
2235165648Spiso	return (1);
2236165648Spisobad:
2237165648Spiso	/* something really bad happened: panic! */
2238165648Spiso	panic("%s\n", panic_err);
2239165648Spiso}
2240165750Spiso#endif
2241165648Spiso
224298943Sluigi/*
224398943Sluigi * The main check routine for the firewall.
224498943Sluigi *
224598943Sluigi * All arguments are in args so we can modify them and return them
224698943Sluigi * back to the caller.
224798943Sluigi *
224898943Sluigi * Parameters:
224998943Sluigi *
225098943Sluigi *	args->m	(in/out) The packet; we set to NULL when/if we nuke it.
225198943Sluigi *		Starts with the IP header.
225298943Sluigi *	args->eh (in)	Mac header if present, or NULL for layer3 packet.
2253165738Sjulian *	args->L3offset	Number of bytes bypassed if we came from L2.
2254165738Sjulian *			e.g. often sizeof(eh)  ** NOTYET **
225598943Sluigi *	args->oif	Outgoing interface, or NULL if packet is incoming.
225698943Sluigi *		The incoming interface is in the mbuf. (in)
225798943Sluigi *	args->divert_rule (in/out)
225898943Sluigi *		Skip up to the first rule past this rule number;
225998943Sluigi *		upon return, non-zero port number for divert or tee.
226098943Sluigi *
226198943Sluigi *	args->rule	Pointer to the last matching rule (in/out)
226298943Sluigi *	args->next_hop	Socket we are forwarding to (out).
226398943Sluigi *	args->f_id	Addresses grabbed from the packet (out)
2264140224Sglebius * 	args->cookie	a cookie depending on rule action
226598943Sluigi *
226698943Sluigi * Return value:
226798943Sluigi *
2268140224Sglebius *	IP_FW_PASS	the packet must be accepted
2269140224Sglebius *	IP_FW_DENY	the packet must be dropped
2270140224Sglebius *	IP_FW_DIVERT	divert packet, port in m_tag
2271140224Sglebius *	IP_FW_TEE	tee packet, port in m_tag
2272140224Sglebius *	IP_FW_DUMMYNET	to dummynet, pipe in args->cookie
2273140224Sglebius *	IP_FW_NETGRAPH	into netgraph, cookie args->cookie
227498943Sluigi *
227598943Sluigi */
2276133920Sandreint
227798943Sluigiipfw_chk(struct ip_fw_args *args)
227898943Sluigi{
227998943Sluigi	/*
2280165738Sjulian	 * Local variables holding state during the processing of a packet:
228198943Sluigi	 *
228298943Sluigi	 * IMPORTANT NOTE: to speed up the processing of rules, there
228398943Sluigi	 * are some assumption on the values of the variables, which
228498943Sluigi	 * are documented here. Should you change them, please check
228598943Sluigi	 * the implementation of the various instructions to make sure
228698943Sluigi	 * that they still work.
2287101978Sluigi	 *
228898943Sluigi	 * args->eh	The MAC header. It is non-null for a layer2
228998943Sluigi	 *	packet, it is NULL for a layer-3 packet.
2290165738Sjulian	 * **notyet**
2291165738Sjulian	 * args->L3offset Offset in the packet to the L3 (IP or equiv.) header.
229298943Sluigi	 *
229398943Sluigi	 * m | args->m	Pointer to the mbuf, as received from the caller.
229498943Sluigi	 *	It may change if ipfw_chk() does an m_pullup, or if it
229598943Sluigi	 *	consumes the packet because it calls send_reject().
229698943Sluigi	 *	XXX This has to change, so that ipfw_chk() never modifies
229798943Sluigi	 *	or consumes the buffer.
2298165738Sjulian	 * ip	is the beginning of the ip(4 or 6) header.
2299165738Sjulian	 *	Calculated by adding the L3offset to the start of data.
2300165738Sjulian	 *	(Until we start using L3offset, the packet is
2301165738Sjulian	 *	supposed to start with the ip header).
230298943Sluigi	 */
230398943Sluigi	struct mbuf *m = args->m;
230498943Sluigi	struct ip *ip = mtod(m, struct ip *);
230598943Sluigi
230698943Sluigi	/*
2307130363Scsjp	 * For rules which contain uid/gid or jail constraints, cache
2308130363Scsjp	 * a copy of the users credentials after the pcb lookup has been
2309130363Scsjp	 * executed. This will speed up the processing of rules with
2310130363Scsjp	 * these types of constraints, as well as decrease contention
2311130363Scsjp	 * on pcb related locks.
2312130363Scsjp	 */
2313130363Scsjp	struct ip_fw_ugid fw_ugid_cache;
2314130363Scsjp	int ugid_lookup = 0;
2315130363Scsjp
2316130363Scsjp	/*
2317136073Sgreen	 * divinput_flags	If non-zero, set to the IP_FW_DIVERT_*_FLAG
2318136073Sgreen	 *	associated with a packet input on a divert socket.  This
2319136073Sgreen	 *	will allow to distinguish traffic and its direction when
2320136073Sgreen	 *	it originates from a divert socket.
2321136073Sgreen	 */
2322136073Sgreen	u_int divinput_flags = 0;
2323136073Sgreen
2324136073Sgreen	/*
232598943Sluigi	 * oif | args->oif	If NULL, ipfw_chk has been called on the
2326150636Smlaier	 *	inbound path (ether_input, ip_input).
232798943Sluigi	 *	If non-NULL, ipfw_chk has been called on the outbound path
232898943Sluigi	 *	(ether_output, ip_output).
232998943Sluigi	 */
233098943Sluigi	struct ifnet *oif = args->oif;
233198943Sluigi
233298943Sluigi	struct ip_fw *f = NULL;		/* matching rule */
233398943Sluigi	int retval = 0;
233498943Sluigi
233598943Sluigi	/*
2336147758Smlaier	 * hlen	The length of the IP header.
233798943Sluigi	 */
233898943Sluigi	u_int hlen = 0;		/* hlen >0 means we have an IP pkt */
233998943Sluigi
234098943Sluigi	/*
234198943Sluigi	 * offset	The offset of a fragment. offset != 0 means that
234298943Sluigi	 *	we have a fragment at this offset of an IPv4 packet.
234398943Sluigi	 *	offset == 0 means that (if this is an IPv4 packet)
234498943Sluigi	 *	this is the first or only fragment.
2345149020Sbz	 *	For IPv6 offset == 0 means there is no Fragment Header.
2346149020Sbz	 *	If offset != 0 for IPv6 always use correct mask to
2347149020Sbz	 *	get the correct offset because we add IP6F_MORE_FRAG
2348149020Sbz	 *	to be able to dectect the first fragment which would
2349149020Sbz	 *	otherwise have offset = 0.
235098943Sluigi	 */
235198943Sluigi	u_short offset = 0;
235298943Sluigi
235398943Sluigi	/*
235498943Sluigi	 * Local copies of addresses. They are only valid if we have
235598943Sluigi	 * an IP packet.
235698943Sluigi	 *
235798943Sluigi	 * proto	The protocol. Set to 0 for non-ip packets,
235898943Sluigi	 *	or to the protocol read from the packet otherwise.
235998943Sluigi	 *	proto != 0 means that we have an IPv4 packet.
236098943Sluigi	 *
236198943Sluigi	 * src_port, dst_port	port numbers, in HOST format. Only
236298943Sluigi	 *	valid for TCP and UDP packets.
236398943Sluigi	 *
236498943Sluigi	 * src_ip, dst_ip	ip addresses, in NETWORK format.
236598943Sluigi	 *	Only valid for IPv4 packets.
236698943Sluigi	 */
236798943Sluigi	u_int8_t proto;
236898943Sluigi	u_int16_t src_port = 0, dst_port = 0;	/* NOTE: host format	*/
236998943Sluigi	struct in_addr src_ip, dst_ip;		/* NOTE: network format	*/
237098943Sluigi	u_int16_t ip_len=0;
2371115750Skbyanc	int pktlen;
2372165738Sjulian	u_int16_t	etype = 0;	/* Host order stored ether type */
2373145093Sbrooks
2374145093Sbrooks	/*
2375145093Sbrooks	 * dyn_dir = MATCH_UNKNOWN when rules unchecked,
2376145093Sbrooks	 * 	MATCH_NONE when checked and not matched (q = NULL),
2377145093Sbrooks	 *	MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL)
2378145093Sbrooks	 */
237998943Sluigi	int dyn_dir = MATCH_UNKNOWN;
238098943Sluigi	ipfw_dyn_rule *q = NULL;
2381120141Ssam	struct ip_fw_chain *chain = &layer3_chain;
2382126239Smlaier	struct m_tag *mtag;
238398943Sluigi
238498943Sluigi	/*
2385145093Sbrooks	 * We store in ulp a pointer to the upper layer protocol header.
2386145093Sbrooks	 * In the ipv4 case this is easy to determine from the header,
2387145093Sbrooks	 * but for ipv6 we might have some additional headers in the middle.
2388145093Sbrooks	 * ulp is NULL if not found.
238998943Sluigi	 */
2390145093Sbrooks	void *ulp = NULL;		/* upper layer protocol pointer. */
2391145246Sbrooks	/* XXX ipv6 variables */
2392145246Sbrooks	int is_ipv6 = 0;
2393145246Sbrooks	u_int16_t ext_hd = 0;	/* bits vector for extension header filtering */
2394145246Sbrooks	/* end of ipv6 variables */
2395146894Smlaier	int is_ipv4 = 0;
239698943Sluigi
2397145093Sbrooks	if (m->m_flags & M_SKIP_FIREWALL)
2398145093Sbrooks		return (IP_FW_PASS);	/* accept */
2399145093Sbrooks
2400115750Skbyanc	pktlen = m->m_pkthdr.len;
2401145093Sbrooks	proto = args->f_id.proto = 0;	/* mark f_id invalid */
2402149020Sbz		/* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */
240398943Sluigi
2404145093Sbrooks/*
2405145093Sbrooks * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous,
2406145093Sbrooks * then it sets p to point at the offset "len" in the mbuf. WARNING: the
2407145093Sbrooks * pointer might become stale after other pullups (but we never use it
2408145093Sbrooks * this way).
2409145093Sbrooks */
2410145093Sbrooks#define PULLUP_TO(len, p, T)						\
2411145093Sbrooksdo {									\
2412145093Sbrooks	int x = (len) + sizeof(T);					\
2413145093Sbrooks	if ((m)->m_len < x) {						\
2414145093Sbrooks		args->m = m = m_pullup(m, x);				\
2415145093Sbrooks		if (m == NULL)						\
2416145093Sbrooks			goto pullup_failed;				\
2417145093Sbrooks	}								\
2418145093Sbrooks	p = (mtod(m, char *) + (len));					\
2419145093Sbrooks} while (0)
242098943Sluigi
2421165738Sjulian	/*
2422165738Sjulian	 * if we have an ether header,
2423165738Sjulian	 */
2424165738Sjulian	if (args->eh)
2425165738Sjulian		etype = ntohs(args->eh->ether_type);
2426165738Sjulian
2427145246Sbrooks	/* Identify IP packets and fill up variables. */
2428145246Sbrooks	if (pktlen >= sizeof(struct ip6_hdr) &&
2429165738Sjulian	    (args->eh == NULL || etype == ETHERTYPE_IPV6) && ip->ip_v == 6) {
2430165738Sjulian		struct ip6_hdr *ip6 = (struct ip6_hdr *)ip;
2431145246Sbrooks		is_ipv6 = 1;
2432145246Sbrooks		args->f_id.addr_type = 6;
2433145246Sbrooks		hlen = sizeof(struct ip6_hdr);
2434165738Sjulian		proto = ip6->ip6_nxt;
2435145246Sbrooks
2436145246Sbrooks		/* Search extension headers to find upper layer protocols */
2437145246Sbrooks		while (ulp == NULL) {
2438145246Sbrooks			switch (proto) {
2439145246Sbrooks			case IPPROTO_ICMPV6:
2440145246Sbrooks				PULLUP_TO(hlen, ulp, struct icmp6_hdr);
2441145246Sbrooks				args->f_id.flags = ICMP6(ulp)->icmp6_type;
2442145246Sbrooks				break;
2443145246Sbrooks
2444145246Sbrooks			case IPPROTO_TCP:
2445145246Sbrooks				PULLUP_TO(hlen, ulp, struct tcphdr);
2446145246Sbrooks				dst_port = TCP(ulp)->th_dport;
2447145246Sbrooks				src_port = TCP(ulp)->th_sport;
2448145246Sbrooks				args->f_id.flags = TCP(ulp)->th_flags;
2449145246Sbrooks				break;
2450145246Sbrooks
2451164258Sbz			case IPPROTO_SCTP:
2452164258Sbz				PULLUP_TO(hlen, ulp, struct sctphdr);
2453164258Sbz				src_port = SCTP(ulp)->src_port;
2454164258Sbz				dst_port = SCTP(ulp)->dest_port;
2455164258Sbz				break;
2456164258Sbz
2457145246Sbrooks			case IPPROTO_UDP:
2458145246Sbrooks				PULLUP_TO(hlen, ulp, struct udphdr);
2459145246Sbrooks				dst_port = UDP(ulp)->uh_dport;
2460145246Sbrooks				src_port = UDP(ulp)->uh_sport;
2461145246Sbrooks				break;
2462145246Sbrooks
2463149020Sbz			case IPPROTO_HOPOPTS:	/* RFC 2460 */
2464145246Sbrooks				PULLUP_TO(hlen, ulp, struct ip6_hbh);
2465145246Sbrooks				ext_hd |= EXT_HOPOPTS;
2466149020Sbz				hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3;
2467145246Sbrooks				proto = ((struct ip6_hbh *)ulp)->ip6h_nxt;
2468145246Sbrooks				ulp = NULL;
2469145246Sbrooks				break;
2470145246Sbrooks
2471149020Sbz			case IPPROTO_ROUTING:	/* RFC 2460 */
2472145246Sbrooks				PULLUP_TO(hlen, ulp, struct ip6_rthdr);
2473159857Sume				switch (((struct ip6_rthdr *)ulp)->ip6r_type) {
2474159857Sume				case 0:
2475169245Sbz					ext_hd |= EXT_RTHDR0;
2476159857Sume					break;
2477169245Sbz				case 2:
2478169245Sbz					ext_hd |= EXT_RTHDR2;
2479169245Sbz					break;
2480159857Sume				default:
2481149020Sbz					printf("IPFW2: IPV6 - Unknown Routing "
2482149020Sbz					    "Header type(%d)\n",
2483149020Sbz					    ((struct ip6_rthdr *)ulp)->ip6r_type);
2484149020Sbz					if (fw_deny_unknown_exthdrs)
2485149020Sbz					    return (IP_FW_DENY);
2486149020Sbz					break;
2487149020Sbz				}
2488145246Sbrooks				ext_hd |= EXT_ROUTING;
2489149020Sbz				hlen += (((struct ip6_rthdr *)ulp)->ip6r_len + 1) << 3;
2490145246Sbrooks				proto = ((struct ip6_rthdr *)ulp)->ip6r_nxt;
2491145246Sbrooks				ulp = NULL;
2492145246Sbrooks				break;
2493145246Sbrooks
2494149020Sbz			case IPPROTO_FRAGMENT:	/* RFC 2460 */
2495145246Sbrooks				PULLUP_TO(hlen, ulp, struct ip6_frag);
2496145246Sbrooks				ext_hd |= EXT_FRAGMENT;
2497145246Sbrooks				hlen += sizeof (struct ip6_frag);
2498145246Sbrooks				proto = ((struct ip6_frag *)ulp)->ip6f_nxt;
2499149020Sbz				offset = ((struct ip6_frag *)ulp)->ip6f_offlg &
2500149020Sbz					IP6F_OFF_MASK;
2501149020Sbz				/* Add IP6F_MORE_FRAG for offset of first
2502149020Sbz				 * fragment to be != 0. */
2503149020Sbz				offset |= ((struct ip6_frag *)ulp)->ip6f_offlg &
2504149020Sbz					IP6F_MORE_FRAG;
2505149020Sbz				if (offset == 0) {
2506149020Sbz					printf("IPFW2: IPV6 - Invalid Fragment "
2507149020Sbz					    "Header\n");
2508149020Sbz					if (fw_deny_unknown_exthdrs)
2509149020Sbz					    return (IP_FW_DENY);
2510149020Sbz					break;
2511149020Sbz				}
2512149020Sbz				args->f_id.frag_id6 =
2513149020Sbz				    ntohl(((struct ip6_frag *)ulp)->ip6f_ident);
2514149020Sbz				ulp = NULL;
2515145246Sbrooks				break;
2516145246Sbrooks
2517149020Sbz			case IPPROTO_DSTOPTS:	/* RFC 2460 */
2518149020Sbz				PULLUP_TO(hlen, ulp, struct ip6_hbh);
2519149020Sbz				ext_hd |= EXT_DSTOPTS;
2520149020Sbz				hlen += (((struct ip6_hbh *)ulp)->ip6h_len + 1) << 3;
2521149020Sbz				proto = ((struct ip6_hbh *)ulp)->ip6h_nxt;
2522149020Sbz				ulp = NULL;
2523149020Sbz				break;
2524149020Sbz
2525149020Sbz			case IPPROTO_AH:	/* RFC 2402 */
2526145246Sbrooks				PULLUP_TO(hlen, ulp, struct ip6_ext);
2527145246Sbrooks				ext_hd |= EXT_AH;
2528149020Sbz				hlen += (((struct ip6_ext *)ulp)->ip6e_len + 2) << 2;
2529145246Sbrooks				proto = ((struct ip6_ext *)ulp)->ip6e_nxt;
2530145246Sbrooks				ulp = NULL;
2531145246Sbrooks				break;
2532145246Sbrooks
2533149020Sbz			case IPPROTO_ESP:	/* RFC 2406 */
2534149020Sbz				PULLUP_TO(hlen, ulp, uint32_t);	/* SPI, Seq# */
2535149020Sbz				/* Anything past Seq# is variable length and
2536149020Sbz				 * data past this ext. header is encrypted. */
2537149020Sbz				ext_hd |= EXT_ESP;
2538149020Sbz				break;
2539149020Sbz
2540149020Sbz			case IPPROTO_NONE:	/* RFC 2460 */
2541174479Sdwmalone				/*
2542174479Sdwmalone				 * Packet ends here, and IPv6 header has
2543174479Sdwmalone				 * already been pulled up. If ip6e_len!=0
2544174479Sdwmalone				 * then octets must be ignored.
2545174479Sdwmalone				 */
2546174479Sdwmalone				ulp = ip; /* non-NULL to get out of loop. */
2547149020Sbz				break;
2548149020Sbz
2549146704Stanimura			case IPPROTO_OSPFIGP:
2550146704Stanimura				/* XXX OSPF header check? */
2551146704Stanimura				PULLUP_TO(hlen, ulp, struct ip6_ext);
2552146704Stanimura				break;
2553146704Stanimura
2554161767Sjhay			case IPPROTO_PIM:
2555161767Sjhay				/* XXX PIM header check? */
2556161767Sjhay				PULLUP_TO(hlen, ulp, struct pim);
2557161767Sjhay				break;
2558161767Sjhay
2559163069Sbz			case IPPROTO_CARP:
2560163069Sbz				PULLUP_TO(hlen, ulp, struct carp_header);
2561163069Sbz				if (((struct carp_header *)ulp)->carp_version !=
2562163069Sbz				    CARP_VERSION)
2563163069Sbz					return (IP_FW_DENY);
2564163069Sbz				if (((struct carp_header *)ulp)->carp_type !=
2565163069Sbz				    CARP_ADVERTISEMENT)
2566163069Sbz					return (IP_FW_DENY);
2567163069Sbz				break;
2568163069Sbz
2569159857Sume			case IPPROTO_IPV6:	/* RFC 2893 */
2570159857Sume				PULLUP_TO(hlen, ulp, struct ip6_hdr);
2571159857Sume				break;
2572159857Sume
2573159857Sume			case IPPROTO_IPV4:	/* RFC 2893 */
2574159857Sume				PULLUP_TO(hlen, ulp, struct ip);
2575159857Sume				break;
2576159857Sume
2577145246Sbrooks			default:
2578149020Sbz				printf("IPFW2: IPV6 - Unknown Extension "
2579149020Sbz				    "Header(%d), ext_hd=%x\n", proto, ext_hd);
2580149020Sbz				if (fw_deny_unknown_exthdrs)
2581149020Sbz				    return (IP_FW_DENY);
2582159857Sume				PULLUP_TO(hlen, ulp, struct ip6_ext);
2583145246Sbrooks				break;
2584145246Sbrooks			} /*switch */
2585145246Sbrooks		}
2586165738Sjulian		ip = mtod(m, struct ip *);
2587165738Sjulian		ip6 = (struct ip6_hdr *)ip;
2588165738Sjulian		args->f_id.src_ip6 = ip6->ip6_src;
2589165738Sjulian		args->f_id.dst_ip6 = ip6->ip6_dst;
2590145246Sbrooks		args->f_id.src_ip = 0;
2591145246Sbrooks		args->f_id.dst_ip = 0;
2592165738Sjulian		args->f_id.flow_id6 = ntohl(ip6->ip6_flow);
2593145246Sbrooks	} else if (pktlen >= sizeof(struct ip) &&
2594165738Sjulian	    (args->eh == NULL || etype == ETHERTYPE_IP) && ip->ip_v == 4) {
2595146894Smlaier	    	is_ipv4 = 1;
2596145093Sbrooks		hlen = ip->ip_hl << 2;
2597145093Sbrooks		args->f_id.addr_type = 4;
259898943Sluigi
2599145093Sbrooks		/*
2600145093Sbrooks		 * Collect parameters into local variables for faster matching.
2601145093Sbrooks		 */
2602145093Sbrooks		proto = ip->ip_p;
2603145093Sbrooks		src_ip = ip->ip_src;
2604145093Sbrooks		dst_ip = ip->ip_dst;
2605145093Sbrooks		if (args->eh != NULL) { /* layer 2 packets are as on the wire */
2606145093Sbrooks			offset = ntohs(ip->ip_off) & IP_OFFMASK;
2607145093Sbrooks			ip_len = ntohs(ip->ip_len);
2608145093Sbrooks		} else {
2609145093Sbrooks			offset = ip->ip_off & IP_OFFMASK;
2610145093Sbrooks			ip_len = ip->ip_len;
2611145093Sbrooks		}
2612145093Sbrooks		pktlen = ip_len < pktlen ? ip_len : pktlen;
261398943Sluigi
2614145093Sbrooks		if (offset == 0) {
2615145093Sbrooks			switch (proto) {
2616145093Sbrooks			case IPPROTO_TCP:
2617145093Sbrooks				PULLUP_TO(hlen, ulp, struct tcphdr);
2618145093Sbrooks				dst_port = TCP(ulp)->th_dport;
2619145093Sbrooks				src_port = TCP(ulp)->th_sport;
2620145093Sbrooks				args->f_id.flags = TCP(ulp)->th_flags;
2621145093Sbrooks				break;
262298943Sluigi
2623145093Sbrooks			case IPPROTO_UDP:
2624145093Sbrooks				PULLUP_TO(hlen, ulp, struct udphdr);
2625145093Sbrooks				dst_port = UDP(ulp)->uh_dport;
2626145093Sbrooks				src_port = UDP(ulp)->uh_sport;
2627145093Sbrooks				break;
262898943Sluigi
2629145093Sbrooks			case IPPROTO_ICMP:
2630145565Sbrooks				PULLUP_TO(hlen, ulp, struct icmphdr);
2631145093Sbrooks				args->f_id.flags = ICMP(ulp)->icmp_type;
2632145093Sbrooks				break;
263398943Sluigi
2634145093Sbrooks			default:
2635145093Sbrooks				break;
263698943Sluigi			}
2637145093Sbrooks		}
263898943Sluigi
2639165738Sjulian		ip = mtod(m, struct ip *);
2640145093Sbrooks		args->f_id.src_ip = ntohl(src_ip.s_addr);
2641145093Sbrooks		args->f_id.dst_ip = ntohl(dst_ip.s_addr);
2642145093Sbrooks	}
264398943Sluigi#undef PULLUP_TO
2644145093Sbrooks	if (proto) { /* we may have port numbers, store them */
2645145093Sbrooks		args->f_id.proto = proto;
2646145093Sbrooks		args->f_id.src_port = src_port = ntohs(src_port);
2647145093Sbrooks		args->f_id.dst_port = dst_port = ntohs(dst_port);
264898943Sluigi	}
264998943Sluigi
2650138642Scsjp	IPFW_RLOCK(chain);
2651126239Smlaier	mtag = m_tag_find(m, PACKET_TAG_DIVERT, NULL);
265298943Sluigi	if (args->rule) {
265398943Sluigi		/*
265498943Sluigi		 * Packet has already been tagged. Look for the next rule
265598943Sluigi		 * to restart processing.
265698943Sluigi		 *
265798943Sluigi		 * If fw_one_pass != 0 then just accept it.
265898943Sluigi		 * XXX should not happen here, but optimized out in
265998943Sluigi		 * the caller.
266098943Sluigi		 */
2661120141Ssam		if (fw_one_pass) {
2662138642Scsjp			IPFW_RUNLOCK(chain);
2663140224Sglebius			return (IP_FW_PASS);
2664120141Ssam		}
266598943Sluigi
266698943Sluigi		f = args->rule->next_rule;
266798943Sluigi		if (f == NULL)
266898943Sluigi			f = lookup_next_rule(args->rule);
266998943Sluigi	} else {
267098943Sluigi		/*
267198943Sluigi		 * Find the starting rule. It can be either the first
267298943Sluigi		 * one, or the one after divert_rule if asked so.
267398943Sluigi		 */
2674126239Smlaier		int skipto = mtag ? divert_cookie(mtag) : 0;
267598943Sluigi
2676120141Ssam		f = chain->rules;
267798943Sluigi		if (args->eh == NULL && skipto != 0) {
2678120141Ssam			if (skipto >= IPFW_DEFAULT_RULE) {
2679138642Scsjp				IPFW_RUNLOCK(chain);
2680140224Sglebius				return (IP_FW_DENY); /* invalid */
2681120141Ssam			}
268298943Sluigi			while (f && f->rulenum <= skipto)
268398943Sluigi				f = f->next;
2684120141Ssam			if (f == NULL) {	/* drop packet */
2685138642Scsjp				IPFW_RUNLOCK(chain);
2686140224Sglebius				return (IP_FW_DENY);
2687120141Ssam			}
268898943Sluigi		}
268998943Sluigi	}
2690126239Smlaier	/* reset divert rule to avoid confusion later */
2691136073Sgreen	if (mtag) {
2692136073Sgreen		divinput_flags = divert_info(mtag) &
2693136073Sgreen		    (IP_FW_DIVERT_OUTPUT_FLAG | IP_FW_DIVERT_LOOPBACK_FLAG);
2694126239Smlaier		m_tag_delete(m, mtag);
2695136073Sgreen	}
269698943Sluigi
269798943Sluigi	/*
269898943Sluigi	 * Now scan the rules, and parse microinstructions for each rule.
269998943Sluigi	 */
270098943Sluigi	for (; f; f = f->next) {
270198943Sluigi		ipfw_insn *cmd;
2702153374Sglebius		uint32_t tablearg = 0;
2703153374Sglebius		int l, cmdlen, skip_or; /* skip rest of OR block */
270498943Sluigi
270598943Sluigiagain:
2706101628Sluigi		if (set_disable & (1 << f->set) )
2707101628Sluigi			continue;
2708101628Sluigi
270998943Sluigi		skip_or = 0;
271098943Sluigi		for (l = f->cmd_len, cmd = f->cmd ; l > 0 ;
271198943Sluigi		    l -= cmdlen, cmd += cmdlen) {
271299622Sluigi			int match;
271398943Sluigi
271498943Sluigi			/*
271598943Sluigi			 * check_body is a jump target used when we find a
271698943Sluigi			 * CHECK_STATE, and need to jump to the body of
271798943Sluigi			 * the target rule.
271898943Sluigi			 */
271998943Sluigi
272098943Sluigicheck_body:
272198943Sluigi			cmdlen = F_LEN(cmd);
272298943Sluigi			/*
272398943Sluigi			 * An OR block (insn_1 || .. || insn_n) has the
272498943Sluigi			 * F_OR bit set in all but the last instruction.
272598943Sluigi			 * The first match will set "skip_or", and cause
272698943Sluigi			 * the following instructions to be skipped until
272798943Sluigi			 * past the one with the F_OR bit clear.
272898943Sluigi			 */
272998943Sluigi			if (skip_or) {		/* skip this instruction */
273098943Sluigi				if ((cmd->len & F_OR) == 0)
273198943Sluigi					skip_or = 0;	/* next one is good */
273298943Sluigi				continue;
273398943Sluigi			}
273499622Sluigi			match = 0; /* set to 1 if we succeed */
273599622Sluigi
273698943Sluigi			switch (cmd->opcode) {
273799622Sluigi			/*
273899622Sluigi			 * The first set of opcodes compares the packet's
273999622Sluigi			 * fields with some pattern, setting 'match' if a
274099622Sluigi			 * match is found. At the end of the loop there is
274199622Sluigi			 * logic to deal with F_NOT and F_OR flags associated
274299622Sluigi			 * with the opcode.
274399622Sluigi			 */
274498943Sluigi			case O_NOP:
274599622Sluigi				match = 1;
274699622Sluigi				break;
274798943Sluigi
274898943Sluigi			case O_FORWARD_MAC:
274998943Sluigi				printf("ipfw: opcode %d unimplemented\n",
275098943Sluigi				    cmd->opcode);
275199622Sluigi				break;
275298943Sluigi
275398943Sluigi			case O_GID:
275498943Sluigi			case O_UID:
2755133600Scsjp			case O_JAIL:
275698943Sluigi				/*
275798943Sluigi				 * We only check offset == 0 && proto != 0,
2758145246Sbrooks				 * as this ensures that we have a
275998943Sluigi				 * packet with the ports info.
276098943Sluigi				 */
276198943Sluigi				if (offset!=0)
276299622Sluigi					break;
2763145246Sbrooks				if (is_ipv6) /* XXX to be fixed later */
2764145246Sbrooks					break;
2765122265Ssam				if (proto == IPPROTO_TCP ||
2766122265Ssam				    proto == IPPROTO_UDP)
2767122265Ssam					match = check_uidgid(
2768122265Ssam						    (ipfw_insn_u32 *)cmd,
2769122265Ssam						    proto, oif,
2770122915Smaxim						    dst_ip, dst_port,
2771130363Scsjp						    src_ip, src_port, &fw_ugid_cache,
2772135920Smlaier						    &ugid_lookup, args->inp);
277399622Sluigi				break;
277498943Sluigi
277598943Sluigi			case O_RECV:
277699622Sluigi				match = iface_match(m->m_pkthdr.rcvif,
277799622Sluigi				    (ipfw_insn_if *)cmd);
277899622Sluigi				break;
277998943Sluigi
278098943Sluigi			case O_XMIT:
278199622Sluigi				match = iface_match(oif, (ipfw_insn_if *)cmd);
278299622Sluigi				break;
278398943Sluigi
278498943Sluigi			case O_VIA:
278599622Sluigi				match = iface_match(oif ? oif :
278699622Sluigi				    m->m_pkthdr.rcvif, (ipfw_insn_if *)cmd);
278799622Sluigi				break;
278898943Sluigi
278998943Sluigi			case O_MACADDR2:
279098943Sluigi				if (args->eh != NULL) {	/* have MAC header */
279198943Sluigi					u_int32_t *want = (u_int32_t *)
279298943Sluigi						((ipfw_insn_mac *)cmd)->addr;
279398943Sluigi					u_int32_t *mask = (u_int32_t *)
279498943Sluigi						((ipfw_insn_mac *)cmd)->mask;
279598943Sluigi					u_int32_t *hdr = (u_int32_t *)args->eh;
279698943Sluigi
279799622Sluigi					match =
279899622Sluigi					    ( want[0] == (hdr[0] & mask[0]) &&
279999622Sluigi					      want[1] == (hdr[1] & mask[1]) &&
280099622Sluigi					      want[2] == (hdr[2] & mask[2]) );
280198943Sluigi				}
280299622Sluigi				break;
280398943Sluigi
280498943Sluigi			case O_MAC_TYPE:
280598943Sluigi				if (args->eh != NULL) {
280698943Sluigi					u_int16_t *p =
280798943Sluigi					    ((ipfw_insn_u16 *)cmd)->ports;
280898943Sluigi					int i;
280998943Sluigi
281099622Sluigi					for (i = cmdlen - 1; !match && i>0;
281199622Sluigi					    i--, p += 2)
2812165738Sjulian						match = (etype >= p[0] &&
2813165738Sjulian						    etype <= p[1]);
281498943Sluigi				}
281599622Sluigi				break;
281698943Sluigi
281798943Sluigi			case O_FRAG:
2818145246Sbrooks				match = (offset != 0);
281999622Sluigi				break;
282098943Sluigi
282198943Sluigi			case O_IN:	/* "out" is "not in" */
282299622Sluigi				match = (oif == NULL);
282399622Sluigi				break;
282498943Sluigi
282598943Sluigi			case O_LAYER2:
282699622Sluigi				match = (args->eh != NULL);
282799622Sluigi				break;
282898943Sluigi
2829136073Sgreen			case O_DIVERTED:
2830136073Sgreen				match = (cmd->arg1 & 1 && divinput_flags &
2831136073Sgreen				    IP_FW_DIVERT_LOOPBACK_FLAG) ||
2832136073Sgreen					(cmd->arg1 & 2 && divinput_flags &
2833136073Sgreen				    IP_FW_DIVERT_OUTPUT_FLAG);
2834136073Sgreen				break;
2835136073Sgreen
283698943Sluigi			case O_PROTO:
283798943Sluigi				/*
283898943Sluigi				 * We do not allow an arg of 0 so the
283998943Sluigi				 * check of "proto" only suffices.
284098943Sluigi				 */
284199622Sluigi				match = (proto == cmd->arg1);
284299622Sluigi				break;
284398943Sluigi
284498943Sluigi			case O_IP_SRC:
2845147758Smlaier				match = is_ipv4 &&
2846147758Smlaier				    (((ipfw_insn_ip *)cmd)->addr.s_addr ==
284799622Sluigi				    src_ip.s_addr);
284899622Sluigi				break;
2849105775Smaxim
2850130281Sru			case O_IP_SRC_LOOKUP:
2851130281Sru			case O_IP_DST_LOOKUP:
2852147758Smlaier				if (is_ipv4) {
2853130281Sru				    uint32_t a =
2854130281Sru					(cmd->opcode == O_IP_DST_LOOKUP) ?
2855130281Sru					    dst_ip.s_addr : src_ip.s_addr;
2856130281Sru				    uint32_t v;
2857130281Sru
2858153163Sglebius				    match = lookup_table(chain, cmd->arg1, a,
2859153163Sglebius					&v);
2860130281Sru				    if (!match)
2861130281Sru					break;
2862130281Sru				    if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
2863130281Sru					match =
2864130281Sru					    ((ipfw_insn_u32 *)cmd)->d[0] == v;
2865153374Sglebius				    else
2866153374Sglebius					tablearg = v;
2867130281Sru				}
2868130281Sru				break;
2869130281Sru
287098943Sluigi			case O_IP_SRC_MASK:
2871117327Sluigi			case O_IP_DST_MASK:
2872147758Smlaier				if (is_ipv4) {
2873117327Sluigi				    uint32_t a =
2874117327Sluigi					(cmd->opcode == O_IP_DST_MASK) ?
2875117327Sluigi					    dst_ip.s_addr : src_ip.s_addr;
2876117327Sluigi				    uint32_t *p = ((ipfw_insn_u32 *)cmd)->d;
2877117327Sluigi				    int i = cmdlen-1;
2878117327Sluigi
2879117327Sluigi				    for (; !match && i>0; i-= 2, p+= 2)
2880117327Sluigi					match = (p[0] == (a & p[1]));
2881117327Sluigi				}
288299622Sluigi				break;
288398943Sluigi
288498943Sluigi			case O_IP_SRC_ME:
2885147758Smlaier				if (is_ipv4) {
288699622Sluigi					struct ifnet *tif;
288798943Sluigi
288899622Sluigi					INADDR_TO_IFP(src_ip, tif);
288999622Sluigi					match = (tif != NULL);
289099622Sluigi				}
289199622Sluigi				break;
2892105775Smaxim
289398943Sluigi			case O_IP_DST_SET:
289498943Sluigi			case O_IP_SRC_SET:
2895147758Smlaier				if (is_ipv4) {
289699622Sluigi					u_int32_t *d = (u_int32_t *)(cmd+1);
289799622Sluigi					u_int32_t addr =
289899622Sluigi					    cmd->opcode == O_IP_DST_SET ?
2899105886Sluigi						args->f_id.dst_ip :
2900105886Sluigi						args->f_id.src_ip;
290198943Sluigi
290299622Sluigi					    if (addr < d[0])
290399622Sluigi						    break;
290499622Sluigi					    addr -= d[0]; /* subtract base */
290599622Sluigi					    match = (addr < cmd->arg1) &&
290699622Sluigi						( d[ 1 + (addr>>5)] &
290799622Sluigi						  (1<<(addr & 0x1f)) );
290899622Sluigi				}
290999622Sluigi				break;
291098943Sluigi
291198943Sluigi			case O_IP_DST:
2912147758Smlaier				match = is_ipv4 &&
2913147758Smlaier				    (((ipfw_insn_ip *)cmd)->addr.s_addr ==
291499622Sluigi				    dst_ip.s_addr);
291599622Sluigi				break;
291698943Sluigi
291798943Sluigi			case O_IP_DST_ME:
2918147758Smlaier				if (is_ipv4) {
291999622Sluigi					struct ifnet *tif;
292099622Sluigi
292199622Sluigi					INADDR_TO_IFP(dst_ip, tif);
292299622Sluigi					match = (tif != NULL);
292399622Sluigi				}
292499622Sluigi				break;
2925105775Smaxim
292698943Sluigi			case O_IP_SRCPORT:
292798943Sluigi			case O_IP_DSTPORT:
292898943Sluigi				/*
292998943Sluigi				 * offset == 0 && proto != 0 is enough
2930145246Sbrooks				 * to guarantee that we have a
293198943Sluigi				 * packet with port info.
293298943Sluigi				 */
293399622Sluigi				if ((proto==IPPROTO_UDP || proto==IPPROTO_TCP)
293499622Sluigi				    && offset == 0) {
293599622Sluigi					u_int16_t x =
293698943Sluigi					    (cmd->opcode == O_IP_SRCPORT) ?
293799622Sluigi						src_port : dst_port ;
293898943Sluigi					u_int16_t *p =
293998943Sluigi					    ((ipfw_insn_u16 *)cmd)->ports;
294098943Sluigi					int i;
294198943Sluigi
294299622Sluigi					for (i = cmdlen - 1; !match && i>0;
294399622Sluigi					    i--, p += 2)
294499622Sluigi						match = (x>=p[0] && x<=p[1]);
294598943Sluigi				}
294699622Sluigi				break;
294798943Sluigi
294898943Sluigi			case O_ICMPTYPE:
294999622Sluigi				match = (offset == 0 && proto==IPPROTO_ICMP &&
2950145093Sbrooks				    icmptype_match(ICMP(ulp), (ipfw_insn_u32 *)cmd) );
295199622Sluigi				break;
295298943Sluigi
2953145266Sphk#ifdef INET6
2954145246Sbrooks			case O_ICMP6TYPE:
2955145246Sbrooks				match = is_ipv6 && offset == 0 &&
2956145246Sbrooks				    proto==IPPROTO_ICMPV6 &&
2957145246Sbrooks				    icmp6type_match(
2958145246Sbrooks					ICMP6(ulp)->icmp6_type,
2959145246Sbrooks					(ipfw_insn_u32 *)cmd);
2960145246Sbrooks				break;
2961145266Sphk#endif /* INET6 */
2962145246Sbrooks
296398943Sluigi			case O_IPOPT:
2964147758Smlaier				match = (is_ipv4 &&
2965165738Sjulian				    ipopts_match(ip, cmd) );
296699622Sluigi				break;
296798943Sluigi
296898943Sluigi			case O_IPVER:
2969147758Smlaier				match = (is_ipv4 &&
2970165738Sjulian				    cmd->arg1 == ip->ip_v);
297199622Sluigi				break;
297298943Sluigi
2973116690Sluigi			case O_IPID:
2974116690Sluigi			case O_IPLEN:
297598943Sluigi			case O_IPTTL:
2976147758Smlaier				if (is_ipv4) {	/* only for IP packets */
2977116690Sluigi				    uint16_t x;
2978116690Sluigi				    uint16_t *p;
2979116690Sluigi				    int i;
298098943Sluigi
2981116690Sluigi				    if (cmd->opcode == O_IPLEN)
2982116690Sluigi					x = ip_len;
2983116690Sluigi				    else if (cmd->opcode == O_IPTTL)
2984165738Sjulian					x = ip->ip_ttl;
2985116690Sluigi				    else /* must be IPID */
2986165738Sjulian					x = ntohs(ip->ip_id);
2987116690Sluigi				    if (cmdlen == 1) {
2988116690Sluigi					match = (cmd->arg1 == x);
2989116690Sluigi					break;
2990116690Sluigi				    }
2991116690Sluigi				    /* otherwise we have ranges */
2992116690Sluigi				    p = ((ipfw_insn_u16 *)cmd)->ports;
2993116690Sluigi				    i = cmdlen - 1;
2994116690Sluigi				    for (; !match && i>0; i--, p += 2)
2995116690Sluigi					match = (x >= p[0] && x <= p[1]);
2996116690Sluigi				}
299799622Sluigi				break;
299898943Sluigi
299999475Sluigi			case O_IPPRECEDENCE:
3000147758Smlaier				match = (is_ipv4 &&
3001165738Sjulian				    (cmd->arg1 == (ip->ip_tos & 0xe0)) );
300299622Sluigi				break;
300399475Sluigi
300498943Sluigi			case O_IPTOS:
3005147758Smlaier				match = (is_ipv4 &&
3006165738Sjulian				    flags_match(cmd, ip->ip_tos));
300799622Sluigi				break;
300898943Sluigi
3009136075Sgreen			case O_TCPDATALEN:
3010136075Sgreen				if (proto == IPPROTO_TCP && offset == 0) {
3011136075Sgreen				    struct tcphdr *tcp;
3012136075Sgreen				    uint16_t x;
3013136075Sgreen				    uint16_t *p;
3014136075Sgreen				    int i;
3015136075Sgreen
3016145093Sbrooks				    tcp = TCP(ulp);
3017136075Sgreen				    x = ip_len -
3018136075Sgreen					((ip->ip_hl + tcp->th_off) << 2);
3019136075Sgreen				    if (cmdlen == 1) {
3020136075Sgreen					match = (cmd->arg1 == x);
3021136075Sgreen					break;
3022136075Sgreen				    }
3023136075Sgreen				    /* otherwise we have ranges */
3024136075Sgreen				    p = ((ipfw_insn_u16 *)cmd)->ports;
3025136075Sgreen				    i = cmdlen - 1;
3026136075Sgreen				    for (; !match && i>0; i--, p += 2)
3027136075Sgreen					match = (x >= p[0] && x <= p[1]);
3028136075Sgreen				}
3029136075Sgreen				break;
3030136075Sgreen
303198943Sluigi			case O_TCPFLAGS:
303299622Sluigi				match = (proto == IPPROTO_TCP && offset == 0 &&
3033145093Sbrooks				    flags_match(cmd, TCP(ulp)->th_flags));
303499622Sluigi				break;
303598943Sluigi
303698943Sluigi			case O_TCPOPTS:
303799622Sluigi				match = (proto == IPPROTO_TCP && offset == 0 &&
3038145093Sbrooks				    tcpopts_match(TCP(ulp), cmd));
303999622Sluigi				break;
304098943Sluigi
304198943Sluigi			case O_TCPSEQ:
304299622Sluigi				match = (proto == IPPROTO_TCP && offset == 0 &&
304399622Sluigi				    ((ipfw_insn_u32 *)cmd)->d[0] ==
3044145093Sbrooks					TCP(ulp)->th_seq);
304599622Sluigi				break;
304698943Sluigi
304798943Sluigi			case O_TCPACK:
304899622Sluigi				match = (proto == IPPROTO_TCP && offset == 0 &&
304999622Sluigi				    ((ipfw_insn_u32 *)cmd)->d[0] ==
3050145093Sbrooks					TCP(ulp)->th_ack);
305199622Sluigi				break;
305298943Sluigi
305398943Sluigi			case O_TCPWIN:
305499622Sluigi				match = (proto == IPPROTO_TCP && offset == 0 &&
3055145093Sbrooks				    cmd->arg1 == TCP(ulp)->th_win);
305699622Sluigi				break;
305798943Sluigi
305898943Sluigi			case O_ESTAB:
305998943Sluigi				/* reject packets which have SYN only */
306099622Sluigi				/* XXX should i also check for TH_ACK ? */
306199622Sluigi				match = (proto == IPPROTO_TCP && offset == 0 &&
3062145093Sbrooks				    (TCP(ulp)->th_flags &
306399622Sluigi				     (TH_RST | TH_ACK | TH_SYN)) != TH_SYN);
306499622Sluigi				break;
306598943Sluigi
3066136071Sgreen			case O_ALTQ: {
3067171173Smlaier				struct pf_mtag *at;
3068136071Sgreen				ipfw_insn_altq *altq = (ipfw_insn_altq *)cmd;
3069136071Sgreen
3070136071Sgreen				match = 1;
3071171173Smlaier				at = pf_find_mtag(m);
3072171173Smlaier				if (at != NULL && at->qid != 0)
3073146962Sgreen					break;
3074171173Smlaier				at = pf_get_mtag(m);
3075171173Smlaier				if (at == NULL) {
3076136071Sgreen					/*
3077136071Sgreen					 * Let the packet fall back to the
3078136071Sgreen					 * default ALTQ.
3079136071Sgreen					 */
3080136071Sgreen					break;
3081136071Sgreen				}
3082136071Sgreen				at->qid = altq->qid;
3083147758Smlaier				if (is_ipv4)
3084136071Sgreen					at->af = AF_INET;
3085136071Sgreen				else
3086136071Sgreen					at->af = AF_LINK;
3087136071Sgreen				at->hdr = ip;
3088136071Sgreen				break;
3089136071Sgreen			}
3090136071Sgreen
309198943Sluigi			case O_LOG:
3092149020Sbz				if (fw_verbose)
3093161456Sjulian					ipfw_log(f, hlen, args, m,
3094165738Sjulian					    oif, offset, tablearg, ip);
309599622Sluigi				match = 1;
309699622Sluigi				break;
309798943Sluigi
309899475Sluigi			case O_PROB:
309999622Sluigi				match = (random()<((ipfw_insn_u32 *)cmd)->d[0]);
310099622Sluigi				break;
310198943Sluigi
3102112250Scjc			case O_VERREVPATH:
3103112250Scjc				/* Outgoing packets automatically pass/match */
3104145246Sbrooks				match = ((oif != NULL) ||
3105116763Sluigi				    (m->m_pkthdr.rcvif == NULL) ||
3106145266Sphk				    (
3107145266Sphk#ifdef INET6
3108145266Sphk				    is_ipv6 ?
3109147418Smlaier					verify_path6(&(args->f_id.src_ip6),
3110145246Sbrooks					    m->m_pkthdr.rcvif) :
3111145266Sphk#endif
3112133485Sandre				    verify_path(src_ip, m->m_pkthdr.rcvif)));
3113112250Scjc				break;
3114112250Scjc
3115128575Sandre			case O_VERSRCREACH:
3116128575Sandre				/* Outgoing packets automatically pass/match */
3117133485Sandre				match = (hlen > 0 && ((oif != NULL) ||
3118147418Smlaier#ifdef INET6
3119147418Smlaier				    is_ipv6 ?
3120147418Smlaier				        verify_path6(&(args->f_id.src_ip6),
3121147418Smlaier				            NULL) :
3122147418Smlaier#endif
3123147418Smlaier				    verify_path(src_ip, NULL)));
3124128575Sandre				break;
3125128575Sandre
3126133387Sandre			case O_ANTISPOOF:
3127133387Sandre				/* Outgoing packets automatically pass/match */
3128133387Sandre				if (oif == NULL && hlen > 0 &&
3129147418Smlaier				    (  (is_ipv4 && in_localaddr(src_ip))
3130147418Smlaier#ifdef INET6
3131147418Smlaier				    || (is_ipv6 &&
3132147418Smlaier				        in6_localaddr(&(args->f_id.src_ip6)))
3133147418Smlaier#endif
3134147418Smlaier				    ))
3135147418Smlaier					match =
3136147418Smlaier#ifdef INET6
3137147418Smlaier					    is_ipv6 ? verify_path6(
3138147418Smlaier					        &(args->f_id.src_ip6),
3139147418Smlaier					        m->m_pkthdr.rcvif) :
3140147418Smlaier#endif
3141147418Smlaier					    verify_path(src_ip,
3142147418Smlaier					        m->m_pkthdr.rcvif);
3143133387Sandre				else
3144133387Sandre					match = 1;
3145133387Sandre				break;
3146133387Sandre
3147117241Sluigi			case O_IPSEC:
3148171167Sgnn#ifdef IPSEC
3149117241Sluigi				match = (m_tag_find(m,
3150117241Sluigi				    PACKET_TAG_IPSEC_IN_DONE, NULL) != NULL);
3151117241Sluigi#endif
3152117241Sluigi				/* otherwise no match */
3153117241Sluigi				break;
3154117241Sluigi
3155150122Sbz#ifdef INET6
3156145246Sbrooks			case O_IP6_SRC:
3157145246Sbrooks				match = is_ipv6 &&
3158145246Sbrooks				    IN6_ARE_ADDR_EQUAL(&args->f_id.src_ip6,
3159145246Sbrooks				    &((ipfw_insn_ip6 *)cmd)->addr6);
3160145246Sbrooks				break;
3161145246Sbrooks
3162145246Sbrooks			case O_IP6_DST:
3163145246Sbrooks				match = is_ipv6 &&
3164145246Sbrooks				IN6_ARE_ADDR_EQUAL(&args->f_id.dst_ip6,
3165145246Sbrooks				    &((ipfw_insn_ip6 *)cmd)->addr6);
3166145246Sbrooks				break;
3167145246Sbrooks			case O_IP6_SRC_MASK:
3168145246Sbrooks			case O_IP6_DST_MASK:
3169145246Sbrooks				if (is_ipv6) {
3170162351Sjhay					int i = cmdlen - 1;
3171162351Sjhay					struct in6_addr p;
3172162351Sjhay					struct in6_addr *d =
3173162351Sjhay					    &((ipfw_insn_ip6 *)cmd)->addr6;
3174145246Sbrooks
3175162351Sjhay					for (; !match && i > 0; d += 2,
3176162351Sjhay					    i -= F_INSN_SIZE(struct in6_addr)
3177162351Sjhay					    * 2) {
3178162351Sjhay						p = (cmd->opcode ==
3179162351Sjhay						    O_IP6_SRC_MASK) ?
3180162351Sjhay						    args->f_id.src_ip6:
3181162351Sjhay						    args->f_id.dst_ip6;
3182162351Sjhay						APPLY_MASK(&p, &d[1]);
3183162351Sjhay						match =
3184162351Sjhay						    IN6_ARE_ADDR_EQUAL(&d[0],
3185162351Sjhay						    &p);
3186162351Sjhay					}
3187145246Sbrooks				}
3188145246Sbrooks				break;
3189145246Sbrooks
3190145246Sbrooks			case O_IP6_SRC_ME:
3191145246Sbrooks				match= is_ipv6 && search_ip6_addr_net(&args->f_id.src_ip6);
3192150122Sbz				break;
3193145246Sbrooks
3194145246Sbrooks			case O_IP6_DST_ME:
3195145246Sbrooks				match= is_ipv6 && search_ip6_addr_net(&args->f_id.dst_ip6);
3196150122Sbz				break;
3197145246Sbrooks
3198145246Sbrooks			case O_FLOW6ID:
3199145246Sbrooks				match = is_ipv6 &&
3200145246Sbrooks				    flow6id_match(args->f_id.flow_id6,
3201145246Sbrooks				    (ipfw_insn_u32 *) cmd);
3202145246Sbrooks				break;
3203145246Sbrooks
3204145246Sbrooks			case O_EXT_HDR:
3205145246Sbrooks				match = is_ipv6 &&
3206145246Sbrooks				    (ext_hd & ((ipfw_insn *) cmd)->arg1);
3207145246Sbrooks				break;
3208145246Sbrooks
3209145246Sbrooks			case O_IP6:
3210145246Sbrooks				match = is_ipv6;
3211145246Sbrooks				break;
3212145266Sphk#endif
3213145246Sbrooks
3214146894Smlaier			case O_IP4:
3215146894Smlaier				match = is_ipv4;
3216146894Smlaier				break;
3217146894Smlaier
3218159636Soleg			case O_TAG: {
3219159636Soleg				uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ?
3220159636Soleg				    tablearg : cmd->arg1;
3221159636Soleg
3222158879Soleg				/* Packet is already tagged with this tag? */
3223159636Soleg				mtag = m_tag_locate(m, MTAG_IPFW, tag, NULL);
3224159636Soleg
3225158879Soleg				/* We have `untag' action when F_NOT flag is
3226158879Soleg				 * present. And we must remove this mtag from
3227158879Soleg				 * mbuf and reset `match' to zero (`match' will
3228158879Soleg				 * be inversed later).
3229158879Soleg				 * Otherwise we should allocate new mtag and
3230158879Soleg				 * push it into mbuf.
3231158879Soleg				 */
3232158879Soleg				if (cmd->len & F_NOT) { /* `untag' action */
3233158879Soleg					if (mtag != NULL)
3234158879Soleg						m_tag_delete(m, mtag);
3235158879Soleg				} else if (mtag == NULL) {
3236159636Soleg					if ((mtag = m_tag_alloc(MTAG_IPFW,
3237159636Soleg					    tag, 0, M_NOWAIT)) != NULL)
3238158879Soleg						m_tag_prepend(m, mtag);
3239158879Soleg				}
3240158879Soleg				match = (cmd->len & F_NOT) ? 0: 1;
3241158879Soleg				break;
3242159636Soleg			}
3243158879Soleg
3244159636Soleg			case O_TAGGED: {
3245159636Soleg				uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ?
3246159636Soleg				    tablearg : cmd->arg1;
3247159636Soleg
3248158879Soleg				if (cmdlen == 1) {
3249159636Soleg					match = m_tag_locate(m, MTAG_IPFW,
3250159636Soleg					    tag, NULL) != NULL;
3251158879Soleg					break;
3252158879Soleg				}
3253158879Soleg
3254158879Soleg				/* we have ranges */
3255158879Soleg				for (mtag = m_tag_first(m);
3256158879Soleg				    mtag != NULL && !match;
3257158879Soleg				    mtag = m_tag_next(m, mtag)) {
3258158879Soleg					uint16_t *p;
3259158879Soleg					int i;
3260158879Soleg
3261158879Soleg					if (mtag->m_tag_cookie != MTAG_IPFW)
3262158879Soleg						continue;
3263158879Soleg
3264158879Soleg					p = ((ipfw_insn_u16 *)cmd)->ports;
3265158879Soleg					i = cmdlen - 1;
3266158879Soleg					for(; !match && i > 0; i--, p += 2)
3267158879Soleg						match =
3268158879Soleg						    mtag->m_tag_id >= p[0] &&
3269158879Soleg						    mtag->m_tag_id <= p[1];
3270158879Soleg				}
3271158879Soleg				break;
3272159636Soleg			}
3273158879Soleg
327499622Sluigi			/*
327599622Sluigi			 * The second set of opcodes represents 'actions',
327699622Sluigi			 * i.e. the terminal part of a rule once the packet
327799622Sluigi			 * matches all previous patterns.
327899622Sluigi			 * Typically there is only one action for each rule,
327999622Sluigi			 * and the opcode is stored at the end of the rule
328099622Sluigi			 * (but there are exceptions -- see below).
328199622Sluigi			 *
328299622Sluigi			 * In general, here we set retval and terminate the
328399622Sluigi			 * outer loop (would be a 'break 3' in some language,
328499622Sluigi			 * but we need to do a 'goto done').
328599622Sluigi			 *
328699622Sluigi			 * Exceptions:
328799622Sluigi			 * O_COUNT and O_SKIPTO actions:
328899622Sluigi			 *   instead of terminating, we jump to the next rule
328999622Sluigi			 *   ('goto next_rule', equivalent to a 'break 2'),
329099622Sluigi			 *   or to the SKIPTO target ('goto again' after
329199622Sluigi			 *   having set f, cmd and l), respectively.
329299622Sluigi			 *
3293158879Soleg			 * O_TAG, O_LOG and O_ALTQ action parameters:
3294136071Sgreen			 *   perform some action and set match = 1;
3295136071Sgreen			 *
329699622Sluigi			 * O_LIMIT and O_KEEP_STATE: these opcodes are
329799622Sluigi			 *   not real 'actions', and are stored right
329899622Sluigi			 *   before the 'action' part of the rule.
329999622Sluigi			 *   These opcodes try to install an entry in the
330099622Sluigi			 *   state tables; if successful, we continue with
330199622Sluigi			 *   the next opcode (match=1; break;), otherwise
330299622Sluigi			 *   the packet *   must be dropped
330399622Sluigi			 *   ('goto done' after setting retval);
330499622Sluigi			 *
330599622Sluigi			 * O_PROBE_STATE and O_CHECK_STATE: these opcodes
330699622Sluigi			 *   cause a lookup of the state table, and a jump
330799622Sluigi			 *   to the 'action' part of the parent rule
330899622Sluigi			 *   ('goto check_body') if an entry is found, or
330999622Sluigi			 *   (CHECK_STATE only) a jump to the next rule if
331099622Sluigi			 *   the entry is not found ('goto next_rule').
331199622Sluigi			 *   The result of the lookup is cached to make
331299622Sluigi			 *   further instances of these opcodes are
331399622Sluigi			 *   effectively NOPs.
331499622Sluigi			 */
331598943Sluigi			case O_LIMIT:
331698943Sluigi			case O_KEEP_STATE:
331798943Sluigi				if (install_state(f,
3318159636Soleg				    (ipfw_insn_limit *)cmd, args, tablearg)) {
3319140224Sglebius					retval = IP_FW_DENY;
332099622Sluigi					goto done; /* error/limit violation */
332199622Sluigi				}
332299622Sluigi				match = 1;
332399622Sluigi				break;
332498943Sluigi
332598943Sluigi			case O_PROBE_STATE:
332698943Sluigi			case O_CHECK_STATE:
332798943Sluigi				/*
332898943Sluigi				 * dynamic rules are checked at the first
332999622Sluigi				 * keep-state or check-state occurrence,
333099622Sluigi				 * with the result being stored in dyn_dir.
333199622Sluigi				 * The compiler introduces a PROBE_STATE
333298943Sluigi				 * instruction for us when we have a
333399622Sluigi				 * KEEP_STATE (because PROBE_STATE needs
333498943Sluigi				 * to be run first).
333598943Sluigi				 */
333699622Sluigi				if (dyn_dir == MATCH_UNKNOWN &&
333799622Sluigi				    (q = lookup_dyn_rule(&args->f_id,
3338100004Sluigi				     &dyn_dir, proto == IPPROTO_TCP ?
3339145093Sbrooks					TCP(ulp) : NULL))
3340100004Sluigi					!= NULL) {
334199622Sluigi					/*
334299622Sluigi					 * Found dynamic entry, update stats
334399622Sluigi					 * and jump to the 'action' part of
334499622Sluigi					 * the parent rule.
334599622Sluigi					 */
334699622Sluigi					q->pcnt++;
3347115750Skbyanc					q->bcnt += pktlen;
334899622Sluigi					f = q->rule;
334999622Sluigi					cmd = ACTION_PTR(f);
335099622Sluigi					l = f->cmd_len - f->act_ofs;
3351120141Ssam					IPFW_DYN_UNLOCK();
335299622Sluigi					goto check_body;
335398943Sluigi				}
335499622Sluigi				/*
335599622Sluigi				 * Dynamic entry not found. If CHECK_STATE,
335699622Sluigi				 * skip to next rule, if PROBE_STATE just
335799622Sluigi				 * ignore and continue with next opcode.
335899622Sluigi				 */
335998943Sluigi				if (cmd->opcode == O_CHECK_STATE)
336098943Sluigi					goto next_rule;
336199622Sluigi				match = 1;
336299622Sluigi				break;
336398943Sluigi
336498943Sluigi			case O_ACCEPT:
336598943Sluigi				retval = 0;	/* accept */
336699622Sluigi				goto done;
336798943Sluigi
336898943Sluigi			case O_PIPE:
336998943Sluigi			case O_QUEUE:
337098943Sluigi				args->rule = f; /* report matching rule */
3371153374Sglebius				if (cmd->arg1 == IP_FW_TABLEARG)
3372153374Sglebius					args->cookie = tablearg;
3373153374Sglebius				else
3374153374Sglebius					args->cookie = cmd->arg1;
3375140224Sglebius				retval = IP_FW_DUMMYNET;
337699622Sluigi				goto done;
3377105775Smaxim
337898943Sluigi			case O_DIVERT:
3379126239Smlaier			case O_TEE: {
3380126239Smlaier				struct divert_tag *dt;
3381126239Smlaier
338298943Sluigi				if (args->eh) /* not on layer 2 */
338399622Sluigi					break;
3384126239Smlaier				mtag = m_tag_get(PACKET_TAG_DIVERT,
3385126239Smlaier						sizeof(struct divert_tag),
3386126239Smlaier						M_NOWAIT);
3387126239Smlaier				if (mtag == NULL) {
3388126239Smlaier					/* XXX statistic */
3389126239Smlaier					/* drop packet */
3390138642Scsjp					IPFW_RUNLOCK(chain);
3391140224Sglebius					return (IP_FW_DENY);
3392126239Smlaier				}
3393126239Smlaier				dt = (struct divert_tag *)(mtag+1);
3394126239Smlaier				dt->cookie = f->rulenum;
3395153374Sglebius				if (cmd->arg1 == IP_FW_TABLEARG)
3396153374Sglebius					dt->info = tablearg;
3397153374Sglebius				else
3398153374Sglebius					dt->info = cmd->arg1;
3399126239Smlaier				m_tag_prepend(m, mtag);
3400140224Sglebius				retval = (cmd->opcode == O_DIVERT) ?
3401140224Sglebius				    IP_FW_DIVERT : IP_FW_TEE;
340299622Sluigi				goto done;
3403126239Smlaier			}
340498943Sluigi
340598943Sluigi			case O_COUNT:
340698943Sluigi			case O_SKIPTO:
340798943Sluigi				f->pcnt++;	/* update stats */
3408115750Skbyanc				f->bcnt += pktlen;
3409150350Sandre				f->timestamp = time_uptime;
341098943Sluigi				if (cmd->opcode == O_COUNT)
341198943Sluigi					goto next_rule;
341298943Sluigi				/* handle skipto */
341398943Sluigi				if (f->next_rule == NULL)
341498943Sluigi					lookup_next_rule(f);
341598943Sluigi				f = f->next_rule;
341698943Sluigi				goto again;
341798943Sluigi
341898943Sluigi			case O_REJECT:
341998943Sluigi				/*
342098943Sluigi				 * Drop the packet and send a reject notice
342198943Sluigi				 * if the packet is not ICMP (or is an ICMP
342298943Sluigi				 * query), and it is not multicast/broadcast.
342398943Sluigi				 */
3424154216Scperciva				if (hlen > 0 && is_ipv4 && offset == 0 &&
342598943Sluigi				    (proto != IPPROTO_ICMP ||
3426145093Sbrooks				     is_icmp_query(ICMP(ulp))) &&
342798943Sluigi				    !(m->m_flags & (M_BCAST|M_MCAST)) &&
3428123572Smaxim				    !IN_MULTICAST(ntohl(dst_ip.s_addr))) {
3429165738Sjulian					send_reject(args, cmd->arg1, ip_len, ip);
343099475Sluigi					m = args->m;
343198943Sluigi				}
343299622Sluigi				/* FALLTHROUGH */
3433149020Sbz#ifdef INET6
3434149020Sbz			case O_UNREACH6:
3435149020Sbz				if (hlen > 0 && is_ipv6 &&
3436160025Sbz				    ((offset & IP6F_OFF_MASK) == 0) &&
3437149020Sbz				    (proto != IPPROTO_ICMPV6 ||
3438149020Sbz				     (is_icmp6_query(args->f_id.flags) == 1)) &&
3439149020Sbz				    !(m->m_flags & (M_BCAST|M_MCAST)) &&
3440149020Sbz				    !IN6_IS_ADDR_MULTICAST(&args->f_id.dst_ip6)) {
3441165738Sjulian					send_reject6(
3442165738Sjulian					    args, cmd->arg1, hlen,
3443165738Sjulian					    (struct ip6_hdr *)ip);
3444149020Sbz					m = args->m;
3445149020Sbz				}
3446149020Sbz				/* FALLTHROUGH */
3447149020Sbz#endif
344899622Sluigi			case O_DENY:
3449140224Sglebius				retval = IP_FW_DENY;
345099622Sluigi				goto done;
345198943Sluigi
3452161424Sjulian			case O_FORWARD_IP: {
3453161424Sjulian				struct sockaddr_in *sa;
3454161424Sjulian				sa = &(((ipfw_insn_sa *)cmd)->sa);
345598943Sluigi				if (args->eh)	/* not valid on layer2 pkts */
345699622Sluigi					break;
3457161424Sjulian				if (!q || dyn_dir == MATCH_FORWARD) {
3458161424Sjulian					if (sa->sin_addr.s_addr == INADDR_ANY) {
3459161424Sjulian						bcopy(sa, &args->hopstore,
3460161424Sjulian							sizeof(*sa));
3461161456Sjulian						args->hopstore.sin_addr.s_addr =
3462161456Sjulian						    htonl(tablearg);
3463161456Sjulian						args->next_hop =
3464161456Sjulian						    &args->hopstore;
3465161424Sjulian					} else {
3466161424Sjulian						args->next_hop = sa;
3467161424Sjulian					}
3468161424Sjulian				}
3469140224Sglebius				retval = IP_FW_PASS;
3470161456Sjulian			    }
3471161456Sjulian			    goto done;
347298943Sluigi
3473141351Sglebius			case O_NETGRAPH:
3474141351Sglebius			case O_NGTEE:
3475141351Sglebius				args->rule = f;	/* report matching rule */
3476153374Sglebius				if (cmd->arg1 == IP_FW_TABLEARG)
3477153374Sglebius					args->cookie = tablearg;
3478153374Sglebius				else
3479153374Sglebius					args->cookie = cmd->arg1;
3480141351Sglebius				retval = (cmd->opcode == O_NETGRAPH) ?
3481141351Sglebius				    IP_FW_NETGRAPH : IP_FW_NGTEE;
3482141351Sglebius				goto done;
3483141351Sglebius
3484165750Spiso#ifdef IPFIREWALL_NAT
3485165648Spiso			case O_NAT: {
3486165648Spiso				struct cfg_nat *t;
3487165648Spiso				struct mbuf *mcl;
3488165648Spiso				/* XXX - libalias duct tape */
3489165648Spiso				int ldt;
3490165648Spiso				char *c;
3491165648Spiso
3492165648Spiso				ldt = 0;
3493165648Spiso				args->rule = f;	/* Report matching rule. */
3494165648Spiso				retval = 0;
3495165648Spiso				t = ((ipfw_insn_nat *)cmd)->nat;
3496165648Spiso				if (t == NULL) {
3497165648Spiso					t = lookup_nat(cmd->arg1);
3498165648Spiso					if (t == NULL) {
3499165648Spiso						retval = IP_FW_DENY;
3500165648Spiso						goto done;
3501165648Spiso					} else
3502165648Spiso						((ipfw_insn_nat *)cmd)->nat =
3503165648Spiso						    t;
3504165648Spiso				}
3505165648Spiso				if ((mcl = m_megapullup(m, m->m_pkthdr.len)) ==
3506165648Spiso				    NULL)
3507165648Spiso					goto badnat;
3508165648Spiso				ip = mtod(mcl, struct ip *);
3509165648Spiso				if (args->eh == NULL) {
3510165648Spiso					ip->ip_len = htons(ip->ip_len);
3511165648Spiso					ip->ip_off = htons(ip->ip_off);
3512165648Spiso				}
3513165648Spiso
3514165648Spiso				/*
3515165648Spiso				 * XXX - Libalias checksum offload 'duct tape':
3516165648Spiso				 *
3517165648Spiso				 * locally generated packets have only
3518165648Spiso				 * pseudo-header checksum calculated
3519165648Spiso				 * and libalias will screw it[1], so
3520165648Spiso				 * mark them for later fix.  Moreover
3521165648Spiso				 * there are cases when libalias
3522165648Spiso				 * modify tcp packet data[2], mark it
3523165648Spiso				 * for later fix too.
3524165648Spiso				 *
3525165648Spiso				 * [1] libalias was never meant to run
3526165648Spiso				 * in kernel, so it doesn't have any
3527165648Spiso				 * knowledge about checksum
3528165648Spiso				 * offloading, and it expects a packet
3529165648Spiso				 * with a full internet
3530165648Spiso				 * checksum. Unfortunately, packets
3531165648Spiso				 * generated locally will have just the
3532165648Spiso				 * pseudo header calculated, and when
3533165648Spiso				 * libalias tries to adjust the
3534165648Spiso				 * checksum it will actually screw it.
3535165648Spiso				 *
3536165648Spiso				 * [2] when libalias modify tcp's data
3537165648Spiso				 * content, full TCP checksum has to
3538165648Spiso				 * be recomputed: the problem is that
3539165648Spiso				 * libalias doesn't have any idea
3540165648Spiso				 * about checksum offloading To
3541165648Spiso				 * workaround this, we do not do
3542165648Spiso				 * checksumming in LibAlias, but only
3543165648Spiso				 * mark the packets in th_x2 field. If
3544165648Spiso				 * we receive a marked packet, we
3545165648Spiso				 * calculate correct checksum for it
3546165648Spiso				 * aware of offloading.  Why such a
3547165648Spiso				 * terrible hack instead of
3548165648Spiso				 * recalculating checksum for each
3549165648Spiso				 * packet?  Because the previous
3550165648Spiso				 * checksum was not checked!
3551165648Spiso				 * Recalculating checksums for EVERY
3552165648Spiso				 * packet will hide ALL transmission
3553165648Spiso				 * errors. Yes, marked packets still
3554165648Spiso				 * suffer from this problem. But,
3555165648Spiso				 * sigh, natd(8) has this problem,
3556165648Spiso				 * too.
3557165648Spiso				 *
3558165648Spiso				 * TODO: -make libalias mbuf aware (so
3559165648Spiso				 * it can handle delayed checksum and tso)
3560165648Spiso				 */
3561165648Spiso
3562165648Spiso				if (mcl->m_pkthdr.rcvif == NULL &&
3563165648Spiso				    mcl->m_pkthdr.csum_flags &
3564165648Spiso				    CSUM_DELAY_DATA)
3565165648Spiso					ldt = 1;
3566165648Spiso
3567165648Spiso				c = mtod(mcl, char *);
3568165648Spiso				if (oif == NULL)
3569165648Spiso					retval = LibAliasIn(t->lib, c,
3570165648Spiso					    MCLBYTES);
3571165648Spiso				else
3572165648Spiso					retval = LibAliasOut(t->lib, c,
3573165648Spiso					    MCLBYTES);
3574165648Spiso				if (retval != PKT_ALIAS_OK) {
3575165648Spiso					/* XXX - should i add some logging? */
3576165648Spiso					m_free(mcl);
3577165648Spiso				badnat:
3578165648Spiso					args->m = NULL;
3579165648Spiso					retval = IP_FW_DENY;
3580165648Spiso					goto done;
3581165648Spiso				}
3582165648Spiso				mcl->m_pkthdr.len = mcl->m_len =
3583165648Spiso				    ntohs(ip->ip_len);
3584165648Spiso
3585165648Spiso				/*
3586165648Spiso				 * XXX - libalias checksum offload
3587165648Spiso				 * 'duct tape' (see above)
3588165648Spiso				 */
3589165648Spiso
3590165648Spiso				if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
3591165648Spiso				    ip->ip_p == IPPROTO_TCP) {
3592165648Spiso					struct tcphdr 	*th;
3593165648Spiso
3594165648Spiso					th = (struct tcphdr *)(ip + 1);
3595165648Spiso					if (th->th_x2)
3596165648Spiso						ldt = 1;
3597165648Spiso				}
3598165648Spiso
3599165648Spiso				if (ldt) {
3600165648Spiso					struct tcphdr 	*th;
3601165648Spiso					struct udphdr 	*uh;
3602165648Spiso					u_short cksum;
3603165648Spiso
3604165648Spiso					ip->ip_len = ntohs(ip->ip_len);
3605165648Spiso					cksum = in_pseudo(
3606165648Spiso						ip->ip_src.s_addr,
3607165648Spiso						ip->ip_dst.s_addr,
3608165648Spiso						htons(ip->ip_p + ip->ip_len -
3609165648Spiso					            (ip->ip_hl << 2))
3610165648Spiso						);
3611165648Spiso
3612165648Spiso					switch (ip->ip_p) {
3613165648Spiso					case IPPROTO_TCP:
3614165648Spiso						th = (struct tcphdr *)(ip + 1);
3615165648Spiso						/*
3616165648Spiso						 * Maybe it was set in
3617165648Spiso						 * libalias...
3618165648Spiso						 */
3619165648Spiso						th->th_x2 = 0;
3620165648Spiso						th->th_sum = cksum;
3621165648Spiso						mcl->m_pkthdr.csum_data =
3622165648Spiso						    offsetof(struct tcphdr,
3623165648Spiso						    th_sum);
3624165648Spiso						break;
3625165648Spiso					case IPPROTO_UDP:
3626165648Spiso						uh = (struct udphdr *)(ip + 1);
3627165648Spiso						uh->uh_sum = cksum;
3628165648Spiso						mcl->m_pkthdr.csum_data =
3629165648Spiso						    offsetof(struct udphdr,
3630165648Spiso						    uh_sum);
3631165648Spiso						break;
3632165648Spiso					}
3633165648Spiso					/*
3634165648Spiso					 * No hw checksum offloading: do it
3635165648Spiso					 * by ourself.
3636165648Spiso					 */
3637165648Spiso					if ((mcl->m_pkthdr.csum_flags &
3638165648Spiso					     CSUM_DELAY_DATA) == 0) {
3639165648Spiso						in_delayed_cksum(mcl);
3640165648Spiso						mcl->m_pkthdr.csum_flags &=
3641165648Spiso						    ~CSUM_DELAY_DATA;
3642165648Spiso					}
3643165648Spiso					ip->ip_len = htons(ip->ip_len);
3644165648Spiso				}
3645165648Spiso
3646165648Spiso				if (args->eh == NULL) {
3647165648Spiso					ip->ip_len = ntohs(ip->ip_len);
3648165648Spiso					ip->ip_off = ntohs(ip->ip_off);
3649165648Spiso				}
3650165648Spiso
3651165648Spiso				args->m = mcl;
3652165648Spiso				retval = IP_FW_NAT;
3653165648Spiso				goto done;
3654165648Spiso			}
3655165750Spiso#endif
3656165648Spiso
365798943Sluigi			default:
365898943Sluigi				panic("-- unknown opcode %d\n", cmd->opcode);
365999622Sluigi			} /* end of switch() on opcodes */
366098943Sluigi
366199622Sluigi			if (cmd->len & F_NOT)
366299622Sluigi				match = !match;
366398943Sluigi
366499622Sluigi			if (match) {
366599622Sluigi				if (cmd->len & F_OR)
366699622Sluigi					skip_or = 1;
366799622Sluigi			} else {
366899622Sluigi				if (!(cmd->len & F_OR)) /* not an OR block, */
366999622Sluigi					break;		/* try next rule    */
367098943Sluigi			}
367198943Sluigi
367298943Sluigi		}	/* end of inner for, scan opcodes */
367398943Sluigi
367498965Sdfrnext_rule:;		/* try next rule		*/
3675105775Smaxim
367698943Sluigi	}		/* end of outer for, scan rules */
3677108258Smaxim	printf("ipfw: ouch!, skip past end of rules, denying packet\n");
3678138642Scsjp	IPFW_RUNLOCK(chain);
3679140224Sglebius	return (IP_FW_DENY);
368098943Sluigi
368199622Sluigidone:
368298943Sluigi	/* Update statistics */
368398943Sluigi	f->pcnt++;
3684115750Skbyanc	f->bcnt += pktlen;
3685150350Sandre	f->timestamp = time_uptime;
3686138642Scsjp	IPFW_RUNLOCK(chain);
3687140224Sglebius	return (retval);
368898943Sluigi
368998943Sluigipullup_failed:
369098943Sluigi	if (fw_verbose)
3691108258Smaxim		printf("ipfw: pullup failed\n");
3692140224Sglebius	return (IP_FW_DENY);
369398943Sluigi}
369498943Sluigi
369598943Sluigi/*
369698943Sluigi * When a rule is added/deleted, clear the next_rule pointers in all rules.
369798943Sluigi * These will be reconstructed on the fly as packets are matched.
369898943Sluigi */
369998943Sluigistatic void
3700120141Ssamflush_rule_ptrs(struct ip_fw_chain *chain)
370198943Sluigi{
370298943Sluigi	struct ip_fw *rule;
370398943Sluigi
3704138642Scsjp	IPFW_WLOCK_ASSERT(chain);
3705120141Ssam
3706120141Ssam	for (rule = chain->rules; rule; rule = rule->next)
370798943Sluigi		rule->next_rule = NULL;
370898943Sluigi}
370998943Sluigi
371098943Sluigi/*
371198943Sluigi * Add a new rule to the list. Copy the rule into a malloc'ed area, then
371298943Sluigi * possibly create a rule number and add the rule to the list.
371398943Sluigi * Update the rule_number in the input struct so the caller knows it as well.
371498943Sluigi */
371598943Sluigistatic int
3716120141Ssamadd_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule)
371798943Sluigi{
371898943Sluigi	struct ip_fw *rule, *f, *prev;
371998943Sluigi	int l = RULESIZE(input_rule);
372098943Sluigi
3721120141Ssam	if (chain->rules == NULL && input_rule->rulenum != IPFW_DEFAULT_RULE)
372298943Sluigi		return (EINVAL);
372398943Sluigi
3724105440Smux	rule = malloc(l, M_IPFW, M_NOWAIT | M_ZERO);
372598943Sluigi	if (rule == NULL)
372698943Sluigi		return (ENOSPC);
372798943Sluigi
372898943Sluigi	bcopy(input_rule, rule, l);
372998943Sluigi
373098943Sluigi	rule->next = NULL;
373198943Sluigi	rule->next_rule = NULL;
373298943Sluigi
373398943Sluigi	rule->pcnt = 0;
373498943Sluigi	rule->bcnt = 0;
373598943Sluigi	rule->timestamp = 0;
373698943Sluigi
3737138642Scsjp	IPFW_WLOCK(chain);
373898943Sluigi
3739120141Ssam	if (chain->rules == NULL) {	/* default rule */
3740120141Ssam		chain->rules = rule;
374198943Sluigi		goto done;
374298943Sluigi        }
374398943Sluigi
374498943Sluigi	/*
374598943Sluigi	 * If rulenum is 0, find highest numbered rule before the
374698943Sluigi	 * default rule, and add autoinc_step
374798943Sluigi	 */
374898943Sluigi	if (autoinc_step < 1)
374998943Sluigi		autoinc_step = 1;
375098943Sluigi	else if (autoinc_step > 1000)
375198943Sluigi		autoinc_step = 1000;
375298943Sluigi	if (rule->rulenum == 0) {
375398943Sluigi		/*
375498943Sluigi		 * locate the highest numbered rule before default
375598943Sluigi		 */
3756120141Ssam		for (f = chain->rules; f; f = f->next) {
375798943Sluigi			if (f->rulenum == IPFW_DEFAULT_RULE)
375898943Sluigi				break;
375998943Sluigi			rule->rulenum = f->rulenum;
376098943Sluigi		}
376198943Sluigi		if (rule->rulenum < IPFW_DEFAULT_RULE - autoinc_step)
376298943Sluigi			rule->rulenum += autoinc_step;
376398943Sluigi		input_rule->rulenum = rule->rulenum;
376498943Sluigi	}
376598943Sluigi
376698943Sluigi	/*
376798943Sluigi	 * Now insert the new rule in the right place in the sorted list.
376898943Sluigi	 */
3769120141Ssam	for (prev = NULL, f = chain->rules; f; prev = f, f = f->next) {
377098943Sluigi		if (f->rulenum > rule->rulenum) { /* found the location */
377198943Sluigi			if (prev) {
377298943Sluigi				rule->next = f;
377398943Sluigi				prev->next = rule;
377498943Sluigi			} else { /* head insert */
3775120141Ssam				rule->next = chain->rules;
3776120141Ssam				chain->rules = rule;
377798943Sluigi			}
377898943Sluigi			break;
377998943Sluigi		}
378098943Sluigi	}
3781120141Ssam	flush_rule_ptrs(chain);
378298943Sluigidone:
378398943Sluigi	static_count++;
378498943Sluigi	static_len += l;
3785138642Scsjp	IPFW_WUNLOCK(chain);
3786108258Smaxim	DEB(printf("ipfw: installed rule %d, static count now %d\n",
378798943Sluigi		rule->rulenum, static_count);)
378898943Sluigi	return (0);
378998943Sluigi}
379098943Sluigi
379198943Sluigi/**
3792120141Ssam * Remove a static rule (including derived * dynamic rules)
3793120141Ssam * and place it on the ``reap list'' for later reclamation.
379498943Sluigi * The caller is in charge of clearing rule pointers to avoid
379598943Sluigi * dangling pointers.
379698943Sluigi * @return a pointer to the next entry.
379798943Sluigi * Arguments are not checked, so they better be correct.
379898943Sluigi */
379998943Sluigistatic struct ip_fw *
3800169454Srwatsonremove_rule(struct ip_fw_chain *chain, struct ip_fw *rule,
3801169454Srwatson    struct ip_fw *prev)
380298943Sluigi{
380398943Sluigi	struct ip_fw *n;
380498943Sluigi	int l = RULESIZE(rule);
380598943Sluigi
3806138642Scsjp	IPFW_WLOCK_ASSERT(chain);
3807120141Ssam
380898943Sluigi	n = rule->next;
3809120141Ssam	IPFW_DYN_LOCK();
381098943Sluigi	remove_dyn_rule(rule, NULL /* force removal */);
3811120141Ssam	IPFW_DYN_UNLOCK();
381298943Sluigi	if (prev == NULL)
3813120141Ssam		chain->rules = n;
381498943Sluigi	else
381598943Sluigi		prev->next = n;
381698943Sluigi	static_count--;
381798943Sluigi	static_len -= l;
381898943Sluigi
3819120141Ssam	rule->next = chain->reap;
3820120141Ssam	chain->reap = rule;
3821120141Ssam
382298943Sluigi	return n;
382398943Sluigi}
382498943Sluigi
3825120141Ssam/**
3826120141Ssam * Reclaim storage associated with a list of rules.  This is
3827120141Ssam * typically the list created using remove_rule.
3828120141Ssam */
3829120141Ssamstatic void
3830120141Ssamreap_rules(struct ip_fw *head)
3831120141Ssam{
3832120141Ssam	struct ip_fw *rule;
3833120141Ssam
3834120141Ssam	while ((rule = head) != NULL) {
3835120141Ssam		head = head->next;
3836120141Ssam		if (DUMMYNET_LOADED)
3837120141Ssam			ip_dn_ruledel_ptr(rule);
3838120141Ssam		free(rule, M_IPFW);
3839120141Ssam	}
3840120141Ssam}
3841120141Ssam
384298943Sluigi/*
3843120141Ssam * Remove all rules from a chain (except rules in set RESVD_SET
3844120141Ssam * unless kill_default = 1).  The caller is responsible for
3845120141Ssam * reclaiming storage for the rules left in chain->reap.
384698943Sluigi */
384798943Sluigistatic void
3848120141Ssamfree_chain(struct ip_fw_chain *chain, int kill_default)
384998943Sluigi{
3850117654Sluigi	struct ip_fw *prev, *rule;
385198943Sluigi
3852138642Scsjp	IPFW_WLOCK_ASSERT(chain);
3853120141Ssam
3854120141Ssam	flush_rule_ptrs(chain); /* more efficient to do outside the loop */
3855120141Ssam	for (prev = NULL, rule = chain->rules; rule ; )
3856117654Sluigi		if (kill_default || rule->set != RESVD_SET)
3857120141Ssam			rule = remove_rule(chain, rule, prev);
3858117654Sluigi		else {
3859117654Sluigi			prev = rule;
3860117654Sluigi			rule = rule->next;
3861117654Sluigi		}
386298943Sluigi}
386398943Sluigi
386498943Sluigi/**
3865101628Sluigi * Remove all rules with given number, and also do set manipulation.
3866117654Sluigi * Assumes chain != NULL && *chain != NULL.
3867101628Sluigi *
3868101978Sluigi * The argument is an u_int32_t. The low 16 bit are the rule or set number,
3869101978Sluigi * the next 8 bits are the new set, the top 8 bits are the command:
3870105775Smaxim *
3871101978Sluigi *	0	delete rules with given number
3872101978Sluigi *	1	delete rules with given set number
3873101978Sluigi *	2	move rules with given number to new set
3874101978Sluigi *	3	move rules with given set number to new set
3875101978Sluigi *	4	swap sets with given numbers
3876170923Smaxim *	5	delete rules with given number and with given set number
387798943Sluigi */
387898943Sluigistatic int
3879120141Ssamdel_entry(struct ip_fw_chain *chain, u_int32_t arg)
388098943Sluigi{
3881120141Ssam	struct ip_fw *prev = NULL, *rule;
3882117654Sluigi	u_int16_t rulenum;	/* rule or old_set */
3883101978Sluigi	u_int8_t cmd, new_set;
388498943Sluigi
3885101628Sluigi	rulenum = arg & 0xffff;
3886101978Sluigi	cmd = (arg >> 24) & 0xff;
3887101978Sluigi	new_set = (arg >> 16) & 0xff;
3888101628Sluigi
3889170923Smaxim	if (cmd > 5 || new_set > RESVD_SET)
389098943Sluigi		return EINVAL;
3891170923Smaxim	if (cmd == 0 || cmd == 2 || cmd == 5) {
3892117654Sluigi		if (rulenum >= IPFW_DEFAULT_RULE)
3893101978Sluigi			return EINVAL;
3894101978Sluigi	} else {
3895117654Sluigi		if (rulenum > RESVD_SET)	/* old_set */
3896101978Sluigi			return EINVAL;
3897101978Sluigi	}
3898105775Smaxim
3899138642Scsjp	IPFW_WLOCK(chain);
3900120141Ssam	rule = chain->rules;
3901120141Ssam	chain->reap = NULL;
3902101628Sluigi	switch (cmd) {
3903101978Sluigi	case 0:	/* delete rules with given number */
3904101628Sluigi		/*
3905101628Sluigi		 * locate first rule to delete
3906101628Sluigi		 */
3907117654Sluigi		for (; rule->rulenum < rulenum; prev = rule, rule = rule->next)
3908101628Sluigi			;
3909120141Ssam		if (rule->rulenum != rulenum) {
3910138642Scsjp			IPFW_WUNLOCK(chain);
3911101628Sluigi			return EINVAL;
3912120141Ssam		}
3913101628Sluigi
3914101628Sluigi		/*
3915101978Sluigi		 * flush pointers outside the loop, then delete all matching
3916101978Sluigi		 * rules. prev remains the same throughout the cycle.
3917101628Sluigi		 */
3918120141Ssam		flush_rule_ptrs(chain);
3919117654Sluigi		while (rule->rulenum == rulenum)
3920120141Ssam			rule = remove_rule(chain, rule, prev);
3921101628Sluigi		break;
3922101628Sluigi
3923101978Sluigi	case 1:	/* delete all rules with given set number */
3924120141Ssam		flush_rule_ptrs(chain);
3925120141Ssam		rule = chain->rules;
3926117654Sluigi		while (rule->rulenum < IPFW_DEFAULT_RULE)
3927101628Sluigi			if (rule->set == rulenum)
3928120141Ssam				rule = remove_rule(chain, rule, prev);
3929101628Sluigi			else {
3930101628Sluigi				prev = rule;
3931101628Sluigi				rule = rule->next;
3932101628Sluigi			}
3933101628Sluigi		break;
3934101628Sluigi
3935101978Sluigi	case 2:	/* move rules with given number to new set */
3936120141Ssam		rule = chain->rules;
3937117654Sluigi		for (; rule->rulenum < IPFW_DEFAULT_RULE; rule = rule->next)
3938101978Sluigi			if (rule->rulenum == rulenum)
3939101978Sluigi				rule->set = new_set;
3940101628Sluigi		break;
3941101628Sluigi
3942101978Sluigi	case 3: /* move rules with given set number to new set */
3943117654Sluigi		for (; rule->rulenum < IPFW_DEFAULT_RULE; rule = rule->next)
3944101978Sluigi			if (rule->set == rulenum)
3945101978Sluigi				rule->set = new_set;
3946101628Sluigi		break;
3947101978Sluigi
3948101978Sluigi	case 4: /* swap two sets */
3949117654Sluigi		for (; rule->rulenum < IPFW_DEFAULT_RULE; rule = rule->next)
3950101978Sluigi			if (rule->set == rulenum)
3951101978Sluigi				rule->set = new_set;
3952101978Sluigi			else if (rule->set == new_set)
3953101978Sluigi				rule->set = rulenum;
3954101978Sluigi		break;
3955170923Smaxim	case 5: /* delete rules with given number and with given set number.
3956170923Smaxim		 * rulenum - given rule number;
3957170923Smaxim		 * new_set - given set number.
3958170923Smaxim		 */
3959170923Smaxim		for (; rule->rulenum < rulenum; prev = rule, rule = rule->next)
3960170923Smaxim			;
3961170923Smaxim		if (rule->rulenum != rulenum) {
3962170923Smaxim			IPFW_WUNLOCK(chain);
3963170923Smaxim			return (EINVAL);
3964170923Smaxim		}
3965170923Smaxim		flush_rule_ptrs(chain);
3966170923Smaxim		while (rule->rulenum == rulenum) {
3967170923Smaxim			if (rule->set == new_set)
3968170923Smaxim				rule = remove_rule(chain, rule, prev);
3969170923Smaxim			else {
3970170923Smaxim				prev = rule;
3971170923Smaxim				rule = rule->next;
3972170923Smaxim			}
3973170923Smaxim		}
3974101628Sluigi	}
3975120141Ssam	/*
3976120141Ssam	 * Look for rules to reclaim.  We grab the list before
3977120141Ssam	 * releasing the lock then reclaim them w/o the lock to
3978120141Ssam	 * avoid a LOR with dummynet.
3979120141Ssam	 */
3980120141Ssam	rule = chain->reap;
3981120141Ssam	chain->reap = NULL;
3982138642Scsjp	IPFW_WUNLOCK(chain);
3983120141Ssam	if (rule)
3984120141Ssam		reap_rules(rule);
398598943Sluigi	return 0;
398698943Sluigi}
398798943Sluigi
398898943Sluigi/*
398998943Sluigi * Clear counters for a specific rule.
3990120141Ssam * The enclosing "table" is assumed locked.
399198943Sluigi */
399298943Sluigistatic void
399398943Sluigiclear_counters(struct ip_fw *rule, int log_only)
399498943Sluigi{
399598943Sluigi	ipfw_insn_log *l = (ipfw_insn_log *)ACTION_PTR(rule);
399698943Sluigi
399798943Sluigi	if (log_only == 0) {
399898943Sluigi		rule->bcnt = rule->pcnt = 0;
399998943Sluigi		rule->timestamp = 0;
400098943Sluigi	}
400198943Sluigi	if (l->o.opcode == O_LOG)
400298943Sluigi		l->log_left = l->max_log;
400398943Sluigi}
400498943Sluigi
400598943Sluigi/**
400698943Sluigi * Reset some or all counters on firewall rules.
4007170923Smaxim * The argument `arg' is an u_int32_t. The low 16 bit are the rule number,
4008170923Smaxim * the next 8 bits are the set number, the top 8 bits are the command:
4009170923Smaxim *	0	work with rules from all set's;
4010170923Smaxim *	1	work with rules only from specified set.
4011170923Smaxim * Specified rule number is zero if we want to clear all entries.
4012170923Smaxim * log_only is 1 if we only want to reset logs, zero otherwise.
401398943Sluigi */
401498943Sluigistatic int
4015170923Smaximzero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only)
401698943Sluigi{
401798943Sluigi	struct ip_fw *rule;
401898943Sluigi	char *msg;
401998943Sluigi
4020170923Smaxim	uint16_t rulenum = arg & 0xffff;
4021170923Smaxim	uint8_t set = (arg >> 16) & 0xff;
4022170923Smaxim	uint8_t cmd = (arg >> 24) & 0xff;
4023170923Smaxim
4024170923Smaxim	if (cmd > 1)
4025170923Smaxim		return (EINVAL);
4026170923Smaxim	if (cmd == 1 && set > RESVD_SET)
4027170923Smaxim		return (EINVAL);
4028170923Smaxim
4029138642Scsjp	IPFW_WLOCK(chain);
403098943Sluigi	if (rulenum == 0) {
403198943Sluigi		norule_counter = 0;
4032170923Smaxim		for (rule = chain->rules; rule; rule = rule->next) {
4033170923Smaxim			/* Skip rules from another set. */
4034170923Smaxim			if (cmd == 1 && rule->set != set)
4035170923Smaxim				continue;
403698943Sluigi			clear_counters(rule, log_only);
4037170923Smaxim		}
403898943Sluigi		msg = log_only ? "ipfw: All logging counts reset.\n" :
4039170923Smaxim		    "ipfw: Accounting cleared.\n";
404098943Sluigi	} else {
404198943Sluigi		int cleared = 0;
404298943Sluigi		/*
404398943Sluigi		 * We can have multiple rules with the same number, so we
404498943Sluigi		 * need to clear them all.
404598943Sluigi		 */
4046120141Ssam		for (rule = chain->rules; rule; rule = rule->next)
404798943Sluigi			if (rule->rulenum == rulenum) {
404898943Sluigi				while (rule && rule->rulenum == rulenum) {
4049170923Smaxim					if (cmd == 0 || rule->set == set)
4050170923Smaxim						clear_counters(rule, log_only);
405198943Sluigi					rule = rule->next;
405298943Sluigi				}
405398943Sluigi				cleared = 1;
405498943Sluigi				break;
405598943Sluigi			}
4056120141Ssam		if (!cleared) {	/* we did not find any matching rules */
4057138642Scsjp			IPFW_WUNLOCK(chain);
405898943Sluigi			return (EINVAL);
4059120141Ssam		}
406098943Sluigi		msg = log_only ? "ipfw: Entry %d logging count reset.\n" :
4061170923Smaxim		    "ipfw: Entry %d cleared.\n";
406298943Sluigi	}
4063138642Scsjp	IPFW_WUNLOCK(chain);
4064120141Ssam
406598943Sluigi	if (fw_verbose)
406698943Sluigi		log(LOG_SECURITY | LOG_NOTICE, msg, rulenum);
406798943Sluigi	return (0);
406898943Sluigi}
406998943Sluigi
407098943Sluigi/*
407198943Sluigi * Check validity of the structure before insert.
407298943Sluigi * Fortunately rules are simple, so this mostly need to check rule sizes.
407398943Sluigi */
407498943Sluigistatic int
407598943Sluigicheck_ipfw_struct(struct ip_fw *rule, int size)
407698943Sluigi{
407798943Sluigi	int l, cmdlen = 0;
407898943Sluigi	int have_action=0;
407998943Sluigi	ipfw_insn *cmd;
408098943Sluigi
408198943Sluigi	if (size < sizeof(*rule)) {
408299622Sluigi		printf("ipfw: rule too short\n");
408398943Sluigi		return (EINVAL);
408498943Sluigi	}
408598943Sluigi	/* first, check for valid size */
408698943Sluigi	l = RULESIZE(rule);
408798943Sluigi	if (l != size) {
408899622Sluigi		printf("ipfw: size mismatch (have %d want %d)\n", size, l);
408998943Sluigi		return (EINVAL);
409098943Sluigi	}
4091135977Sgreen	if (rule->act_ofs >= rule->cmd_len) {
4092135977Sgreen		printf("ipfw: bogus action offset (%u > %u)\n",
4093135977Sgreen		    rule->act_ofs, rule->cmd_len - 1);
4094135977Sgreen		return (EINVAL);
4095135977Sgreen	}
409698943Sluigi	/*
409798943Sluigi	 * Now go for the individual checks. Very simple ones, basically only
409898943Sluigi	 * instruction sizes.
409998943Sluigi	 */
410098943Sluigi	for (l = rule->cmd_len, cmd = rule->cmd ;
410198943Sluigi			l > 0 ; l -= cmdlen, cmd += cmdlen) {
410298943Sluigi		cmdlen = F_LEN(cmd);
410398943Sluigi		if (cmdlen > l) {
410499622Sluigi			printf("ipfw: opcode %d size truncated\n",
410598943Sluigi			    cmd->opcode);
410698943Sluigi			return EINVAL;
410798943Sluigi		}
410899622Sluigi		DEB(printf("ipfw: opcode %d\n", cmd->opcode);)
410998943Sluigi		switch (cmd->opcode) {
411098943Sluigi		case O_PROBE_STATE:
411198943Sluigi		case O_KEEP_STATE:
411298943Sluigi		case O_PROTO:
411398943Sluigi		case O_IP_SRC_ME:
411498943Sluigi		case O_IP_DST_ME:
411598943Sluigi		case O_LAYER2:
411698943Sluigi		case O_IN:
411798943Sluigi		case O_FRAG:
4118136073Sgreen		case O_DIVERTED:
411998943Sluigi		case O_IPOPT:
412098943Sluigi		case O_IPTOS:
412199475Sluigi		case O_IPPRECEDENCE:
412298943Sluigi		case O_IPVER:
412398943Sluigi		case O_TCPWIN:
412498943Sluigi		case O_TCPFLAGS:
412598943Sluigi		case O_TCPOPTS:
412698943Sluigi		case O_ESTAB:
4127112250Scjc		case O_VERREVPATH:
4128128575Sandre		case O_VERSRCREACH:
4129133387Sandre		case O_ANTISPOOF:
4130117241Sluigi		case O_IPSEC:
4131150122Sbz#ifdef INET6
4132145246Sbrooks		case O_IP6_SRC_ME:
4133145246Sbrooks		case O_IP6_DST_ME:
4134145246Sbrooks		case O_EXT_HDR:
4135145246Sbrooks		case O_IP6:
4136150122Sbz#endif
4137146894Smlaier		case O_IP4:
4138158879Soleg		case O_TAG:
413998943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn))
414098943Sluigi				goto bad_size;
414198943Sluigi			break;
414298943Sluigi
414398943Sluigi		case O_UID:
414498943Sluigi		case O_GID:
4145133600Scsjp		case O_JAIL:
414698943Sluigi		case O_IP_SRC:
414798943Sluigi		case O_IP_DST:
414898943Sluigi		case O_TCPSEQ:
414998943Sluigi		case O_TCPACK:
415098943Sluigi		case O_PROB:
415198943Sluigi		case O_ICMPTYPE:
415298943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
415398943Sluigi				goto bad_size;
415498943Sluigi			break;
415598943Sluigi
415698943Sluigi		case O_LIMIT:
415798943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn_limit))
415898943Sluigi				goto bad_size;
415998943Sluigi			break;
416098943Sluigi
416198943Sluigi		case O_LOG:
416298943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn_log))
416398943Sluigi				goto bad_size;
4164105775Smaxim
416598943Sluigi			((ipfw_insn_log *)cmd)->log_left =
416698943Sluigi			    ((ipfw_insn_log *)cmd)->max_log;
416798943Sluigi
416898943Sluigi			break;
416998943Sluigi
417098943Sluigi		case O_IP_SRC_MASK:
417198943Sluigi		case O_IP_DST_MASK:
4172117327Sluigi			/* only odd command lengths */
4173117327Sluigi			if ( !(cmdlen & 1) || cmdlen > 31)
417498943Sluigi				goto bad_size;
417598943Sluigi			break;
417698943Sluigi
417798943Sluigi		case O_IP_SRC_SET:
417898943Sluigi		case O_IP_DST_SET:
417998943Sluigi			if (cmd->arg1 == 0 || cmd->arg1 > 256) {
418099622Sluigi				printf("ipfw: invalid set size %d\n",
418198943Sluigi					cmd->arg1);
418298943Sluigi				return EINVAL;
418398943Sluigi			}
418498943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
418598943Sluigi			    (cmd->arg1+31)/32 )
418698943Sluigi				goto bad_size;
418798943Sluigi			break;
418898943Sluigi
4189130281Sru		case O_IP_SRC_LOOKUP:
4190130281Sru		case O_IP_DST_LOOKUP:
4191130281Sru			if (cmd->arg1 >= IPFW_TABLES_MAX) {
4192130281Sru				printf("ipfw: invalid table number %d\n",
4193130281Sru				    cmd->arg1);
4194130281Sru				return (EINVAL);
4195130281Sru			}
4196130281Sru			if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
4197130281Sru			    cmdlen != F_INSN_SIZE(ipfw_insn_u32))
4198130281Sru				goto bad_size;
4199130281Sru			break;
4200130281Sru
420198943Sluigi		case O_MACADDR2:
420298943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
420398943Sluigi				goto bad_size;
420498943Sluigi			break;
420598943Sluigi
4206117468Sluigi		case O_NOP:
4207116690Sluigi		case O_IPID:
4208116690Sluigi		case O_IPTTL:
4209116690Sluigi		case O_IPLEN:
4210136075Sgreen		case O_TCPDATALEN:
4211158879Soleg		case O_TAGGED:
4212116690Sluigi			if (cmdlen < 1 || cmdlen > 31)
4213116690Sluigi				goto bad_size;
4214116690Sluigi			break;
4215116690Sluigi
421698943Sluigi		case O_MAC_TYPE:
421798943Sluigi		case O_IP_SRCPORT:
4218102086Sluigi		case O_IP_DSTPORT: /* XXX artificial limit, 30 port pairs */
4219102086Sluigi			if (cmdlen < 2 || cmdlen > 31)
422098943Sluigi				goto bad_size;
422198943Sluigi			break;
422298943Sluigi
422398943Sluigi		case O_RECV:
422498943Sluigi		case O_XMIT:
422598943Sluigi		case O_VIA:
422698943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn_if))
422798943Sluigi				goto bad_size;
422898943Sluigi			break;
422998943Sluigi
4230136071Sgreen		case O_ALTQ:
4231136071Sgreen			if (cmdlen != F_INSN_SIZE(ipfw_insn_altq))
4232136071Sgreen				goto bad_size;
4233136071Sgreen			break;
4234136071Sgreen
423598943Sluigi		case O_PIPE:
423698943Sluigi		case O_QUEUE:
4237152917Sglebius			if (cmdlen != F_INSN_SIZE(ipfw_insn))
423898943Sluigi				goto bad_size;
423998943Sluigi			goto check_action;
424098943Sluigi
424199475Sluigi		case O_FORWARD_IP:
4242135168Sandre#ifdef	IPFIREWALL_FORWARD
424398943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn_sa))
424498943Sluigi				goto bad_size;
424598943Sluigi			goto check_action;
4246135168Sandre#else
4247135168Sandre			return EINVAL;
4248135168Sandre#endif
424998943Sluigi
4250134823Sglebius		case O_DIVERT:
4251134823Sglebius		case O_TEE:
4252136714Sandre			if (ip_divert_ptr == NULL)
4253136714Sandre				return EINVAL;
4254141383Sglebius			else
4255141383Sglebius				goto check_size;
4256141351Sglebius		case O_NETGRAPH:
4257141351Sglebius		case O_NGTEE:
4258141351Sglebius			if (!NG_IPFW_LOADED)
4259141351Sglebius				return EINVAL;
4260141383Sglebius			else
4261141383Sglebius				goto check_size;
4262165648Spiso		case O_NAT:
4263165802Spiso#ifdef IPFIREWALL_NAT
4264165648Spiso			if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
4265165648Spiso 				goto bad_size;
4266165648Spiso 			goto check_action;
4267165802Spiso#else
4268165802Spiso			return EINVAL;
4269165802Spiso#endif
427099475Sluigi		case O_FORWARD_MAC: /* XXX not implemented yet */
427198943Sluigi		case O_CHECK_STATE:
427298943Sluigi		case O_COUNT:
427398943Sluigi		case O_ACCEPT:
427498943Sluigi		case O_DENY:
427598943Sluigi		case O_REJECT:
4276150122Sbz#ifdef INET6
4277149020Sbz		case O_UNREACH6:
4278150122Sbz#endif
427998943Sluigi		case O_SKIPTO:
4280141383Sglebiuscheck_size:
428198943Sluigi			if (cmdlen != F_INSN_SIZE(ipfw_insn))
428298943Sluigi				goto bad_size;
428398943Sluigicheck_action:
428498943Sluigi			if (have_action) {
428599622Sluigi				printf("ipfw: opcode %d, multiple actions"
428698943Sluigi					" not allowed\n",
428798943Sluigi					cmd->opcode);
428898943Sluigi				return EINVAL;
428998943Sluigi			}
429098943Sluigi			have_action = 1;
429198943Sluigi			if (l != cmdlen) {
429299622Sluigi				printf("ipfw: opcode %d, action must be"
429398943Sluigi					" last opcode\n",
429498943Sluigi					cmd->opcode);
429598943Sluigi				return EINVAL;
429698943Sluigi			}
429798943Sluigi			break;
4298150122Sbz#ifdef INET6
4299145246Sbrooks		case O_IP6_SRC:
4300145246Sbrooks		case O_IP6_DST:
4301145246Sbrooks			if (cmdlen != F_INSN_SIZE(struct in6_addr) +
4302145246Sbrooks			    F_INSN_SIZE(ipfw_insn))
4303145246Sbrooks				goto bad_size;
4304145246Sbrooks			break;
4305145246Sbrooks
4306145246Sbrooks		case O_FLOW6ID:
4307145246Sbrooks			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32) +
4308145246Sbrooks			    ((ipfw_insn_u32 *)cmd)->o.arg1)
4309145246Sbrooks				goto bad_size;
4310145246Sbrooks			break;
4311145246Sbrooks
4312145246Sbrooks		case O_IP6_SRC_MASK:
4313145246Sbrooks		case O_IP6_DST_MASK:
4314145246Sbrooks			if ( !(cmdlen & 1) || cmdlen > 127)
4315145246Sbrooks				goto bad_size;
4316145246Sbrooks			break;
4317145246Sbrooks		case O_ICMP6TYPE:
4318145246Sbrooks			if( cmdlen != F_INSN_SIZE( ipfw_insn_icmp6 ) )
4319145246Sbrooks				goto bad_size;
4320145246Sbrooks			break;
4321150122Sbz#endif
4322145246Sbrooks
432398943Sluigi		default:
4324150122Sbz			switch (cmd->opcode) {
4325150122Sbz#ifndef INET6
4326150122Sbz			case O_IP6_SRC_ME:
4327150122Sbz			case O_IP6_DST_ME:
4328150122Sbz			case O_EXT_HDR:
4329150122Sbz			case O_IP6:
4330150122Sbz			case O_UNREACH6:
4331150122Sbz			case O_IP6_SRC:
4332150122Sbz			case O_IP6_DST:
4333150122Sbz			case O_FLOW6ID:
4334150122Sbz			case O_IP6_SRC_MASK:
4335150122Sbz			case O_IP6_DST_MASK:
4336150122Sbz			case O_ICMP6TYPE:
4337150122Sbz				printf("ipfw: no IPv6 support in kernel\n");
4338150122Sbz				return EPROTONOSUPPORT;
4339150122Sbz#endif
4340150122Sbz			default:
4341150122Sbz				printf("ipfw: opcode %d, unknown opcode\n",
4342150122Sbz					cmd->opcode);
4343150122Sbz				return EINVAL;
4344150122Sbz			}
434598943Sluigi		}
434698943Sluigi	}
434798943Sluigi	if (have_action == 0) {
434899622Sluigi		printf("ipfw: missing action\n");
434998943Sluigi		return EINVAL;
435098943Sluigi	}
435198943Sluigi	return 0;
435298943Sluigi
435398943Sluigibad_size:
435499622Sluigi	printf("ipfw: opcode %d size %d wrong\n",
435598943Sluigi		cmd->opcode, cmdlen);
435698943Sluigi	return EINVAL;
435798943Sluigi}
435898943Sluigi
4359120141Ssam/*
4360120141Ssam * Copy the static and dynamic rules to the supplied buffer
4361120141Ssam * and return the amount of space actually used.
4362120141Ssam */
4363120141Ssamstatic size_t
4364120141Ssamipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
4365120141Ssam{
4366120141Ssam	char *bp = buf;
4367120141Ssam	char *ep = bp + space;
4368120141Ssam	struct ip_fw *rule;
4369120141Ssam	int i;
4370168328Sjulian	time_t	boot_seconds;
437198943Sluigi
4372168328Sjulian        boot_seconds = boottime.tv_sec;
4373120141Ssam	/* XXX this can take a long time and locking will block packet flow */
4374138642Scsjp	IPFW_RLOCK(chain);
4375120141Ssam	for (rule = chain->rules; rule ; rule = rule->next) {
4376120141Ssam		/*
4377120141Ssam		 * Verify the entry fits in the buffer in case the
4378120141Ssam		 * rules changed between calculating buffer space and
4379120141Ssam		 * now.  This would be better done using a generation
4380120141Ssam		 * number but should suffice for now.
4381120141Ssam		 */
4382120141Ssam		i = RULESIZE(rule);
4383120141Ssam		if (bp + i <= ep) {
4384120141Ssam			bcopy(rule, bp, i);
4385168328Sjulian			/*
4386168328Sjulian			 * XXX HACK. Store the disable mask in the "next" pointer
4387168328Sjulian			 * in a wild attempt to keep the ABI the same.
4388168328Sjulian			 * Why do we do this on EVERY rule?
4389168328Sjulian			 */
4390120141Ssam			bcopy(&set_disable, &(((struct ip_fw *)bp)->next_rule),
4391120141Ssam			    sizeof(set_disable));
4392168328Sjulian			if (((struct ip_fw *)bp)->timestamp)
4393168328Sjulian				((struct ip_fw *)bp)->timestamp += boot_seconds;
4394120141Ssam			bp += i;
4395120141Ssam		}
4396120141Ssam	}
4397138642Scsjp	IPFW_RUNLOCK(chain);
4398120141Ssam	if (ipfw_dyn_v) {
4399120141Ssam		ipfw_dyn_rule *p, *last = NULL;
4400120141Ssam
4401120141Ssam		IPFW_DYN_LOCK();
4402120141Ssam		for (i = 0 ; i < curr_dyn_buckets; i++)
4403120141Ssam			for (p = ipfw_dyn_v[i] ; p != NULL; p = p->next) {
4404120141Ssam				if (bp + sizeof *p <= ep) {
4405120141Ssam					ipfw_dyn_rule *dst =
4406120141Ssam						(ipfw_dyn_rule *)bp;
4407120141Ssam					bcopy(p, dst, sizeof *p);
4408120141Ssam					bcopy(&(p->rule->rulenum), &(dst->rule),
4409120141Ssam					    sizeof(p->rule->rulenum));
4410120141Ssam					/*
4411170923Smaxim					 * store set number into high word of
4412170923Smaxim					 * dst->rule pointer.
4413170923Smaxim					 */
4414171989Smaxim					bcopy(&(p->rule->set),
4415171989Smaxim					    (char *)&dst->rule +
4416170923Smaxim					    sizeof(p->rule->rulenum),
4417170923Smaxim					    sizeof(p->rule->set));
4418170923Smaxim					/*
4419120141Ssam					 * store a non-null value in "next".
4420120141Ssam					 * The userland code will interpret a
4421120141Ssam					 * NULL here as a marker
4422120141Ssam					 * for the last dynamic rule.
4423120141Ssam					 */
4424120141Ssam					bcopy(&dst, &dst->next, sizeof(dst));
4425120141Ssam					last = dst;
4426120141Ssam					dst->expire =
4427150350Sandre					    TIME_LEQ(dst->expire, time_uptime) ?
4428150350Sandre						0 : dst->expire - time_uptime ;
4429120141Ssam					bp += sizeof(ipfw_dyn_rule);
4430120141Ssam				}
4431120141Ssam			}
4432120141Ssam		IPFW_DYN_UNLOCK();
4433120141Ssam		if (last != NULL) /* mark last dynamic rule */
4434120141Ssam			bzero(&last->next, sizeof(last));
4435120141Ssam	}
4436120141Ssam	return (bp - (char *)buf);
4437120141Ssam}
4438120141Ssam
4439120141Ssam
444098943Sluigi/**
444198943Sluigi * {set|get}sockopt parser.
444298943Sluigi */
444398943Sluigistatic int
444498943Sluigiipfw_ctl(struct sockopt *sopt)
444598943Sluigi{
4446120141Ssam#define	RULE_MAXSIZE	(256*sizeof(u_int32_t))
4447170923Smaxim	int error;
444898943Sluigi	size_t size;
4449120141Ssam	struct ip_fw *buf, *rule;
4450120141Ssam	u_int32_t rulenum[2];
445198943Sluigi
4452164033Srwatson	error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
4453129720Scsjp	if (error)
4454129720Scsjp		return (error);
4455129720Scsjp
445698943Sluigi	/*
445798943Sluigi	 * Disallow modifications in really-really secure mode, but still allow
445898943Sluigi	 * the logging counters to be reset.
445998943Sluigi	 */
446098943Sluigi	if (sopt->sopt_name == IP_FW_ADD ||
446198943Sluigi	    (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) {
446298943Sluigi		error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
446398943Sluigi		if (error)
446498943Sluigi			return (error);
446598943Sluigi	}
446698943Sluigi
446798943Sluigi	error = 0;
446898943Sluigi
446998943Sluigi	switch (sopt->sopt_name) {
447098943Sluigi	case IP_FW_GET:
447198943Sluigi		/*
447298943Sluigi		 * pass up a copy of the current rules. Static rules
447398943Sluigi		 * come first (the last of which has number IPFW_DEFAULT_RULE),
447498943Sluigi		 * followed by a possibly empty list of dynamic rule.
447598943Sluigi		 * The last dynamic rule has NULL in the "next" field.
4476120141Ssam		 *
4477120141Ssam		 * Note that the calculated size is used to bound the
4478120141Ssam		 * amount of data returned to the user.  The rule set may
4479120141Ssam		 * change between calculating the size and returning the
4480120141Ssam		 * data in which case we'll just return what fits.
448198943Sluigi		 */
448298943Sluigi		size = static_len;	/* size of static rules */
448398943Sluigi		if (ipfw_dyn_v)		/* add size of dyn.rules */
448498943Sluigi			size += (dyn_count * sizeof(ipfw_dyn_rule));
448598943Sluigi
448698943Sluigi		/*
448798943Sluigi		 * XXX todo: if the user passes a short length just to know
448898943Sluigi		 * how much room is needed, do not bother filling up the
448998943Sluigi		 * buffer, just jump to the sooptcopyout.
449098943Sluigi		 */
4491111119Simp		buf = malloc(size, M_TEMP, M_WAITOK);
4492120141Ssam		error = sooptcopyout(sopt, buf,
4493120141Ssam				ipfw_getrules(&layer3_chain, buf, size));
449498943Sluigi		free(buf, M_TEMP);
449598943Sluigi		break;
449698943Sluigi
449798943Sluigi	case IP_FW_FLUSH:
449898943Sluigi		/*
449998943Sluigi		 * Normally we cannot release the lock on each iteration.
450098943Sluigi		 * We could do it here only because we start from the head all
450198943Sluigi		 * the times so there is no risk of missing some entries.
450298943Sluigi		 * On the other hand, the risk is that we end up with
450398943Sluigi		 * a very inconsistent ruleset, so better keep the lock
450498943Sluigi		 * around the whole cycle.
4505105775Smaxim		 *
450698943Sluigi		 * XXX this code can be improved by resetting the head of
450798943Sluigi		 * the list to point to the default rule, and then freeing
450898943Sluigi		 * the old list without the need for a lock.
450998943Sluigi		 */
451098943Sluigi
4511138642Scsjp		IPFW_WLOCK(&layer3_chain);
4512120141Ssam		layer3_chain.reap = NULL;
451398943Sluigi		free_chain(&layer3_chain, 0 /* keep default rule */);
4514160920Soleg		rule = layer3_chain.reap;
4515160920Soleg		layer3_chain.reap = NULL;
4516138642Scsjp		IPFW_WUNLOCK(&layer3_chain);
4517160920Soleg		if (rule != NULL)
4518120141Ssam			reap_rules(rule);
451998943Sluigi		break;
452098943Sluigi
452198943Sluigi	case IP_FW_ADD:
4522120141Ssam		rule = malloc(RULE_MAXSIZE, M_TEMP, M_WAITOK);
4523120141Ssam		error = sooptcopyin(sopt, rule, RULE_MAXSIZE,
452498943Sluigi			sizeof(struct ip_fw) );
4525120141Ssam		if (error == 0)
4526120141Ssam			error = check_ipfw_struct(rule, sopt->sopt_valsize);
4527120141Ssam		if (error == 0) {
4528120141Ssam			error = add_rule(&layer3_chain, rule);
4529120141Ssam			size = RULESIZE(rule);
4530120141Ssam			if (!error && sopt->sopt_dir == SOPT_GET)
4531120141Ssam				error = sooptcopyout(sopt, rule, size);
4532120141Ssam		}
4533120141Ssam		free(rule, M_TEMP);
453498943Sluigi		break;
453598943Sluigi
4536101978Sluigi	case IP_FW_DEL:
4537101628Sluigi		/*
4538101978Sluigi		 * IP_FW_DEL is used for deleting single rules or sets,
4539101978Sluigi		 * and (ab)used to atomically manipulate sets. Argument size
4540101978Sluigi		 * is used to distinguish between the two:
4541101978Sluigi		 *    sizeof(u_int32_t)
4542101978Sluigi		 *	delete single rule or set of rules,
4543101978Sluigi		 *	or reassign rules (or sets) to a different set.
4544101978Sluigi		 *    2*sizeof(u_int32_t)
4545101978Sluigi		 *	atomic disable/enable sets.
4546101978Sluigi		 *	first u_int32_t contains sets to be disabled,
4547101978Sluigi		 *	second u_int32_t contains sets to be enabled.
4548101628Sluigi		 */
4549120141Ssam		error = sooptcopyin(sopt, rulenum,
4550101978Sluigi			2*sizeof(u_int32_t), sizeof(u_int32_t));
455198943Sluigi		if (error)
455298943Sluigi			break;
4553101978Sluigi		size = sopt->sopt_valsize;
4554101978Sluigi		if (size == sizeof(u_int32_t))	/* delete or reassign */
4555120141Ssam			error = del_entry(&layer3_chain, rulenum[0]);
4556101978Sluigi		else if (size == 2*sizeof(u_int32_t)) /* set enable/disable */
4557101978Sluigi			set_disable =
4558120141Ssam			    (set_disable | rulenum[0]) & ~rulenum[1] &
4559117654Sluigi			    ~(1<<RESVD_SET); /* set RESVD_SET always enabled */
4560101978Sluigi		else
456198943Sluigi			error = EINVAL;
456298943Sluigi		break;
456398943Sluigi
456498943Sluigi	case IP_FW_ZERO:
4565170923Smaxim	case IP_FW_RESETLOG: /* argument is an u_int_32, the rule number */
4566170923Smaxim		rulenum[0] = 0;
456798943Sluigi		if (sopt->sopt_val != 0) {
4568170923Smaxim		    error = sooptcopyin(sopt, rulenum,
4569170923Smaxim			    sizeof(u_int32_t), sizeof(u_int32_t));
457098943Sluigi		    if (error)
457198943Sluigi			break;
457298943Sluigi		}
4573170923Smaxim		error = zero_entry(&layer3_chain, rulenum[0],
4574120141Ssam			sopt->sopt_name == IP_FW_RESETLOG);
457598943Sluigi		break;
457698943Sluigi
4577130281Sru	case IP_FW_TABLE_ADD:
4578130281Sru		{
4579130281Sru			ipfw_table_entry ent;
4580130281Sru
4581130281Sru			error = sooptcopyin(sopt, &ent,
4582130281Sru			    sizeof(ent), sizeof(ent));
4583130281Sru			if (error)
4584130281Sru				break;
4585153163Sglebius			error = add_table_entry(&layer3_chain, ent.tbl,
4586153163Sglebius			    ent.addr, ent.masklen, ent.value);
4587130281Sru		}
4588130281Sru		break;
4589130281Sru
4590130281Sru	case IP_FW_TABLE_DEL:
4591130281Sru		{
4592130281Sru			ipfw_table_entry ent;
4593130281Sru
4594130281Sru			error = sooptcopyin(sopt, &ent,
4595130281Sru			    sizeof(ent), sizeof(ent));
4596130281Sru			if (error)
4597130281Sru				break;
4598153163Sglebius			error = del_table_entry(&layer3_chain, ent.tbl,
4599153163Sglebius			    ent.addr, ent.masklen);
4600130281Sru		}
4601130281Sru		break;
4602130281Sru
4603130281Sru	case IP_FW_TABLE_FLUSH:
4604130281Sru		{
4605130281Sru			u_int16_t tbl;
4606130281Sru
4607130281Sru			error = sooptcopyin(sopt, &tbl,
4608130281Sru			    sizeof(tbl), sizeof(tbl));
4609130281Sru			if (error)
4610130281Sru				break;
4611153163Sglebius			IPFW_WLOCK(&layer3_chain);
4612153163Sglebius			error = flush_table(&layer3_chain, tbl);
4613153163Sglebius			IPFW_WUNLOCK(&layer3_chain);
4614130281Sru		}
4615130281Sru		break;
4616130281Sru
4617130281Sru	case IP_FW_TABLE_GETSIZE:
4618130281Sru		{
4619130281Sru			u_int32_t tbl, cnt;
4620130281Sru
4621130281Sru			if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl),
4622130281Sru			    sizeof(tbl))))
4623130281Sru				break;
4624153163Sglebius			IPFW_RLOCK(&layer3_chain);
4625156240Sglebius			error = count_table(&layer3_chain, tbl, &cnt);
4626156240Sglebius			IPFW_RUNLOCK(&layer3_chain);
4627156240Sglebius			if (error)
4628130281Sru				break;
4629130281Sru			error = sooptcopyout(sopt, &cnt, sizeof(cnt));
4630130281Sru		}
4631130281Sru		break;
4632130281Sru
4633130281Sru	case IP_FW_TABLE_LIST:
4634130281Sru		{
4635130281Sru			ipfw_table *tbl;
4636130281Sru
4637130281Sru			if (sopt->sopt_valsize < sizeof(*tbl)) {
4638130281Sru				error = EINVAL;
4639130281Sru				break;
4640130281Sru			}
4641130281Sru			size = sopt->sopt_valsize;
4642130281Sru			tbl = malloc(size, M_TEMP, M_WAITOK);
4643130281Sru			error = sooptcopyin(sopt, tbl, size, sizeof(*tbl));
4644130281Sru			if (error) {
4645130281Sru				free(tbl, M_TEMP);
4646130281Sru				break;
4647130281Sru			}
4648130281Sru			tbl->size = (size - sizeof(*tbl)) /
4649130281Sru			    sizeof(ipfw_table_entry);
4650156240Sglebius			IPFW_RLOCK(&layer3_chain);
4651153163Sglebius			error = dump_table(&layer3_chain, tbl);
4652156240Sglebius			IPFW_RUNLOCK(&layer3_chain);
4653130281Sru			if (error) {
4654130281Sru				free(tbl, M_TEMP);
4655130281Sru				break;
4656130281Sru			}
4657130281Sru			error = sooptcopyout(sopt, tbl, size);
4658130281Sru			free(tbl, M_TEMP);
4659130281Sru		}
4660130281Sru		break;
4661130281Sru
4662165750Spiso#ifdef IPFIREWALL_NAT
4663165648Spiso	case IP_FW_NAT_CFG:
4664165648Spiso	{
4665165648Spiso		struct cfg_nat *ptr, *ser_n;
4666165648Spiso		char *buf;
4667165648Spiso
4668165648Spiso		buf = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
4669165648Spiso		error = sooptcopyin(sopt, buf, NAT_BUF_LEN,
4670165648Spiso		    sizeof(struct cfg_nat));
4671165648Spiso		ser_n = (struct cfg_nat *)buf;
4672165648Spiso
4673165648Spiso		/*
4674165648Spiso		 * Find/create nat rule.
4675165648Spiso		 */
4676165648Spiso		IPFW_WLOCK(&layer3_chain);
4677165648Spiso		ptr = lookup_nat(ser_n->id);
4678165648Spiso		if (ptr == NULL) {
4679165648Spiso			/* New rule: allocate and init new instance. */
4680165648Spiso			ptr = malloc(sizeof(struct cfg_nat),
4681165648Spiso		            M_IPFW, M_NOWAIT | M_ZERO);
4682165648Spiso			if (ptr == NULL) {
4683165648Spiso				IPFW_WUNLOCK(&layer3_chain);
4684165648Spiso				free(buf, M_IPFW);
4685165648Spiso				return (ENOSPC);
4686165648Spiso			}
4687165648Spiso			ptr->lib = LibAliasInit(NULL);
4688165648Spiso			if (ptr->lib == NULL) {
4689165648Spiso				IPFW_WUNLOCK(&layer3_chain);
4690165648Spiso				free(ptr, M_IPFW);
4691165648Spiso				free(buf, M_IPFW);
4692165648Spiso				return (EINVAL);
4693165648Spiso			}
4694165648Spiso			LIST_INIT(&ptr->redir_chain);
4695165648Spiso		} else {
4696165648Spiso			/* Entry already present: temporarly unhook it. */
4697165648Spiso			UNHOOK_NAT(ptr);
4698165648Spiso			flush_nat_ptrs(ser_n->id);
4699165648Spiso		}
4700165648Spiso		IPFW_WUNLOCK(&layer3_chain);
4701165648Spiso
4702165648Spiso		/*
4703165648Spiso		 * Basic nat configuration.
4704165648Spiso		 */
4705165648Spiso		ptr->id = ser_n->id;
4706165648Spiso		/*
4707165648Spiso		 * XXX - what if this rule doesn't nat any ip and just
4708165648Spiso		 * redirect?
4709165648Spiso		 * do we set aliasaddress to 0.0.0.0?
4710165648Spiso		 */
4711165648Spiso		ptr->ip = ser_n->ip;
4712165648Spiso		ptr->redir_cnt = ser_n->redir_cnt;
4713165648Spiso		ptr->mode = ser_n->mode;
4714165648Spiso		LibAliasSetMode(ptr->lib, ser_n->mode, ser_n->mode);
4715165648Spiso		LibAliasSetAddress(ptr->lib, ptr->ip);
4716165648Spiso		memcpy(ptr->if_name, ser_n->if_name, IF_NAMESIZE);
4717165648Spiso
4718165648Spiso		/*
4719165648Spiso		 * Redir and LSNAT configuration.
4720165648Spiso		 */
4721165648Spiso		/* Delete old cfgs. */
4722165648Spiso		del_redir_spool_cfg(ptr, &ptr->redir_chain);
4723165648Spiso		/* Add new entries. */
4724165648Spiso		add_redir_spool_cfg(&buf[(sizeof(struct cfg_nat))], ptr);
4725165648Spiso		free(buf, M_IPFW);
4726165648Spiso		IPFW_WLOCK(&layer3_chain);
4727165648Spiso		HOOK_NAT(&layer3_chain.nat, ptr);
4728165648Spiso		IPFW_WUNLOCK(&layer3_chain);
4729165648Spiso	}
4730165648Spiso	break;
4731165648Spiso
4732165648Spiso	case IP_FW_NAT_DEL:
4733165648Spiso	{
4734165648Spiso		struct cfg_nat *ptr;
4735165648Spiso		int i;
4736165648Spiso
4737165648Spiso		error = sooptcopyin(sopt, &i, sizeof i, sizeof i);
4738165648Spiso		IPFW_WLOCK(&layer3_chain);
4739165648Spiso		ptr = lookup_nat(i);
4740165648Spiso		if (ptr == NULL) {
4741165648Spiso			error = EINVAL;
4742165648Spiso			IPFW_WUNLOCK(&layer3_chain);
4743165648Spiso			break;
4744165648Spiso		}
4745165648Spiso		UNHOOK_NAT(ptr);
4746165648Spiso		flush_nat_ptrs(i);
4747165648Spiso		IPFW_WUNLOCK(&layer3_chain);
4748165648Spiso		del_redir_spool_cfg(ptr, &ptr->redir_chain);
4749165648Spiso		LibAliasUninit(ptr->lib);
4750165648Spiso		free(ptr, M_IPFW);
4751165648Spiso	}
4752165648Spiso	break;
4753165648Spiso
4754165648Spiso	case IP_FW_NAT_GET_CONFIG:
4755165648Spiso	{
4756165648Spiso		uint8_t *data;
4757165648Spiso		struct cfg_nat *n;
4758165648Spiso		struct cfg_redir *r;
4759165648Spiso		struct cfg_spool *s;
4760165648Spiso		int nat_cnt, off;
4761165648Spiso
4762165648Spiso		nat_cnt = 0;
4763165648Spiso		off = sizeof(nat_cnt);
4764165648Spiso
4765165648Spiso		data = malloc(NAT_BUF_LEN, M_IPFW, M_WAITOK | M_ZERO);
4766165648Spiso		IPFW_RLOCK(&layer3_chain);
4767165648Spiso		/* Serialize all the data. */
4768165648Spiso		LIST_FOREACH(n, &layer3_chain.nat, _next) {
4769165648Spiso			nat_cnt++;
4770165648Spiso			if (off + SOF_NAT < NAT_BUF_LEN) {
4771165648Spiso				bcopy(n, &data[off], SOF_NAT);
4772165648Spiso				off += SOF_NAT;
4773165648Spiso				LIST_FOREACH(r, &n->redir_chain, _next) {
4774165648Spiso					if (off + SOF_REDIR < NAT_BUF_LEN) {
4775165648Spiso						bcopy(r, &data[off],
4776165648Spiso						    SOF_REDIR);
4777165648Spiso						off += SOF_REDIR;
4778165648Spiso						LIST_FOREACH(s, &r->spool_chain,
4779165648Spiso						    _next) {
4780165648Spiso							if (off + SOF_SPOOL <
4781165648Spiso							    NAT_BUF_LEN) {
4782165648Spiso								bcopy(s,
4783165648Spiso								    &data[off],
4784165648Spiso								    SOF_SPOOL);
4785165648Spiso								off +=
4786165648Spiso								    SOF_SPOOL;
4787165648Spiso							} else
4788165648Spiso								goto nospace;
4789165648Spiso						}
4790165648Spiso					} else
4791165648Spiso						goto nospace;
4792165648Spiso				}
4793165648Spiso			} else
4794165648Spiso				goto nospace;
4795165648Spiso		}
4796165648Spiso		bcopy(&nat_cnt, data, sizeof(nat_cnt));
4797165648Spiso		IPFW_RUNLOCK(&layer3_chain);
4798165648Spiso		error = sooptcopyout(sopt, data, NAT_BUF_LEN);
4799165648Spiso		free(data, M_IPFW);
4800165648Spiso		break;
4801165648Spiso	nospace:
4802165648Spiso		IPFW_RUNLOCK(&layer3_chain);
4803165648Spiso		printf("serialized data buffer not big enough:"
4804165648Spiso		    "please increase NAT_BUF_LEN\n");
4805165648Spiso		free(data, M_IPFW);
4806165648Spiso	}
4807165648Spiso	break;
4808165648Spiso
4809165648Spiso	case IP_FW_NAT_GET_LOG:
4810165648Spiso	{
4811165648Spiso		uint8_t *data;
4812165648Spiso		struct cfg_nat *ptr;
4813165648Spiso		int i, size, cnt, sof;
4814165648Spiso
4815165648Spiso		data = NULL;
4816165648Spiso		sof = LIBALIAS_BUF_SIZE;
4817165648Spiso		cnt = 0;
4818165648Spiso
4819165648Spiso		IPFW_RLOCK(&layer3_chain);
4820165648Spiso		size = i = 0;
4821165648Spiso		LIST_FOREACH(ptr, &layer3_chain.nat, _next) {
4822165648Spiso			if (ptr->lib->logDesc == NULL)
4823165648Spiso				continue;
4824165648Spiso			cnt++;
4825165648Spiso			size = cnt * (sof + sizeof(int));
4826165648Spiso			data = realloc(data, size, M_IPFW, M_NOWAIT | M_ZERO);
4827165648Spiso			if (data == NULL) {
4828165648Spiso				IPFW_RUNLOCK(&layer3_chain);
4829165648Spiso				return (ENOSPC);
4830165648Spiso			}
4831165648Spiso			bcopy(&ptr->id, &data[i], sizeof(int));
4832165648Spiso			i += sizeof(int);
4833165648Spiso			bcopy(ptr->lib->logDesc, &data[i], sof);
4834165648Spiso			i += sof;
4835165648Spiso		}
4836165648Spiso		IPFW_RUNLOCK(&layer3_chain);
4837165648Spiso		error = sooptcopyout(sopt, data, size);
4838165648Spiso		free(data, M_IPFW);
4839165648Spiso	}
4840165648Spiso	break;
4841165750Spiso#endif
4842165648Spiso
484398943Sluigi	default:
4844108258Smaxim		printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
484598943Sluigi		error = EINVAL;
484698943Sluigi	}
484798943Sluigi
484898943Sluigi	return (error);
4849120141Ssam#undef RULE_MAXSIZE
485098943Sluigi}
485198943Sluigi
485298943Sluigi/**
485398943Sluigi * dummynet needs a reference to the default rule, because rules can be
485498943Sluigi * deleted while packets hold a reference to them. When this happens,
485598943Sluigi * dummynet changes the reference to the default rule (it could well be a
485698943Sluigi * NULL pointer, but this way we do not need to check for the special
485798943Sluigi * case, plus here he have info on the default behaviour).
485898943Sluigi */
485998943Sluigistruct ip_fw *ip_fw_default_rule;
486098943Sluigi
4861101978Sluigi/*
4862101978Sluigi * This procedure is only used to handle keepalives. It is invoked
4863101978Sluigi * every dyn_keepalive_period
4864101978Sluigi */
486598943Sluigistatic void
4866100004Sluigiipfw_tick(void * __unused unused)
4867100004Sluigi{
4868147247Sgreen	struct mbuf *m0, *m, *mnext, **mtailp;
4869100004Sluigi	int i;
4870100004Sluigi	ipfw_dyn_rule *q;
4871100004Sluigi
4872100004Sluigi	if (dyn_keepalive == 0 || ipfw_dyn_v == NULL || dyn_count == 0)
4873100004Sluigi		goto done;
4874100004Sluigi
4875147247Sgreen	/*
4876147247Sgreen	 * We make a chain of packets to go out here -- not deferring
4877147247Sgreen	 * until after we drop the IPFW dynamic rule lock would result
4878147247Sgreen	 * in a lock order reversal with the normal packet input -> ipfw
4879147247Sgreen	 * call stack.
4880147247Sgreen	 */
4881147247Sgreen	m0 = NULL;
4882147247Sgreen	mtailp = &m0;
4883120141Ssam	IPFW_DYN_LOCK();
4884100004Sluigi	for (i = 0 ; i < curr_dyn_buckets ; i++) {
4885100004Sluigi		for (q = ipfw_dyn_v[i] ; q ; q = q->next ) {
4886100004Sluigi			if (q->dyn_type == O_LIMIT_PARENT)
4887100004Sluigi				continue;
4888100004Sluigi			if (q->id.proto != IPPROTO_TCP)
4889100004Sluigi				continue;
4890100004Sluigi			if ( (q->state & BOTH_SYN) != BOTH_SYN)
4891100004Sluigi				continue;
4892150350Sandre			if (TIME_LEQ( time_uptime+dyn_keepalive_interval,
4893101978Sluigi			    q->expire))
4894100004Sluigi				continue;	/* too early */
4895150350Sandre			if (TIME_LEQ(q->expire, time_uptime))
4896100004Sluigi				continue;	/* too late, rule expired */
4897100004Sluigi
4898162238Scsjp			*mtailp = send_pkt(NULL, &(q->id), q->ack_rev - 1,
4899147247Sgreen				q->ack_fwd, TH_SYN);
4900147247Sgreen			if (*mtailp != NULL)
4901147247Sgreen				mtailp = &(*mtailp)->m_nextpkt;
4902162238Scsjp			*mtailp = send_pkt(NULL, &(q->id), q->ack_fwd - 1,
4903147247Sgreen				q->ack_rev, 0);
4904147247Sgreen			if (*mtailp != NULL)
4905147247Sgreen				mtailp = &(*mtailp)->m_nextpkt;
4906100004Sluigi		}
4907100004Sluigi	}
4908120141Ssam	IPFW_DYN_UNLOCK();
4909147247Sgreen	for (m = mnext = m0; m != NULL; m = mnext) {
4910147247Sgreen		mnext = m->m_nextpkt;
4911147247Sgreen		m->m_nextpkt = NULL;
4912147247Sgreen		ip_output(m, NULL, NULL, 0, NULL, NULL);
4913147247Sgreen	}
4914100004Sluigidone:
4915120141Ssam	callout_reset(&ipfw_timeout, dyn_keepalive_period*hz, ipfw_tick, NULL);
4916100004Sluigi}
4917100004Sluigi
4918133920Sandreint
491998943Sluigiipfw_init(void)
492098943Sluigi{
492198943Sluigi	struct ip_fw default_rule;
4922120141Ssam	int error;
492398943Sluigi
4924149052Sbz#ifdef INET6
4925149020Sbz	/* Setup IPv6 fw sysctl tree. */
4926149020Sbz	sysctl_ctx_init(&ip6_fw_sysctl_ctx);
4927149020Sbz	ip6_fw_sysctl_tree = SYSCTL_ADD_NODE(&ip6_fw_sysctl_ctx,
4928158470Smlaier	    SYSCTL_STATIC_CHILDREN(_net_inet6_ip6), OID_AUTO, "fw",
4929158470Smlaier	    CTLFLAG_RW | CTLFLAG_SECURE, 0, "Firewall");
4930158470Smlaier	SYSCTL_ADD_PROC(&ip6_fw_sysctl_ctx, SYSCTL_CHILDREN(ip6_fw_sysctl_tree),
4931158470Smlaier	    OID_AUTO, "enable", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3,
4932158470Smlaier	    &fw6_enable, 0, ipfw_chg_hook, "I", "Enable ipfw+6");
4933149020Sbz	SYSCTL_ADD_INT(&ip6_fw_sysctl_ctx, SYSCTL_CHILDREN(ip6_fw_sysctl_tree),
4934158470Smlaier	    OID_AUTO, "deny_unknown_exthdrs", CTLFLAG_RW | CTLFLAG_SECURE,
4935158470Smlaier	    &fw_deny_unknown_exthdrs, 0,
4936158470Smlaier	    "Deny packets with unknown IPv6 Extension Headers");
4937149052Sbz#endif
4938149020Sbz
4939120141Ssam	layer3_chain.rules = NULL;
4940120141Ssam	IPFW_LOCK_INIT(&layer3_chain);
4941168812Srwatson	ipfw_dyn_rule_zone = uma_zcreate("IPFW dynamic rule",
4942141076Scsjp	    sizeof(ipfw_dyn_rule), NULL, NULL, NULL, NULL,
4943141076Scsjp	    UMA_ALIGN_PTR, 0);
4944120141Ssam	IPFW_DYN_LOCK_INIT();
4945171637Srwatson	callout_init(&ipfw_timeout, CALLOUT_MPSAFE);
494698943Sluigi
494798943Sluigi	bzero(&default_rule, sizeof default_rule);
494898943Sluigi
494998943Sluigi	default_rule.act_ofs = 0;
495098943Sluigi	default_rule.rulenum = IPFW_DEFAULT_RULE;
495198943Sluigi	default_rule.cmd_len = 1;
4952117654Sluigi	default_rule.set = RESVD_SET;
495398943Sluigi
495498943Sluigi	default_rule.cmd[0].len = 1;
495598943Sluigi	default_rule.cmd[0].opcode =
495698943Sluigi#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT
495798943Sluigi				1 ? O_ACCEPT :
495898943Sluigi#endif
495998943Sluigi				O_DENY;
496098943Sluigi
4961120141Ssam	error = add_rule(&layer3_chain, &default_rule);
4962120141Ssam	if (error != 0) {
4963120141Ssam		printf("ipfw2: error %u initializing default rule "
4964120141Ssam			"(support disabled)\n", error);
4965120141Ssam		IPFW_DYN_LOCK_DESTROY();
4966120141Ssam		IPFW_LOCK_DESTROY(&layer3_chain);
4967154563Scsjp		uma_zdestroy(ipfw_dyn_rule_zone);
4968120141Ssam		return (error);
4969120141Ssam	}
497098943Sluigi
4971120141Ssam	ip_fw_default_rule = layer3_chain.rules;
4972158433Smlaier	printf("ipfw2 "
4973158433Smlaier#ifdef INET6
4974158433Smlaier		"(+ipv6) "
4975158433Smlaier#endif
4976158433Smlaier		"initialized, divert %s, "
4977133920Sandre		"rule-based forwarding "
4978133920Sandre#ifdef IPFIREWALL_FORWARD
4979133920Sandre		"enabled, "
4980133920Sandre#else
4981133920Sandre		"disabled, "
4982133920Sandre#endif
4983133920Sandre		"default to %s, logging ",
498498943Sluigi#ifdef IPDIVERT
498598943Sluigi		"enabled",
498698943Sluigi#else
4987136790Sandre		"loadable",
498898943Sluigi#endif
498998943Sluigi		default_rule.cmd[0].opcode == O_ACCEPT ? "accept" : "deny");
499098943Sluigi
499198943Sluigi#ifdef IPFIREWALL_VERBOSE
499298943Sluigi	fw_verbose = 1;
499398943Sluigi#endif
499498943Sluigi#ifdef IPFIREWALL_VERBOSE_LIMIT
499598943Sluigi	verbose_limit = IPFIREWALL_VERBOSE_LIMIT;
499698943Sluigi#endif
499798943Sluigi	if (fw_verbose == 0)
499898943Sluigi		printf("disabled\n");
499998943Sluigi	else if (verbose_limit == 0)
500098943Sluigi		printf("unlimited\n");
500198943Sluigi	else
500298943Sluigi		printf("limited to %d packets/entry by default\n",
500398943Sluigi		    verbose_limit);
5004120141Ssam
5005154567Scsjp	error = init_tables(&layer3_chain);
5006154567Scsjp	if (error) {
5007154567Scsjp		IPFW_DYN_LOCK_DESTROY();
5008154567Scsjp		IPFW_LOCK_DESTROY(&layer3_chain);
5009154567Scsjp		uma_zdestroy(ipfw_dyn_rule_zone);
5010154567Scsjp		return (error);
5011154567Scsjp	}
5012133920Sandre	ip_fw_ctl_ptr = ipfw_ctl;
5013120141Ssam	ip_fw_chk_ptr = ipfw_chk;
5014165648Spiso	callout_reset(&ipfw_timeout, hz, ipfw_tick, NULL);
5015165750Spiso#ifdef IPFIREWALL_NAT
5016165648Spiso	LIST_INIT(&layer3_chain.nat);
5017165648Spiso	ifaddr_event_tag = EVENTHANDLER_REGISTER(ifaddr_event, ifaddr_change,
5018165648Spiso	    NULL, EVENTHANDLER_PRI_ANY);
5019165750Spiso#endif
5020120141Ssam	return (0);
502198943Sluigi}
502298943Sluigi
5023133920Sandrevoid
5024120141Ssamipfw_destroy(void)
5025120141Ssam{
5026120141Ssam	struct ip_fw *reap;
5027165750Spiso#ifdef IPFIREWALL_NAT
5028165648Spiso	struct cfg_nat *ptr, *ptr_temp;
5029165750Spiso#endif
5030120141Ssam
5031133920Sandre	ip_fw_chk_ptr = NULL;
5032133920Sandre	ip_fw_ctl_ptr = NULL;
5033134049Sandre	callout_drain(&ipfw_timeout);
5034138642Scsjp	IPFW_WLOCK(&layer3_chain);
5035153163Sglebius	flush_tables(&layer3_chain);
5036165750Spiso#ifdef IPFIREWALL_NAT
5037165648Spiso	LIST_FOREACH_SAFE(ptr, &layer3_chain.nat, _next, ptr_temp) {
5038165648Spiso		LIST_REMOVE(ptr, _next);
5039165648Spiso		del_redir_spool_cfg(ptr, &ptr->redir_chain);
5040165648Spiso		LibAliasUninit(ptr->lib);
5041165648Spiso		free(ptr, M_IPFW);
5042165648Spiso	}
5043165648Spiso	EVENTHANDLER_DEREGISTER(ifaddr_event, ifaddr_event_tag);
5044165750Spiso#endif
5045120141Ssam	layer3_chain.reap = NULL;
5046120141Ssam	free_chain(&layer3_chain, 1 /* kill default rule */);
5047120141Ssam	reap = layer3_chain.reap, layer3_chain.reap = NULL;
5048138642Scsjp	IPFW_WUNLOCK(&layer3_chain);
5049120141Ssam	if (reap != NULL)
5050120141Ssam		reap_rules(reap);
5051120141Ssam	IPFW_DYN_LOCK_DESTROY();
5052141076Scsjp	uma_zdestroy(ipfw_dyn_rule_zone);
5053171989Smaxim	if (ipfw_dyn_v != NULL)
5054171989Smaxim		free(ipfw_dyn_v, M_IPFW);
5055120141Ssam	IPFW_LOCK_DESTROY(&layer3_chain);
5056149020Sbz
5057149052Sbz#ifdef INET6
5058149020Sbz	/* Free IPv6 fw sysctl tree. */
5059149020Sbz	sysctl_ctx_free(&ip6_fw_sysctl_ctx);
5060149052Sbz#endif
5061149020Sbz
5062120141Ssam	printf("IP firewall unloaded\n");
5063120141Ssam}
5064