1200580Sluigi/*- 2200580Sluigi * Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa 3200580Sluigi * 4200580Sluigi * Redistribution and use in source and binary forms, with or without 5200580Sluigi * modification, are permitted provided that the following conditions 6200580Sluigi * are met: 7200580Sluigi * 1. Redistributions of source code must retain the above copyright 8200580Sluigi * notice, this list of conditions and the following disclaimer. 9200580Sluigi * 2. Redistributions in binary form must reproduce the above copyright 10200580Sluigi * notice, this list of conditions and the following disclaimer in the 11200580Sluigi * documentation and/or other materials provided with the distribution. 12200580Sluigi * 13200580Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14200580Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15200580Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16200580Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17200580Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18200580Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19200580Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20200580Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21200580Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22200580Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23200580Sluigi * SUCH DAMAGE. 24200580Sluigi */ 25200580Sluigi 26200580Sluigi#include <sys/cdefs.h> 27200580Sluigi__FBSDID("$FreeBSD: stable/10/sys/netpfil/ipfw/ip_fw_dynamic.c 314667 2017-03-04 13:03:31Z avg $"); 28200580Sluigi 29200580Sluigi#define DEB(x) 30200580Sluigi#define DDB(x) x 31200580Sluigi 32200580Sluigi/* 33200580Sluigi * Dynamic rule support for ipfw 34200580Sluigi */ 35200580Sluigi 36225518Sjhb#include "opt_ipfw.h" 37200580Sluigi#include "opt_inet.h" 38200580Sluigi#ifndef INET 39200580Sluigi#error IPFIREWALL requires INET. 40200580Sluigi#endif /* INET */ 41200580Sluigi#include "opt_inet6.h" 42200580Sluigi 43200580Sluigi#include <sys/param.h> 44200580Sluigi#include <sys/systm.h> 45200580Sluigi#include <sys/malloc.h> 46200580Sluigi#include <sys/mbuf.h> 47200580Sluigi#include <sys/kernel.h> 48200580Sluigi#include <sys/lock.h> 49200580Sluigi#include <sys/socket.h> 50200580Sluigi#include <sys/sysctl.h> 51200580Sluigi#include <sys/syslog.h> 52200580Sluigi#include <net/ethernet.h> /* for ETHERTYPE_IP */ 53200580Sluigi#include <net/if.h> 54200580Sluigi#include <net/vnet.h> 55200580Sluigi 56200580Sluigi#include <netinet/in.h> 57200580Sluigi#include <netinet/ip.h> 58200580Sluigi#include <netinet/ip_var.h> /* ip_defttl */ 59200580Sluigi#include <netinet/ip_fw.h> 60200580Sluigi#include <netinet/tcp_var.h> 61200580Sluigi#include <netinet/udp.h> 62200580Sluigi 63200603Sluigi#include <netinet/ip6.h> /* IN6_ARE_ADDR_EQUAL */ 64200580Sluigi#ifdef INET6 65200580Sluigi#include <netinet6/in6_var.h> 66200580Sluigi#include <netinet6/ip6_var.h> 67200580Sluigi#endif 68200580Sluigi 69240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 70240494Sglebius 71200580Sluigi#include <machine/in_cksum.h> /* XXX for in_cksum */ 72200580Sluigi 73200580Sluigi#ifdef MAC 74200580Sluigi#include <security/mac/mac_framework.h> 75200580Sluigi#endif 76200580Sluigi 77200580Sluigi/* 78200580Sluigi * Description of dynamic rules. 79200580Sluigi * 80200580Sluigi * Dynamic rules are stored in lists accessed through a hash table 81200580Sluigi * (ipfw_dyn_v) whose size is curr_dyn_buckets. This value can 82200580Sluigi * be modified through the sysctl variable dyn_buckets which is 83200580Sluigi * updated when the table becomes empty. 84200580Sluigi * 85200580Sluigi * XXX currently there is only one list, ipfw_dyn. 86200580Sluigi * 87200580Sluigi * When a packet is received, its address fields are first masked 88200580Sluigi * with the mask defined for the rule, then hashed, then matched 89200580Sluigi * against the entries in the corresponding list. 90200580Sluigi * Dynamic rules can be used for different purposes: 91200580Sluigi * + stateful rules; 92200580Sluigi * + enforcing limits on the number of sessions; 93200580Sluigi * + in-kernel NAT (not implemented yet) 94200580Sluigi * 95200580Sluigi * The lifetime of dynamic rules is regulated by dyn_*_lifetime, 96200580Sluigi * measured in seconds and depending on the flags. 97200580Sluigi * 98243707Smelifaro * The total number of dynamic rules is equal to UMA zone items count. 99200580Sluigi * The max number of dynamic rules is dyn_max. When we reach 100200580Sluigi * the maximum number of rules we do not create anymore. This is 101200580Sluigi * done to avoid consuming too much memory, but also too much 102200580Sluigi * time when searching on each packet (ideally, we should try instead 103200580Sluigi * to put a limit on the length of the list on each bucket...). 104200580Sluigi * 105200580Sluigi * Each dynamic rule holds a pointer to the parent ipfw rule so 106200580Sluigi * we know what action to perform. Dynamic rules are removed when 107200580Sluigi * the parent rule is deleted. XXX we should make them survive. 108200580Sluigi * 109200580Sluigi * There are some limitations with dynamic rules -- we do not 110200580Sluigi * obey the 'randomized match', and we do not do multiple 111200580Sluigi * passes through the firewall. XXX check the latter!!! 112200580Sluigi */ 113200601Sluigi 114243707Smelifarostruct ipfw_dyn_bucket { 115243707Smelifaro struct mtx mtx; /* Bucket protecting lock */ 116243707Smelifaro ipfw_dyn_rule *head; /* Pointer to first rule */ 117243707Smelifaro}; 118243707Smelifaro 119200601Sluigi/* 120200601Sluigi * Static variables followed by global ones 121200601Sluigi */ 122243707Smelifarostatic VNET_DEFINE(struct ipfw_dyn_bucket *, ipfw_dyn_v); 123243707Smelifarostatic VNET_DEFINE(u_int32_t, dyn_buckets_max); 124215701Sdimstatic VNET_DEFINE(u_int32_t, curr_dyn_buckets); 125215701Sdimstatic VNET_DEFINE(struct callout, ipfw_timeout); 126200580Sluigi#define V_ipfw_dyn_v VNET(ipfw_dyn_v) 127243707Smelifaro#define V_dyn_buckets_max VNET(dyn_buckets_max) 128200580Sluigi#define V_curr_dyn_buckets VNET(curr_dyn_buckets) 129200580Sluigi#define V_ipfw_timeout VNET(ipfw_timeout) 130200580Sluigi 131243707Smelifarostatic VNET_DEFINE(uma_zone_t, ipfw_dyn_rule_zone); 132243707Smelifaro#define V_ipfw_dyn_rule_zone VNET(ipfw_dyn_rule_zone) 133200580Sluigi 134243707Smelifaro#define IPFW_BUCK_LOCK_INIT(b) \ 135243707Smelifaro mtx_init(&(b)->mtx, "IPFW dynamic bucket", NULL, MTX_DEF) 136243707Smelifaro#define IPFW_BUCK_LOCK_DESTROY(b) \ 137243707Smelifaro mtx_destroy(&(b)->mtx) 138243707Smelifaro#define IPFW_BUCK_LOCK(i) mtx_lock(&V_ipfw_dyn_v[(i)].mtx) 139243707Smelifaro#define IPFW_BUCK_UNLOCK(i) mtx_unlock(&V_ipfw_dyn_v[(i)].mtx) 140243707Smelifaro#define IPFW_BUCK_ASSERT(i) mtx_assert(&V_ipfw_dyn_v[(i)].mtx, MA_OWNED) 141200580Sluigi 142200580Sluigi/* 143200580Sluigi * Timeouts for various events in handing dynamic rules. 144200580Sluigi */ 145215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_ack_lifetime); 146215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_syn_lifetime); 147215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_fin_lifetime); 148215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_rst_lifetime); 149215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_udp_lifetime); 150215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_short_lifetime); 151200580Sluigi 152200580Sluigi#define V_dyn_ack_lifetime VNET(dyn_ack_lifetime) 153200580Sluigi#define V_dyn_syn_lifetime VNET(dyn_syn_lifetime) 154200580Sluigi#define V_dyn_fin_lifetime VNET(dyn_fin_lifetime) 155200580Sluigi#define V_dyn_rst_lifetime VNET(dyn_rst_lifetime) 156200580Sluigi#define V_dyn_udp_lifetime VNET(dyn_udp_lifetime) 157200580Sluigi#define V_dyn_short_lifetime VNET(dyn_short_lifetime) 158200580Sluigi 159200580Sluigi/* 160200580Sluigi * Keepalives are sent if dyn_keepalive is set. They are sent every 161200580Sluigi * dyn_keepalive_period seconds, in the last dyn_keepalive_interval 162200580Sluigi * seconds of lifetime of a rule. 163200580Sluigi * dyn_rst_lifetime and dyn_fin_lifetime should be strictly lower 164200580Sluigi * than dyn_keepalive_period. 165200580Sluigi */ 166200580Sluigi 167215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_keepalive_interval); 168215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_keepalive_period); 169215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_keepalive); 170243707Smelifarostatic VNET_DEFINE(time_t, dyn_keepalive_last); 171200580Sluigi 172200580Sluigi#define V_dyn_keepalive_interval VNET(dyn_keepalive_interval) 173200580Sluigi#define V_dyn_keepalive_period VNET(dyn_keepalive_period) 174200580Sluigi#define V_dyn_keepalive VNET(dyn_keepalive) 175243707Smelifaro#define V_dyn_keepalive_last VNET(dyn_keepalive_last) 176200580Sluigi 177215701Sdimstatic VNET_DEFINE(u_int32_t, dyn_max); /* max # of dynamic rules */ 178200580Sluigi 179243707Smelifaro#define DYN_COUNT uma_zone_get_cur(V_ipfw_dyn_rule_zone) 180200580Sluigi#define V_dyn_max VNET(dyn_max) 181200580Sluigi 182243707Smelifarostatic int last_log; /* Log ratelimiting */ 183243707Smelifaro 184243707Smelifarostatic void ipfw_dyn_tick(void *vnetx); 185243707Smelifarostatic void check_dyn_rules(struct ip_fw_chain *, struct ip_fw *, 186243707Smelifaro int, int, int); 187200580Sluigi#ifdef SYSCTL_NODE 188204591Sluigi 189243707Smelifarostatic int sysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS); 190243707Smelifarostatic int sysctl_ipfw_dyn_max(SYSCTL_HANDLER_ARGS); 191243707Smelifaro 192204591SluigiSYSBEGIN(f2) 193204591Sluigi 194200580SluigiSYSCTL_DECL(_net_inet_ip_fw); 195217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_buckets, 196243707Smelifaro CTLFLAG_RW, &VNET_NAME(dyn_buckets_max), 0, 197243707Smelifaro "Max number of dyn. buckets"); 198217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets, 199200580Sluigi CTLFLAG_RD, &VNET_NAME(curr_dyn_buckets), 0, 200200580Sluigi "Current Number of dyn. buckets"); 201243707SmelifaroSYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, dyn_count, 202243707Smelifaro CTLTYPE_UINT|CTLFLAG_RD, 0, 0, sysctl_ipfw_dyn_count, "IU", 203200580Sluigi "Number of dyn. rules"); 204243707SmelifaroSYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, dyn_max, 205243707Smelifaro CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_dyn_max, "IU", 206200580Sluigi "Max number of dyn. rules"); 207217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime, 208200580Sluigi CTLFLAG_RW, &VNET_NAME(dyn_ack_lifetime), 0, 209200580Sluigi "Lifetime of dyn. rules for acks"); 210217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime, 211200580Sluigi CTLFLAG_RW, &VNET_NAME(dyn_syn_lifetime), 0, 212200580Sluigi "Lifetime of dyn. rules for syn"); 213217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime, 214200580Sluigi CTLFLAG_RW, &VNET_NAME(dyn_fin_lifetime), 0, 215200580Sluigi "Lifetime of dyn. rules for fin"); 216217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime, 217200580Sluigi CTLFLAG_RW, &VNET_NAME(dyn_rst_lifetime), 0, 218200580Sluigi "Lifetime of dyn. rules for rst"); 219217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime, 220200580Sluigi CTLFLAG_RW, &VNET_NAME(dyn_udp_lifetime), 0, 221200580Sluigi "Lifetime of dyn. rules for UDP"); 222217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime, 223200580Sluigi CTLFLAG_RW, &VNET_NAME(dyn_short_lifetime), 0, 224200580Sluigi "Lifetime of dyn. rules for other situations"); 225217322SmdfSYSCTL_VNET_UINT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive, 226200580Sluigi CTLFLAG_RW, &VNET_NAME(dyn_keepalive), 0, 227200580Sluigi "Enable keepalives for dyn. rules"); 228204591Sluigi 229204591SluigiSYSEND 230204591Sluigi 231200580Sluigi#endif /* SYSCTL_NODE */ 232200580Sluigi 233200580Sluigi 234262210Sdim#ifdef INET6 235200580Sluigistatic __inline int 236200580Sluigihash_packet6(struct ipfw_flow_id *id) 237200580Sluigi{ 238200580Sluigi u_int32_t i; 239200580Sluigi i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ 240200580Sluigi (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ 241200580Sluigi (id->src_ip6.__u6_addr.__u6_addr32[2]) ^ 242200580Sluigi (id->src_ip6.__u6_addr.__u6_addr32[3]) ^ 243200580Sluigi (id->dst_port) ^ (id->src_port); 244200580Sluigi return i; 245200580Sluigi} 246262210Sdim#endif 247200580Sluigi 248200580Sluigi/* 249200580Sluigi * IMPORTANT: the hash function for dynamic rules must be commutative 250200580Sluigi * in source and destination (ip,port), because rules are bidirectional 251200580Sluigi * and we want to find both in the same bucket. 252200580Sluigi */ 253200580Sluigistatic __inline int 254243707Smelifarohash_packet(struct ipfw_flow_id *id, int buckets) 255200580Sluigi{ 256200580Sluigi u_int32_t i; 257200580Sluigi 258200580Sluigi#ifdef INET6 259200580Sluigi if (IS_IP6_FLOW_ID(id)) 260200580Sluigi i = hash_packet6(id); 261200580Sluigi else 262200580Sluigi#endif /* INET6 */ 263200580Sluigi i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); 264243707Smelifaro i &= (buckets - 1); 265200580Sluigi return i; 266200580Sluigi} 267200580Sluigi 268242631Smelifaro/** 269242631Smelifaro * Print customizable flow id description via log(9) facility. 270242631Smelifaro */ 271242631Smelifarostatic void 272242631Smelifaroprint_dyn_rule_flags(struct ipfw_flow_id *id, int dyn_type, int log_flags, 273242631Smelifaro char *prefix, char *postfix) 274200580Sluigi{ 275200580Sluigi struct in_addr da; 276200580Sluigi#ifdef INET6 277200580Sluigi char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; 278200580Sluigi#else 279200580Sluigi char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 280200580Sluigi#endif 281200580Sluigi 282200580Sluigi#ifdef INET6 283200580Sluigi if (IS_IP6_FLOW_ID(id)) { 284200580Sluigi ip6_sprintf(src, &id->src_ip6); 285200580Sluigi ip6_sprintf(dst, &id->dst_ip6); 286200580Sluigi } else 287200580Sluigi#endif 288200580Sluigi { 289200580Sluigi da.s_addr = htonl(id->src_ip); 290238978Sluigi inet_ntop(AF_INET, &da, src, sizeof(src)); 291200580Sluigi da.s_addr = htonl(id->dst_ip); 292238978Sluigi inet_ntop(AF_INET, &da, dst, sizeof(dst)); 293200580Sluigi } 294242631Smelifaro log(log_flags, "ipfw: %s type %d %s %d -> %s %d, %d %s\n", 295242631Smelifaro prefix, dyn_type, src, id->src_port, dst, 296243707Smelifaro id->dst_port, DYN_COUNT, postfix); 297200580Sluigi} 298200580Sluigi 299242631Smelifaro#define print_dyn_rule(id, dtype, prefix, postfix) \ 300242631Smelifaro print_dyn_rule_flags(id, dtype, LOG_DEBUG, prefix, postfix) 301242631Smelifaro 302200580Sluigi#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) 303200580Sluigi 304232272Soleg/* 305232272Soleg * Lookup a dynamic rule, locked version. 306200580Sluigi */ 307200601Sluigistatic ipfw_dyn_rule * 308243707Smelifarolookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction, 309200580Sluigi struct tcphdr *tcp) 310200580Sluigi{ 311200580Sluigi /* 312232272Soleg * Stateful ipfw extensions. 313232272Soleg * Lookup into dynamic session queue. 314200580Sluigi */ 315200580Sluigi#define MATCH_REVERSE 0 316200580Sluigi#define MATCH_FORWARD 1 317200580Sluigi#define MATCH_NONE 2 318200580Sluigi#define MATCH_UNKNOWN 3 319243707Smelifaro int dir = MATCH_NONE; 320232272Soleg ipfw_dyn_rule *prev, *q = NULL; 321200580Sluigi 322243707Smelifaro IPFW_BUCK_ASSERT(i); 323200580Sluigi 324243707Smelifaro for (prev = NULL, q = V_ipfw_dyn_v[i].head; q; prev = q, q = q->next) { 325200580Sluigi if (q->dyn_type == O_LIMIT_PARENT && q->count) 326200580Sluigi continue; 327243707Smelifaro 328232272Soleg if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT) 329243707Smelifaro continue; 330232272Soleg 331232272Soleg if (IS_IP6_FLOW_ID(pkt)) { 332232272Soleg if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.src_ip6) && 333232272Soleg IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.dst_ip6) && 334200580Sluigi pkt->src_port == q->id.src_port && 335232272Soleg pkt->dst_port == q->id.dst_port) { 336200580Sluigi dir = MATCH_FORWARD; 337200580Sluigi break; 338200580Sluigi } 339232272Soleg if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.dst_ip6) && 340232272Soleg IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.src_ip6) && 341232272Soleg pkt->src_port == q->id.dst_port && 342232272Soleg pkt->dst_port == q->id.src_port) { 343232272Soleg dir = MATCH_REVERSE; 344232272Soleg break; 345232272Soleg } 346232272Soleg } else { 347232272Soleg if (pkt->src_ip == q->id.src_ip && 348232272Soleg pkt->dst_ip == q->id.dst_ip && 349232272Soleg pkt->src_port == q->id.src_port && 350232272Soleg pkt->dst_port == q->id.dst_port) { 351232272Soleg dir = MATCH_FORWARD; 352232272Soleg break; 353232272Soleg } 354232272Soleg if (pkt->src_ip == q->id.dst_ip && 355232272Soleg pkt->dst_ip == q->id.src_ip && 356232272Soleg pkt->src_port == q->id.dst_port && 357232272Soleg pkt->dst_port == q->id.src_port) { 358232272Soleg dir = MATCH_REVERSE; 359232272Soleg break; 360232272Soleg } 361200580Sluigi } 362200580Sluigi } 363200580Sluigi if (q == NULL) 364232272Soleg goto done; /* q = NULL, not found */ 365200580Sluigi 366232272Soleg if (prev != NULL) { /* found and not in front */ 367200580Sluigi prev->next = q->next; 368243707Smelifaro q->next = V_ipfw_dyn_v[i].head; 369243707Smelifaro V_ipfw_dyn_v[i].head = q; 370200580Sluigi } 371200580Sluigi if (pkt->proto == IPPROTO_TCP) { /* update state according to flags */ 372232272Soleg uint32_t ack; 373232272Soleg u_char flags = pkt->_flags & (TH_FIN | TH_SYN | TH_RST); 374200580Sluigi 375200580Sluigi#define BOTH_SYN (TH_SYN | (TH_SYN << 8)) 376200580Sluigi#define BOTH_FIN (TH_FIN | (TH_FIN << 8)) 377232273Soleg#define TCP_FLAGS (TH_FLAGS | (TH_FLAGS << 8)) 378232273Soleg#define ACK_FWD 0x10000 /* fwd ack seen */ 379232273Soleg#define ACK_REV 0x20000 /* rev ack seen */ 380232273Soleg 381232272Soleg q->state |= (dir == MATCH_FORWARD) ? flags : (flags << 8); 382232273Soleg switch (q->state & TCP_FLAGS) { 383232272Soleg case TH_SYN: /* opening */ 384200580Sluigi q->expire = time_uptime + V_dyn_syn_lifetime; 385200580Sluigi break; 386200580Sluigi 387200580Sluigi case BOTH_SYN: /* move to established */ 388232272Soleg case BOTH_SYN | TH_FIN: /* one side tries to close */ 389232272Soleg case BOTH_SYN | (TH_FIN << 8): 390200580Sluigi#define _SEQ_GE(a,b) ((int)(a) - (int)(b) >= 0) 391232273Soleg if (tcp == NULL) 392232272Soleg break; 393232272Soleg 394232272Soleg ack = ntohl(tcp->th_ack); 395232272Soleg if (dir == MATCH_FORWARD) { 396232273Soleg if (q->ack_fwd == 0 || 397232273Soleg _SEQ_GE(ack, q->ack_fwd)) { 398232272Soleg q->ack_fwd = ack; 399232273Soleg q->state |= ACK_FWD; 400232273Soleg } 401232272Soleg } else { 402232273Soleg if (q->ack_rev == 0 || 403232273Soleg _SEQ_GE(ack, q->ack_rev)) { 404232272Soleg q->ack_rev = ack; 405232273Soleg q->state |= ACK_REV; 406232273Soleg } 407200580Sluigi } 408232273Soleg if ((q->state & (ACK_FWD | ACK_REV)) == 409232273Soleg (ACK_FWD | ACK_REV)) { 410232273Soleg q->expire = time_uptime + V_dyn_ack_lifetime; 411232273Soleg q->state &= ~(ACK_FWD | ACK_REV); 412232273Soleg } 413200580Sluigi break; 414200580Sluigi 415200580Sluigi case BOTH_SYN | BOTH_FIN: /* both sides closed */ 416200580Sluigi if (V_dyn_fin_lifetime >= V_dyn_keepalive_period) 417200580Sluigi V_dyn_fin_lifetime = V_dyn_keepalive_period - 1; 418200580Sluigi q->expire = time_uptime + V_dyn_fin_lifetime; 419200580Sluigi break; 420200580Sluigi 421200580Sluigi default: 422200580Sluigi#if 0 423200580Sluigi /* 424200580Sluigi * reset or some invalid combination, but can also 425200580Sluigi * occur if we use keep-state the wrong way. 426200580Sluigi */ 427200580Sluigi if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0) 428200580Sluigi printf("invalid state: 0x%x\n", q->state); 429200580Sluigi#endif 430200580Sluigi if (V_dyn_rst_lifetime >= V_dyn_keepalive_period) 431200580Sluigi V_dyn_rst_lifetime = V_dyn_keepalive_period - 1; 432200580Sluigi q->expire = time_uptime + V_dyn_rst_lifetime; 433200580Sluigi break; 434200580Sluigi } 435200580Sluigi } else if (pkt->proto == IPPROTO_UDP) { 436200580Sluigi q->expire = time_uptime + V_dyn_udp_lifetime; 437200580Sluigi } else { 438200580Sluigi /* other protocols */ 439200580Sluigi q->expire = time_uptime + V_dyn_short_lifetime; 440200580Sluigi } 441200580Sluigidone: 442232272Soleg if (match_direction != NULL) 443200580Sluigi *match_direction = dir; 444232272Soleg return (q); 445200580Sluigi} 446200580Sluigi 447200580Sluigiipfw_dyn_rule * 448200601Sluigiipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, 449200580Sluigi struct tcphdr *tcp) 450200580Sluigi{ 451200580Sluigi ipfw_dyn_rule *q; 452243707Smelifaro int i; 453200580Sluigi 454243707Smelifaro i = hash_packet(pkt, V_curr_dyn_buckets); 455243707Smelifaro 456243707Smelifaro IPFW_BUCK_LOCK(i); 457243707Smelifaro q = lookup_dyn_rule_locked(pkt, i, match_direction, tcp); 458200580Sluigi if (q == NULL) 459243707Smelifaro IPFW_BUCK_UNLOCK(i); 460200580Sluigi /* NB: return table locked when q is not NULL */ 461200580Sluigi return q; 462200580Sluigi} 463200580Sluigi 464243707Smelifaro/* 465243707Smelifaro * Unlock bucket mtx 466243707Smelifaro * @p - pointer to dynamic rule 467243707Smelifaro */ 468243707Smelifarovoid 469243707Smelifaroipfw_dyn_unlock(ipfw_dyn_rule *q) 470200580Sluigi{ 471200580Sluigi 472243707Smelifaro IPFW_BUCK_UNLOCK(q->bucket); 473243707Smelifaro} 474243707Smelifaro 475243707Smelifarostatic int 476243707Smelifaroresize_dynamic_table(struct ip_fw_chain *chain, int nbuckets) 477243707Smelifaro{ 478243707Smelifaro int i, k, nbuckets_old; 479243707Smelifaro ipfw_dyn_rule *q; 480243707Smelifaro struct ipfw_dyn_bucket *dyn_v, *dyn_v_old; 481243707Smelifaro 482243707Smelifaro /* Check if given number is power of 2 and less than 64k */ 483243707Smelifaro if ((nbuckets > 65536) || (!powerof2(nbuckets))) 484243707Smelifaro return 1; 485243707Smelifaro 486243707Smelifaro CTR3(KTR_NET, "%s: resize dynamic hash: %d -> %d", __func__, 487243707Smelifaro V_curr_dyn_buckets, nbuckets); 488243707Smelifaro 489243707Smelifaro /* Allocate and initialize new hash */ 490296649Sae dyn_v = malloc(nbuckets * sizeof(*dyn_v), M_IPFW, 491243707Smelifaro M_WAITOK | M_ZERO); 492243707Smelifaro 493243707Smelifaro for (i = 0 ; i < nbuckets; i++) 494243707Smelifaro IPFW_BUCK_LOCK_INIT(&dyn_v[i]); 495243707Smelifaro 496200580Sluigi /* 497243707Smelifaro * Call upper half lock, as get_map() do to ease 498243707Smelifaro * read-only access to dynamic rules hash from sysctl 499200580Sluigi */ 500243707Smelifaro IPFW_UH_WLOCK(chain); 501200580Sluigi 502243707Smelifaro /* 503243707Smelifaro * Acquire chain write lock to permit hash access 504243707Smelifaro * for main traffic path without additional locks 505243707Smelifaro */ 506243707Smelifaro IPFW_WLOCK(chain); 507243707Smelifaro 508243707Smelifaro /* Save old values */ 509243707Smelifaro nbuckets_old = V_curr_dyn_buckets; 510243707Smelifaro dyn_v_old = V_ipfw_dyn_v; 511243707Smelifaro 512243707Smelifaro /* Skip relinking if array is not set up */ 513243707Smelifaro if (V_ipfw_dyn_v == NULL) 514243707Smelifaro V_curr_dyn_buckets = 0; 515243707Smelifaro 516243707Smelifaro /* Re-link all dynamic states */ 517243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets ; i++) { 518243707Smelifaro while (V_ipfw_dyn_v[i].head != NULL) { 519243707Smelifaro /* Remove from current chain */ 520243707Smelifaro q = V_ipfw_dyn_v[i].head; 521243707Smelifaro V_ipfw_dyn_v[i].head = q->next; 522243707Smelifaro 523243707Smelifaro /* Get new hash value */ 524243707Smelifaro k = hash_packet(&q->id, nbuckets); 525243707Smelifaro q->bucket = k; 526243707Smelifaro /* Add to the new head */ 527243707Smelifaro q->next = dyn_v[k].head; 528243707Smelifaro dyn_v[k].head = q; 529243707Smelifaro } 530200580Sluigi } 531243707Smelifaro 532243707Smelifaro /* Update current pointers/buckets values */ 533243707Smelifaro V_curr_dyn_buckets = nbuckets; 534243707Smelifaro V_ipfw_dyn_v = dyn_v; 535243707Smelifaro 536243707Smelifaro IPFW_WUNLOCK(chain); 537243707Smelifaro 538243707Smelifaro IPFW_UH_WUNLOCK(chain); 539243707Smelifaro 540243707Smelifaro /* Start periodic callout on initial creation */ 541243707Smelifaro if (dyn_v_old == NULL) { 542243707Smelifaro callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, curvnet, 0); 543243707Smelifaro return (0); 544200580Sluigi } 545243707Smelifaro 546243707Smelifaro /* Destroy all mutexes */ 547243707Smelifaro for (i = 0 ; i < nbuckets_old ; i++) 548243707Smelifaro IPFW_BUCK_LOCK_DESTROY(&dyn_v_old[i]); 549243707Smelifaro 550243707Smelifaro /* Free old hash */ 551243707Smelifaro free(dyn_v_old, M_IPFW); 552243707Smelifaro 553243707Smelifaro return 0; 554200580Sluigi} 555200580Sluigi 556200580Sluigi/** 557200580Sluigi * Install state of type 'type' for a dynamic session. 558200580Sluigi * The hash table contains two type of rules: 559200580Sluigi * - regular rules (O_KEEP_STATE) 560200580Sluigi * - rules for sessions with limited number of sess per user 561200580Sluigi * (O_LIMIT). When they are created, the parent is 562200580Sluigi * increased by 1, and decreased on delete. In this case, 563200580Sluigi * the third parameter is the parent rule and not the chain. 564200580Sluigi * - "parent" rules for the above (O_LIMIT_PARENT). 565200580Sluigi */ 566200580Sluigistatic ipfw_dyn_rule * 567243707Smelifaroadd_dyn_rule(struct ipfw_flow_id *id, int i, u_int8_t dyn_type, struct ip_fw *rule) 568200580Sluigi{ 569200580Sluigi ipfw_dyn_rule *r; 570200580Sluigi 571243707Smelifaro IPFW_BUCK_ASSERT(i); 572200580Sluigi 573243707Smelifaro r = uma_zalloc(V_ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO); 574200580Sluigi if (r == NULL) { 575243707Smelifaro if (last_log != time_uptime) { 576243707Smelifaro last_log = time_uptime; 577243707Smelifaro log(LOG_DEBUG, "ipfw: %s: Cannot allocate rule\n", 578243707Smelifaro __func__); 579243707Smelifaro } 580200580Sluigi return NULL; 581200580Sluigi } 582200580Sluigi 583243707Smelifaro /* 584243707Smelifaro * refcount on parent is already incremented, so 585243707Smelifaro * it is safe to use parent unlocked. 586243707Smelifaro */ 587200580Sluigi if (dyn_type == O_LIMIT) { 588200580Sluigi ipfw_dyn_rule *parent = (ipfw_dyn_rule *)rule; 589200580Sluigi if ( parent->dyn_type != O_LIMIT_PARENT) 590200580Sluigi panic("invalid parent"); 591200580Sluigi r->parent = parent; 592200580Sluigi rule = parent->rule; 593200580Sluigi } 594200580Sluigi 595200580Sluigi r->id = *id; 596200580Sluigi r->expire = time_uptime + V_dyn_syn_lifetime; 597200580Sluigi r->rule = rule; 598200580Sluigi r->dyn_type = dyn_type; 599243711Smelifaro IPFW_ZERO_DYN_COUNTER(r); 600200580Sluigi r->count = 0; 601200580Sluigi 602200580Sluigi r->bucket = i; 603243707Smelifaro r->next = V_ipfw_dyn_v[i].head; 604243707Smelifaro V_ipfw_dyn_v[i].head = r; 605242631Smelifaro DEB(print_dyn_rule(id, dyn_type, "add dyn entry", "total");) 606200580Sluigi return r; 607200580Sluigi} 608200580Sluigi 609200580Sluigi/** 610200580Sluigi * lookup dynamic parent rule using pkt and rule as search keys. 611200580Sluigi * If the lookup fails, then install one. 612200580Sluigi */ 613200580Sluigistatic ipfw_dyn_rule * 614243707Smelifarolookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule) 615200580Sluigi{ 616200580Sluigi ipfw_dyn_rule *q; 617243707Smelifaro int i, is_v6; 618200580Sluigi 619243707Smelifaro is_v6 = IS_IP6_FLOW_ID(pkt); 620243707Smelifaro i = hash_packet( pkt, V_curr_dyn_buckets ); 621243707Smelifaro *pindex = i; 622243707Smelifaro IPFW_BUCK_LOCK(i); 623243707Smelifaro for (q = V_ipfw_dyn_v[i].head ; q != NULL ; q=q->next) 624243707Smelifaro if (q->dyn_type == O_LIMIT_PARENT && 625243707Smelifaro rule== q->rule && 626243707Smelifaro pkt->proto == q->id.proto && 627243707Smelifaro pkt->src_port == q->id.src_port && 628243707Smelifaro pkt->dst_port == q->id.dst_port && 629243707Smelifaro ( 630243707Smelifaro (is_v6 && 631243707Smelifaro IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), 632243707Smelifaro &(q->id.src_ip6)) && 633243707Smelifaro IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), 634243707Smelifaro &(q->id.dst_ip6))) || 635243707Smelifaro (!is_v6 && 636243707Smelifaro pkt->src_ip == q->id.src_ip && 637243707Smelifaro pkt->dst_ip == q->id.dst_ip) 638243707Smelifaro ) 639243707Smelifaro ) { 640243707Smelifaro q->expire = time_uptime + V_dyn_short_lifetime; 641243707Smelifaro DEB(print_dyn_rule(pkt, q->dyn_type, 642243707Smelifaro "lookup_dyn_parent found", "");) 643243707Smelifaro return q; 644243707Smelifaro } 645200580Sluigi 646243707Smelifaro /* Add virtual limiting rule */ 647243707Smelifaro return add_dyn_rule(pkt, i, O_LIMIT_PARENT, rule); 648200580Sluigi} 649200580Sluigi 650200580Sluigi/** 651200580Sluigi * Install dynamic state for rule type cmd->o.opcode 652200580Sluigi * 653200580Sluigi * Returns 1 (failure) if state is not installed because of errors or because 654200580Sluigi * session limitations are enforced. 655200580Sluigi */ 656200580Sluigiint 657200601Sluigiipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, 658200580Sluigi struct ip_fw_args *args, uint32_t tablearg) 659200580Sluigi{ 660200580Sluigi ipfw_dyn_rule *q; 661243707Smelifaro int i; 662200580Sluigi 663242631Smelifaro DEB(print_dyn_rule(&args->f_id, cmd->o.opcode, "install_state", "");) 664243707Smelifaro 665243707Smelifaro i = hash_packet(&args->f_id, V_curr_dyn_buckets); 666200580Sluigi 667243707Smelifaro IPFW_BUCK_LOCK(i); 668200580Sluigi 669243707Smelifaro q = lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL); 670200580Sluigi 671200580Sluigi if (q != NULL) { /* should never occur */ 672222559Sae DEB( 673200580Sluigi if (last_log != time_uptime) { 674200580Sluigi last_log = time_uptime; 675200580Sluigi printf("ipfw: %s: entry already present, done\n", 676200580Sluigi __func__); 677222559Sae }) 678243707Smelifaro IPFW_BUCK_UNLOCK(i); 679200580Sluigi return (0); 680200580Sluigi } 681200580Sluigi 682243707Smelifaro /* 683243707Smelifaro * State limiting is done via uma(9) zone limiting. 684243707Smelifaro * Save pointer to newly-installed rule and reject 685243707Smelifaro * packet if add_dyn_rule() returned NULL. 686243707Smelifaro * Note q is currently set to NULL. 687243707Smelifaro */ 688200580Sluigi 689200580Sluigi switch (cmd->o.opcode) { 690200580Sluigi case O_KEEP_STATE: /* bidir rule */ 691243707Smelifaro q = add_dyn_rule(&args->f_id, i, O_KEEP_STATE, rule); 692200580Sluigi break; 693200580Sluigi 694200580Sluigi case O_LIMIT: { /* limit number of sessions */ 695200580Sluigi struct ipfw_flow_id id; 696200580Sluigi ipfw_dyn_rule *parent; 697200580Sluigi uint32_t conn_limit; 698200580Sluigi uint16_t limit_mask = cmd->limit_mask; 699243707Smelifaro int pindex; 700200580Sluigi 701244633Smelifaro conn_limit = IP_FW_ARG_TABLEARG(cmd->conn_limit); 702200580Sluigi 703200580Sluigi DEB( 704200580Sluigi if (cmd->conn_limit == IP_FW_TABLEARG) 705200580Sluigi printf("ipfw: %s: O_LIMIT rule, conn_limit: %u " 706200580Sluigi "(tablearg)\n", __func__, conn_limit); 707200580Sluigi else 708200580Sluigi printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n", 709200580Sluigi __func__, conn_limit); 710200580Sluigi ) 711200580Sluigi 712200580Sluigi id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0; 713200580Sluigi id.proto = args->f_id.proto; 714200580Sluigi id.addr_type = args->f_id.addr_type; 715200580Sluigi id.fib = M_GETFIB(args->m); 716200580Sluigi 717200580Sluigi if (IS_IP6_FLOW_ID (&(args->f_id))) { 718291772Sbdrewery bzero(&id.src_ip6, sizeof(id.src_ip6)); 719291772Sbdrewery bzero(&id.dst_ip6, sizeof(id.dst_ip6)); 720291772Sbdrewery 721200580Sluigi if (limit_mask & DYN_SRC_ADDR) 722200580Sluigi id.src_ip6 = args->f_id.src_ip6; 723200580Sluigi if (limit_mask & DYN_DST_ADDR) 724200580Sluigi id.dst_ip6 = args->f_id.dst_ip6; 725200580Sluigi } else { 726200580Sluigi if (limit_mask & DYN_SRC_ADDR) 727200580Sluigi id.src_ip = args->f_id.src_ip; 728200580Sluigi if (limit_mask & DYN_DST_ADDR) 729200580Sluigi id.dst_ip = args->f_id.dst_ip; 730200580Sluigi } 731200580Sluigi if (limit_mask & DYN_SRC_PORT) 732200580Sluigi id.src_port = args->f_id.src_port; 733200580Sluigi if (limit_mask & DYN_DST_PORT) 734200580Sluigi id.dst_port = args->f_id.dst_port; 735243707Smelifaro 736243707Smelifaro /* 737243707Smelifaro * We have to release lock for previous bucket to 738243707Smelifaro * avoid possible deadlock 739243707Smelifaro */ 740243707Smelifaro IPFW_BUCK_UNLOCK(i); 741243707Smelifaro 742243707Smelifaro if ((parent = lookup_dyn_parent(&id, &pindex, rule)) == NULL) { 743200580Sluigi printf("ipfw: %s: add parent failed\n", __func__); 744243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 745200580Sluigi return (1); 746200580Sluigi } 747200580Sluigi 748200580Sluigi if (parent->count >= conn_limit) { 749243707Smelifaro if (V_fw_verbose && last_log != time_uptime) { 750243707Smelifaro last_log = time_uptime; 751243707Smelifaro char sbuf[24]; 752243707Smelifaro last_log = time_uptime; 753243707Smelifaro snprintf(sbuf, sizeof(sbuf), 754243707Smelifaro "%d drop session", 755243707Smelifaro parent->rule->rulenum); 756243707Smelifaro print_dyn_rule_flags(&args->f_id, 757243707Smelifaro cmd->o.opcode, 758243707Smelifaro LOG_SECURITY | LOG_DEBUG, 759243707Smelifaro sbuf, "too many entries"); 760200580Sluigi } 761243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 762243707Smelifaro return (1); 763200580Sluigi } 764243707Smelifaro /* Increment counter on parent */ 765243707Smelifaro parent->count++; 766243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 767243707Smelifaro 768243707Smelifaro IPFW_BUCK_LOCK(i); 769243707Smelifaro q = add_dyn_rule(&args->f_id, i, O_LIMIT, (struct ip_fw *)parent); 770243707Smelifaro if (q == NULL) { 771243707Smelifaro /* Decrement index and notify caller */ 772243707Smelifaro IPFW_BUCK_UNLOCK(i); 773243707Smelifaro IPFW_BUCK_LOCK(pindex); 774243707Smelifaro parent->count--; 775243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 776243707Smelifaro return (1); 777243707Smelifaro } 778200580Sluigi break; 779200580Sluigi } 780200580Sluigi default: 781200580Sluigi printf("ipfw: %s: unknown dynamic rule type %u\n", 782200580Sluigi __func__, cmd->o.opcode); 783200580Sluigi } 784200580Sluigi 785243707Smelifaro if (q == NULL) { 786243707Smelifaro IPFW_BUCK_UNLOCK(i); 787243707Smelifaro return (1); /* Notify caller about failure */ 788243707Smelifaro } 789243707Smelifaro 790200580Sluigi /* XXX just set lifetime */ 791243707Smelifaro lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL); 792200580Sluigi 793243707Smelifaro IPFW_BUCK_UNLOCK(i); 794200580Sluigi return (0); 795200580Sluigi} 796200580Sluigi 797200580Sluigi/* 798200580Sluigi * Generate a TCP packet, containing either a RST or a keepalive. 799200580Sluigi * When flags & TH_RST, we are sending a RST packet, because of a 800200580Sluigi * "reset" action matched the packet. 801200580Sluigi * Otherwise we are sending a keepalive, and flags & TH_ 802200580Sluigi * The 'replyto' mbuf is the mbuf being replied to, if any, and is required 803200580Sluigi * so that MAC can label the reply appropriately. 804200580Sluigi */ 805200580Sluigistruct mbuf * 806200601Sluigiipfw_send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq, 807200580Sluigi u_int32_t ack, int flags) 808200580Sluigi{ 809210123Sluigi struct mbuf *m = NULL; /* stupid compiler */ 810200580Sluigi int len, dir; 811200580Sluigi struct ip *h = NULL; /* stupid compiler */ 812200580Sluigi#ifdef INET6 813200580Sluigi struct ip6_hdr *h6 = NULL; 814200580Sluigi#endif 815200580Sluigi struct tcphdr *th = NULL; 816200580Sluigi 817243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 818200580Sluigi if (m == NULL) 819200580Sluigi return (NULL); 820200580Sluigi 821200580Sluigi M_SETFIB(m, id->fib); 822200580Sluigi#ifdef MAC 823200580Sluigi if (replyto != NULL) 824200580Sluigi mac_netinet_firewall_reply(replyto, m); 825200580Sluigi else 826200580Sluigi mac_netinet_firewall_send(m); 827200580Sluigi#else 828200580Sluigi (void)replyto; /* don't warn about unused arg */ 829200580Sluigi#endif 830200580Sluigi 831200580Sluigi switch (id->addr_type) { 832200580Sluigi case 4: 833200580Sluigi len = sizeof(struct ip) + sizeof(struct tcphdr); 834200580Sluigi break; 835200580Sluigi#ifdef INET6 836200580Sluigi case 6: 837200580Sluigi len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); 838200580Sluigi break; 839200580Sluigi#endif 840200580Sluigi default: 841200580Sluigi /* XXX: log me?!? */ 842201527Sluigi FREE_PKT(m); 843200580Sluigi return (NULL); 844200580Sluigi } 845200580Sluigi dir = ((flags & (TH_SYN | TH_RST)) == TH_SYN); 846200580Sluigi 847200580Sluigi m->m_data += max_linkhdr; 848200580Sluigi m->m_flags |= M_SKIP_FIREWALL; 849200580Sluigi m->m_pkthdr.len = m->m_len = len; 850200580Sluigi m->m_pkthdr.rcvif = NULL; 851200580Sluigi bzero(m->m_data, len); 852200580Sluigi 853200580Sluigi switch (id->addr_type) { 854200580Sluigi case 4: 855200580Sluigi h = mtod(m, struct ip *); 856200580Sluigi 857200580Sluigi /* prepare for checksum */ 858200580Sluigi h->ip_p = IPPROTO_TCP; 859200580Sluigi h->ip_len = htons(sizeof(struct tcphdr)); 860200580Sluigi if (dir) { 861200580Sluigi h->ip_src.s_addr = htonl(id->src_ip); 862200580Sluigi h->ip_dst.s_addr = htonl(id->dst_ip); 863200580Sluigi } else { 864200580Sluigi h->ip_src.s_addr = htonl(id->dst_ip); 865200580Sluigi h->ip_dst.s_addr = htonl(id->src_ip); 866200580Sluigi } 867200580Sluigi 868200580Sluigi th = (struct tcphdr *)(h + 1); 869200580Sluigi break; 870200580Sluigi#ifdef INET6 871200580Sluigi case 6: 872200580Sluigi h6 = mtod(m, struct ip6_hdr *); 873200580Sluigi 874200580Sluigi /* prepare for checksum */ 875200580Sluigi h6->ip6_nxt = IPPROTO_TCP; 876200580Sluigi h6->ip6_plen = htons(sizeof(struct tcphdr)); 877200580Sluigi if (dir) { 878200580Sluigi h6->ip6_src = id->src_ip6; 879200580Sluigi h6->ip6_dst = id->dst_ip6; 880200580Sluigi } else { 881200580Sluigi h6->ip6_src = id->dst_ip6; 882200580Sluigi h6->ip6_dst = id->src_ip6; 883200580Sluigi } 884200580Sluigi 885200580Sluigi th = (struct tcphdr *)(h6 + 1); 886200580Sluigi break; 887200580Sluigi#endif 888200580Sluigi } 889200580Sluigi 890200580Sluigi if (dir) { 891200580Sluigi th->th_sport = htons(id->src_port); 892200580Sluigi th->th_dport = htons(id->dst_port); 893200580Sluigi } else { 894200580Sluigi th->th_sport = htons(id->dst_port); 895200580Sluigi th->th_dport = htons(id->src_port); 896200580Sluigi } 897200580Sluigi th->th_off = sizeof(struct tcphdr) >> 2; 898200580Sluigi 899200580Sluigi if (flags & TH_RST) { 900200580Sluigi if (flags & TH_ACK) { 901200580Sluigi th->th_seq = htonl(ack); 902200580Sluigi th->th_flags = TH_RST; 903200580Sluigi } else { 904200580Sluigi if (flags & TH_SYN) 905200580Sluigi seq++; 906200580Sluigi th->th_ack = htonl(seq); 907200580Sluigi th->th_flags = TH_RST | TH_ACK; 908200580Sluigi } 909200580Sluigi } else { 910200580Sluigi /* 911200580Sluigi * Keepalive - use caller provided sequence numbers 912200580Sluigi */ 913200580Sluigi th->th_seq = htonl(seq); 914200580Sluigi th->th_ack = htonl(ack); 915200580Sluigi th->th_flags = TH_ACK; 916200580Sluigi } 917200580Sluigi 918200580Sluigi switch (id->addr_type) { 919200580Sluigi case 4: 920200580Sluigi th->th_sum = in_cksum(m, len); 921200580Sluigi 922200580Sluigi /* finish the ip header */ 923200580Sluigi h->ip_v = 4; 924200580Sluigi h->ip_hl = sizeof(*h) >> 2; 925200580Sluigi h->ip_tos = IPTOS_LOWDELAY; 926241913Sglebius h->ip_off = htons(0); 927241913Sglebius h->ip_len = htons(len); 928200580Sluigi h->ip_ttl = V_ip_defttl; 929200580Sluigi h->ip_sum = 0; 930200580Sluigi break; 931200580Sluigi#ifdef INET6 932200580Sluigi case 6: 933200580Sluigi th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*h6), 934200580Sluigi sizeof(struct tcphdr)); 935200580Sluigi 936200580Sluigi /* finish the ip6 header */ 937200580Sluigi h6->ip6_vfc |= IPV6_VERSION; 938200580Sluigi h6->ip6_hlim = IPV6_DEFHLIM; 939200580Sluigi break; 940200580Sluigi#endif 941200580Sluigi } 942200580Sluigi 943200580Sluigi return (m); 944200580Sluigi} 945200580Sluigi 946200580Sluigi/* 947242834Smelifaro * Queue keepalive packets for given dynamic rule 948242834Smelifaro */ 949242834Smelifarostatic struct mbuf ** 950242834Smelifaroipfw_dyn_send_ka(struct mbuf **mtailp, ipfw_dyn_rule *q) 951242834Smelifaro{ 952242834Smelifaro struct mbuf *m_rev, *m_fwd; 953242834Smelifaro 954242834Smelifaro m_rev = (q->state & ACK_REV) ? NULL : 955242834Smelifaro ipfw_send_pkt(NULL, &(q->id), q->ack_rev - 1, q->ack_fwd, TH_SYN); 956242834Smelifaro m_fwd = (q->state & ACK_FWD) ? NULL : 957242834Smelifaro ipfw_send_pkt(NULL, &(q->id), q->ack_fwd - 1, q->ack_rev, 0); 958242834Smelifaro 959242834Smelifaro if (m_rev != NULL) { 960242834Smelifaro *mtailp = m_rev; 961242834Smelifaro mtailp = &(*mtailp)->m_nextpkt; 962242834Smelifaro } 963242834Smelifaro if (m_fwd != NULL) { 964242834Smelifaro *mtailp = m_fwd; 965242834Smelifaro mtailp = &(*mtailp)->m_nextpkt; 966242834Smelifaro } 967242834Smelifaro 968242834Smelifaro return (mtailp); 969242834Smelifaro} 970242834Smelifaro 971242834Smelifaro/* 972243707Smelifaro * This procedure is used to perform various maintance 973243707Smelifaro * on dynamic hash list. Currently it is called every second. 974200580Sluigi */ 975200580Sluigistatic void 976243707Smelifaroipfw_dyn_tick(void * vnetx) 977200580Sluigi{ 978243707Smelifaro struct ip_fw_chain *chain; 979243707Smelifaro int check_ka = 0; 980200580Sluigi#ifdef VIMAGE 981200580Sluigi struct vnet *vp = vnetx; 982200580Sluigi#endif 983200580Sluigi 984200580Sluigi CURVNET_SET(vp); 985200580Sluigi 986243707Smelifaro chain = &V_layer3_chain; 987243707Smelifaro 988247626Smelifaro /* Run keepalive checks every keepalive_period iff ka is enabled */ 989247626Smelifaro if ((V_dyn_keepalive_last + V_dyn_keepalive_period <= time_uptime) && 990243707Smelifaro (V_dyn_keepalive != 0)) { 991243707Smelifaro V_dyn_keepalive_last = time_uptime; 992243707Smelifaro check_ka = 1; 993243707Smelifaro } 994243707Smelifaro 995243707Smelifaro check_dyn_rules(chain, NULL, RESVD_SET, check_ka, 1); 996243707Smelifaro 997243707Smelifaro callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, vnetx, 0); 998243707Smelifaro 999243707Smelifaro CURVNET_RESTORE(); 1000243707Smelifaro} 1001243707Smelifaro 1002243707Smelifaro 1003243707Smelifaro/* 1004243707Smelifaro * Walk thru all dynamic states doing generic maintance: 1005243707Smelifaro * 1) free expired states 1006243707Smelifaro * 2) free all states based on deleted rule / set 1007243707Smelifaro * 3) send keepalives for states if needed 1008243707Smelifaro * 1009243707Smelifaro * @chain - pointer to current ipfw rules chain 1010243707Smelifaro * @rule - delete all states originated by given rule if != NULL 1011243707Smelifaro * @set - delete all states originated by any rule in set @set if != RESVD_SET 1012243707Smelifaro * @check_ka - perform checking/sending keepalives 1013243707Smelifaro * @timer - indicate call from timer routine. 1014243707Smelifaro * 1015243707Smelifaro * Timer routine must call this function unlocked to permit 1016243707Smelifaro * sending keepalives/resizing table. 1017243707Smelifaro * 1018243707Smelifaro * Others has to call function with IPFW_UH_WLOCK held. 1019243707Smelifaro * Additionally, function assume that dynamic rule/set is 1020243707Smelifaro * ALREADY deleted so no new states can be generated by 1021243707Smelifaro * 'deleted' rules. 1022243707Smelifaro * 1023243707Smelifaro * Write lock is needed to ensure that unused parent rules 1024243707Smelifaro * are not freed by other instance (see stage 2, 3) 1025243707Smelifaro */ 1026243707Smelifarostatic void 1027243707Smelifarocheck_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, 1028243707Smelifaro int set, int check_ka, int timer) 1029243707Smelifaro{ 1030243707Smelifaro struct mbuf *m0, *m, *mnext, **mtailp; 1031243707Smelifaro struct ip *h; 1032243707Smelifaro int i, dyn_count, new_buckets = 0, max_buckets; 1033243707Smelifaro int expired = 0, expired_limits = 0, parents = 0, total = 0; 1034243707Smelifaro ipfw_dyn_rule *q, *q_prev, *q_next; 1035243707Smelifaro ipfw_dyn_rule *exp_head, **exptailp; 1036243707Smelifaro ipfw_dyn_rule *exp_lhead, **expltailp; 1037243707Smelifaro 1038243707Smelifaro KASSERT(V_ipfw_dyn_v != NULL, ("%s: dynamic table not allocated", 1039243707Smelifaro __func__)); 1040243707Smelifaro 1041243707Smelifaro /* Avoid possible LOR */ 1042243707Smelifaro KASSERT(!check_ka || timer, ("%s: keepalive check with lock held", 1043243707Smelifaro __func__)); 1044243707Smelifaro 1045200580Sluigi /* 1046243707Smelifaro * Do not perform any checks if we currently have no dynamic states 1047243707Smelifaro */ 1048243707Smelifaro if (DYN_COUNT == 0) 1049243707Smelifaro return; 1050243707Smelifaro 1051243707Smelifaro /* Expired states */ 1052243707Smelifaro exp_head = NULL; 1053243707Smelifaro exptailp = &exp_head; 1054243707Smelifaro 1055243707Smelifaro /* Expired limit states */ 1056243707Smelifaro exp_lhead = NULL; 1057243707Smelifaro expltailp = &exp_lhead; 1058243707Smelifaro 1059243707Smelifaro /* 1060200580Sluigi * We make a chain of packets to go out here -- not deferring 1061200580Sluigi * until after we drop the IPFW dynamic rule lock would result 1062200580Sluigi * in a lock order reversal with the normal packet input -> ipfw 1063200580Sluigi * call stack. 1064200580Sluigi */ 1065200580Sluigi m0 = NULL; 1066200580Sluigi mtailp = &m0; 1067243707Smelifaro 1068243707Smelifaro /* Protect from hash resizing */ 1069243707Smelifaro if (timer != 0) 1070243707Smelifaro IPFW_UH_WLOCK(chain); 1071243707Smelifaro else 1072243707Smelifaro IPFW_UH_WLOCK_ASSERT(chain); 1073243707Smelifaro 1074243707Smelifaro#define NEXT_RULE() { q_prev = q; q = q->next ; continue; } 1075243707Smelifaro 1076243707Smelifaro /* Stage 1: perform requested deletion */ 1077200580Sluigi for (i = 0 ; i < V_curr_dyn_buckets ; i++) { 1078243707Smelifaro IPFW_BUCK_LOCK(i); 1079243707Smelifaro for (q = V_ipfw_dyn_v[i].head, q_prev = q; q ; ) { 1080243707Smelifaro /* account every rule */ 1081243707Smelifaro total++; 1082242834Smelifaro 1083243707Smelifaro /* Skip parent rules at all */ 1084243707Smelifaro if (q->dyn_type == O_LIMIT_PARENT) { 1085243707Smelifaro parents++; 1086243707Smelifaro NEXT_RULE(); 1087243707Smelifaro } 1088243707Smelifaro 1089243707Smelifaro /* 1090243707Smelifaro * Remove rules which are: 1091243707Smelifaro * 1) expired 1092243707Smelifaro * 2) created by given rule 1093243707Smelifaro * 3) created by any rule in given set 1094243707Smelifaro */ 1095243707Smelifaro if ((TIME_LEQ(q->expire, time_uptime)) || 1096243707Smelifaro ((rule != NULL) && (q->rule == rule)) || 1097243707Smelifaro ((set != RESVD_SET) && (q->rule->set == set))) { 1098243707Smelifaro /* Unlink q from current list */ 1099243707Smelifaro q_next = q->next; 1100243707Smelifaro if (q == V_ipfw_dyn_v[i].head) 1101243707Smelifaro V_ipfw_dyn_v[i].head = q_next; 1102243707Smelifaro else 1103243707Smelifaro q_prev->next = q_next; 1104243707Smelifaro 1105243707Smelifaro q->next = NULL; 1106243707Smelifaro 1107243707Smelifaro /* queue q to expire list */ 1108243707Smelifaro if (q->dyn_type != O_LIMIT) { 1109243707Smelifaro *exptailp = q; 1110243707Smelifaro exptailp = &(*exptailp)->next; 1111243707Smelifaro DEB(print_dyn_rule(&q->id, q->dyn_type, 1112243707Smelifaro "unlink entry", "left"); 1113243707Smelifaro ) 1114243707Smelifaro } else { 1115243707Smelifaro /* Separate list for limit rules */ 1116243707Smelifaro *expltailp = q; 1117243707Smelifaro expltailp = &(*expltailp)->next; 1118243707Smelifaro expired_limits++; 1119243707Smelifaro DEB(print_dyn_rule(&q->id, q->dyn_type, 1120243707Smelifaro "unlink limit entry", "left"); 1121243707Smelifaro ) 1122243707Smelifaro } 1123243707Smelifaro 1124243707Smelifaro q = q_next; 1125243707Smelifaro expired++; 1126200580Sluigi continue; 1127243707Smelifaro } 1128200580Sluigi 1129243707Smelifaro /* 1130243707Smelifaro * Check if we need to send keepalive: 1131243707Smelifaro * we need to ensure if is time to do KA, 1132243707Smelifaro * this is established TCP session, and 1133243707Smelifaro * expire time is within keepalive interval 1134243707Smelifaro */ 1135243707Smelifaro if ((check_ka != 0) && (q->id.proto == IPPROTO_TCP) && 1136243707Smelifaro ((q->state & BOTH_SYN) == BOTH_SYN) && 1137243707Smelifaro (TIME_LEQ(q->expire, time_uptime + 1138243707Smelifaro V_dyn_keepalive_interval))) 1139243707Smelifaro mtailp = ipfw_dyn_send_ka(mtailp, q); 1140243707Smelifaro 1141243707Smelifaro NEXT_RULE(); 1142200580Sluigi } 1143243707Smelifaro IPFW_BUCK_UNLOCK(i); 1144200580Sluigi } 1145242834Smelifaro 1146243707Smelifaro /* Stage 2: decrement counters from O_LIMIT parents */ 1147243707Smelifaro if (expired_limits != 0) { 1148243707Smelifaro /* 1149243707Smelifaro * XXX: Note that deleting set with more than one 1150243707Smelifaro * heavily-used LIMIT rules can result in overwhelming 1151243707Smelifaro * locking due to lack of per-hash value sorting 1152243707Smelifaro * 1153243707Smelifaro * We should probably think about: 1154243707Smelifaro * 1) pre-allocating hash of size, say, 1155243707Smelifaro * MAX(16, V_curr_dyn_buckets / 1024) 1156243707Smelifaro * 2) checking if expired_limits is large enough 1157243707Smelifaro * 3) If yes, init hash (or its part), re-link 1158243707Smelifaro * current list and start decrementing procedure in 1159243707Smelifaro * each bucket separately 1160243707Smelifaro */ 1161243707Smelifaro 1162243707Smelifaro /* 1163243707Smelifaro * Small optimization: do not unlock bucket until 1164243707Smelifaro * we see the next item resides in different bucket 1165243707Smelifaro */ 1166243707Smelifaro if (exp_lhead != NULL) { 1167243707Smelifaro i = exp_lhead->parent->bucket; 1168243707Smelifaro IPFW_BUCK_LOCK(i); 1169243707Smelifaro } 1170243707Smelifaro for (q = exp_lhead; q != NULL; q = q->next) { 1171243707Smelifaro if (i != q->parent->bucket) { 1172243707Smelifaro IPFW_BUCK_UNLOCK(i); 1173243707Smelifaro i = q->parent->bucket; 1174243707Smelifaro IPFW_BUCK_LOCK(i); 1175243707Smelifaro } 1176243707Smelifaro 1177243707Smelifaro /* Decrease parent refcount */ 1178243707Smelifaro q->parent->count--; 1179243707Smelifaro } 1180243707Smelifaro if (exp_lhead != NULL) 1181243707Smelifaro IPFW_BUCK_UNLOCK(i); 1182243707Smelifaro } 1183243707Smelifaro 1184243707Smelifaro /* 1185243707Smelifaro * We protectet ourselves from unused parent deletion 1186243707Smelifaro * (from the timer function) by holding UH write lock. 1187243707Smelifaro */ 1188243707Smelifaro 1189243707Smelifaro /* Stage 3: remove unused parent rules */ 1190243707Smelifaro if ((parents != 0) && (expired != 0)) { 1191243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets ; i++) { 1192243707Smelifaro IPFW_BUCK_LOCK(i); 1193243707Smelifaro for (q = V_ipfw_dyn_v[i].head, q_prev = q ; q ; ) { 1194243707Smelifaro if (q->dyn_type != O_LIMIT_PARENT) 1195243707Smelifaro NEXT_RULE(); 1196243707Smelifaro 1197243707Smelifaro if (q->count != 0) 1198243707Smelifaro NEXT_RULE(); 1199243707Smelifaro 1200243707Smelifaro /* Parent rule without consumers */ 1201243707Smelifaro 1202243707Smelifaro /* Unlink q from current list */ 1203243707Smelifaro q_next = q->next; 1204243707Smelifaro if (q == V_ipfw_dyn_v[i].head) 1205243707Smelifaro V_ipfw_dyn_v[i].head = q_next; 1206243707Smelifaro else 1207243707Smelifaro q_prev->next = q_next; 1208243707Smelifaro 1209243707Smelifaro q->next = NULL; 1210243707Smelifaro 1211243707Smelifaro /* Add to expired list */ 1212243707Smelifaro *exptailp = q; 1213243707Smelifaro exptailp = &(*exptailp)->next; 1214243707Smelifaro 1215243707Smelifaro DEB(print_dyn_rule(&q->id, q->dyn_type, 1216243707Smelifaro "unlink parent entry", "left"); 1217243707Smelifaro ) 1218243707Smelifaro 1219243707Smelifaro expired++; 1220243707Smelifaro 1221243707Smelifaro q = q_next; 1222243707Smelifaro } 1223243707Smelifaro IPFW_BUCK_UNLOCK(i); 1224243707Smelifaro } 1225243707Smelifaro } 1226243707Smelifaro 1227243707Smelifaro#undef NEXT_RULE 1228243707Smelifaro 1229243707Smelifaro if (timer != 0) { 1230243707Smelifaro /* 1231243707Smelifaro * Check if we need to resize hash: 1232243707Smelifaro * if current number of states exceeds number of buckes in hash, 1233243707Smelifaro * grow hash size to the minimum power of 2 which is bigger than 1234243707Smelifaro * current states count. Limit hash size by 64k. 1235243707Smelifaro */ 1236243707Smelifaro max_buckets = (V_dyn_buckets_max > 65536) ? 1237243707Smelifaro 65536 : V_dyn_buckets_max; 1238243707Smelifaro 1239243707Smelifaro dyn_count = DYN_COUNT; 1240243707Smelifaro 1241243707Smelifaro if ((dyn_count > V_curr_dyn_buckets * 2) && 1242243707Smelifaro (dyn_count < max_buckets)) { 1243243707Smelifaro new_buckets = V_curr_dyn_buckets; 1244243707Smelifaro while (new_buckets < dyn_count) { 1245243707Smelifaro new_buckets *= 2; 1246243707Smelifaro 1247243707Smelifaro if (new_buckets >= max_buckets) 1248243707Smelifaro break; 1249243707Smelifaro } 1250243707Smelifaro } 1251243707Smelifaro 1252243707Smelifaro IPFW_UH_WUNLOCK(chain); 1253243707Smelifaro } 1254243707Smelifaro 1255243707Smelifaro /* Finally delete old states ad limits if any */ 1256243707Smelifaro for (q = exp_head; q != NULL; q = q_next) { 1257243707Smelifaro q_next = q->next; 1258243707Smelifaro uma_zfree(V_ipfw_dyn_rule_zone, q); 1259243707Smelifaro } 1260243707Smelifaro 1261243707Smelifaro for (q = exp_lhead; q != NULL; q = q_next) { 1262243707Smelifaro q_next = q->next; 1263243707Smelifaro uma_zfree(V_ipfw_dyn_rule_zone, q); 1264243707Smelifaro } 1265243707Smelifaro 1266243707Smelifaro /* 1267243707Smelifaro * The rest code MUST be called from timer routine only 1268243707Smelifaro * without holding any locks 1269243707Smelifaro */ 1270243707Smelifaro if (timer == 0) 1271243707Smelifaro return; 1272243707Smelifaro 1273242834Smelifaro /* Send keepalive packets if any */ 1274232273Soleg for (m = m0; m != NULL; m = mnext) { 1275200580Sluigi mnext = m->m_nextpkt; 1276200580Sluigi m->m_nextpkt = NULL; 1277242834Smelifaro h = mtod(m, struct ip *); 1278242834Smelifaro if (h->ip_v == 4) 1279242834Smelifaro ip_output(m, NULL, NULL, 0, NULL, NULL); 1280200580Sluigi#ifdef INET6 1281242834Smelifaro else 1282242834Smelifaro ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 1283242834Smelifaro#endif 1284200580Sluigi } 1285243707Smelifaro 1286243707Smelifaro /* Run table resize without holding any locks */ 1287243707Smelifaro if (new_buckets != 0) 1288243707Smelifaro resize_dynamic_table(chain, new_buckets); 1289200580Sluigi} 1290200580Sluigi 1291243707Smelifaro/* 1292243707Smelifaro * Deletes all dynamic rules originated by given rule or all rules in 1293243707Smelifaro * given set. Specify RESVD_SET to indicate set should not be used. 1294243707Smelifaro * @chain - pointer to current ipfw rules chain 1295243707Smelifaro * @rule - delete all states originated by given rule if != NULL 1296243707Smelifaro * @set - delete all states originated by any rule in set @set if != RESVD_SET 1297243707Smelifaro * 1298243707Smelifaro * Function has to be called with IPFW_UH_WLOCK held. 1299243707Smelifaro * Additionally, function assume that dynamic rule/set is 1300243707Smelifaro * ALREADY deleted so no new states can be generated by 1301243707Smelifaro * 'deleted' rules. 1302243707Smelifaro */ 1303200580Sluigivoid 1304243707Smelifaroipfw_expire_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, int set) 1305200580Sluigi{ 1306200580Sluigi 1307243707Smelifaro check_dyn_rules(chain, rule, set, 0, 0); 1308200580Sluigi} 1309200580Sluigi 1310200580Sluigivoid 1311243707Smelifaroipfw_dyn_init(struct ip_fw_chain *chain) 1312200580Sluigi{ 1313200580Sluigi 1314200580Sluigi V_ipfw_dyn_v = NULL; 1315243707Smelifaro V_dyn_buckets_max = 256; /* must be power of 2 */ 1316200580Sluigi V_curr_dyn_buckets = 256; /* must be power of 2 */ 1317200580Sluigi 1318200580Sluigi V_dyn_ack_lifetime = 300; 1319200580Sluigi V_dyn_syn_lifetime = 20; 1320200580Sluigi V_dyn_fin_lifetime = 1; 1321200580Sluigi V_dyn_rst_lifetime = 1; 1322200580Sluigi V_dyn_udp_lifetime = 10; 1323200580Sluigi V_dyn_short_lifetime = 5; 1324200580Sluigi 1325200580Sluigi V_dyn_keepalive_interval = 20; 1326200580Sluigi V_dyn_keepalive_period = 5; 1327200580Sluigi V_dyn_keepalive = 1; /* do send keepalives */ 1328247626Smelifaro V_dyn_keepalive_last = time_uptime; 1329200580Sluigi 1330200580Sluigi V_dyn_max = 4096; /* max # of dynamic rules */ 1331243707Smelifaro 1332243707Smelifaro V_ipfw_dyn_rule_zone = uma_zcreate("IPFW dynamic rule", 1333243707Smelifaro sizeof(ipfw_dyn_rule), NULL, NULL, NULL, NULL, 1334243707Smelifaro UMA_ALIGN_PTR, 0); 1335243707Smelifaro 1336243707Smelifaro /* Enforce limit on dynamic rules */ 1337243707Smelifaro uma_zone_set_max(V_ipfw_dyn_rule_zone, V_dyn_max); 1338243707Smelifaro 1339314667Savg callout_init(&V_ipfw_timeout, 1); 1340243707Smelifaro 1341243707Smelifaro /* 1342243707Smelifaro * This can potentially be done on first dynamic rule 1343243707Smelifaro * being added to chain. 1344243707Smelifaro */ 1345243707Smelifaro resize_dynamic_table(chain, V_curr_dyn_buckets); 1346200580Sluigi} 1347200580Sluigi 1348200580Sluigivoid 1349200580Sluigiipfw_dyn_uninit(int pass) 1350200580Sluigi{ 1351243707Smelifaro int i; 1352243707Smelifaro 1353243707Smelifaro if (pass == 0) { 1354200580Sluigi callout_drain(&V_ipfw_timeout); 1355243707Smelifaro return; 1356200580Sluigi } 1357243707Smelifaro 1358243707Smelifaro if (V_ipfw_dyn_v != NULL) { 1359243707Smelifaro /* 1360243707Smelifaro * Skip deleting all dynamic states - 1361243707Smelifaro * uma_zdestroy() does this more efficiently; 1362243707Smelifaro */ 1363243707Smelifaro 1364243707Smelifaro /* Destroy all mutexes */ 1365243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets ; i++) 1366243707Smelifaro IPFW_BUCK_LOCK_DESTROY(&V_ipfw_dyn_v[i]); 1367243707Smelifaro free(V_ipfw_dyn_v, M_IPFW); 1368243707Smelifaro V_ipfw_dyn_v = NULL; 1369243707Smelifaro } 1370243707Smelifaro 1371243707Smelifaro uma_zdestroy(V_ipfw_dyn_rule_zone); 1372200580Sluigi} 1373200580Sluigi 1374243707Smelifaro#ifdef SYSCTL_NODE 1375243707Smelifaro/* 1376243707Smelifaro * Get/set maximum number of dynamic states in given VNET instance. 1377243707Smelifaro */ 1378243707Smelifarostatic int 1379243707Smelifarosysctl_ipfw_dyn_max(SYSCTL_HANDLER_ARGS) 1380243707Smelifaro{ 1381243707Smelifaro int error; 1382243707Smelifaro unsigned int nstates; 1383243707Smelifaro 1384243707Smelifaro nstates = V_dyn_max; 1385243707Smelifaro 1386243707Smelifaro error = sysctl_handle_int(oidp, &nstates, 0, req); 1387243707Smelifaro /* Read operation or some error */ 1388243707Smelifaro if ((error != 0) || (req->newptr == NULL)) 1389243707Smelifaro return (error); 1390243707Smelifaro 1391243707Smelifaro V_dyn_max = nstates; 1392243707Smelifaro uma_zone_set_max(V_ipfw_dyn_rule_zone, V_dyn_max); 1393243707Smelifaro 1394243707Smelifaro return (0); 1395243707Smelifaro} 1396243707Smelifaro 1397243707Smelifaro/* 1398243707Smelifaro * Get current number of dynamic states in given VNET instance. 1399243707Smelifaro */ 1400243707Smelifarostatic int 1401243707Smelifarosysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS) 1402243707Smelifaro{ 1403243707Smelifaro int error; 1404243707Smelifaro unsigned int nstates; 1405243707Smelifaro 1406243707Smelifaro nstates = DYN_COUNT; 1407243707Smelifaro 1408243707Smelifaro error = sysctl_handle_int(oidp, &nstates, 0, req); 1409243707Smelifaro 1410243707Smelifaro return (error); 1411243707Smelifaro} 1412243707Smelifaro#endif 1413243707Smelifaro 1414243707Smelifaro/* 1415243707Smelifaro * Returns number of dynamic rules. 1416243707Smelifaro */ 1417200580Sluigiint 1418200580Sluigiipfw_dyn_len(void) 1419200580Sluigi{ 1420243707Smelifaro 1421200580Sluigi return (V_ipfw_dyn_v == NULL) ? 0 : 1422243707Smelifaro (DYN_COUNT * sizeof(ipfw_dyn_rule)); 1423200580Sluigi} 1424200580Sluigi 1425243707Smelifaro/* 1426243707Smelifaro * Fill given buffer with dynamic states. 1427243707Smelifaro * IPFW_UH_RLOCK has to be held while calling. 1428243707Smelifaro */ 1429200580Sluigivoid 1430243707Smelifaroipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep) 1431200580Sluigi{ 1432200580Sluigi ipfw_dyn_rule *p, *last = NULL; 1433200580Sluigi char *bp; 1434200580Sluigi int i; 1435200580Sluigi 1436200580Sluigi if (V_ipfw_dyn_v == NULL) 1437200580Sluigi return; 1438200580Sluigi bp = *pbp; 1439200580Sluigi 1440243707Smelifaro IPFW_UH_RLOCK_ASSERT(chain); 1441243707Smelifaro 1442243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets; i++) { 1443243707Smelifaro IPFW_BUCK_LOCK(i); 1444243707Smelifaro for (p = V_ipfw_dyn_v[i].head ; p != NULL; p = p->next) { 1445200580Sluigi if (bp + sizeof *p <= ep) { 1446200580Sluigi ipfw_dyn_rule *dst = 1447200580Sluigi (ipfw_dyn_rule *)bp; 1448200580Sluigi bcopy(p, dst, sizeof *p); 1449200580Sluigi bcopy(&(p->rule->rulenum), &(dst->rule), 1450200580Sluigi sizeof(p->rule->rulenum)); 1451200580Sluigi /* 1452200580Sluigi * store set number into high word of 1453200580Sluigi * dst->rule pointer. 1454200580Sluigi */ 1455200580Sluigi bcopy(&(p->rule->set), 1456200580Sluigi (char *)&dst->rule + 1457200580Sluigi sizeof(p->rule->rulenum), 1458200580Sluigi sizeof(p->rule->set)); 1459200580Sluigi /* 1460200580Sluigi * store a non-null value in "next". 1461200580Sluigi * The userland code will interpret a 1462200580Sluigi * NULL here as a marker 1463200580Sluigi * for the last dynamic rule. 1464200580Sluigi */ 1465200580Sluigi bcopy(&dst, &dst->next, sizeof(dst)); 1466200580Sluigi last = dst; 1467200580Sluigi dst->expire = 1468200580Sluigi TIME_LEQ(dst->expire, time_uptime) ? 1469200580Sluigi 0 : dst->expire - time_uptime ; 1470200580Sluigi bp += sizeof(ipfw_dyn_rule); 1471200580Sluigi } 1472200580Sluigi } 1473243707Smelifaro IPFW_BUCK_UNLOCK(i); 1474243707Smelifaro } 1475243707Smelifaro 1476200580Sluigi if (last != NULL) /* mark last dynamic rule */ 1477200580Sluigi bzero(&last->next, sizeof(last)); 1478200580Sluigi *pbp = bp; 1479200580Sluigi} 1480200601Sluigi/* end of file */ 1481