From 2953b3f6403e94874c0c7b19faf52706cff66138 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Wed, 16 Oct 2013 11:59:26 -0600 Subject: [PATCH] Feature #901 - VLAN defrag support. Take VLAN IDs into account when re-assembling fragments. Prevents fragments that would otherwise match, but on different VLANs from being reassembled with each other. --- src/defrag-hash.c | 21 +++++++--- src/defrag.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ src/defrag.h | 2 + suricata.yaml.in | 8 ++-- 4 files changed, 119 insertions(+), 9 deletions(-) diff --git a/src/defrag-hash.c b/src/defrag-hash.c index 266e24070a..ffb957bb7f 100644 --- a/src/defrag-hash.c +++ b/src/defrag-hash.c @@ -86,6 +86,8 @@ static void DefragTrackerInit(DefragTracker *dt, Packet *p) { dt->id = (int32_t)IPV6_EXTHDR_GET_FH_ID(p); dt->af = AF_INET6; } + dt->vlan_id[0] = p->vlan_id[0]; + dt->vlan_id[1] = p->vlan_id[1]; dt->policy = DefragGetOsPolicy(p); TAILQ_INIT(&dt->frags); (void) DefragTrackerIncrUsecnt(dt); @@ -318,8 +320,9 @@ typedef struct DefragHashKey4_ { struct { uint32_t src, dst; uint32_t id; + uint16_t vlan_id[2]; }; - uint32_t u32[3]; + uint32_t u32[4]; }; } DefragHashKey4; @@ -328,8 +331,9 @@ typedef struct DefragHashKey6_ { struct { uint32_t src[4], dst[4]; uint32_t id; + uint16_t vlan_id[2]; }; - uint32_t u32[9]; + uint32_t u32[10]; }; } DefragHashKey6; @@ -340,6 +344,7 @@ typedef struct DefragHashKey6_ { * source address * destination address * id + * vlan_id */ static inline uint32_t DefragHashGetKey(Packet *p) { uint32_t key; @@ -354,8 +359,10 @@ static inline uint32_t DefragHashGetKey(Packet *p) { dhk.dst = p->src.addr_data32[0]; } dhk.id = (uint32_t)IPV4_GET_IPID(p); + dhk.vlan_id[0] = p->vlan_id[0]; + dhk.vlan_id[1] = p->vlan_id[1]; - uint32_t hash = hashword(dhk.u32, 3, defrag_config.hash_rand); + uint32_t hash = hashword(dhk.u32, 4, defrag_config.hash_rand); key = hash % defrag_config.hash_size; } else if (p->ip6h != NULL) { DefragHashKey6 dhk; @@ -379,8 +386,10 @@ static inline uint32_t DefragHashGetKey(Packet *p) { dhk.dst[3] = p->src.addr_data32[3]; } dhk.id = IPV6_EXTHDR_GET_FH_ID(p); + dhk.vlan_id[0] = p->vlan_id[0]; + dhk.vlan_id[1] = p->vlan_id[1]; - uint32_t hash = hashword(dhk.u32, 9, defrag_config.hash_rand); + uint32_t hash = hashword(dhk.u32, 10, defrag_config.hash_rand); key = hash % defrag_config.hash_size; } else key = 0; @@ -395,7 +404,9 @@ static inline uint32_t DefragHashGetKey(Packet *p) { CMP_ADDR(&(d1)->dst_addr, &(d2)->dst)) || \ (CMP_ADDR(&(d1)->src_addr, &(d2)->dst) && \ CMP_ADDR(&(d1)->dst_addr, &(d2)->src))) && \ - (d1)->id == (id)) + (d1)->id == (id) && \ + (d1)->vlan_id[0] == (d2)->vlan_id[0] && \ + (d1)->vlan_id[1] == (d2)->vlan_id[1]) static inline int DefragTrackerCompare(DefragTracker *t, Packet *p) { uint32_t id; diff --git a/src/defrag.c b/src/defrag.c index 9cc25a8c05..496997c0e2 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -2156,6 +2156,100 @@ end: return ret; } +/** + * Test that fragments in different VLANs that would otherwise be + * re-assembled, are not re-assembled. Just use simple in-order + * fragments. + */ +static int +DefragVlanTest(void) { + Packet *p1 = NULL, *p2 = NULL, *r = NULL; + int ret = 0; + + DefragInit(); + + p1 = BuildTestPacket(1, 0, 1, 'A', 8); + if (p1 == NULL) + goto end; + p2 = BuildTestPacket(1, 1, 0, 'B', 8); + if (p2 == NULL) + goto end; + + /* With no VLAN IDs set, packets should re-assemble. */ + if ((r = Defrag(NULL, NULL, p1)) != NULL) + goto end; + if ((r = Defrag(NULL, NULL, p2)) == NULL) + goto end; + SCFree(r); + + /* With mismatched VLANs, packets should not re-assemble. */ + p1->vlan_id[0] = 1; + p2->vlan_id[0] = 2; + if ((r = Defrag(NULL, NULL, p1)) != NULL) + goto end; + if ((r = Defrag(NULL, NULL, p2)) != NULL) + goto end; + + /* Pass. */ + ret = 1; + +end: + if (p1 != NULL) + SCFree(p1); + if (p2 != NULL) + SCFree(p2); + DefragDestroy(); + + return ret; +} + +/** + * Like DefragVlanTest, but for QinQ, testing the second level VLAN ID. + */ +static int +DefragVlanQinQTest(void) { + Packet *p1 = NULL, *p2 = NULL, *r = NULL; + int ret = 0; + + DefragInit(); + + p1 = BuildTestPacket(1, 0, 1, 'A', 8); + if (p1 == NULL) + goto end; + p2 = BuildTestPacket(1, 1, 0, 'B', 8); + if (p2 == NULL) + goto end; + + /* With no VLAN IDs set, packets should re-assemble. */ + if ((r = Defrag(NULL, NULL, p1)) != NULL) + goto end; + if ((r = Defrag(NULL, NULL, p2)) == NULL) + goto end; + SCFree(r); + + /* With mismatched VLANs, packets should not re-assemble. */ + p1->vlan_id[0] = 1; + p2->vlan_id[0] = 1; + p1->vlan_id[1] = 1; + p2->vlan_id[1] = 2; + if ((r = Defrag(NULL, NULL, p1)) != NULL) + goto end; + if ((r = Defrag(NULL, NULL, p2)) != NULL) + goto end; + + /* Pass. */ + ret = 1; + +end: + if (p1 != NULL) + SCFree(p1); + if (p2 != NULL) + SCFree(p2); + DefragDestroy(); + + return ret; +} + #endif /* UNITTESTS */ void @@ -2199,6 +2293,9 @@ DefragRegisterTests(void) UtRegisterTest("IPV6DefragSturgesNovakLastTest", IPV6DefragSturgesNovakLastTest, 1); + UtRegisterTest("DefragVlanTest", DefragVlanTest, 1); + UtRegisterTest("DefragVlanQinQTest", DefragVlanQinQTest, 1); + UtRegisterTest("DefragTimeoutTest", DefragTimeoutTest, 1); #endif /* UNITTESTS */ diff --git a/src/defrag.h b/src/defrag.h index 6d4b399275..f9889bea06 100644 --- a/src/defrag.h +++ b/src/defrag.h @@ -93,6 +93,8 @@ typedef struct DefragTracker_ { SCMutex lock; /**< Mutex for locking list operations on * this tracker. */ + uint16_t vlan_id[2]; /**< VLAN ID tracker applies to. */ + uint32_t id; /**< IP ID for this tracker. 32 bits for IPv6, 16 * for IPv4. */ diff --git a/suricata.yaml.in b/suricata.yaml.in index 78a3410506..7d8fd671bf 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -554,10 +554,10 @@ flow: prealloc: 10000 emergency-recovery: 30 -# This option controls the use of vlan ids in the flow hashing. Normally this -# should be enabled, but in some (broken) setups where both sides of a flow are -# not tagged with the same vlan tag, we can ignore the vlan id's in the flow -# hashing. +# This option controls the use of vlan ids in the flow (and defrag) +# hashing. Normally this should be enabled, but in some (broken) +# setups where both sides of a flow are not tagged with the same vlan +# tag, we can ignore the vlan id's in the flow hashing. vlan: use-for-tracking: true -- 2.47.2