From 5c880b04c9cb506f871c361649300ee111f83f62 Mon Sep 17 00:00:00 2001 From: Anoop Saldanha Date: Fri, 1 Apr 2011 16:01:02 +0530 Subject: [PATCH] fix ipv4 defrag + fix recursion level in defrag pseudo packet --- src/decode-ipv4.c | 455 +++++++++++++++++++++++++++++++++++++++++++++- src/defrag.c | 25 ++- src/suricata.c | 2 +- 3 files changed, 457 insertions(+), 25 deletions(-) diff --git a/src/decode-ipv4.c b/src/decode-ipv4.c index 67dfd13e38..1d94e7658a 100644 --- a/src/decode-ipv4.c +++ b/src/decode-ipv4.c @@ -32,6 +32,7 @@ #include "defrag.h" #include "util-unittest.h" #include "util-debug.h" +#include "pkt-var.h" /* Generic validation * @@ -519,6 +520,17 @@ void DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, return; } + /* If a fragment, pass off for re-assembly. */ + if (IPV4_GET_IPOFFSET(p) > 0 || IPV4_GET_MF(p) == 1) { + Packet *rp = Defrag(tv, dtv, NULL, p); + if (rp != NULL) { + /* Got re-assembled packet, re-run through decoder. */ + DecodeIPV4(tv, dtv, rp, (void *)rp->ip4h, IPV4_GET_IPLEN(rp), pq); + PacketEnqueue(pq, rp); + } + return; + } + /* do hdr test, process hdr rules */ #ifdef DEBUG @@ -583,16 +595,6 @@ void DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, uint8_t *pkt, break; } - /* If a fragment, pass off for re-assembly. */ - if (IPV4_GET_IPOFFSET(p) > 0 || IPV4_GET_MF(p) == 1) { - Packet *rp = Defrag(tv, dtv, NULL, p); - if (rp != NULL) { - /* Got re-assembled packet, re-run through decoder. */ - DecodeIPV4(tv, dtv, rp, GET_PKT_DATA(rp), GET_PKT_LEN(rp), pq); - PacketEnqueue(pq, rp); - } - } - return; } @@ -1552,6 +1554,436 @@ static int IPV4CalculateInvalidChecksumtest02(void) return (csum == IPV4CalculateChecksum((uint16_t *)raw_ipv4, sizeof(raw_ipv4))); } + +/** + * \test IPV4 defrag and packet recursion level test + */ +int DecodeIPV4DefragTest01(void) +{ + uint8_t pkt1[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x00, 0x40, 0x06, + 0x9a, 0xc8, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, + 0x81, 0x5e + }; + uint8_t pkt2[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x01, 0x40, 0x06, + 0x9a, 0xc7, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, + 0x80, 0x00 + }; + uint8_t pkt3[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x18, 0xe9, 0xef, 0x00, 0x02, 0x40, 0x06, + 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0xb1, 0xa3, 0x00, 0x00 + }; + uint8_t tunnel_pkt[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x28, 0xe9, 0xef, 0x00, 0x00, 0x40, 0x06, + 0xba, 0xbc, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, + 0x81, 0x5e, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, + 0x80, 0x00, 0xb1, 0xa3, 0x00, 0x00 + }; + + Packet *p = SCMalloc(SIZE_OF_PACKET); + if (p == NULL) + return 0; + ThreadVars tv; + DecodeThreadVars dtv; + PacketQueue pq; + int result = 1; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&pq, 0, sizeof(PacketQueue)); + memset(p, 0, sizeof(Packet)); + + FlowInitConfig(FLOW_QUIET); + + p->pkt = pkt1; + p->pktlen = sizeof(pkt1); + DecodeIPV4(&tv, &dtv, p, pkt1 + ETHERNET_HEADER_LEN, + sizeof(pkt1) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + PACKET_DO_RECYCLE(p); + + p->pkt = pkt2; + p->pktlen = sizeof(pkt2); + DecodeIPV4(&tv, &dtv, p, pkt2 + ETHERNET_HEADER_LEN, + sizeof(pkt2) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + PACKET_DO_RECYCLE(p); + + p->pkt = pkt3; + p->pktlen = sizeof(pkt3); + DecodeIPV4(&tv, &dtv, p, pkt3 + ETHERNET_HEADER_LEN, + sizeof(pkt3) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + Packet *tp = PacketDequeue(&pq); + if (tp == NULL) { + printf("Failed to get defragged pseudo packet\n"); + result = 0; + goto end; + } + if (tp->recursion_level != p->recursion_level) { + printf("defragged pseudo packet's and parent packet's recursion " + "level don't match\n %d != %d", + tp->recursion_level, p->recursion_level); + result = 0; + goto end; + } + if (tp->ip4h == NULL || tp->tcph == NULL) { + printf("pseudo packet's ip header and tcp header shouldn't be NULL, " + "but it is\n"); + result = 0; + goto end; + } + if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) { + printf("defragged pseudo packet's and parent packet's pkt lens " + "don't match\n %u != %lu", + GET_PKT_LEN(tp), sizeof(tunnel_pkt)); + result = 0; + goto end; + } + size_t i; + for (i = 0; i < sizeof(tunnel_pkt); i++) { + if (tunnel_pkt[i] != tp->pkt[i]) { + result = 0; + goto end; + } + } + + SCFree(tp); + + end: + FlowShutdown(); + SCFree(p); + return result; +} + +/** + * \test Don't send IPv4 fragments to the upper layer decoder and + * and packet recursion level test. + */ +int DecodeIPV4DefragTest02(void) +{ + uint8_t pkt1[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x24, 0xe9, 0xef, 0x20, 0x00, 0x40, 0x06, + 0x9a, 0xc8, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, + /* first frag */ + 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, + 0x81, 0x5e, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, + 0x80, 0x00, + }; + uint8_t pkt2[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x2c, 0xe9, 0xef, 0x20, 0x02, 0x40, 0x06, + 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, + /* second frag */ + 0xb1, 0xa3, 0x00, 0x10, 0x5b, 0xa3, 0x81, 0x5e, + 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, 0x80, 0x00, + 0xb1, 0xa3, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04 + }; + uint8_t pkt3[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x16, 0xe9, 0xef, 0x00, 0x05, 0x40, 0x06, + 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, + /* final frag */ + 0xb1, 0xa3, + }; + + uint8_t tunnel_pkt[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x3e, 0xe9, 0xef, 0x00, 0x00, 0x40, 0x06, + 0xba, 0xae, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, + 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, 0x81, 0x5e, + 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, 0x80, 0x00, + 0xb1, 0xa3, 0x00, 0x10, 0x5b, 0xa3, 0x81, 0x5e, + 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, 0x80, 0x00, + 0xb1, 0xa3, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04, + 0xb1, 0xa3, + }; + + Packet *p = SCMalloc(SIZE_OF_PACKET); + if (p == NULL) + return 0; + ThreadVars tv; + DecodeThreadVars dtv; + PacketQueue pq; + int result = 1; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&pq, 0, sizeof(PacketQueue)); + memset(p, 0, sizeof(Packet)); + + FlowInitConfig(FLOW_QUIET); + + p->pkt = pkt1; + p->pktlen = sizeof(pkt1); + DecodeIPV4(&tv, &dtv, p, pkt1 + ETHERNET_HEADER_LEN, + sizeof(pkt1) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + PACKET_DO_RECYCLE(p); + + p->pkt = pkt2; + p->pktlen = sizeof(pkt2); + DecodeIPV4(&tv, &dtv, p, pkt2 + ETHERNET_HEADER_LEN, + sizeof(pkt2) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + PACKET_DO_RECYCLE(p); + + p->recursion_level = 3; + p->pkt = pkt3; + p->pktlen = sizeof(pkt3); + DecodeIPV4(&tv, &dtv, p, pkt3 + ETHERNET_HEADER_LEN, + sizeof(pkt3) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + Packet *tp = PacketDequeue(&pq); + if (tp == NULL) { + printf("Failed to get defragged pseudo packet\n"); + result = 0; + goto end; + } + if (tp->recursion_level != p->recursion_level) { + printf("defragged pseudo packet's and parent packet's recursion " + "level don't match\n %d != %d", + tp->recursion_level, p->recursion_level); + result = 0; + goto end; + } + if (tp->ip4h == NULL || tp->tcph == NULL) { + printf("pseudo packet's ip header and tcp header shouldn't be NULL, " + "but it is\n"); + result = 0; + goto end; + } + if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) { + printf("defragged pseudo packet's and parent packet's pkt lens " + "don't match\n %u != %lu", + GET_PKT_LEN(tp), sizeof(tunnel_pkt)); + result = 0; + goto end; + } + size_t i; + for (i = 0; i < sizeof(tunnel_pkt); i++) { + if (tunnel_pkt[i] != tp->pkt[i]) { + result = 0; + goto end; + } + } + + SCFree(tp); + + end: + FlowShutdown(); + SCFree(p); + return result; +} + +/** + * \test IPV4 defrag and flow retrieval test. + */ +int DecodeIPV4DefragTest03(void) +{ + uint8_t pkt[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x28, 0xe9, 0xee, 0x00, 0x00, 0x40, 0x06, + 0xba, 0xbd, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, + 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, + 0x80, 0x00, 0x0c, 0xee, 0x00, 0x00 + }; + uint8_t pkt1[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x00, 0x40, 0x06, + 0x9a, 0xc8, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, + 0x81, 0x5e + }; + uint8_t pkt2[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x1c, 0xe9, 0xef, 0x20, 0x01, 0x40, 0x06, + 0x9a, 0xc7, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, + 0x80, 0x00 + }; + uint8_t pkt3[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x18, 0xe9, 0xef, 0x00, 0x02, 0x40, 0x06, + 0xba, 0xca, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0xb1, 0xa3, 0x00, 0x00 + }; + uint8_t tunnel_pkt[] = { + 0x00, 0x50, 0x56, 0x00, 0x03, 0x05, 0xde, 0xad, + 0x01, 0xa3, 0xa2, 0x2f, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x28, 0xe9, 0xef, 0x00, 0x00, 0x40, 0x06, + 0xba, 0xbc, 0x0a, 0x00, 0xe1, 0x17, 0x0a, 0x00, + 0xe1, 0x0c, 0x6e, 0x12, 0x01, 0xbd, 0x5b, 0xa3, + 0x81, 0x5e, 0xac, 0xb0, 0xae, 0x8a, 0x50, 0x10, + 0x80, 0x00, 0xb1, 0xa3, 0x00, 0x00 + }; + + Flow *f = NULL; + Packet *p = SCMalloc(SIZE_OF_PACKET); + if (p == NULL) + return 0; + ThreadVars tv; + DecodeThreadVars dtv; + PacketQueue pq; + int result = 1; + + memset(&tv, 0, sizeof(ThreadVars)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&pq, 0, sizeof(PacketQueue)); + memset(p, 0, sizeof(Packet)); + + FlowInitConfig(FLOW_QUIET); + + p->pkt = pkt; + p->pktlen = sizeof(pkt); + DecodeIPV4(&tv, &dtv, p, pkt + ETHERNET_HEADER_LEN, + sizeof(pkt) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph == NULL) { + printf("tcp header shouldn't be NULL, but it is\n"); + result = 0; + goto end; + } + if (p->flow == NULL) { + printf("packet flow shouldn't be NULL\n"); + result = 0; + goto end; + } + f = p->flow; + PACKET_DO_RECYCLE(p); + + p->pkt = pkt1; + p->pktlen = sizeof(pkt1); + DecodeIPV4(&tv, &dtv, p, pkt1 + ETHERNET_HEADER_LEN, + sizeof(pkt1) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + PACKET_DO_RECYCLE(p); + + p->pkt = pkt2; + p->pktlen = sizeof(pkt2); + DecodeIPV4(&tv, &dtv, p, pkt2 + ETHERNET_HEADER_LEN, + sizeof(pkt2) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + PACKET_DO_RECYCLE(p); + + p->pkt = pkt3; + p->pktlen = sizeof(pkt3); + DecodeIPV4(&tv, &dtv, p, pkt3 + ETHERNET_HEADER_LEN, + sizeof(pkt3) - ETHERNET_HEADER_LEN, &pq); + if (p->tcph != NULL) { + printf("tcp header should be NULL for ip fragment, but it isn't\n"); + result = 0; + goto end; + } + + Packet *tp = PacketDequeue(&pq); + if (tp == NULL) { + printf("Failed to get defragged pseudo packet\n"); + result = 0; + goto end; + } + if (tp->flow == NULL) { + result = 0; + goto end; + } + if (tp->flow != f) { + result = 0; + goto end; + } + if (tp->recursion_level != p->recursion_level) { + printf("defragged pseudo packet's and parent packet's recursion " + "level don't match\n %d != %d", + tp->recursion_level, p->recursion_level); + result = 0; + goto end; + } + if (tp->ip4h == NULL || tp->tcph == NULL) { + printf("pseudo packet's ip header and tcp header shouldn't be NULL, " + "but it is\n"); + result = 0; + goto end; + } + if (GET_PKT_LEN(tp) != sizeof(tunnel_pkt)) { + printf("defragged pseudo packet's and parent packet's pkt lens " + "don't match\n %u != %lu", + GET_PKT_LEN(tp), sizeof(tunnel_pkt)); + result = 0; + goto end; + } + size_t i; + for (i = 0; i < sizeof(tunnel_pkt); i++) { + if (tunnel_pkt[i] != tp->pkt[i]) { + result = 0; + goto end; + } + } + + SCFree(tp); + + end: + FlowShutdown(); + SCFree(p); + return result; +} + #endif /* UNITTESTS */ void DecodeIPV4RegisterTests(void) { @@ -1588,5 +2020,8 @@ void DecodeIPV4RegisterTests(void) { IPV4CalculateValidChecksumtest01, 1); UtRegisterTest("IPV4CalculateInvalidChecksumtest02", IPV4CalculateInvalidChecksumtest02, 0); + UtRegisterTest("DecodeIPV4DefragTest01", DecodeIPV4DefragTest01, 1); + UtRegisterTest("DecodeIPV4DefragTest02", DecodeIPV4DefragTest02, 1); + UtRegisterTest("DecodeIPV4DefragTest03", DecodeIPV4DefragTest03, 1); #endif /* UNITTESTS */ } diff --git a/src/defrag.c b/src/defrag.c index 5e447f08a3..4333188884 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -534,17 +534,6 @@ Defrag4Reassemble(ThreadVars *tv, DefragContext *dc, DefragTracker *tracker, } } - /* Allocate a Packet for the reassembled packet. On failure we - * SCFree all the resources held by this tracker. */ - rp = PacketPseudoPktSetup(p, (uint8_t *)p->ip4h, IPV4_GET_IPLEN(p), - IPV4_GET_IPPROTO(p)); - if (rp == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate packet for " - "fragmentation re-assembly, dumping fragments."); - goto remove_tracker; - } - SCLogDebug("Packet rp %p, p %p, rp->root %p", rp, p, rp->root); - int fragmentable_offset = 0; int fragmentable_len = 0; int hlen = 0; @@ -555,10 +544,18 @@ Defrag4Reassemble(ThreadVars *tv, DefragContext *dc, DefragTracker *tracker, if (frag->data_len - frag->ltrim <= 0) continue; if (frag->offset == 0) { - /* This is the first packet, we use this packets link and - * IPv4 header. We also copy in its data. */ - if (PacketCopyData(rp, frag->pkt, frag->len) == -1) + /* Allocate a Packet for the reassembled packet. On failure we + * SCFree all the resources held by this tracker. */ + rp = PacketPseudoPktSetup(p, frag->pkt, frag->len, + IPV4_GET_IPPROTO(p)); + if (rp == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate packet for " + "fragmentation re-assembly, dumping fragments."); goto remove_tracker; + } + SCLogDebug("Packet rp %p, p %p, rp->root %p", rp, p, rp->root); + rp->recursion_level = p->recursion_level; + rp->ip4h = (IPV4Hdr *)(GET_PKT_DATA(rp) + frag->ip_hdr_offset); hlen = frag->hlen; ip_hdr_offset = frag->ip_hdr_offset; diff --git a/src/suricata.c b/src/suricata.c index 340982b120..7d199031a8 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -965,6 +965,7 @@ int main(int argc, char **argv) /* Load the Host-OS lookup. */ SCHInfoLoadFromConfig(); + DefragInit(); if (run_mode == MODE_UNKNOWN) { if (!engine_analysis) { @@ -1342,7 +1343,6 @@ int main(int argc, char **argv) FlowManagerThreadSpawn(); StreamTcpInitConfig(STREAM_VERBOSE); - DefragInit(); /* Spawn the L7 App Detect thread */ //AppLayerDetectProtoThreadSpawn();