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$"); 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 234200580Sluigistatic __inline int 235200580Sluigihash_packet6(struct ipfw_flow_id *id) 236200580Sluigi{ 237200580Sluigi u_int32_t i; 238200580Sluigi i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ 239200580Sluigi (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ 240200580Sluigi (id->src_ip6.__u6_addr.__u6_addr32[2]) ^ 241200580Sluigi (id->src_ip6.__u6_addr.__u6_addr32[3]) ^ 242200580Sluigi (id->dst_port) ^ (id->src_port); 243200580Sluigi return i; 244200580Sluigi} 245200580Sluigi 246200580Sluigi/* 247200580Sluigi * IMPORTANT: the hash function for dynamic rules must be commutative 248200580Sluigi * in source and destination (ip,port), because rules are bidirectional 249200580Sluigi * and we want to find both in the same bucket. 250200580Sluigi */ 251200580Sluigistatic __inline int 252243707Smelifarohash_packet(struct ipfw_flow_id *id, int buckets) 253200580Sluigi{ 254200580Sluigi u_int32_t i; 255200580Sluigi 256200580Sluigi#ifdef INET6 257200580Sluigi if (IS_IP6_FLOW_ID(id)) 258200580Sluigi i = hash_packet6(id); 259200580Sluigi else 260200580Sluigi#endif /* INET6 */ 261200580Sluigi i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port); 262243707Smelifaro i &= (buckets - 1); 263200580Sluigi return i; 264200580Sluigi} 265200580Sluigi 266242631Smelifaro/** 267242631Smelifaro * Print customizable flow id description via log(9) facility. 268242631Smelifaro */ 269242631Smelifarostatic void 270242631Smelifaroprint_dyn_rule_flags(struct ipfw_flow_id *id, int dyn_type, int log_flags, 271242631Smelifaro char *prefix, char *postfix) 272200580Sluigi{ 273200580Sluigi struct in_addr da; 274200580Sluigi#ifdef INET6 275200580Sluigi char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; 276200580Sluigi#else 277200580Sluigi char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; 278200580Sluigi#endif 279200580Sluigi 280200580Sluigi#ifdef INET6 281200580Sluigi if (IS_IP6_FLOW_ID(id)) { 282200580Sluigi ip6_sprintf(src, &id->src_ip6); 283200580Sluigi ip6_sprintf(dst, &id->dst_ip6); 284200580Sluigi } else 285200580Sluigi#endif 286200580Sluigi { 287200580Sluigi da.s_addr = htonl(id->src_ip); 288238978Sluigi inet_ntop(AF_INET, &da, src, sizeof(src)); 289200580Sluigi da.s_addr = htonl(id->dst_ip); 290238978Sluigi inet_ntop(AF_INET, &da, dst, sizeof(dst)); 291200580Sluigi } 292242631Smelifaro log(log_flags, "ipfw: %s type %d %s %d -> %s %d, %d %s\n", 293242631Smelifaro prefix, dyn_type, src, id->src_port, dst, 294243707Smelifaro id->dst_port, DYN_COUNT, postfix); 295200580Sluigi} 296200580Sluigi 297242631Smelifaro#define print_dyn_rule(id, dtype, prefix, postfix) \ 298242631Smelifaro print_dyn_rule_flags(id, dtype, LOG_DEBUG, prefix, postfix) 299242631Smelifaro 300200580Sluigi#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) 301200580Sluigi 302232272Soleg/* 303232272Soleg * Lookup a dynamic rule, locked version. 304200580Sluigi */ 305200601Sluigistatic ipfw_dyn_rule * 306243707Smelifarolookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction, 307200580Sluigi struct tcphdr *tcp) 308200580Sluigi{ 309200580Sluigi /* 310232272Soleg * Stateful ipfw extensions. 311232272Soleg * Lookup into dynamic session queue. 312200580Sluigi */ 313200580Sluigi#define MATCH_REVERSE 0 314200580Sluigi#define MATCH_FORWARD 1 315200580Sluigi#define MATCH_NONE 2 316200580Sluigi#define MATCH_UNKNOWN 3 317243707Smelifaro int dir = MATCH_NONE; 318232272Soleg ipfw_dyn_rule *prev, *q = NULL; 319200580Sluigi 320243707Smelifaro IPFW_BUCK_ASSERT(i); 321200580Sluigi 322243707Smelifaro for (prev = NULL, q = V_ipfw_dyn_v[i].head; q; prev = q, q = q->next) { 323200580Sluigi if (q->dyn_type == O_LIMIT_PARENT && q->count) 324200580Sluigi continue; 325243707Smelifaro 326232272Soleg if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT) 327243707Smelifaro continue; 328232272Soleg 329232272Soleg if (IS_IP6_FLOW_ID(pkt)) { 330232272Soleg if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.src_ip6) && 331232272Soleg IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.dst_ip6) && 332200580Sluigi pkt->src_port == q->id.src_port && 333232272Soleg pkt->dst_port == q->id.dst_port) { 334200580Sluigi dir = MATCH_FORWARD; 335200580Sluigi break; 336200580Sluigi } 337232272Soleg if (IN6_ARE_ADDR_EQUAL(&pkt->src_ip6, &q->id.dst_ip6) && 338232272Soleg IN6_ARE_ADDR_EQUAL(&pkt->dst_ip6, &q->id.src_ip6) && 339232272Soleg pkt->src_port == q->id.dst_port && 340232272Soleg pkt->dst_port == q->id.src_port) { 341232272Soleg dir = MATCH_REVERSE; 342232272Soleg break; 343232272Soleg } 344232272Soleg } else { 345232272Soleg if (pkt->src_ip == q->id.src_ip && 346232272Soleg pkt->dst_ip == q->id.dst_ip && 347232272Soleg pkt->src_port == q->id.src_port && 348232272Soleg pkt->dst_port == q->id.dst_port) { 349232272Soleg dir = MATCH_FORWARD; 350232272Soleg break; 351232272Soleg } 352232272Soleg if (pkt->src_ip == q->id.dst_ip && 353232272Soleg pkt->dst_ip == q->id.src_ip && 354232272Soleg pkt->src_port == q->id.dst_port && 355232272Soleg pkt->dst_port == q->id.src_port) { 356232272Soleg dir = MATCH_REVERSE; 357232272Soleg break; 358232272Soleg } 359200580Sluigi } 360200580Sluigi } 361200580Sluigi if (q == NULL) 362232272Soleg goto done; /* q = NULL, not found */ 363200580Sluigi 364232272Soleg if (prev != NULL) { /* found and not in front */ 365200580Sluigi prev->next = q->next; 366243707Smelifaro q->next = V_ipfw_dyn_v[i].head; 367243707Smelifaro V_ipfw_dyn_v[i].head = q; 368200580Sluigi } 369200580Sluigi if (pkt->proto == IPPROTO_TCP) { /* update state according to flags */ 370232272Soleg uint32_t ack; 371232272Soleg u_char flags = pkt->_flags & (TH_FIN | TH_SYN | TH_RST); 372200580Sluigi 373200580Sluigi#define BOTH_SYN (TH_SYN | (TH_SYN << 8)) 374200580Sluigi#define BOTH_FIN (TH_FIN | (TH_FIN << 8)) 375232273Soleg#define TCP_FLAGS (TH_FLAGS | (TH_FLAGS << 8)) 376232273Soleg#define ACK_FWD 0x10000 /* fwd ack seen */ 377232273Soleg#define ACK_REV 0x20000 /* rev ack seen */ 378232273Soleg 379232272Soleg q->state |= (dir == MATCH_FORWARD) ? flags : (flags << 8); 380232273Soleg switch (q->state & TCP_FLAGS) { 381232272Soleg case TH_SYN: /* opening */ 382200580Sluigi q->expire = time_uptime + V_dyn_syn_lifetime; 383200580Sluigi break; 384200580Sluigi 385200580Sluigi case BOTH_SYN: /* move to established */ 386232272Soleg case BOTH_SYN | TH_FIN: /* one side tries to close */ 387232272Soleg case BOTH_SYN | (TH_FIN << 8): 388200580Sluigi#define _SEQ_GE(a,b) ((int)(a) - (int)(b) >= 0) 389232273Soleg if (tcp == NULL) 390232272Soleg break; 391232272Soleg 392232272Soleg ack = ntohl(tcp->th_ack); 393232272Soleg if (dir == MATCH_FORWARD) { 394232273Soleg if (q->ack_fwd == 0 || 395232273Soleg _SEQ_GE(ack, q->ack_fwd)) { 396232272Soleg q->ack_fwd = ack; 397232273Soleg q->state |= ACK_FWD; 398232273Soleg } 399232272Soleg } else { 400232273Soleg if (q->ack_rev == 0 || 401232273Soleg _SEQ_GE(ack, q->ack_rev)) { 402232272Soleg q->ack_rev = ack; 403232273Soleg q->state |= ACK_REV; 404232273Soleg } 405200580Sluigi } 406232273Soleg if ((q->state & (ACK_FWD | ACK_REV)) == 407232273Soleg (ACK_FWD | ACK_REV)) { 408232273Soleg q->expire = time_uptime + V_dyn_ack_lifetime; 409232273Soleg q->state &= ~(ACK_FWD | ACK_REV); 410232273Soleg } 411200580Sluigi break; 412200580Sluigi 413200580Sluigi case BOTH_SYN | BOTH_FIN: /* both sides closed */ 414200580Sluigi if (V_dyn_fin_lifetime >= V_dyn_keepalive_period) 415200580Sluigi V_dyn_fin_lifetime = V_dyn_keepalive_period - 1; 416200580Sluigi q->expire = time_uptime + V_dyn_fin_lifetime; 417200580Sluigi break; 418200580Sluigi 419200580Sluigi default: 420200580Sluigi#if 0 421200580Sluigi /* 422200580Sluigi * reset or some invalid combination, but can also 423200580Sluigi * occur if we use keep-state the wrong way. 424200580Sluigi */ 425200580Sluigi if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0) 426200580Sluigi printf("invalid state: 0x%x\n", q->state); 427200580Sluigi#endif 428200580Sluigi if (V_dyn_rst_lifetime >= V_dyn_keepalive_period) 429200580Sluigi V_dyn_rst_lifetime = V_dyn_keepalive_period - 1; 430200580Sluigi q->expire = time_uptime + V_dyn_rst_lifetime; 431200580Sluigi break; 432200580Sluigi } 433200580Sluigi } else if (pkt->proto == IPPROTO_UDP) { 434200580Sluigi q->expire = time_uptime + V_dyn_udp_lifetime; 435200580Sluigi } else { 436200580Sluigi /* other protocols */ 437200580Sluigi q->expire = time_uptime + V_dyn_short_lifetime; 438200580Sluigi } 439200580Sluigidone: 440232272Soleg if (match_direction != NULL) 441200580Sluigi *match_direction = dir; 442232272Soleg return (q); 443200580Sluigi} 444200580Sluigi 445200580Sluigiipfw_dyn_rule * 446200601Sluigiipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, 447200580Sluigi struct tcphdr *tcp) 448200580Sluigi{ 449200580Sluigi ipfw_dyn_rule *q; 450243707Smelifaro int i; 451200580Sluigi 452243707Smelifaro i = hash_packet(pkt, V_curr_dyn_buckets); 453243707Smelifaro 454243707Smelifaro IPFW_BUCK_LOCK(i); 455243707Smelifaro q = lookup_dyn_rule_locked(pkt, i, match_direction, tcp); 456200580Sluigi if (q == NULL) 457243707Smelifaro IPFW_BUCK_UNLOCK(i); 458200580Sluigi /* NB: return table locked when q is not NULL */ 459200580Sluigi return q; 460200580Sluigi} 461200580Sluigi 462243707Smelifaro/* 463243707Smelifaro * Unlock bucket mtx 464243707Smelifaro * @p - pointer to dynamic rule 465243707Smelifaro */ 466243707Smelifarovoid 467243707Smelifaroipfw_dyn_unlock(ipfw_dyn_rule *q) 468200580Sluigi{ 469200580Sluigi 470243707Smelifaro IPFW_BUCK_UNLOCK(q->bucket); 471243707Smelifaro} 472243707Smelifaro 473243707Smelifarostatic int 474243707Smelifaroresize_dynamic_table(struct ip_fw_chain *chain, int nbuckets) 475243707Smelifaro{ 476243707Smelifaro int i, k, nbuckets_old; 477243707Smelifaro ipfw_dyn_rule *q; 478243707Smelifaro struct ipfw_dyn_bucket *dyn_v, *dyn_v_old; 479243707Smelifaro 480243707Smelifaro /* Check if given number is power of 2 and less than 64k */ 481243707Smelifaro if ((nbuckets > 65536) || (!powerof2(nbuckets))) 482243707Smelifaro return 1; 483243707Smelifaro 484243707Smelifaro CTR3(KTR_NET, "%s: resize dynamic hash: %d -> %d", __func__, 485243707Smelifaro V_curr_dyn_buckets, nbuckets); 486243707Smelifaro 487243707Smelifaro /* Allocate and initialize new hash */ 488243707Smelifaro dyn_v = malloc(nbuckets * sizeof(ipfw_dyn_rule), M_IPFW, 489243707Smelifaro M_WAITOK | M_ZERO); 490243707Smelifaro 491243707Smelifaro for (i = 0 ; i < nbuckets; i++) 492243707Smelifaro IPFW_BUCK_LOCK_INIT(&dyn_v[i]); 493243707Smelifaro 494200580Sluigi /* 495243707Smelifaro * Call upper half lock, as get_map() do to ease 496243707Smelifaro * read-only access to dynamic rules hash from sysctl 497200580Sluigi */ 498243707Smelifaro IPFW_UH_WLOCK(chain); 499200580Sluigi 500243707Smelifaro /* 501243707Smelifaro * Acquire chain write lock to permit hash access 502243707Smelifaro * for main traffic path without additional locks 503243707Smelifaro */ 504243707Smelifaro IPFW_WLOCK(chain); 505243707Smelifaro 506243707Smelifaro /* Save old values */ 507243707Smelifaro nbuckets_old = V_curr_dyn_buckets; 508243707Smelifaro dyn_v_old = V_ipfw_dyn_v; 509243707Smelifaro 510243707Smelifaro /* Skip relinking if array is not set up */ 511243707Smelifaro if (V_ipfw_dyn_v == NULL) 512243707Smelifaro V_curr_dyn_buckets = 0; 513243707Smelifaro 514243707Smelifaro /* Re-link all dynamic states */ 515243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets ; i++) { 516243707Smelifaro while (V_ipfw_dyn_v[i].head != NULL) { 517243707Smelifaro /* Remove from current chain */ 518243707Smelifaro q = V_ipfw_dyn_v[i].head; 519243707Smelifaro V_ipfw_dyn_v[i].head = q->next; 520243707Smelifaro 521243707Smelifaro /* Get new hash value */ 522243707Smelifaro k = hash_packet(&q->id, nbuckets); 523243707Smelifaro q->bucket = k; 524243707Smelifaro /* Add to the new head */ 525243707Smelifaro q->next = dyn_v[k].head; 526243707Smelifaro dyn_v[k].head = q; 527243707Smelifaro } 528200580Sluigi } 529243707Smelifaro 530243707Smelifaro /* Update current pointers/buckets values */ 531243707Smelifaro V_curr_dyn_buckets = nbuckets; 532243707Smelifaro V_ipfw_dyn_v = dyn_v; 533243707Smelifaro 534243707Smelifaro IPFW_WUNLOCK(chain); 535243707Smelifaro 536243707Smelifaro IPFW_UH_WUNLOCK(chain); 537243707Smelifaro 538243707Smelifaro /* Start periodic callout on initial creation */ 539243707Smelifaro if (dyn_v_old == NULL) { 540243707Smelifaro callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, curvnet, 0); 541243707Smelifaro return (0); 542200580Sluigi } 543243707Smelifaro 544243707Smelifaro /* Destroy all mutexes */ 545243707Smelifaro for (i = 0 ; i < nbuckets_old ; i++) 546243707Smelifaro IPFW_BUCK_LOCK_DESTROY(&dyn_v_old[i]); 547243707Smelifaro 548243707Smelifaro /* Free old hash */ 549243707Smelifaro free(dyn_v_old, M_IPFW); 550243707Smelifaro 551243707Smelifaro return 0; 552200580Sluigi} 553200580Sluigi 554200580Sluigi/** 555200580Sluigi * Install state of type 'type' for a dynamic session. 556200580Sluigi * The hash table contains two type of rules: 557200580Sluigi * - regular rules (O_KEEP_STATE) 558200580Sluigi * - rules for sessions with limited number of sess per user 559200580Sluigi * (O_LIMIT). When they are created, the parent is 560200580Sluigi * increased by 1, and decreased on delete. In this case, 561200580Sluigi * the third parameter is the parent rule and not the chain. 562200580Sluigi * - "parent" rules for the above (O_LIMIT_PARENT). 563200580Sluigi */ 564200580Sluigistatic ipfw_dyn_rule * 565243707Smelifaroadd_dyn_rule(struct ipfw_flow_id *id, int i, u_int8_t dyn_type, struct ip_fw *rule) 566200580Sluigi{ 567200580Sluigi ipfw_dyn_rule *r; 568200580Sluigi 569243707Smelifaro IPFW_BUCK_ASSERT(i); 570200580Sluigi 571243707Smelifaro r = uma_zalloc(V_ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO); 572200580Sluigi if (r == NULL) { 573243707Smelifaro if (last_log != time_uptime) { 574243707Smelifaro last_log = time_uptime; 575243707Smelifaro log(LOG_DEBUG, "ipfw: %s: Cannot allocate rule\n", 576243707Smelifaro __func__); 577243707Smelifaro } 578200580Sluigi return NULL; 579200580Sluigi } 580200580Sluigi 581243707Smelifaro /* 582243707Smelifaro * refcount on parent is already incremented, so 583243707Smelifaro * it is safe to use parent unlocked. 584243707Smelifaro */ 585200580Sluigi if (dyn_type == O_LIMIT) { 586200580Sluigi ipfw_dyn_rule *parent = (ipfw_dyn_rule *)rule; 587200580Sluigi if ( parent->dyn_type != O_LIMIT_PARENT) 588200580Sluigi panic("invalid parent"); 589200580Sluigi r->parent = parent; 590200580Sluigi rule = parent->rule; 591200580Sluigi } 592200580Sluigi 593200580Sluigi r->id = *id; 594200580Sluigi r->expire = time_uptime + V_dyn_syn_lifetime; 595200580Sluigi r->rule = rule; 596200580Sluigi r->dyn_type = dyn_type; 597243711Smelifaro IPFW_ZERO_DYN_COUNTER(r); 598200580Sluigi r->count = 0; 599200580Sluigi 600200580Sluigi r->bucket = i; 601243707Smelifaro r->next = V_ipfw_dyn_v[i].head; 602243707Smelifaro V_ipfw_dyn_v[i].head = r; 603242631Smelifaro DEB(print_dyn_rule(id, dyn_type, "add dyn entry", "total");) 604200580Sluigi return r; 605200580Sluigi} 606200580Sluigi 607200580Sluigi/** 608200580Sluigi * lookup dynamic parent rule using pkt and rule as search keys. 609200580Sluigi * If the lookup fails, then install one. 610200580Sluigi */ 611200580Sluigistatic ipfw_dyn_rule * 612243707Smelifarolookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule) 613200580Sluigi{ 614200580Sluigi ipfw_dyn_rule *q; 615243707Smelifaro int i, is_v6; 616200580Sluigi 617243707Smelifaro is_v6 = IS_IP6_FLOW_ID(pkt); 618243707Smelifaro i = hash_packet( pkt, V_curr_dyn_buckets ); 619243707Smelifaro *pindex = i; 620243707Smelifaro IPFW_BUCK_LOCK(i); 621243707Smelifaro for (q = V_ipfw_dyn_v[i].head ; q != NULL ; q=q->next) 622243707Smelifaro if (q->dyn_type == O_LIMIT_PARENT && 623243707Smelifaro rule== q->rule && 624243707Smelifaro pkt->proto == q->id.proto && 625243707Smelifaro pkt->src_port == q->id.src_port && 626243707Smelifaro pkt->dst_port == q->id.dst_port && 627243707Smelifaro ( 628243707Smelifaro (is_v6 && 629243707Smelifaro IN6_ARE_ADDR_EQUAL(&(pkt->src_ip6), 630243707Smelifaro &(q->id.src_ip6)) && 631243707Smelifaro IN6_ARE_ADDR_EQUAL(&(pkt->dst_ip6), 632243707Smelifaro &(q->id.dst_ip6))) || 633243707Smelifaro (!is_v6 && 634243707Smelifaro pkt->src_ip == q->id.src_ip && 635243707Smelifaro pkt->dst_ip == q->id.dst_ip) 636243707Smelifaro ) 637243707Smelifaro ) { 638243707Smelifaro q->expire = time_uptime + V_dyn_short_lifetime; 639243707Smelifaro DEB(print_dyn_rule(pkt, q->dyn_type, 640243707Smelifaro "lookup_dyn_parent found", "");) 641243707Smelifaro return q; 642243707Smelifaro } 643200580Sluigi 644243707Smelifaro /* Add virtual limiting rule */ 645243707Smelifaro return add_dyn_rule(pkt, i, O_LIMIT_PARENT, rule); 646200580Sluigi} 647200580Sluigi 648200580Sluigi/** 649200580Sluigi * Install dynamic state for rule type cmd->o.opcode 650200580Sluigi * 651200580Sluigi * Returns 1 (failure) if state is not installed because of errors or because 652200580Sluigi * session limitations are enforced. 653200580Sluigi */ 654200580Sluigiint 655200601Sluigiipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, 656200580Sluigi struct ip_fw_args *args, uint32_t tablearg) 657200580Sluigi{ 658200580Sluigi ipfw_dyn_rule *q; 659243707Smelifaro int i; 660200580Sluigi 661242631Smelifaro DEB(print_dyn_rule(&args->f_id, cmd->o.opcode, "install_state", "");) 662243707Smelifaro 663243707Smelifaro i = hash_packet(&args->f_id, V_curr_dyn_buckets); 664200580Sluigi 665243707Smelifaro IPFW_BUCK_LOCK(i); 666200580Sluigi 667243707Smelifaro q = lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL); 668200580Sluigi 669200580Sluigi if (q != NULL) { /* should never occur */ 670222559Sae DEB( 671200580Sluigi if (last_log != time_uptime) { 672200580Sluigi last_log = time_uptime; 673200580Sluigi printf("ipfw: %s: entry already present, done\n", 674200580Sluigi __func__); 675222559Sae }) 676243707Smelifaro IPFW_BUCK_UNLOCK(i); 677200580Sluigi return (0); 678200580Sluigi } 679200580Sluigi 680243707Smelifaro /* 681243707Smelifaro * State limiting is done via uma(9) zone limiting. 682243707Smelifaro * Save pointer to newly-installed rule and reject 683243707Smelifaro * packet if add_dyn_rule() returned NULL. 684243707Smelifaro * Note q is currently set to NULL. 685243707Smelifaro */ 686200580Sluigi 687200580Sluigi switch (cmd->o.opcode) { 688200580Sluigi case O_KEEP_STATE: /* bidir rule */ 689243707Smelifaro q = add_dyn_rule(&args->f_id, i, O_KEEP_STATE, rule); 690200580Sluigi break; 691200580Sluigi 692200580Sluigi case O_LIMIT: { /* limit number of sessions */ 693200580Sluigi struct ipfw_flow_id id; 694200580Sluigi ipfw_dyn_rule *parent; 695200580Sluigi uint32_t conn_limit; 696200580Sluigi uint16_t limit_mask = cmd->limit_mask; 697243707Smelifaro int pindex; 698200580Sluigi 699244633Smelifaro conn_limit = IP_FW_ARG_TABLEARG(cmd->conn_limit); 700200580Sluigi 701200580Sluigi DEB( 702200580Sluigi if (cmd->conn_limit == IP_FW_TABLEARG) 703200580Sluigi printf("ipfw: %s: O_LIMIT rule, conn_limit: %u " 704200580Sluigi "(tablearg)\n", __func__, conn_limit); 705200580Sluigi else 706200580Sluigi printf("ipfw: %s: O_LIMIT rule, conn_limit: %u\n", 707200580Sluigi __func__, conn_limit); 708200580Sluigi ) 709200580Sluigi 710200580Sluigi id.dst_ip = id.src_ip = id.dst_port = id.src_port = 0; 711200580Sluigi id.proto = args->f_id.proto; 712200580Sluigi id.addr_type = args->f_id.addr_type; 713200580Sluigi id.fib = M_GETFIB(args->m); 714200580Sluigi 715200580Sluigi if (IS_IP6_FLOW_ID (&(args->f_id))) { 716200580Sluigi if (limit_mask & DYN_SRC_ADDR) 717200580Sluigi id.src_ip6 = args->f_id.src_ip6; 718200580Sluigi if (limit_mask & DYN_DST_ADDR) 719200580Sluigi id.dst_ip6 = args->f_id.dst_ip6; 720200580Sluigi } else { 721200580Sluigi if (limit_mask & DYN_SRC_ADDR) 722200580Sluigi id.src_ip = args->f_id.src_ip; 723200580Sluigi if (limit_mask & DYN_DST_ADDR) 724200580Sluigi id.dst_ip = args->f_id.dst_ip; 725200580Sluigi } 726200580Sluigi if (limit_mask & DYN_SRC_PORT) 727200580Sluigi id.src_port = args->f_id.src_port; 728200580Sluigi if (limit_mask & DYN_DST_PORT) 729200580Sluigi id.dst_port = args->f_id.dst_port; 730243707Smelifaro 731243707Smelifaro /* 732243707Smelifaro * We have to release lock for previous bucket to 733243707Smelifaro * avoid possible deadlock 734243707Smelifaro */ 735243707Smelifaro IPFW_BUCK_UNLOCK(i); 736243707Smelifaro 737243707Smelifaro if ((parent = lookup_dyn_parent(&id, &pindex, rule)) == NULL) { 738200580Sluigi printf("ipfw: %s: add parent failed\n", __func__); 739243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 740200580Sluigi return (1); 741200580Sluigi } 742200580Sluigi 743200580Sluigi if (parent->count >= conn_limit) { 744243707Smelifaro if (V_fw_verbose && last_log != time_uptime) { 745243707Smelifaro last_log = time_uptime; 746243707Smelifaro char sbuf[24]; 747243707Smelifaro last_log = time_uptime; 748243707Smelifaro snprintf(sbuf, sizeof(sbuf), 749243707Smelifaro "%d drop session", 750243707Smelifaro parent->rule->rulenum); 751243707Smelifaro print_dyn_rule_flags(&args->f_id, 752243707Smelifaro cmd->o.opcode, 753243707Smelifaro LOG_SECURITY | LOG_DEBUG, 754243707Smelifaro sbuf, "too many entries"); 755200580Sluigi } 756243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 757243707Smelifaro return (1); 758200580Sluigi } 759243707Smelifaro /* Increment counter on parent */ 760243707Smelifaro parent->count++; 761243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 762243707Smelifaro 763243707Smelifaro IPFW_BUCK_LOCK(i); 764243707Smelifaro q = add_dyn_rule(&args->f_id, i, O_LIMIT, (struct ip_fw *)parent); 765243707Smelifaro if (q == NULL) { 766243707Smelifaro /* Decrement index and notify caller */ 767243707Smelifaro IPFW_BUCK_UNLOCK(i); 768243707Smelifaro IPFW_BUCK_LOCK(pindex); 769243707Smelifaro parent->count--; 770243707Smelifaro IPFW_BUCK_UNLOCK(pindex); 771243707Smelifaro return (1); 772243707Smelifaro } 773200580Sluigi break; 774200580Sluigi } 775200580Sluigi default: 776200580Sluigi printf("ipfw: %s: unknown dynamic rule type %u\n", 777200580Sluigi __func__, cmd->o.opcode); 778200580Sluigi } 779200580Sluigi 780243707Smelifaro if (q == NULL) { 781243707Smelifaro IPFW_BUCK_UNLOCK(i); 782243707Smelifaro return (1); /* Notify caller about failure */ 783243707Smelifaro } 784243707Smelifaro 785200580Sluigi /* XXX just set lifetime */ 786243707Smelifaro lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL); 787200580Sluigi 788243707Smelifaro IPFW_BUCK_UNLOCK(i); 789200580Sluigi return (0); 790200580Sluigi} 791200580Sluigi 792200580Sluigi/* 793200580Sluigi * Generate a TCP packet, containing either a RST or a keepalive. 794200580Sluigi * When flags & TH_RST, we are sending a RST packet, because of a 795200580Sluigi * "reset" action matched the packet. 796200580Sluigi * Otherwise we are sending a keepalive, and flags & TH_ 797200580Sluigi * The 'replyto' mbuf is the mbuf being replied to, if any, and is required 798200580Sluigi * so that MAC can label the reply appropriately. 799200580Sluigi */ 800200580Sluigistruct mbuf * 801200601Sluigiipfw_send_pkt(struct mbuf *replyto, struct ipfw_flow_id *id, u_int32_t seq, 802200580Sluigi u_int32_t ack, int flags) 803200580Sluigi{ 804210123Sluigi struct mbuf *m = NULL; /* stupid compiler */ 805200580Sluigi int len, dir; 806200580Sluigi struct ip *h = NULL; /* stupid compiler */ 807200580Sluigi#ifdef INET6 808200580Sluigi struct ip6_hdr *h6 = NULL; 809200580Sluigi#endif 810200580Sluigi struct tcphdr *th = NULL; 811200580Sluigi 812243882Sglebius MGETHDR(m, M_NOWAIT, MT_DATA); 813200580Sluigi if (m == NULL) 814200580Sluigi return (NULL); 815200580Sluigi 816200580Sluigi M_SETFIB(m, id->fib); 817200580Sluigi#ifdef MAC 818200580Sluigi if (replyto != NULL) 819200580Sluigi mac_netinet_firewall_reply(replyto, m); 820200580Sluigi else 821200580Sluigi mac_netinet_firewall_send(m); 822200580Sluigi#else 823200580Sluigi (void)replyto; /* don't warn about unused arg */ 824200580Sluigi#endif 825200580Sluigi 826200580Sluigi switch (id->addr_type) { 827200580Sluigi case 4: 828200580Sluigi len = sizeof(struct ip) + sizeof(struct tcphdr); 829200580Sluigi break; 830200580Sluigi#ifdef INET6 831200580Sluigi case 6: 832200580Sluigi len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr); 833200580Sluigi break; 834200580Sluigi#endif 835200580Sluigi default: 836200580Sluigi /* XXX: log me?!? */ 837201527Sluigi FREE_PKT(m); 838200580Sluigi return (NULL); 839200580Sluigi } 840200580Sluigi dir = ((flags & (TH_SYN | TH_RST)) == TH_SYN); 841200580Sluigi 842200580Sluigi m->m_data += max_linkhdr; 843200580Sluigi m->m_flags |= M_SKIP_FIREWALL; 844200580Sluigi m->m_pkthdr.len = m->m_len = len; 845200580Sluigi m->m_pkthdr.rcvif = NULL; 846200580Sluigi bzero(m->m_data, len); 847200580Sluigi 848200580Sluigi switch (id->addr_type) { 849200580Sluigi case 4: 850200580Sluigi h = mtod(m, struct ip *); 851200580Sluigi 852200580Sluigi /* prepare for checksum */ 853200580Sluigi h->ip_p = IPPROTO_TCP; 854200580Sluigi h->ip_len = htons(sizeof(struct tcphdr)); 855200580Sluigi if (dir) { 856200580Sluigi h->ip_src.s_addr = htonl(id->src_ip); 857200580Sluigi h->ip_dst.s_addr = htonl(id->dst_ip); 858200580Sluigi } else { 859200580Sluigi h->ip_src.s_addr = htonl(id->dst_ip); 860200580Sluigi h->ip_dst.s_addr = htonl(id->src_ip); 861200580Sluigi } 862200580Sluigi 863200580Sluigi th = (struct tcphdr *)(h + 1); 864200580Sluigi break; 865200580Sluigi#ifdef INET6 866200580Sluigi case 6: 867200580Sluigi h6 = mtod(m, struct ip6_hdr *); 868200580Sluigi 869200580Sluigi /* prepare for checksum */ 870200580Sluigi h6->ip6_nxt = IPPROTO_TCP; 871200580Sluigi h6->ip6_plen = htons(sizeof(struct tcphdr)); 872200580Sluigi if (dir) { 873200580Sluigi h6->ip6_src = id->src_ip6; 874200580Sluigi h6->ip6_dst = id->dst_ip6; 875200580Sluigi } else { 876200580Sluigi h6->ip6_src = id->dst_ip6; 877200580Sluigi h6->ip6_dst = id->src_ip6; 878200580Sluigi } 879200580Sluigi 880200580Sluigi th = (struct tcphdr *)(h6 + 1); 881200580Sluigi break; 882200580Sluigi#endif 883200580Sluigi } 884200580Sluigi 885200580Sluigi if (dir) { 886200580Sluigi th->th_sport = htons(id->src_port); 887200580Sluigi th->th_dport = htons(id->dst_port); 888200580Sluigi } else { 889200580Sluigi th->th_sport = htons(id->dst_port); 890200580Sluigi th->th_dport = htons(id->src_port); 891200580Sluigi } 892200580Sluigi th->th_off = sizeof(struct tcphdr) >> 2; 893200580Sluigi 894200580Sluigi if (flags & TH_RST) { 895200580Sluigi if (flags & TH_ACK) { 896200580Sluigi th->th_seq = htonl(ack); 897200580Sluigi th->th_flags = TH_RST; 898200580Sluigi } else { 899200580Sluigi if (flags & TH_SYN) 900200580Sluigi seq++; 901200580Sluigi th->th_ack = htonl(seq); 902200580Sluigi th->th_flags = TH_RST | TH_ACK; 903200580Sluigi } 904200580Sluigi } else { 905200580Sluigi /* 906200580Sluigi * Keepalive - use caller provided sequence numbers 907200580Sluigi */ 908200580Sluigi th->th_seq = htonl(seq); 909200580Sluigi th->th_ack = htonl(ack); 910200580Sluigi th->th_flags = TH_ACK; 911200580Sluigi } 912200580Sluigi 913200580Sluigi switch (id->addr_type) { 914200580Sluigi case 4: 915200580Sluigi th->th_sum = in_cksum(m, len); 916200580Sluigi 917200580Sluigi /* finish the ip header */ 918200580Sluigi h->ip_v = 4; 919200580Sluigi h->ip_hl = sizeof(*h) >> 2; 920200580Sluigi h->ip_tos = IPTOS_LOWDELAY; 921241913Sglebius h->ip_off = htons(0); 922241913Sglebius h->ip_len = htons(len); 923200580Sluigi h->ip_ttl = V_ip_defttl; 924200580Sluigi h->ip_sum = 0; 925200580Sluigi break; 926200580Sluigi#ifdef INET6 927200580Sluigi case 6: 928200580Sluigi th->th_sum = in6_cksum(m, IPPROTO_TCP, sizeof(*h6), 929200580Sluigi sizeof(struct tcphdr)); 930200580Sluigi 931200580Sluigi /* finish the ip6 header */ 932200580Sluigi h6->ip6_vfc |= IPV6_VERSION; 933200580Sluigi h6->ip6_hlim = IPV6_DEFHLIM; 934200580Sluigi break; 935200580Sluigi#endif 936200580Sluigi } 937200580Sluigi 938200580Sluigi return (m); 939200580Sluigi} 940200580Sluigi 941200580Sluigi/* 942242834Smelifaro * Queue keepalive packets for given dynamic rule 943242834Smelifaro */ 944242834Smelifarostatic struct mbuf ** 945242834Smelifaroipfw_dyn_send_ka(struct mbuf **mtailp, ipfw_dyn_rule *q) 946242834Smelifaro{ 947242834Smelifaro struct mbuf *m_rev, *m_fwd; 948242834Smelifaro 949242834Smelifaro m_rev = (q->state & ACK_REV) ? NULL : 950242834Smelifaro ipfw_send_pkt(NULL, &(q->id), q->ack_rev - 1, q->ack_fwd, TH_SYN); 951242834Smelifaro m_fwd = (q->state & ACK_FWD) ? NULL : 952242834Smelifaro ipfw_send_pkt(NULL, &(q->id), q->ack_fwd - 1, q->ack_rev, 0); 953242834Smelifaro 954242834Smelifaro if (m_rev != NULL) { 955242834Smelifaro *mtailp = m_rev; 956242834Smelifaro mtailp = &(*mtailp)->m_nextpkt; 957242834Smelifaro } 958242834Smelifaro if (m_fwd != NULL) { 959242834Smelifaro *mtailp = m_fwd; 960242834Smelifaro mtailp = &(*mtailp)->m_nextpkt; 961242834Smelifaro } 962242834Smelifaro 963242834Smelifaro return (mtailp); 964242834Smelifaro} 965242834Smelifaro 966242834Smelifaro/* 967243707Smelifaro * This procedure is used to perform various maintance 968243707Smelifaro * on dynamic hash list. Currently it is called every second. 969200580Sluigi */ 970200580Sluigistatic void 971243707Smelifaroipfw_dyn_tick(void * vnetx) 972200580Sluigi{ 973243707Smelifaro struct ip_fw_chain *chain; 974243707Smelifaro int check_ka = 0; 975200580Sluigi#ifdef VIMAGE 976200580Sluigi struct vnet *vp = vnetx; 977200580Sluigi#endif 978200580Sluigi 979200580Sluigi CURVNET_SET(vp); 980200580Sluigi 981243707Smelifaro chain = &V_layer3_chain; 982243707Smelifaro 983247626Smelifaro /* Run keepalive checks every keepalive_period iff ka is enabled */ 984247626Smelifaro if ((V_dyn_keepalive_last + V_dyn_keepalive_period <= time_uptime) && 985243707Smelifaro (V_dyn_keepalive != 0)) { 986243707Smelifaro V_dyn_keepalive_last = time_uptime; 987243707Smelifaro check_ka = 1; 988243707Smelifaro } 989243707Smelifaro 990243707Smelifaro check_dyn_rules(chain, NULL, RESVD_SET, check_ka, 1); 991243707Smelifaro 992243707Smelifaro callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, vnetx, 0); 993243707Smelifaro 994243707Smelifaro CURVNET_RESTORE(); 995243707Smelifaro} 996243707Smelifaro 997243707Smelifaro 998243707Smelifaro/* 999243707Smelifaro * Walk thru all dynamic states doing generic maintance: 1000243707Smelifaro * 1) free expired states 1001243707Smelifaro * 2) free all states based on deleted rule / set 1002243707Smelifaro * 3) send keepalives for states if needed 1003243707Smelifaro * 1004243707Smelifaro * @chain - pointer to current ipfw rules chain 1005243707Smelifaro * @rule - delete all states originated by given rule if != NULL 1006243707Smelifaro * @set - delete all states originated by any rule in set @set if != RESVD_SET 1007243707Smelifaro * @check_ka - perform checking/sending keepalives 1008243707Smelifaro * @timer - indicate call from timer routine. 1009243707Smelifaro * 1010243707Smelifaro * Timer routine must call this function unlocked to permit 1011243707Smelifaro * sending keepalives/resizing table. 1012243707Smelifaro * 1013243707Smelifaro * Others has to call function with IPFW_UH_WLOCK held. 1014243707Smelifaro * Additionally, function assume that dynamic rule/set is 1015243707Smelifaro * ALREADY deleted so no new states can be generated by 1016243707Smelifaro * 'deleted' rules. 1017243707Smelifaro * 1018243707Smelifaro * Write lock is needed to ensure that unused parent rules 1019243707Smelifaro * are not freed by other instance (see stage 2, 3) 1020243707Smelifaro */ 1021243707Smelifarostatic void 1022243707Smelifarocheck_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, 1023243707Smelifaro int set, int check_ka, int timer) 1024243707Smelifaro{ 1025243707Smelifaro struct mbuf *m0, *m, *mnext, **mtailp; 1026243707Smelifaro struct ip *h; 1027243707Smelifaro int i, dyn_count, new_buckets = 0, max_buckets; 1028243707Smelifaro int expired = 0, expired_limits = 0, parents = 0, total = 0; 1029243707Smelifaro ipfw_dyn_rule *q, *q_prev, *q_next; 1030243707Smelifaro ipfw_dyn_rule *exp_head, **exptailp; 1031243707Smelifaro ipfw_dyn_rule *exp_lhead, **expltailp; 1032243707Smelifaro 1033243707Smelifaro KASSERT(V_ipfw_dyn_v != NULL, ("%s: dynamic table not allocated", 1034243707Smelifaro __func__)); 1035243707Smelifaro 1036243707Smelifaro /* Avoid possible LOR */ 1037243707Smelifaro KASSERT(!check_ka || timer, ("%s: keepalive check with lock held", 1038243707Smelifaro __func__)); 1039243707Smelifaro 1040200580Sluigi /* 1041243707Smelifaro * Do not perform any checks if we currently have no dynamic states 1042243707Smelifaro */ 1043243707Smelifaro if (DYN_COUNT == 0) 1044243707Smelifaro return; 1045243707Smelifaro 1046243707Smelifaro /* Expired states */ 1047243707Smelifaro exp_head = NULL; 1048243707Smelifaro exptailp = &exp_head; 1049243707Smelifaro 1050243707Smelifaro /* Expired limit states */ 1051243707Smelifaro exp_lhead = NULL; 1052243707Smelifaro expltailp = &exp_lhead; 1053243707Smelifaro 1054243707Smelifaro /* 1055200580Sluigi * We make a chain of packets to go out here -- not deferring 1056200580Sluigi * until after we drop the IPFW dynamic rule lock would result 1057200580Sluigi * in a lock order reversal with the normal packet input -> ipfw 1058200580Sluigi * call stack. 1059200580Sluigi */ 1060200580Sluigi m0 = NULL; 1061200580Sluigi mtailp = &m0; 1062243707Smelifaro 1063243707Smelifaro /* Protect from hash resizing */ 1064243707Smelifaro if (timer != 0) 1065243707Smelifaro IPFW_UH_WLOCK(chain); 1066243707Smelifaro else 1067243707Smelifaro IPFW_UH_WLOCK_ASSERT(chain); 1068243707Smelifaro 1069243707Smelifaro#define NEXT_RULE() { q_prev = q; q = q->next ; continue; } 1070243707Smelifaro 1071243707Smelifaro /* Stage 1: perform requested deletion */ 1072200580Sluigi for (i = 0 ; i < V_curr_dyn_buckets ; i++) { 1073243707Smelifaro IPFW_BUCK_LOCK(i); 1074243707Smelifaro for (q = V_ipfw_dyn_v[i].head, q_prev = q; q ; ) { 1075243707Smelifaro /* account every rule */ 1076243707Smelifaro total++; 1077242834Smelifaro 1078243707Smelifaro /* Skip parent rules at all */ 1079243707Smelifaro if (q->dyn_type == O_LIMIT_PARENT) { 1080243707Smelifaro parents++; 1081243707Smelifaro NEXT_RULE(); 1082243707Smelifaro } 1083243707Smelifaro 1084243707Smelifaro /* 1085243707Smelifaro * Remove rules which are: 1086243707Smelifaro * 1) expired 1087243707Smelifaro * 2) created by given rule 1088243707Smelifaro * 3) created by any rule in given set 1089243707Smelifaro */ 1090243707Smelifaro if ((TIME_LEQ(q->expire, time_uptime)) || 1091243707Smelifaro ((rule != NULL) && (q->rule == rule)) || 1092243707Smelifaro ((set != RESVD_SET) && (q->rule->set == set))) { 1093243707Smelifaro /* Unlink q from current list */ 1094243707Smelifaro q_next = q->next; 1095243707Smelifaro if (q == V_ipfw_dyn_v[i].head) 1096243707Smelifaro V_ipfw_dyn_v[i].head = q_next; 1097243707Smelifaro else 1098243707Smelifaro q_prev->next = q_next; 1099243707Smelifaro 1100243707Smelifaro q->next = NULL; 1101243707Smelifaro 1102243707Smelifaro /* queue q to expire list */ 1103243707Smelifaro if (q->dyn_type != O_LIMIT) { 1104243707Smelifaro *exptailp = q; 1105243707Smelifaro exptailp = &(*exptailp)->next; 1106243707Smelifaro DEB(print_dyn_rule(&q->id, q->dyn_type, 1107243707Smelifaro "unlink entry", "left"); 1108243707Smelifaro ) 1109243707Smelifaro } else { 1110243707Smelifaro /* Separate list for limit rules */ 1111243707Smelifaro *expltailp = q; 1112243707Smelifaro expltailp = &(*expltailp)->next; 1113243707Smelifaro expired_limits++; 1114243707Smelifaro DEB(print_dyn_rule(&q->id, q->dyn_type, 1115243707Smelifaro "unlink limit entry", "left"); 1116243707Smelifaro ) 1117243707Smelifaro } 1118243707Smelifaro 1119243707Smelifaro q = q_next; 1120243707Smelifaro expired++; 1121200580Sluigi continue; 1122243707Smelifaro } 1123200580Sluigi 1124243707Smelifaro /* 1125243707Smelifaro * Check if we need to send keepalive: 1126243707Smelifaro * we need to ensure if is time to do KA, 1127243707Smelifaro * this is established TCP session, and 1128243707Smelifaro * expire time is within keepalive interval 1129243707Smelifaro */ 1130243707Smelifaro if ((check_ka != 0) && (q->id.proto == IPPROTO_TCP) && 1131243707Smelifaro ((q->state & BOTH_SYN) == BOTH_SYN) && 1132243707Smelifaro (TIME_LEQ(q->expire, time_uptime + 1133243707Smelifaro V_dyn_keepalive_interval))) 1134243707Smelifaro mtailp = ipfw_dyn_send_ka(mtailp, q); 1135243707Smelifaro 1136243707Smelifaro NEXT_RULE(); 1137200580Sluigi } 1138243707Smelifaro IPFW_BUCK_UNLOCK(i); 1139200580Sluigi } 1140242834Smelifaro 1141243707Smelifaro /* Stage 2: decrement counters from O_LIMIT parents */ 1142243707Smelifaro if (expired_limits != 0) { 1143243707Smelifaro /* 1144243707Smelifaro * XXX: Note that deleting set with more than one 1145243707Smelifaro * heavily-used LIMIT rules can result in overwhelming 1146243707Smelifaro * locking due to lack of per-hash value sorting 1147243707Smelifaro * 1148243707Smelifaro * We should probably think about: 1149243707Smelifaro * 1) pre-allocating hash of size, say, 1150243707Smelifaro * MAX(16, V_curr_dyn_buckets / 1024) 1151243707Smelifaro * 2) checking if expired_limits is large enough 1152243707Smelifaro * 3) If yes, init hash (or its part), re-link 1153243707Smelifaro * current list and start decrementing procedure in 1154243707Smelifaro * each bucket separately 1155243707Smelifaro */ 1156243707Smelifaro 1157243707Smelifaro /* 1158243707Smelifaro * Small optimization: do not unlock bucket until 1159243707Smelifaro * we see the next item resides in different bucket 1160243707Smelifaro */ 1161243707Smelifaro if (exp_lhead != NULL) { 1162243707Smelifaro i = exp_lhead->parent->bucket; 1163243707Smelifaro IPFW_BUCK_LOCK(i); 1164243707Smelifaro } 1165243707Smelifaro for (q = exp_lhead; q != NULL; q = q->next) { 1166243707Smelifaro if (i != q->parent->bucket) { 1167243707Smelifaro IPFW_BUCK_UNLOCK(i); 1168243707Smelifaro i = q->parent->bucket; 1169243707Smelifaro IPFW_BUCK_LOCK(i); 1170243707Smelifaro } 1171243707Smelifaro 1172243707Smelifaro /* Decrease parent refcount */ 1173243707Smelifaro q->parent->count--; 1174243707Smelifaro } 1175243707Smelifaro if (exp_lhead != NULL) 1176243707Smelifaro IPFW_BUCK_UNLOCK(i); 1177243707Smelifaro } 1178243707Smelifaro 1179243707Smelifaro /* 1180243707Smelifaro * We protectet ourselves from unused parent deletion 1181243707Smelifaro * (from the timer function) by holding UH write lock. 1182243707Smelifaro */ 1183243707Smelifaro 1184243707Smelifaro /* Stage 3: remove unused parent rules */ 1185243707Smelifaro if ((parents != 0) && (expired != 0)) { 1186243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets ; i++) { 1187243707Smelifaro IPFW_BUCK_LOCK(i); 1188243707Smelifaro for (q = V_ipfw_dyn_v[i].head, q_prev = q ; q ; ) { 1189243707Smelifaro if (q->dyn_type != O_LIMIT_PARENT) 1190243707Smelifaro NEXT_RULE(); 1191243707Smelifaro 1192243707Smelifaro if (q->count != 0) 1193243707Smelifaro NEXT_RULE(); 1194243707Smelifaro 1195243707Smelifaro /* Parent rule without consumers */ 1196243707Smelifaro 1197243707Smelifaro /* Unlink q from current list */ 1198243707Smelifaro q_next = q->next; 1199243707Smelifaro if (q == V_ipfw_dyn_v[i].head) 1200243707Smelifaro V_ipfw_dyn_v[i].head = q_next; 1201243707Smelifaro else 1202243707Smelifaro q_prev->next = q_next; 1203243707Smelifaro 1204243707Smelifaro q->next = NULL; 1205243707Smelifaro 1206243707Smelifaro /* Add to expired list */ 1207243707Smelifaro *exptailp = q; 1208243707Smelifaro exptailp = &(*exptailp)->next; 1209243707Smelifaro 1210243707Smelifaro DEB(print_dyn_rule(&q->id, q->dyn_type, 1211243707Smelifaro "unlink parent entry", "left"); 1212243707Smelifaro ) 1213243707Smelifaro 1214243707Smelifaro expired++; 1215243707Smelifaro 1216243707Smelifaro q = q_next; 1217243707Smelifaro } 1218243707Smelifaro IPFW_BUCK_UNLOCK(i); 1219243707Smelifaro } 1220243707Smelifaro } 1221243707Smelifaro 1222243707Smelifaro#undef NEXT_RULE 1223243707Smelifaro 1224243707Smelifaro if (timer != 0) { 1225243707Smelifaro /* 1226243707Smelifaro * Check if we need to resize hash: 1227243707Smelifaro * if current number of states exceeds number of buckes in hash, 1228243707Smelifaro * grow hash size to the minimum power of 2 which is bigger than 1229243707Smelifaro * current states count. Limit hash size by 64k. 1230243707Smelifaro */ 1231243707Smelifaro max_buckets = (V_dyn_buckets_max > 65536) ? 1232243707Smelifaro 65536 : V_dyn_buckets_max; 1233243707Smelifaro 1234243707Smelifaro dyn_count = DYN_COUNT; 1235243707Smelifaro 1236243707Smelifaro if ((dyn_count > V_curr_dyn_buckets * 2) && 1237243707Smelifaro (dyn_count < max_buckets)) { 1238243707Smelifaro new_buckets = V_curr_dyn_buckets; 1239243707Smelifaro while (new_buckets < dyn_count) { 1240243707Smelifaro new_buckets *= 2; 1241243707Smelifaro 1242243707Smelifaro if (new_buckets >= max_buckets) 1243243707Smelifaro break; 1244243707Smelifaro } 1245243707Smelifaro } 1246243707Smelifaro 1247243707Smelifaro IPFW_UH_WUNLOCK(chain); 1248243707Smelifaro } 1249243707Smelifaro 1250243707Smelifaro /* Finally delete old states ad limits if any */ 1251243707Smelifaro for (q = exp_head; q != NULL; q = q_next) { 1252243707Smelifaro q_next = q->next; 1253243707Smelifaro uma_zfree(V_ipfw_dyn_rule_zone, q); 1254243707Smelifaro } 1255243707Smelifaro 1256243707Smelifaro for (q = exp_lhead; q != NULL; q = q_next) { 1257243707Smelifaro q_next = q->next; 1258243707Smelifaro uma_zfree(V_ipfw_dyn_rule_zone, q); 1259243707Smelifaro } 1260243707Smelifaro 1261243707Smelifaro /* 1262243707Smelifaro * The rest code MUST be called from timer routine only 1263243707Smelifaro * without holding any locks 1264243707Smelifaro */ 1265243707Smelifaro if (timer == 0) 1266243707Smelifaro return; 1267243707Smelifaro 1268242834Smelifaro /* Send keepalive packets if any */ 1269232273Soleg for (m = m0; m != NULL; m = mnext) { 1270200580Sluigi mnext = m->m_nextpkt; 1271200580Sluigi m->m_nextpkt = NULL; 1272242834Smelifaro h = mtod(m, struct ip *); 1273242834Smelifaro if (h->ip_v == 4) 1274242834Smelifaro ip_output(m, NULL, NULL, 0, NULL, NULL); 1275200580Sluigi#ifdef INET6 1276242834Smelifaro else 1277242834Smelifaro ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL); 1278242834Smelifaro#endif 1279200580Sluigi } 1280243707Smelifaro 1281243707Smelifaro /* Run table resize without holding any locks */ 1282243707Smelifaro if (new_buckets != 0) 1283243707Smelifaro resize_dynamic_table(chain, new_buckets); 1284200580Sluigi} 1285200580Sluigi 1286243707Smelifaro/* 1287243707Smelifaro * Deletes all dynamic rules originated by given rule or all rules in 1288243707Smelifaro * given set. Specify RESVD_SET to indicate set should not be used. 1289243707Smelifaro * @chain - pointer to current ipfw rules chain 1290243707Smelifaro * @rule - delete all states originated by given rule if != NULL 1291243707Smelifaro * @set - delete all states originated by any rule in set @set if != RESVD_SET 1292243707Smelifaro * 1293243707Smelifaro * Function has to be called with IPFW_UH_WLOCK held. 1294243707Smelifaro * Additionally, function assume that dynamic rule/set is 1295243707Smelifaro * ALREADY deleted so no new states can be generated by 1296243707Smelifaro * 'deleted' rules. 1297243707Smelifaro */ 1298200580Sluigivoid 1299243707Smelifaroipfw_expire_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, int set) 1300200580Sluigi{ 1301200580Sluigi 1302243707Smelifaro check_dyn_rules(chain, rule, set, 0, 0); 1303200580Sluigi} 1304200580Sluigi 1305200580Sluigivoid 1306243707Smelifaroipfw_dyn_init(struct ip_fw_chain *chain) 1307200580Sluigi{ 1308200580Sluigi 1309200580Sluigi V_ipfw_dyn_v = NULL; 1310243707Smelifaro V_dyn_buckets_max = 256; /* must be power of 2 */ 1311200580Sluigi V_curr_dyn_buckets = 256; /* must be power of 2 */ 1312200580Sluigi 1313200580Sluigi V_dyn_ack_lifetime = 300; 1314200580Sluigi V_dyn_syn_lifetime = 20; 1315200580Sluigi V_dyn_fin_lifetime = 1; 1316200580Sluigi V_dyn_rst_lifetime = 1; 1317200580Sluigi V_dyn_udp_lifetime = 10; 1318200580Sluigi V_dyn_short_lifetime = 5; 1319200580Sluigi 1320200580Sluigi V_dyn_keepalive_interval = 20; 1321200580Sluigi V_dyn_keepalive_period = 5; 1322200580Sluigi V_dyn_keepalive = 1; /* do send keepalives */ 1323247626Smelifaro V_dyn_keepalive_last = time_uptime; 1324200580Sluigi 1325200580Sluigi V_dyn_max = 4096; /* max # of dynamic rules */ 1326243707Smelifaro 1327243707Smelifaro V_ipfw_dyn_rule_zone = uma_zcreate("IPFW dynamic rule", 1328243707Smelifaro sizeof(ipfw_dyn_rule), NULL, NULL, NULL, NULL, 1329243707Smelifaro UMA_ALIGN_PTR, 0); 1330243707Smelifaro 1331243707Smelifaro /* Enforce limit on dynamic rules */ 1332243707Smelifaro uma_zone_set_max(V_ipfw_dyn_rule_zone, V_dyn_max); 1333243707Smelifaro 1334200580Sluigi callout_init(&V_ipfw_timeout, CALLOUT_MPSAFE); 1335243707Smelifaro 1336243707Smelifaro /* 1337243707Smelifaro * This can potentially be done on first dynamic rule 1338243707Smelifaro * being added to chain. 1339243707Smelifaro */ 1340243707Smelifaro resize_dynamic_table(chain, V_curr_dyn_buckets); 1341200580Sluigi} 1342200580Sluigi 1343200580Sluigivoid 1344200580Sluigiipfw_dyn_uninit(int pass) 1345200580Sluigi{ 1346243707Smelifaro int i; 1347243707Smelifaro 1348243707Smelifaro if (pass == 0) { 1349200580Sluigi callout_drain(&V_ipfw_timeout); 1350243707Smelifaro return; 1351200580Sluigi } 1352243707Smelifaro 1353243707Smelifaro if (V_ipfw_dyn_v != NULL) { 1354243707Smelifaro /* 1355243707Smelifaro * Skip deleting all dynamic states - 1356243707Smelifaro * uma_zdestroy() does this more efficiently; 1357243707Smelifaro */ 1358243707Smelifaro 1359243707Smelifaro /* Destroy all mutexes */ 1360243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets ; i++) 1361243707Smelifaro IPFW_BUCK_LOCK_DESTROY(&V_ipfw_dyn_v[i]); 1362243707Smelifaro free(V_ipfw_dyn_v, M_IPFW); 1363243707Smelifaro V_ipfw_dyn_v = NULL; 1364243707Smelifaro } 1365243707Smelifaro 1366243707Smelifaro uma_zdestroy(V_ipfw_dyn_rule_zone); 1367200580Sluigi} 1368200580Sluigi 1369243707Smelifaro#ifdef SYSCTL_NODE 1370243707Smelifaro/* 1371243707Smelifaro * Get/set maximum number of dynamic states in given VNET instance. 1372243707Smelifaro */ 1373243707Smelifarostatic int 1374243707Smelifarosysctl_ipfw_dyn_max(SYSCTL_HANDLER_ARGS) 1375243707Smelifaro{ 1376243707Smelifaro int error; 1377243707Smelifaro unsigned int nstates; 1378243707Smelifaro 1379243707Smelifaro nstates = V_dyn_max; 1380243707Smelifaro 1381243707Smelifaro error = sysctl_handle_int(oidp, &nstates, 0, req); 1382243707Smelifaro /* Read operation or some error */ 1383243707Smelifaro if ((error != 0) || (req->newptr == NULL)) 1384243707Smelifaro return (error); 1385243707Smelifaro 1386243707Smelifaro V_dyn_max = nstates; 1387243707Smelifaro uma_zone_set_max(V_ipfw_dyn_rule_zone, V_dyn_max); 1388243707Smelifaro 1389243707Smelifaro return (0); 1390243707Smelifaro} 1391243707Smelifaro 1392243707Smelifaro/* 1393243707Smelifaro * Get current number of dynamic states in given VNET instance. 1394243707Smelifaro */ 1395243707Smelifarostatic int 1396243707Smelifarosysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS) 1397243707Smelifaro{ 1398243707Smelifaro int error; 1399243707Smelifaro unsigned int nstates; 1400243707Smelifaro 1401243707Smelifaro nstates = DYN_COUNT; 1402243707Smelifaro 1403243707Smelifaro error = sysctl_handle_int(oidp, &nstates, 0, req); 1404243707Smelifaro 1405243707Smelifaro return (error); 1406243707Smelifaro} 1407243707Smelifaro#endif 1408243707Smelifaro 1409243707Smelifaro/* 1410243707Smelifaro * Returns number of dynamic rules. 1411243707Smelifaro */ 1412200580Sluigiint 1413200580Sluigiipfw_dyn_len(void) 1414200580Sluigi{ 1415243707Smelifaro 1416200580Sluigi return (V_ipfw_dyn_v == NULL) ? 0 : 1417243707Smelifaro (DYN_COUNT * sizeof(ipfw_dyn_rule)); 1418200580Sluigi} 1419200580Sluigi 1420243707Smelifaro/* 1421243707Smelifaro * Fill given buffer with dynamic states. 1422243707Smelifaro * IPFW_UH_RLOCK has to be held while calling. 1423243707Smelifaro */ 1424200580Sluigivoid 1425243707Smelifaroipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep) 1426200580Sluigi{ 1427200580Sluigi ipfw_dyn_rule *p, *last = NULL; 1428200580Sluigi char *bp; 1429200580Sluigi int i; 1430200580Sluigi 1431200580Sluigi if (V_ipfw_dyn_v == NULL) 1432200580Sluigi return; 1433200580Sluigi bp = *pbp; 1434200580Sluigi 1435243707Smelifaro IPFW_UH_RLOCK_ASSERT(chain); 1436243707Smelifaro 1437243707Smelifaro for (i = 0 ; i < V_curr_dyn_buckets; i++) { 1438243707Smelifaro IPFW_BUCK_LOCK(i); 1439243707Smelifaro for (p = V_ipfw_dyn_v[i].head ; p != NULL; p = p->next) { 1440200580Sluigi if (bp + sizeof *p <= ep) { 1441200580Sluigi ipfw_dyn_rule *dst = 1442200580Sluigi (ipfw_dyn_rule *)bp; 1443200580Sluigi bcopy(p, dst, sizeof *p); 1444200580Sluigi bcopy(&(p->rule->rulenum), &(dst->rule), 1445200580Sluigi sizeof(p->rule->rulenum)); 1446200580Sluigi /* 1447200580Sluigi * store set number into high word of 1448200580Sluigi * dst->rule pointer. 1449200580Sluigi */ 1450200580Sluigi bcopy(&(p->rule->set), 1451200580Sluigi (char *)&dst->rule + 1452200580Sluigi sizeof(p->rule->rulenum), 1453200580Sluigi sizeof(p->rule->set)); 1454200580Sluigi /* 1455200580Sluigi * store a non-null value in "next". 1456200580Sluigi * The userland code will interpret a 1457200580Sluigi * NULL here as a marker 1458200580Sluigi * for the last dynamic rule. 1459200580Sluigi */ 1460200580Sluigi bcopy(&dst, &dst->next, sizeof(dst)); 1461200580Sluigi last = dst; 1462200580Sluigi dst->expire = 1463200580Sluigi TIME_LEQ(dst->expire, time_uptime) ? 1464200580Sluigi 0 : dst->expire - time_uptime ; 1465200580Sluigi bp += sizeof(ipfw_dyn_rule); 1466200580Sluigi } 1467200580Sluigi } 1468243707Smelifaro IPFW_BUCK_UNLOCK(i); 1469243707Smelifaro } 1470243707Smelifaro 1471200580Sluigi if (last != NULL) /* mark last dynamic rule */ 1472200580Sluigi bzero(&last->next, sizeof(last)); 1473200580Sluigi *pbp = bp; 1474200580Sluigi} 1475200601Sluigi/* end of file */ 1476