]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
flow: optionally use pkt recursion for hash
authorCole Dishington <Cole.Dishington@alliedtelesis.co.nz>
Tue, 8 Aug 2023 19:47:12 +0000 (07:47 +1200)
committerVictor Julien <victor@inliniac.net>
Tue, 28 Jan 2025 08:32:40 +0000 (09:32 +0100)
If a Suricata inline IPS device is routing traffic over a
non-encrypted tunnel, like IPv6 tunnels, packets in a flow
will be dropped and not be matched. e.g.

The following example is a Suricata inline IPS with an IPv6 tunnel:
request: IPv4]ICMP] -> |IPS| -> IPv6]IPv4]ICMP]
reply:              <- |IPS| <- IPv6]IPv4]ICMP]
Both the IPv4 request and IPv6 reply will be seen by Suricata on
ingress. The flows will not be matched due to flow recursion level.

Optionally use pkt recursion level in flow hash. Excluding recursion
level in flow hash allows matching of packet flows and defrag on an
inline IPS Suricata scenario where the IPS device is a tunnel
terminator.

Feature: 6260

doc/userguide/configuration/suricata-yaml.rst
src/flow-hash.c
src/suricata.c
src/suricata.h
suricata.yaml.in

index 8004ecce9167ed96c9c456c5650f4403dbbaca7c..f6b0a7cb4b1fad0f0f9064d246377f626ae1d50b 100644 (file)
@@ -2871,6 +2871,24 @@ Using this default configuration, Teredo detection will run on UDP port
 3544. If the `ports` parameter is missing, or set to `any`, all ports will be
 inspected for possible presence of Teredo.
 
+Recursion Level
+~~~~~~~~~~~~~~~
+
+Flow matching via recursion level can be disabled. It is enabled by
+default.
+
+::
+
+    decoder:
+      # Depending on packet pickup, incoming and outgoing tunnelled packets
+      # can be scanned before the kernel has stripped and encapsulated headers,
+      # respectively, leading to incoming and outgoing flows not being associated.
+      recursion-level:
+        use-for-tracking: true
+
+Using this default setting, flows will be associated only if the compared packet
+headers are encapsulated in the same number of headers.
+
 Advanced Options
 ----------------
 
index c08b6e12c7eedc30a65e53706c2dd3c73a6ebc6b..21a39b3cfdde53c08f0b8ee2947f9b7820c30bdd 100644 (file)
@@ -130,7 +130,10 @@ uint32_t FlowGetIpPairProtoHash(const Packet *p)
         fhk.ports[1] = 0xba98;
 
         fhk.proto = (uint8_t)p->proto;
-        fhk.recur = (uint8_t)p->recursion_level;
+        /* g_recurlvl_mask sets the recursion_level to 0 if
+         * decoder.recursion-level.use-for-tracking is disabled.
+         */
+        fhk.recur = (uint8_t)p->recursion_level & g_recurlvl_mask;
         /* g_vlan_mask sets the vlan_ids to 0 if vlan.use-for-tracking
          * is disabled. */
         fhk.vlan_id[0] = p->vlan_id[0] & g_vlan_mask;
@@ -165,7 +168,7 @@ uint32_t FlowGetIpPairProtoHash(const Packet *p)
         fhk.ports[0] = 0xfedc;
         fhk.ports[1] = 0xba98;
         fhk.proto = (uint8_t)p->proto;
-        fhk.recur = (uint8_t)p->recursion_level;
+        fhk.recur = (uint8_t)p->recursion_level & g_recurlvl_mask;
         fhk.vlan_id[0] = p->vlan_id[0] & g_vlan_mask;
         fhk.vlan_id[1] = p->vlan_id[1] & g_vlan_mask;
         fhk.vlan_id[2] = p->vlan_id[2] & g_vlan_mask;
@@ -205,7 +208,10 @@ static inline uint32_t FlowGetHash(const Packet *p)
             fhk.ports[pi] = p->dp;
 
             fhk.proto = p->proto;
-            fhk.recur = p->recursion_level;
+            /* g_recurlvl_mask sets the recursion_level to 0 if
+             * decoder.recursion-level.use-for-tracking is disabled.
+             */
+            fhk.recur = p->recursion_level & g_recurlvl_mask;
             /* g_livedev_mask sets the livedev ids to 0 if livedev.use-for-tracking
              * is disabled. */
             uint16_t devid = p->livedev ? p->livedev->id : 0;
@@ -232,7 +238,7 @@ static inline uint32_t FlowGetHash(const Packet *p)
             fhk.ports[pi] = p->l4.vars.icmpv4.emb_dport;
 
             fhk.proto = ICMPV4_GET_EMB_PROTO(p);
-            fhk.recur = p->recursion_level;
+            fhk.recur = p->recursion_level & g_recurlvl_mask;
             uint16_t devid = p->livedev ? p->livedev->id : 0;
             fhk.livedev = devid & g_livedev_mask;
             fhk.vlan_id[0] = p->vlan_id[0] & g_vlan_mask;
@@ -249,7 +255,7 @@ static inline uint32_t FlowGetHash(const Packet *p)
             fhk.ports[0] = 0xfeed;
             fhk.ports[1] = 0xbeef;
             fhk.proto = p->proto;
-            fhk.recur = p->recursion_level;
+            fhk.recur = p->recursion_level & g_recurlvl_mask;
             uint16_t devid = p->livedev ? p->livedev->id : 0;
             fhk.livedev = devid & g_livedev_mask;
             fhk.vlan_id[0] = p->vlan_id[0] & g_vlan_mask;
@@ -284,7 +290,7 @@ static inline uint32_t FlowGetHash(const Packet *p)
         fhk.ports[1-pi] = p->sp;
         fhk.ports[pi] = p->dp;
         fhk.proto = p->proto;
-        fhk.recur = p->recursion_level;
+        fhk.recur = p->recursion_level & g_recurlvl_mask;
         uint16_t devid = p->livedev ? p->livedev->id : 0;
         fhk.livedev = devid & g_livedev_mask;
         fhk.vlan_id[0] = p->vlan_id[0] & g_vlan_mask;
@@ -322,7 +328,7 @@ uint32_t FlowKeyGetHash(FlowKey *fk)
         fhk.ports[pi] = fk->dp;
 
         fhk.proto = fk->proto;
-        fhk.recur = fk->recursion_level;
+        fhk.recur = fk->recursion_level & g_recurlvl_mask;
         fhk.livedev = fk->livedev_id & g_livedev_mask;
         fhk.vlan_id[0] = fk->vlan_id[0] & g_vlan_mask;
         fhk.vlan_id[1] = fk->vlan_id[1] & g_vlan_mask;
@@ -358,7 +364,7 @@ uint32_t FlowKeyGetHash(FlowKey *fk)
         fhk.ports[1-pi] = fk->sp;
         fhk.ports[pi] = fk->dp;
         fhk.proto = fk->proto;
-        fhk.recur = fk->recursion_level;
+        fhk.recur = fk->recursion_level & g_recurlvl_mask;
         fhk.livedev = fk->livedev_id & g_livedev_mask;
         fhk.vlan_id[0] = fk->vlan_id[0] & g_vlan_mask;
         fhk.vlan_id[1] = fk->vlan_id[1] & g_vlan_mask;
@@ -412,7 +418,8 @@ static inline bool CmpFlowPacket(const Flow *f, const Packet *p)
     const uint32_t *p_src = p->src.address.address_un_data32;
     const uint32_t *p_dst = p->dst.address.address_un_data32;
     return CmpAddrsAndPorts(f_src, f_dst, f->sp, f->dp, p_src, p_dst, p->sp, p->dp) &&
-           f->proto == p->proto && f->recursion_level == p->recursion_level &&
+           f->proto == p->proto &&
+           (f->recursion_level == p->recursion_level || g_recurlvl_mask == 0) &&
            CmpVlanIds(f->vlan_id, p->vlan_id) && (f->livedev == p->livedev || g_livedev_mask == 0);
 }
 
@@ -423,7 +430,8 @@ static inline bool CmpFlowKey(const Flow *f, const FlowKey *k)
     const uint32_t *k_src = k->src.address.address_un_data32;
     const uint32_t *k_dst = k->dst.address.address_un_data32;
     return CmpAddrsAndPorts(f_src, f_dst, f->sp, f->dp, k_src, k_dst, k->sp, k->dp) &&
-           f->proto == k->proto && f->recursion_level == k->recursion_level &&
+           f->proto == k->proto &&
+           (f->recursion_level == k->recursion_level || g_recurlvl_mask == 0) &&
            CmpVlanIds(f->vlan_id, k->vlan_id) && CmpLiveDevIds(f->livedev, k->livedev_id);
 }
 
@@ -449,7 +457,8 @@ static inline bool CmpFlowICMPPacket(const Flow *f, const Packet *p)
     const uint32_t *p_dst = p->dst.address.address_un_data32;
     return CmpAddrsAndICMPTypes(f_src, f_dst, f->icmp_s.type, f->icmp_d.type, p_src, p_dst,
                    p->icmp_s.type, p->icmp_d.type) &&
-           f->proto == p->proto && f->recursion_level == p->recursion_level &&
+           f->proto == p->proto &&
+           (f->recursion_level == p->recursion_level || g_recurlvl_mask == 0) &&
            CmpVlanIds(f->vlan_id, p->vlan_id) && (f->livedev == p->livedev || g_livedev_mask == 0);
 }
 
@@ -472,7 +481,8 @@ static inline int FlowCompareICMPv4(Flow *f, const Packet *p)
         if ((f->src.addr_data32[0] == IPV4_GET_RAW_IPSRC_U32(PacketGetICMPv4EmbIPv4(p))) &&
                 (f->dst.addr_data32[0] == IPV4_GET_RAW_IPDST_U32(PacketGetICMPv4EmbIPv4(p))) &&
                 f->sp == p->l4.vars.icmpv4.emb_sport && f->dp == p->l4.vars.icmpv4.emb_dport &&
-                f->proto == ICMPV4_GET_EMB_PROTO(p) && f->recursion_level == p->recursion_level &&
+                f->proto == ICMPV4_GET_EMB_PROTO(p) &&
+                (f->recursion_level == p->recursion_level || g_recurlvl_mask == 0) &&
                 CmpVlanIds(f->vlan_id, p->vlan_id) &&
                 (f->livedev == p->livedev || g_livedev_mask == 0)) {
             return 1;
@@ -483,7 +493,8 @@ static inline int FlowCompareICMPv4(Flow *f, const Packet *p)
                    (f->src.addr_data32[0] == IPV4_GET_RAW_IPDST_U32(PacketGetICMPv4EmbIPv4(p))) &&
                    f->dp == p->l4.vars.icmpv4.emb_sport && f->sp == p->l4.vars.icmpv4.emb_dport &&
                    f->proto == ICMPV4_GET_EMB_PROTO(p) &&
-                   f->recursion_level == p->recursion_level && CmpVlanIds(f->vlan_id, p->vlan_id) &&
+                   (f->recursion_level == p->recursion_level || g_recurlvl_mask == 0) &&
+                   CmpVlanIds(f->vlan_id, p->vlan_id) &&
                    (f->livedev == p->livedev || g_livedev_mask == 0)) {
             return 1;
         }
@@ -514,8 +525,8 @@ static inline int FlowCompareESP(Flow *f, const Packet *p)
     const uint32_t *p_dst = p->dst.address.address_un_data32;
 
     return CmpAddrs(f_src, p_src) && CmpAddrs(f_dst, p_dst) && f->proto == p->proto &&
-           f->recursion_level == p->recursion_level && CmpVlanIds(f->vlan_id, p->vlan_id) &&
-           f->esp.spi == ESP_GET_SPI(PacketGetESP(p)) &&
+           (f->recursion_level == p->recursion_level || g_recurlvl_mask == 0) &&
+           CmpVlanIds(f->vlan_id, p->vlan_id) && f->esp.spi == ESP_GET_SPI(PacketGetESP(p)) &&
            (f->livedev == p->livedev || g_livedev_mask == 0);
 }
 
index 7c238c48cc5e6b6d3059113d431c2f4f4b656c5c..60b67f717b5eef3dd58dfbdfec6e6de7dff81a5a 100644 (file)
@@ -202,6 +202,10 @@ uint16_t g_vlan_mask = 0xffff;
  * comparing flows */
 uint16_t g_livedev_mask = 0xffff;
 
+/** determine (without branching) if we include the recursion levels when hashing or
+ * comparing flows */
+uint8_t g_recurlvl_mask = 0xff;
+
 /* flag to disable hashing almost globally, to be similar to disabling nss
  * support */
 bool g_disable_hashing = false;
@@ -2914,6 +2918,10 @@ void SuricataInit(void)
         /* Ignore livedev id when comparing flows. */
         g_livedev_mask = 0x0000;
     }
+    if (ConfGetBool("decoder.recursion-level.use-for-tracking", &tracking) == 1 && !tracking) {
+        /* Ignore recursion level when comparing flows. */
+        g_recurlvl_mask = 0x00;
+    }
     SetupUserMode(&suricata);
     InitRunAs(&suricata);
 
index 70393541f6fc6ccc52902dc70a9ec6fc259085b5..70209f393e9af2fa20c4f8ba3b2e271310d288dc 100644 (file)
@@ -173,6 +173,7 @@ extern volatile uint8_t suricata_ctl_flags;
 extern int g_disable_randomness;
 extern uint16_t g_vlan_mask;
 extern uint16_t g_livedev_mask;
+extern uint8_t g_recurlvl_mask;
 
 /* Flag to disable hashing (almost) globally. */
 extern bool g_disable_hashing;
index 4bc9e87aa2af64f94721de22a59d0a80d97d3e06..46c94f0defbb1c0bfbe2e9207265dfcfd5c4ad96 100644 (file)
@@ -1673,6 +1673,13 @@ decoder:
   # maximum number of decoder layers for a packet
   # max-layers: 16
 
+  # This option controls the use of packet recursion level in the flow
+  # (and defrag) hashing. This is enabled by default and should be
+  # disabled if packet pickup of tunneled packets occurs before the kernel
+  # has put the headers on, like when using netmap driver pickup.
+  recursion-level:
+    use-for-tracking: true
+
 ##
 ## Performance tuning and profiling
 ##