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