From fdfb76741115a93599e3e9c6c07493f569e94fbf Mon Sep 17 00:00:00 2001 From: Cole Dishington Date: Wed, 9 Aug 2023 07:47:12 +1200 Subject: [PATCH] flow: optionally use pkt recursion for hash 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. Bug: #6260 --- src/flow-hash.c | 42 +++++++++++++++++++++++++++--------------- src/suricata.c | 8 ++++++++ src/suricata.h | 1 + suricata.yaml.in | 7 +++++++ 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/flow-hash.c b/src/flow-hash.c index 3b221e2ffffc..f672d93898f1 100644 --- a/src/flow-hash.c +++ b/src/flow-hash.c @@ -129,7 +129,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; @@ -164,7 +167,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; @@ -204,7 +207,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; @@ -231,7 +237,7 @@ static inline uint32_t FlowGetHash(const Packet *p) fhk.ports[pi] = p->icmpv4vars.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; @@ -248,7 +254,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; @@ -283,7 +289,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; @@ -321,7 +327,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; @@ -357,7 +363,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; @@ -411,7 +417,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); } @@ -422,7 +429,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); } @@ -448,7 +456,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); } @@ -471,7 +480,8 @@ static inline int FlowCompareICMPv4(Flow *f, const Packet *p) if ((f->src.addr_data32[0] == IPV4_GET_RAW_IPSRC_U32(ICMPV4_GET_EMB_IPV4(p))) && (f->dst.addr_data32[0] == IPV4_GET_RAW_IPDST_U32(ICMPV4_GET_EMB_IPV4(p))) && f->sp == p->icmpv4vars.emb_sport && f->dp == p->icmpv4vars.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; @@ -482,7 +492,8 @@ static inline int FlowCompareICMPv4(Flow *f, const Packet *p) (f->src.addr_data32[0] == IPV4_GET_RAW_IPDST_U32(ICMPV4_GET_EMB_IPV4(p))) && f->dp == p->icmpv4vars.emb_sport && f->sp == p->icmpv4vars.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; } @@ -513,8 +524,9 @@ 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(p) && (f->livedev == p->livedev || g_livedev_mask == 0); + (f->recursion_level == p->recursion_level || g_recurlvl_mask == 0) && + CmpVlanIds(f->vlan_id, p->vlan_id) && f->esp.spi == ESP_GET_SPI(p) && + (f->livedev == p->livedev || g_livedev_mask == 0); } void FlowSetupPacket(Packet *p) diff --git a/src/suricata.c b/src/suricata.c index 7fe469acf24d..e14e4181d566 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -208,6 +208,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; @@ -2932,6 +2936,10 @@ int SuricataMain(int argc, char **argv) /* 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); diff --git a/src/suricata.h b/src/suricata.h index 957134b92c06..ec48642298c6 100644 --- a/src/suricata.h +++ b/src/suricata.h @@ -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; diff --git a/suricata.yaml.in b/suricata.yaml.in index 630399126dbe..ad2eb6aadddb 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1630,6 +1630,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 ##