Author: ae
Date: Wed Apr 11 10:36:20 2018
New Revision: 332401
URL: https://svnweb.freebsd.org/changeset/base/332401

Log:
  MFC r328988,r328989:
    Rework ipfw dynamic states implementation to be lockless on fast path.
  
    o added struct ipfw_dyn_info that keeps all needed for ipfw_chk and
      for dynamic states implementation information;
    o added DYN_LOOKUP_NEEDED() macro that can be used to determine the
      need of new lookup of dynamic states;
    o ipfw_dyn_rule now becomes obsolete. Currently it used to pass
      information from kernel to userland only.
    o IPv4 and IPv6 states now described by different structures
      dyn_ipv4_state and dyn_ipv6_state;
    o IPv6 scope zones support is added;
    o ipfw(4) now depends from Concurrency Kit;
    o states are linked with "entry" field using CK_SLIST. This allows
      lockless lookup and protected by mutex modifications.
    o the "expired" SLIST field is used for states expiring.
    o struct dyn_data is used to keep generic information for both IPv4
      and IPv6;
    o struct dyn_parent is used to keep O_LIMIT_PARENT information;
    o IPv4 and IPv6 states are stored in different hash tables;
    o O_LIMIT_PARENT states now are kept separately from O_LIMIT and
      O_KEEP_STATE states;
    o per-cpu dyn_hp pointers are used to implement hazard pointers and they
      prevent freeing states that are locklessly used by lookup threads;
    o mutexes to protect modification of lists in hash tables now kept in
      separate arrays. 65535 limit to maximum number of hash buckets now
      removed.
    o Separate lookup and install functions added for IPv4 and IPv6 states
      and for parent states.
    o By default now is used Jenkinks hash function.
  
    Obtained from:      Yandex LLC
    Sponsored by:       Yandex LLC
    Differential Revision:      https://reviews.freebsd.org/D12685

Modified:
  stable/11/sys/conf/files
  stable/11/sys/modules/ipfw/Makefile
  stable/11/sys/netinet/ip_fw.h
  stable/11/sys/netpfil/ipfw/ip_fw2.c
  stable/11/sys/netpfil/ipfw/ip_fw_dynamic.c
  stable/11/sys/netpfil/ipfw/ip_fw_private.h
  stable/11/sys/netpfil/ipfw/ip_fw_sockopt.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/conf/files
==============================================================================
--- stable/11/sys/conf/files    Wed Apr 11 10:24:47 2018        (r332400)
+++ stable/11/sys/conf/files    Wed Apr 11 10:36:20 2018        (r332401)
@@ -4201,7 +4201,8 @@ netpfil/ipfw/ip_dn_io.c           optional inet dummynet
 netpfil/ipfw/ip_dn_glue.c      optional inet dummynet
 netpfil/ipfw/ip_fw2.c          optional inet ipfirewall
 netpfil/ipfw/ip_fw_bpf.c       optional inet ipfirewall
-netpfil/ipfw/ip_fw_dynamic.c   optional inet ipfirewall
+netpfil/ipfw/ip_fw_dynamic.c   optional inet ipfirewall \
+       compile-with "${NORMAL_C} -I$S/contrib/ck/include"
 netpfil/ipfw/ip_fw_eaction.c   optional inet ipfirewall
 netpfil/ipfw/ip_fw_log.c       optional inet ipfirewall
 netpfil/ipfw/ip_fw_pfil.c      optional inet ipfirewall

Modified: stable/11/sys/modules/ipfw/Makefile
==============================================================================
--- stable/11/sys/modules/ipfw/Makefile Wed Apr 11 10:24:47 2018        
(r332400)
+++ stable/11/sys/modules/ipfw/Makefile Wed Apr 11 10:36:20 2018        
(r332401)
@@ -9,7 +9,7 @@ SRCS+=  ip_fw_sockopt.c ip_fw_table.c ip_fw_table_algo.
 SRCS+= ip_fw_table_value.c
 SRCS+= opt_inet.h opt_inet6.h opt_ipdivert.h opt_ipfw.h
 
-CFLAGS+= -DIPFIREWALL
+CFLAGS+= -DIPFIREWALL -I${SRCTOP}/sys/contrib/ck/include
 #
 #If you want it verbose
 #CFLAGS+= -DIPFIREWALL_VERBOSE

Modified: stable/11/sys/netinet/ip_fw.h
==============================================================================
--- stable/11/sys/netinet/ip_fw.h       Wed Apr 11 10:24:47 2018        
(r332400)
+++ stable/11/sys/netinet/ip_fw.h       Wed Apr 11 10:36:20 2018        
(r332401)
@@ -669,7 +669,7 @@ struct ipfw_flow_id {
        uint32_t        src_ip;
        uint16_t        dst_port;
        uint16_t        src_port;
-       uint8_t         fib;
+       uint8_t         fib;    /* XXX: must be uint16_t */
        uint8_t         proto;
        uint8_t         _flags; /* protocol-specific flags */
        uint8_t         addr_type; /* 4=ip4, 6=ip6, 1=ether ? */
@@ -680,6 +680,7 @@ struct ipfw_flow_id {
 };
 #endif
 
+#define        IS_IP4_FLOW_ID(id)      ((id)->addr_type == 4)
 #define IS_IP6_FLOW_ID(id)     ((id)->addr_type == 6)
 
 /*

Modified: stable/11/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- stable/11/sys/netpfil/ipfw/ip_fw2.c Wed Apr 11 10:24:47 2018        
(r332400)
+++ stable/11/sys/netpfil/ipfw/ip_fw2.c Wed Apr 11 10:36:20 2018        
(r332401)
@@ -1385,8 +1385,7 @@ ipfw_chk(struct ip_fw_args *args)
         *      MATCH_NONE when checked and not matched (q = NULL),
         *      MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL)
         */
-       int dyn_dir = MATCH_UNKNOWN;
-       uint16_t dyn_name = 0;
+       struct ipfw_dyn_info dyn_info;
        struct ip_fw *q = NULL;
        struct ip_fw_chain *chain = &V_layer3_chain;
 
@@ -1418,6 +1417,7 @@ ipfw_chk(struct ip_fw_args *args)
        proto = args->f_id.proto = 0;   /* mark f_id invalid */
                /* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */
 
+       DYN_INFO_INIT(&dyn_info);
 /*
  * PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous,
  * then it sets p to point at the offset "len" in the mbuf. WARNING: the
@@ -2603,7 +2603,8 @@ do {                                                      
        \
                        case O_LIMIT:
                        case O_KEEP_STATE:
                                if (ipfw_dyn_install_state(chain, f,
-                                   (ipfw_insn_limit *)cmd, args, tablearg)) {
+                                   (ipfw_insn_limit *)cmd, args, ulp,
+                                   pktlen, &dyn_info, tablearg)) {
                                        /* error or limit violation */
                                        retval = IP_FW_DENY;
                                        l = 0;  /* exit inner loop */
@@ -2617,34 +2618,15 @@ do {                                                    
        \
                                /*
                                 * dynamic rules are checked at the first
                                 * keep-state or check-state occurrence,
-                                * with the result being stored in dyn_dir
-                                * and dyn_name.
+                                * with the result being stored in dyn_info.
                                 * The compiler introduces a PROBE_STATE
                                 * instruction for us when we have a
                                 * KEEP_STATE (because PROBE_STATE needs
                                 * to be run first).
-                                *
-                                * (dyn_dir == MATCH_UNKNOWN) means this is
-                                * first lookup for such f_id. Do lookup.
-                                *
-                                * (dyn_dir != MATCH_UNKNOWN &&
-                                *  dyn_name != 0 && dyn_name != cmd->arg1)
-                                * means previous lookup didn't find dynamic
-                                * rule for specific state name and current
-                                * lookup will search rule with another state
-                                * name. Redo lookup.
-                                *
-                                * (dyn_dir != MATCH_UNKNOWN && dyn_name == 0)
-                                * means previous lookup was for `any' name
-                                * and it didn't find rule. No need to do
-                                * lookup again.
                                 */
-                               if ((dyn_dir == MATCH_UNKNOWN ||
-                                   (dyn_name != 0 &&
-                                   dyn_name != cmd->arg1)) &&
-                                   (q = ipfw_dyn_lookup_state(&args->f_id,
-                                    ulp, pktlen, &dyn_dir,
-                                    (dyn_name = cmd->arg1))) != NULL) {
+                               if (DYN_LOOKUP_NEEDED(&dyn_info, cmd) &&
+                                   (q = ipfw_dyn_lookup_state(args, ulp,
+                                   pktlen, cmd, &dyn_info)) != NULL) {
                                        /*
                                         * Found dynamic entry, jump to the
                                         * 'action' part of the parent rule
@@ -2652,13 +2634,7 @@ do {                                                     
        \
                                         * cmdlen.
                                         */
                                        f = q;
-                                       /* XXX we would like to have f_pos
-                                        * readily accessible in the dynamic
-                                        * rule, instead of having to
-                                        * lookup q->rule.
-                                        */
-                                       f_pos = ipfw_find_rule(chain,
-                                           f->rulenum, f->id);
+                                       f_pos = dyn_info.f_pos;
                                        cmd = ACTION_PTR(f);
                                        l = f->cmd_len - f->act_ofs;
                                        cmdlen = 0;
@@ -2875,7 +2851,8 @@ do {                                                      
        \
                        case O_FORWARD_IP:
                                if (args->eh)   /* not valid on layer2 pkts */
                                        break;
-                               if (q != f || dyn_dir == MATCH_FORWARD) {
+                               if (q != f ||
+                                   dyn_info.direction == MATCH_FORWARD) {
                                    struct sockaddr_in *sa;
 
                                    sa = &(((ipfw_insn_sa *)cmd)->sa);
@@ -2935,7 +2912,8 @@ do {                                                      
        \
                        case O_FORWARD_IP6:
                                if (args->eh)   /* not valid on layer2 pkts */
                                        break;
-                               if (q != f || dyn_dir == MATCH_FORWARD) {
+                               if (q != f ||
+                                   dyn_info.direction == MATCH_FORWARD) {
                                        struct sockaddr_in6 *sin6;
 
                                        sin6 = &(((ipfw_insn_sa6 *)cmd)->sa);
@@ -3089,7 +3067,7 @@ do {                                                      
        \
                                         * @args content, and it may be
                                         * used for new state lookup later.
                                         */
-                                       dyn_dir = MATCH_UNKNOWN;
+                                       DYN_INFO_INIT(&dyn_info);
                                }
                                break;
 

Modified: stable/11/sys/netpfil/ipfw/ip_fw_dynamic.c
==============================================================================
--- stable/11/sys/netpfil/ipfw/ip_fw_dynamic.c  Wed Apr 11 10:24:47 2018        
(r332400)
+++ stable/11/sys/netpfil/ipfw/ip_fw_dynamic.c  Wed Apr 11 10:36:20 2018        
(r332401)
@@ -1,4 +1,6 @@
 /*-
+ * Copyright (c) 2017-2018 Yandex LLC
+ * Copyright (c) 2017-2018 Andrey V. Elsukov <a...@freebsd.org>
  * Copyright (c) 2002 Luigi Rizzo, Universita` di Pisa
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,32 +28,27 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
-#define        DEB(x)
-#define        DDB(x) x
-
-/*
- * Dynamic rule support for ipfw
- */
-
-#include "opt_ipfw.h"
 #include "opt_inet.h"
+#include "opt_inet6.h"
+#include "opt_ipfw.h"
 #ifndef INET
 #error IPFIREWALL requires INET.
 #endif /* INET */
-#include "opt_inet6.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/malloc.h>
+#include <sys/hash.h>
 #include <sys/mbuf.h>
 #include <sys/kernel.h>
-#include <sys/ktr.h>
 #include <sys/lock.h>
+#include <sys/pcpu.h>
+#include <sys/queue.h>
 #include <sys/rmlock.h>
+#include <sys/smp.h>
 #include <sys/socket.h>
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
-#include <net/ethernet.h> /* for ETHERTYPE_IP */
+#include <net/ethernet.h>
 #include <net/if.h>
 #include <net/if_var.h>
 #include <net/pfil.h>
@@ -59,7 +56,7 @@ __FBSDID("$FreeBSD$");
 
 #include <netinet/in.h>
 #include <netinet/ip.h>
-#include <netinet/ip_var.h>    /* ip_defttl */
+#include <netinet/ip_var.h>
 #include <netinet/ip_fw.h>
 #include <netinet/tcp_var.h>
 #include <netinet/udp.h>
@@ -68,6 +65,7 @@ __FBSDID("$FreeBSD$");
 #ifdef INET6
 #include <netinet6/in6_var.h>
 #include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
 #endif
 
 #include <netpfil/ipfw/ip_fw_private.h>
@@ -77,86 +75,261 @@ __FBSDID("$FreeBSD$");
 #ifdef MAC
 #include <security/mac/mac_framework.h>
 #endif
+#include <ck_queue.h>
 
 /*
- * Description of dynamic rules.
+ * Description of dynamic states.
  *
- * Dynamic rules are stored in lists accessed through a hash table
- * (ipfw_dyn_v) whose size is curr_dyn_buckets. This value can
- * be modified through the sysctl variable dyn_buckets which is
- * updated when the table becomes empty.
+ * Dynamic states are stored in lists accessed through a hash tables
+ * whose size is curr_dyn_buckets. This value can be modified through
+ * the sysctl variable dyn_buckets.
  *
- * XXX currently there is only one list, ipfw_dyn.
+ * Currently there are four tables: dyn_ipv4, dyn_ipv6, dyn_ipv4_parent,
+ * and dyn_ipv6_parent.
  *
- * When a packet is received, its address fields are first masked
- * with the mask defined for the rule, then hashed, then matched
- * against the entries in the corresponding list.
- * Dynamic rules can be used for different purposes:
+ * When a packet is received, its address fields hashed, then matched
+ * against the entries in the corresponding list by addr_type.
+ * Dynamic states can be used for different purposes:
  *  + stateful rules;
  *  + enforcing limits on the number of sessions;
  *  + in-kernel NAT (not implemented yet)
  *
- * The lifetime of dynamic rules is regulated by dyn_*_lifetime,
+ * The lifetime of dynamic states is regulated by dyn_*_lifetime,
  * measured in seconds and depending on the flags.
  *
- * The total number of dynamic rules is equal to UMA zone items count.
- * The max number of dynamic rules is dyn_max. When we reach
+ * The total number of dynamic states is equal to UMA zone items count.
+ * The max number of dynamic states is dyn_max. When we reach
  * the maximum number of rules we do not create anymore. This is
  * done to avoid consuming too much memory, but also too much
  * time when searching on each packet (ideally, we should try instead
  * to put a limit on the length of the list on each bucket...).
  *
- * Each dynamic rule holds a pointer to the parent ipfw rule so
- * we know what action to perform. Dynamic rules are removed when
- * the parent rule is deleted. This can be changed by dyn_keep_states
- * sysctl.
+ * Each state holds a pointer to the parent ipfw rule so we know what
+ * action to perform. Dynamic rules are removed when the parent rule is
+ * deleted.
  *
  * There are some limitations with dynamic rules -- we do not
  * obey the 'randomized match', and we do not do multiple
  * passes through the firewall. XXX check the latter!!!
  */
 
-struct ipfw_dyn_bucket {
-       struct mtx      mtx;            /* Bucket protecting lock */
-       ipfw_dyn_rule   *head;          /* Pointer to first rule */
+/* By default use jenkins hash function */
+#define        IPFIREWALL_JENKINSHASH
+
+#define        DYN_COUNTER_INC(d, dir, pktlen) do {    \
+       (d)->pcnt_ ## dir++;                    \
+       (d)->bcnt_ ## dir += pktlen;            \
+       } while (0)
+
+struct dyn_data {
+       void            *parent;        /* pointer to parent rule */
+       uint32_t        chain_id;       /* cached ruleset id */
+       uint32_t        f_pos;          /* cached rule index */
+
+       uint32_t        hashval;        /* hash value used for hash resize */
+       uint16_t        fibnum;         /* fib used to send keepalives */
+       uint8_t         _pad[3];
+       uint8_t         set;            /* parent rule set number */
+       uint16_t        rulenum;        /* parent rule number */
+       uint32_t        ruleid;         /* parent rule id */
+
+       uint32_t        state;          /* TCP session state and flags */
+       uint32_t        ack_fwd;        /* most recent ACKs in forward */
+       uint32_t        ack_rev;        /* and reverse direction (used */
+                                       /* to generate keepalives) */
+       uint32_t        sync;           /* synchronization time */
+       uint32_t        expire;         /* expire time */
+
+       uint64_t        pcnt_fwd;       /* bytes counter in forward */
+       uint64_t        bcnt_fwd;       /* packets counter in forward */
+       uint64_t        pcnt_rev;       /* bytes counter in reverse */
+       uint64_t        bcnt_rev;       /* packets counter in reverse */
 };
 
+#define        DPARENT_COUNT_DEC(p)    do {                    \
+       MPASS(p->count > 0);                            \
+       ck_pr_dec_32(&(p)->count);                      \
+} while (0)
+#define        DPARENT_COUNT_INC(p)    ck_pr_inc_32(&(p)->count)
+#define        DPARENT_COUNT(p)        ck_pr_load_32(&(p)->count)
+struct dyn_parent {
+       void            *parent;        /* pointer to parent rule */
+       uint32_t        count;          /* number of linked states */
+       uint8_t         _pad;
+       uint8_t         set;            /* parent rule set number */
+       uint16_t        rulenum;        /* parent rule number */
+       uint32_t        ruleid;         /* parent rule id */
+       uint32_t        hashval;        /* hash value used for hash resize */
+       uint32_t        expire;         /* expire time */
+};
+
+struct dyn_ipv4_state {
+       uint8_t         type;           /* State type */
+       uint8_t         proto;          /* UL Protocol */
+       uint16_t        kidx;           /* named object index */
+       uint16_t        sport, dport;   /* ULP source and destination ports */
+       in_addr_t       src, dst;       /* IPv4 source and destination */
+
+       union {
+               struct dyn_data *data;
+               struct dyn_parent *limit;
+       };
+       CK_SLIST_ENTRY(dyn_ipv4_state)  entry;
+       SLIST_ENTRY(dyn_ipv4_state)     expired;
+};
+CK_SLIST_HEAD(dyn_ipv4ck_slist, dyn_ipv4_state);
+static VNET_DEFINE(struct dyn_ipv4ck_slist *, dyn_ipv4);
+static VNET_DEFINE(struct dyn_ipv4ck_slist *, dyn_ipv4_parent);
+
+SLIST_HEAD(dyn_ipv4_slist, dyn_ipv4_state);
+static VNET_DEFINE(struct dyn_ipv4_slist, dyn_expired_ipv4);
+#define        V_dyn_ipv4                      VNET(dyn_ipv4)
+#define        V_dyn_ipv4_parent               VNET(dyn_ipv4_parent)
+#define        V_dyn_expired_ipv4              VNET(dyn_expired_ipv4)
+
+#ifdef INET6
+struct dyn_ipv6_state {
+       uint8_t         type;           /* State type */
+       uint8_t         proto;          /* UL Protocol */
+       uint16_t        kidx;           /* named object index */
+       uint16_t        sport, dport;   /* ULP source and destination ports */
+       struct in6_addr src, dst;       /* IPv6 source and destination */
+       uint32_t        zoneid;         /* IPv6 scope zone id */
+       union {
+               struct dyn_data *data;
+               struct dyn_parent *limit;
+       };
+       CK_SLIST_ENTRY(dyn_ipv6_state)  entry;
+       SLIST_ENTRY(dyn_ipv6_state)     expired;
+};
+CK_SLIST_HEAD(dyn_ipv6ck_slist, dyn_ipv6_state);
+static VNET_DEFINE(struct dyn_ipv6ck_slist *, dyn_ipv6);
+static VNET_DEFINE(struct dyn_ipv6ck_slist *, dyn_ipv6_parent);
+
+SLIST_HEAD(dyn_ipv6_slist, dyn_ipv6_state);
+static VNET_DEFINE(struct dyn_ipv6_slist, dyn_expired_ipv6);
+#define        V_dyn_ipv6                      VNET(dyn_ipv6)
+#define        V_dyn_ipv6_parent               VNET(dyn_ipv6_parent)
+#define        V_dyn_expired_ipv6              VNET(dyn_expired_ipv6)
+#endif /* INET6 */
+
 /*
- * Static variables followed by global ones
+ * Per-CPU pointer indicates that specified state is currently in use
+ * and must not be reclaimed by expiration callout.
  */
-static VNET_DEFINE(struct ipfw_dyn_bucket *, ipfw_dyn_v);
-static VNET_DEFINE(u_int32_t, dyn_buckets_max);
-static VNET_DEFINE(u_int32_t, curr_dyn_buckets);
-static VNET_DEFINE(struct callout, ipfw_timeout);
-#define        V_ipfw_dyn_v                    VNET(ipfw_dyn_v)
-#define        V_dyn_buckets_max               VNET(dyn_buckets_max)
-#define        V_curr_dyn_buckets              VNET(curr_dyn_buckets)
-#define V_ipfw_timeout                  VNET(ipfw_timeout)
+static void **dyn_hp_cache;
+static DPCPU_DEFINE(void *, dyn_hp);
+#define        DYNSTATE_GET(cpu)       ck_pr_load_ptr(DPCPU_ID_PTR((cpu), 
dyn_hp))
+#define        DYNSTATE_PROTECT(v)     ck_pr_store_ptr(DPCPU_PTR(dyn_hp), (v))
+#define        DYNSTATE_RELEASE()      DYNSTATE_PROTECT(NULL)
+#define        DYNSTATE_CRITICAL_ENTER()       critical_enter()
+#define        DYNSTATE_CRITICAL_EXIT()        do {    \
+       DYNSTATE_RELEASE();                     \
+       critical_exit();                        \
+} while (0);
 
-static VNET_DEFINE(uma_zone_t, ipfw_dyn_rule_zone);
-#define        V_ipfw_dyn_rule_zone            VNET(ipfw_dyn_rule_zone)
+/*
+ * We keep two version numbers, one is updated when new entry added to
+ * the list. Second is updated when an entry deleted from the list.
+ * Versions are updated under bucket lock.
+ *
+ * Bucket "add" version number is used to know, that in the time between
+ * state lookup (i.e. ipfw_dyn_lookup_state()) and the followed state
+ * creation (i.e. ipfw_dyn_install_state()) another concurrent thread did
+ * not install some state in this bucket. Using this info we can avoid
+ * additional state lookup, because we are sure that we will not install
+ * the state twice.
+ *
+ * Also doing the tracking of bucket "del" version during lookup we can
+ * be sure, that state entry was not unlinked and freed in time between
+ * we read the state pointer and protect it with hazard pointer.
+ *
+ * An entry unlinked from CK list keeps unchanged until it is freed.
+ * Unlinked entries are linked into expired lists using "expired" field.
+ */
 
-#define        IPFW_BUCK_LOCK_INIT(b)  \
-       mtx_init(&(b)->mtx, "IPFW dynamic bucket", NULL, MTX_DEF)
-#define        IPFW_BUCK_LOCK_DESTROY(b)       \
-       mtx_destroy(&(b)->mtx)
-#define        IPFW_BUCK_LOCK(i)       mtx_lock(&V_ipfw_dyn_v[(i)].mtx)
-#define        IPFW_BUCK_UNLOCK(i)     mtx_unlock(&V_ipfw_dyn_v[(i)].mtx)
-#define        IPFW_BUCK_ASSERT(i)     mtx_assert(&V_ipfw_dyn_v[(i)].mtx, 
MA_OWNED)
+/*
+ * dyn_expire_lock is used to protect access to dyn_expired_xxx lists.
+ * dyn_bucket_lock is used to get write access to lists in specific bucket.
+ * Currently one dyn_bucket_lock is used for all ipv4, ipv4_parent, ipv6,
+ * and ipv6_parent lists.
+ */
+static VNET_DEFINE(struct mtx, dyn_expire_lock);
+static VNET_DEFINE(struct mtx *, dyn_bucket_lock);
+#define        V_dyn_expire_lock               VNET(dyn_expire_lock)
+#define        V_dyn_bucket_lock               VNET(dyn_bucket_lock)
 
+/*
+ * Bucket's add/delete generation versions.
+ */
+static VNET_DEFINE(uint32_t *, dyn_ipv4_add);
+static VNET_DEFINE(uint32_t *, dyn_ipv4_del);
+static VNET_DEFINE(uint32_t *, dyn_ipv4_parent_add);
+static VNET_DEFINE(uint32_t *, dyn_ipv4_parent_del);
+#define        V_dyn_ipv4_add                  VNET(dyn_ipv4_add)
+#define        V_dyn_ipv4_del                  VNET(dyn_ipv4_del)
+#define        V_dyn_ipv4_parent_add           VNET(dyn_ipv4_parent_add)
+#define        V_dyn_ipv4_parent_del           VNET(dyn_ipv4_parent_del)
 
-static VNET_DEFINE(int, dyn_keep_states);
-#define        V_dyn_keep_states               VNET(dyn_keep_states)
+#ifdef INET6
+static VNET_DEFINE(uint32_t *, dyn_ipv6_add);
+static VNET_DEFINE(uint32_t *, dyn_ipv6_del);
+static VNET_DEFINE(uint32_t *, dyn_ipv6_parent_add);
+static VNET_DEFINE(uint32_t *, dyn_ipv6_parent_del);
+#define        V_dyn_ipv6_add                  VNET(dyn_ipv6_add)
+#define        V_dyn_ipv6_del                  VNET(dyn_ipv6_del)
+#define        V_dyn_ipv6_parent_add           VNET(dyn_ipv6_parent_add)
+#define        V_dyn_ipv6_parent_del           VNET(dyn_ipv6_parent_del)
+#endif /* INET6 */
 
+#define        DYN_BUCKET(h, b)                ((h) & (b - 1))
+#define        DYN_BUCKET_VERSION(b, v)        ck_pr_load_32(&V_dyn_ ## v[(b)])
+#define        DYN_BUCKET_VERSION_BUMP(b, v)   ck_pr_inc_32(&V_dyn_ ## v[(b)])
+
+#define        DYN_BUCKET_LOCK_INIT(lock, b)           \
+    mtx_init(&lock[(b)], "IPFW dynamic bucket", NULL, MTX_DEF)
+#define        DYN_BUCKET_LOCK_DESTROY(lock, b)        mtx_destroy(&lock[(b)])
+#define        DYN_BUCKET_LOCK(b)      mtx_lock(&V_dyn_bucket_lock[(b)])
+#define        DYN_BUCKET_UNLOCK(b)    mtx_unlock(&V_dyn_bucket_lock[(b)])
+#define        DYN_BUCKET_ASSERT(b)    mtx_assert(&V_dyn_bucket_lock[(b)], 
MA_OWNED)
+
+#define        DYN_EXPIRED_LOCK_INIT()         \
+    mtx_init(&V_dyn_expire_lock, "IPFW expired states list", NULL, MTX_DEF)
+#define        DYN_EXPIRED_LOCK_DESTROY()      mtx_destroy(&V_dyn_expire_lock)
+#define        DYN_EXPIRED_LOCK()              mtx_lock(&V_dyn_expire_lock)
+#define        DYN_EXPIRED_UNLOCK()            mtx_unlock(&V_dyn_expire_lock)
+
+static VNET_DEFINE(uint32_t, dyn_buckets_max);
+static VNET_DEFINE(uint32_t, curr_dyn_buckets);
+static VNET_DEFINE(struct callout, dyn_timeout);
+#define        V_dyn_buckets_max               VNET(dyn_buckets_max)
+#define        V_curr_dyn_buckets              VNET(curr_dyn_buckets)
+#define        V_dyn_timeout                   VNET(dyn_timeout)
+
+/* Maximum length of states chain in a bucket */
+static VNET_DEFINE(uint32_t, curr_max_length);
+#define        V_curr_max_length               VNET(curr_max_length)
+
+static VNET_DEFINE(uma_zone_t, dyn_data_zone);
+static VNET_DEFINE(uma_zone_t, dyn_parent_zone);
+static VNET_DEFINE(uma_zone_t, dyn_ipv4_zone);
+#ifdef INET6
+static VNET_DEFINE(uma_zone_t, dyn_ipv6_zone);
+#define        V_dyn_ipv6_zone                 VNET(dyn_ipv6_zone)
+#endif /* INET6 */
+#define        V_dyn_data_zone                 VNET(dyn_data_zone)
+#define        V_dyn_parent_zone               VNET(dyn_parent_zone)
+#define        V_dyn_ipv4_zone                 VNET(dyn_ipv4_zone)
+
 /*
  * Timeouts for various events in handing dynamic rules.
  */
-static VNET_DEFINE(u_int32_t, dyn_ack_lifetime);
-static VNET_DEFINE(u_int32_t, dyn_syn_lifetime);
-static VNET_DEFINE(u_int32_t, dyn_fin_lifetime);
-static VNET_DEFINE(u_int32_t, dyn_rst_lifetime);
-static VNET_DEFINE(u_int32_t, dyn_udp_lifetime);
-static VNET_DEFINE(u_int32_t, dyn_short_lifetime);
+static VNET_DEFINE(uint32_t, dyn_ack_lifetime);
+static VNET_DEFINE(uint32_t, dyn_syn_lifetime);
+static VNET_DEFINE(uint32_t, dyn_fin_lifetime);
+static VNET_DEFINE(uint32_t, dyn_rst_lifetime);
+static VNET_DEFINE(uint32_t, dyn_udp_lifetime);
+static VNET_DEFINE(uint32_t, dyn_short_lifetime);
 
 #define        V_dyn_ack_lifetime              VNET(dyn_ack_lifetime)
 #define        V_dyn_syn_lifetime              VNET(dyn_syn_lifetime)
@@ -172,10 +345,10 @@ static VNET_DEFINE(u_int32_t, dyn_short_lifetime);
  * dyn_rst_lifetime and dyn_fin_lifetime should be strictly lower
  * than dyn_keepalive_period.
  */
-
-static VNET_DEFINE(u_int32_t, dyn_keepalive_interval);
-static VNET_DEFINE(u_int32_t, dyn_keepalive_period);
-static VNET_DEFINE(u_int32_t, dyn_keepalive);
+#define        DYN_KEEPALIVE_MAXQ              512
+static VNET_DEFINE(uint32_t, dyn_keepalive_interval);
+static VNET_DEFINE(uint32_t, dyn_keepalive_period);
+static VNET_DEFINE(uint32_t, dyn_keepalive);
 static VNET_DEFINE(time_t, dyn_keepalive_last);
 
 #define        V_dyn_keepalive_interval        VNET(dyn_keepalive_interval)
@@ -183,113 +356,208 @@ static VNET_DEFINE(time_t, dyn_keepalive_last);
 #define        V_dyn_keepalive                 VNET(dyn_keepalive)
 #define        V_dyn_keepalive_last            VNET(dyn_keepalive_last)
 
-static VNET_DEFINE(u_int32_t, dyn_max);                /* max # of dynamic 
rules */
-
-#define        DYN_COUNT                       
uma_zone_get_cur(V_ipfw_dyn_rule_zone)
+static VNET_DEFINE(uint32_t, dyn_max);         /* max # of dynamic states */
+static VNET_DEFINE(uint32_t, dyn_count);       /* number of states */
+static VNET_DEFINE(uint32_t, dyn_parent_max);  /* max # of parent states */
+static VNET_DEFINE(uint32_t, dyn_parent_count);        /* number of parent 
states */
 #define        V_dyn_max                       VNET(dyn_max)
+#define        V_dyn_count                     VNET(dyn_count)
+#define        V_dyn_parent_max                VNET(dyn_parent_max)
+#define        V_dyn_parent_count              VNET(dyn_parent_count)
 
-/* for userspace, we emulate the uma_zone_counter with ipfw_dyn_count */
-static int ipfw_dyn_count;     /* number of objects */
+#define        DYN_COUNT_DEC(name)     do {                    \
+       MPASS((V_ ## name) > 0);                        \
+       ck_pr_dec_32(&(V_ ## name));                    \
+} while (0)
+#define        DYN_COUNT_INC(name)     ck_pr_inc_32(&(V_ ## name))
+#define        DYN_COUNT(name)         ck_pr_load_32(&(V_ ## name))
 
-#ifdef USERSPACE /* emulation of UMA object counters for userspace */
-#define uma_zone_get_cur(x)    ipfw_dyn_count
-#endif /* USERSPACE */
+static time_t last_log;        /* Log ratelimiting */
 
-static int last_log;   /* Log ratelimiting */
+/*
+ * Get/set maximum number of dynamic states in given VNET instance.
+ */
+static int
+sysctl_dyn_max(SYSCTL_HANDLER_ARGS)
+{
+       uint32_t nstates;
+       int error;
 
-static void ipfw_dyn_tick(void *vnetx);
-static void check_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *, int, int);
-#ifdef SYSCTL_NODE
+       nstates = V_dyn_max;
+       error = sysctl_handle_32(oidp, &nstates, 0, req);
+       /* Read operation or some error */
+       if ((error != 0) || (req->newptr == NULL))
+               return (error);
 
-static int sysctl_ipfw_dyn_count(SYSCTL_HANDLER_ARGS);
-static int sysctl_ipfw_dyn_max(SYSCTL_HANDLER_ARGS);
+       V_dyn_max = nstates;
+       uma_zone_set_max(V_dyn_data_zone, V_dyn_max);
+       return (0);
+}
 
-SYSBEGIN(f2)
+static int
+sysctl_dyn_parent_max(SYSCTL_HANDLER_ARGS)
+{
+       uint32_t nstates;
+       int error;
 
+       nstates = V_dyn_parent_max;
+       error = sysctl_handle_32(oidp, &nstates, 0, req);
+       /* Read operation or some error */
+       if ((error != 0) || (req->newptr == NULL))
+               return (error);
+
+       V_dyn_parent_max = nstates;
+       uma_zone_set_max(V_dyn_parent_zone, V_dyn_parent_max);
+       return (0);
+}
+
+static int
+sysctl_dyn_buckets(SYSCTL_HANDLER_ARGS)
+{
+       uint32_t nbuckets;
+       int error;
+
+       nbuckets = V_dyn_buckets_max;
+       error = sysctl_handle_32(oidp, &nbuckets, 0, req);
+       /* Read operation or some error */
+       if ((error != 0) || (req->newptr == NULL))
+               return (error);
+
+       if (nbuckets > 256)
+               V_dyn_buckets_max = 1 << fls(nbuckets - 1);
+       else
+               return (EINVAL);
+       return (0);
+}
+
 SYSCTL_DECL(_net_inet_ip_fw);
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_buckets,
-    CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_buckets_max), 0,
-    "Max number of dyn. buckets");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets,
+
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_count,
+    CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(dyn_count), 0,
+    "Current number of dynamic states.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_parent_count,
+    CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(dyn_parent_count), 0,
+    "Current number of parent states. ");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, curr_dyn_buckets,
     CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(curr_dyn_buckets), 0,
-    "Current Number of dyn. buckets");
-SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, dyn_count,
-    CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RD, 0, 0, sysctl_ipfw_dyn_count, 
"IU",
-    "Number of dyn. rules");
+    "Current number of buckets for states hash table.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, curr_max_length,
+    CTLFLAG_VNET | CTLFLAG_RD, &VNET_NAME(curr_max_length), 0,
+    "Current maximum length of states chains in hash buckets.");
+SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, dyn_buckets,
+    CTLFLAG_VNET | CTLTYPE_U32 | CTLFLAG_RW, 0, 0, sysctl_dyn_buckets,
+    "IU", "Max number of buckets for dynamic states hash table.");
 SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, dyn_max,
-    CTLFLAG_VNET | CTLTYPE_UINT | CTLFLAG_RW, 0, 0, sysctl_ipfw_dyn_max, "IU",
-    "Max number of dyn. rules");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime,
+    CTLFLAG_VNET | CTLTYPE_U32 | CTLFLAG_RW, 0, 0, sysctl_dyn_max,
+    "IU", "Max number of dynamic states.");
+SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, dyn_parent_max,
+    CTLFLAG_VNET | CTLTYPE_U32 | CTLFLAG_RW, 0, 0, sysctl_dyn_parent_max,
+    "IU", "Max number of parent dynamic states.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_ack_lifetime,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_ack_lifetime), 0,
-    "Lifetime of dyn. rules for acks");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime,
+    "Lifetime of dynamic states for TCP ACK.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_syn_lifetime,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_syn_lifetime), 0,
-    "Lifetime of dyn. rules for syn");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime,
+    "Lifetime of dynamic states for TCP SYN.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_fin_lifetime,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_fin_lifetime), 0,
-    "Lifetime of dyn. rules for fin");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime,
+    "Lifetime of dynamic states for TCP FIN.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_rst_lifetime,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_rst_lifetime), 0,
-    "Lifetime of dyn. rules for rst");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime,
+    "Lifetime of dynamic states for TCP RST.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_udp_lifetime,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_udp_lifetime), 0,
-    "Lifetime of dyn. rules for UDP");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime,
+    "Lifetime of dynamic states for UDP.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_short_lifetime,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_short_lifetime), 0,
-    "Lifetime of dyn. rules for other situations");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_keepalive,
+    "Lifetime of dynamic states for other situations.");
+SYSCTL_U32(_net_inet_ip_fw, OID_AUTO, dyn_keepalive,
     CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_keepalive), 0,
-    "Enable keepalives for dyn. rules");
-SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, dyn_keep_states,
-    CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(dyn_keep_states), 0,
-    "Do not flush dynamic states on rule deletion");
+    "Enable keepalives for dynamic states.");
 
-SYSEND
+#ifdef IPFIREWALL_DYNDEBUG
+#define        DYN_DEBUG(fmt, ...)     do {                    \
+       printf("%s: " fmt "\n", __func__, __VA_ARGS__); \
+} while (0)
+#else
+#define        DYN_DEBUG(fmt, ...)
+#endif /* !IPFIREWALL_DYNDEBUG */
 
-#endif /* SYSCTL_NODE */
-
-
 #ifdef INET6
-static __inline int
-hash_packet6(const struct ipfw_flow_id *id)
-{
-       u_int32_t i;
-       i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^
-           (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^
-           (id->src_ip6.__u6_addr.__u6_addr32[2]) ^
-           (id->src_ip6.__u6_addr.__u6_addr32[3]);
-       return ntohl(i);
-}
-#endif
+/* Functions to work with IPv6 states */
+static struct dyn_ipv6_state *dyn_lookup_ipv6_state(
+    const struct ipfw_flow_id *, uint32_t, const void *,
+    struct ipfw_dyn_info *, int);
+static int dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *,
+    uint32_t, const void *, int, const void *, uint32_t, uint16_t, uint32_t,
+    uint16_t);
+static struct dyn_ipv6_state *dyn_alloc_ipv6_state(
+    const struct ipfw_flow_id *, uint32_t, uint16_t, uint8_t);
+static int dyn_add_ipv6_state(void *, uint32_t, uint16_t, uint8_t,
+    const struct ipfw_flow_id *, uint32_t, const void *, int, uint32_t,
+    struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t);
+static void dyn_export_ipv6_state(const struct dyn_ipv6_state *,
+    ipfw_dyn_rule *);
 
-/*
- * IMPORTANT: the hash function for dynamic rules must be commutative
- * in source and destination (ip,port), because rules are bidirectional
- * and we want to find both in the same bucket.
- */
-static __inline int
-hash_packet(const struct ipfw_flow_id *id, int buckets)
-{
-       u_int32_t i;
+static uint32_t dyn_getscopeid(const struct ip_fw_args *);
+static void dyn_make_keepalive_ipv6(struct mbuf *, const struct in6_addr *,
+    const struct in6_addr *, uint32_t, uint32_t, uint32_t, uint16_t,
+    uint16_t);
+static void dyn_enqueue_keepalive_ipv6(struct mbufq *,
+    const struct dyn_ipv6_state *);
+static void dyn_send_keepalive_ipv6(struct ip_fw_chain *);
 
-#ifdef INET6
-       if (IS_IP6_FLOW_ID(id)) 
-               i = hash_packet6(id);
-       else
+static struct dyn_ipv6_state *dyn_lookup_ipv6_parent(
+    const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t,
+    uint32_t);
+static struct dyn_ipv6_state *dyn_lookup_ipv6_parent_locked(
+    const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t,
+    uint32_t);
+static struct dyn_ipv6_state *dyn_add_ipv6_parent(void *, uint32_t, uint16_t,
+    uint8_t, const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t,
+    uint16_t);
 #endif /* INET6 */
-       i = (id->dst_ip) ^ (id->src_ip);
-       i ^= (id->dst_port) ^ (id->src_port);
-       return (i & (buckets - 1));
-}
 
-#if 0
-#define        DYN_DEBUG(fmt, ...)     do {                    \
-       printf("%s: " fmt "\n", __func__, __VA_ARGS__); \
-} while (0)
-#else
-#define        DYN_DEBUG(fmt, ...)
-#endif
+/* Functions to work with limit states */
+static void *dyn_get_parent_state(const struct ipfw_flow_id *, uint32_t,
+    struct ip_fw *, uint32_t, uint32_t, uint16_t);
+static struct dyn_ipv4_state *dyn_lookup_ipv4_parent(
+    const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t);
+static struct dyn_ipv4_state *dyn_lookup_ipv4_parent_locked(
+    const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t);
+static struct dyn_parent *dyn_alloc_parent(void *, uint32_t, uint16_t,
+    uint8_t, uint32_t);
+static struct dyn_ipv4_state *dyn_add_ipv4_parent(void *, uint32_t, uint16_t,
+    uint8_t, const struct ipfw_flow_id *, uint32_t, uint32_t, uint16_t);
 
+static void dyn_tick(void *);
+static void dyn_expire_states(struct ip_fw_chain *, ipfw_range_tlv *);
+static void dyn_free_states(struct ip_fw_chain *);
+static void dyn_export_parent(const struct dyn_parent *, uint16_t,
+    ipfw_dyn_rule *);
+static void dyn_export_data(const struct dyn_data *, uint16_t, uint8_t,
+    ipfw_dyn_rule *);
+static uint32_t dyn_update_tcp_state(struct dyn_data *,
+    const struct ipfw_flow_id *, const struct tcphdr *, int);
+static void dyn_update_proto_state(struct dyn_data *,
+    const struct ipfw_flow_id *, const void *, int, int);
+
+/* Functions to work with IPv4 states */
+struct dyn_ipv4_state *dyn_lookup_ipv4_state(const struct ipfw_flow_id *,
+    const void *, struct ipfw_dyn_info *, int);
+static int dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *,
+    const void *, int, const void *, uint32_t, uint16_t, uint32_t, uint16_t);
+static struct dyn_ipv4_state *dyn_alloc_ipv4_state(
+    const struct ipfw_flow_id *, uint16_t, uint8_t);
+static int dyn_add_ipv4_state(void *, uint32_t, uint16_t, uint8_t,
+    const struct ipfw_flow_id *, const void *, int, uint32_t,
+    struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t);
+static void dyn_export_ipv4_state(const struct dyn_ipv4_state *,
+    ipfw_dyn_rule *);
+
+/*
+ * Named states support.
+ */
 static char *default_state_name = "default";
 struct dyn_state_obj {
        struct named_object     no;
@@ -436,7 +704,6 @@ dyn_destroy(struct ip_fw_chain *ch, struct named_objec
        KASSERT(no->refcnt == 1,
            ("Destroying object '%s' (type %u, idx %u) with refcnt %u",
            no->name, no->etlv, no->kidx, no->refcnt));
-
        DYN_DEBUG("kidx %d", no->kidx);
        obj = SRV_OBJECT(ch, no->kidx);
        SRV_OBJECT(ch, no->kidx) = NULL;
@@ -472,7 +739,137 @@ static struct opcode_obj_rewrite dyn_opcodes[] = {
                dyn_create, dyn_destroy
        },
 };
-/**
+
+/*
+ * IMPORTANT: the hash function for dynamic rules must be commutative
+ * in source and destination (ip,port), because rules are bidirectional
+ * and we want to find both in the same bucket.
+ */
+#ifndef IPFIREWALL_JENKINSHASH
+static __inline uint32_t
+hash_packet(const struct ipfw_flow_id *id)
+{
+       uint32_t i;
+
+#ifdef INET6
+       if (IS_IP6_FLOW_ID(id))
+               i = ntohl((id->dst_ip6.__u6_addr.__u6_addr32[2]) ^
+                   (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^
+                   (id->src_ip6.__u6_addr.__u6_addr32[2]) ^
+                   (id->src_ip6.__u6_addr.__u6_addr32[3]));
+       else
+#endif /* INET6 */
+       i = (id->dst_ip) ^ (id->src_ip);
+       i ^= (id->dst_port) ^ (id->src_port);
+       return (i);
+}
+
+static __inline uint32_t
+hash_parent(const struct ipfw_flow_id *id, const void *rule)
+{
+
+       return (hash_packet(id) ^ ((uintptr_t)rule));
+}
+
+#else /* IPFIREWALL_JENKINSHASH */
+
+static VNET_DEFINE(uint32_t, dyn_hashseed);
+#define        V_dyn_hashseed          VNET(dyn_hashseed)
+
+static __inline int
+addrcmp4(const struct ipfw_flow_id *id)
+{
+
+       if (id->src_ip < id->dst_ip)
+               return (0);
+       if (id->src_ip > id->dst_ip)
+               return (1);
+       if (id->src_port <= id->dst_port)
+               return (0);
+       return (1);
+}
+
+#ifdef INET6
+static __inline int
+addrcmp6(const struct ipfw_flow_id *id)
+{
+       int ret;
+
+       ret = memcmp(&id->src_ip6, &id->dst_ip6, sizeof(struct in6_addr));
+       if (ret < 0)
+               return (0);
+       if (ret > 0)
+               return (1);
+       if (id->src_port <= id->dst_port)
+               return (0);
+       return (1);
+}
+
+static __inline uint32_t
+hash_packet6(const struct ipfw_flow_id *id)
+{
+       struct tuple6 {
+               struct in6_addr addr[2];
+               uint16_t        port[2];
+       } t6;
+
+       if (addrcmp6(id) == 0) {
+               t6.addr[0] = id->src_ip6;
+               t6.addr[1] = id->dst_ip6;
+               t6.port[0] = id->src_port;
+               t6.port[1] = id->dst_port;
+       } else {
+               t6.addr[0] = id->dst_ip6;
+               t6.addr[1] = id->src_ip6;
+               t6.port[0] = id->dst_port;
+               t6.port[1] = id->src_port;
+       }
+       return (jenkins_hash32((const uint32_t *)&t6,
+           sizeof(t6) / sizeof(uint32_t), V_dyn_hashseed));
+}
+#endif
+
+static __inline uint32_t
+hash_packet(const struct ipfw_flow_id *id)
+{
+       struct tuple4 {
+               in_addr_t       addr[2];
+               uint16_t        port[2];
+       } t4;
+
+       if (IS_IP4_FLOW_ID(id)) {
+               /* All fields are in host byte order */
+               if (addrcmp4(id) == 0) {
+                       t4.addr[0] = id->src_ip;
+                       t4.addr[1] = id->dst_ip;
+                       t4.port[0] = id->src_port;
+                       t4.port[1] = id->dst_port;
+               } else {
+                       t4.addr[0] = id->dst_ip;
+                       t4.addr[1] = id->src_ip;
+                       t4.port[0] = id->dst_port;
+                       t4.port[1] = id->src_port;
+               }
+               return (jenkins_hash32((const uint32_t *)&t4,
+                   sizeof(t4) / sizeof(uint32_t), V_dyn_hashseed));
+       } else
+#ifdef INET6
+       if (IS_IP6_FLOW_ID(id))
+               return (hash_packet6(id));
+#endif
+       return (0);
+}
+
+static __inline uint32_t
+hash_parent(const struct ipfw_flow_id *id, const void *rule)
+{
+
+       return (jenkins_hash32((const uint32_t *)&rule,
+           sizeof(rule) / sizeof(uint32_t), hash_packet(id)));
+}
+#endif /* IPFIREWALL_JENKINSHASH */
+
+/*
  * Print customizable flow id description via log(9) facility.
  */
 static void
@@ -500,904 +897,1809 @@ print_dyn_rule_flags(const struct ipfw_flow_id *id, in
        }
        log(log_flags, "ipfw: %s type %d %s %d -> %s %d, %d %s\n",
            prefix, dyn_type, src, id->src_port, dst,
-           id->dst_port, DYN_COUNT, postfix);
+           id->dst_port, V_dyn_count, postfix);
 }
 
 #define        print_dyn_rule(id, dtype, prefix, postfix)      \
        print_dyn_rule_flags(id, dtype, LOG_DEBUG, prefix, postfix)
 
-#define TIME_LEQ(a,b)       ((int)((a)-(b)) <= 0)
-#define TIME_LE(a,b)       ((int)((a)-(b)) < 0)
+#define        TIME_LEQ(a,b)   ((int)((a)-(b)) <= 0)
+#define        TIME_LE(a,b)    ((int)((a)-(b)) < 0)
+#define        _SEQ_GE(a,b)    ((int)((a)-(b)) >= 0)
+#define        BOTH_SYN        (TH_SYN | (TH_SYN << 8))
+#define        BOTH_FIN        (TH_FIN | (TH_FIN << 8))
+#define        TCP_FLAGS       (TH_FLAGS | (TH_FLAGS << 8))

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to