diff options
Diffstat (limited to 'src')
33 files changed, 1528 insertions, 626 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index 4f15923..229aaef 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1233,6 +1233,7 @@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ POW_LIB = @POW_LIB@ RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ RUSTC = @RUSTC@ RUSTUP_HOME_PATH = @RUSTUP_HOME_PATH@ RUST_FEATURES = @RUST_FEATURES@ diff --git a/src/autoconf.h b/src/autoconf.h index 336c550..457a903 100644 --- a/src/autoconf.h +++ b/src/autoconf.h @@ -696,7 +696,7 @@ #define PACKAGE_NAME "suricata" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "suricata 7.0.4" +#define PACKAGE_STRING "suricata 7.0.5" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "suricata" @@ -705,7 +705,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "7.0.4" +#define PACKAGE_VERSION "7.0.5" /* Pcre code unit width is 8 bits */ #define PCRE2_CODE_UNIT_WIDTH 8 @@ -723,7 +723,7 @@ /* #undef PROFILING */ /* Git revision */ -#define REVISION d8bad3b1a 2024-03-19 +#define REVISION c4cf7b09f 2024-04-23 /* Define to 1 if all of the C90 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for @@ -743,7 +743,7 @@ /* #undef UNITTESTS */ /* Version number of package */ -#define VERSION "7.0.4" +#define VERSION "7.0.5" /* Enable Windows WinDivert support for inline IDP */ /* #undef WINDIVERT */ diff --git a/src/conf-yaml-loader.c b/src/conf-yaml-loader.c index ea64563..463eb2e 100644 --- a/src/conf-yaml-loader.c +++ b/src/conf-yaml-loader.c @@ -394,8 +394,19 @@ static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int if (inseq) { char sequence_node_name[DEFAULT_NAME_LEN]; snprintf(sequence_node_name, DEFAULT_NAME_LEN, "%d", seq_idx++); - ConfNode *seq_node = ConfNodeLookupChild(node, - sequence_node_name); + ConfNode *seq_node = NULL; + if (was_empty < 0) { + // initialize was_empty + if (TAILQ_EMPTY(&node->head)) { + was_empty = 1; + } else { + was_empty = 0; + } + } + // we only check if the node's list was not empty at first + if (was_empty == 0) { + seq_node = ConfNodeLookupChild(node, sequence_node_name); + } if (seq_node != NULL) { /* The sequence node has already been set, probably * from the command line. Remove it so it gets diff --git a/src/datasets.c b/src/datasets.c index d89ed8d..01ef5bb 100644 --- a/src/datasets.c +++ b/src/datasets.c @@ -746,6 +746,11 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; } + if (set->hash && SC_ATOMIC_GET(set->hash->memcap_reached)) { + SCLogError("dataset too large for set memcap"); + goto out_err; + } + SCLogDebug("set %p/%s type %u save %s load %s", set, set->name, set->type, set->save, set->load); diff --git a/src/decode-ipv4.h b/src/decode-ipv4.h index d247fa9..a825007 100644 --- a/src/decode-ipv4.h +++ b/src/decode-ipv4.h @@ -154,20 +154,18 @@ typedef struct IPV4Hdr_ memset(&p->ip4vars, 0x00, sizeof(p->ip4vars)); \ } while (0) -enum IPV4OptionFlags { - IPV4_OPT_FLAG_EOL = 0, - IPV4_OPT_FLAG_NOP, - IPV4_OPT_FLAG_RR, - IPV4_OPT_FLAG_TS, - IPV4_OPT_FLAG_QS, - IPV4_OPT_FLAG_LSRR, - IPV4_OPT_FLAG_SSRR, - IPV4_OPT_FLAG_SID, - IPV4_OPT_FLAG_SEC, - IPV4_OPT_FLAG_CIPSO, - IPV4_OPT_FLAG_RTRALT, - IPV4_OPT_FLAG_ESEC, -}; +#define IPV4_OPT_FLAG_EOL BIT_U16(1) +#define IPV4_OPT_FLAG_NOP BIT_U16(2) +#define IPV4_OPT_FLAG_RR BIT_U16(3) +#define IPV4_OPT_FLAG_TS BIT_U16(4) +#define IPV4_OPT_FLAG_QS BIT_U16(5) +#define IPV4_OPT_FLAG_LSRR BIT_U16(6) +#define IPV4_OPT_FLAG_SSRR BIT_U16(7) +#define IPV4_OPT_FLAG_SID BIT_U16(8) +#define IPV4_OPT_FLAG_SEC BIT_U16(9) +#define IPV4_OPT_FLAG_CIPSO BIT_U16(10) +#define IPV4_OPT_FLAG_RTRALT BIT_U16(11) +#define IPV4_OPT_FLAG_ESEC BIT_U16(12) /* helper structure with parsed ipv4 info */ typedef struct IPV4Vars_ diff --git a/src/decode.c b/src/decode.c index 5cdeeea..13c6541 100644 --- a/src/decode.c +++ b/src/decode.c @@ -408,7 +408,6 @@ Packet *PacketDefragPktSetup(Packet *parent, const uint8_t *pkt, uint32_t len, u } p->recursion_level = parent->recursion_level; /* NOT incremented */ p->ts = parent->ts; - p->datalink = DLT_RAW; p->tenant_id = parent->tenant_id; /* tell new packet it's part of a tunnel */ SET_TUNNEL_PKT(p); diff --git a/src/decode.h b/src/decode.h index dedfbb0..4516d37 100644 --- a/src/decode.h +++ b/src/decode.h @@ -974,6 +974,7 @@ void DecodeUnregisterCounters(void); * Libpcap on at least OpenBSD returns 101 as datalink type for RAW pcaps though. */ #define LINKTYPE_RAW2 101 #define LINKTYPE_IPV4 228 +#define LINKTYPE_IPV6 229 #define LINKTYPE_GRE_OVER_IP 778 #define LINKTYPE_CISCO_HDLC DLT_C_HDLC #define PPP_OVER_GRE 11 diff --git a/src/defrag.c b/src/defrag.c index 71cf420..4596d72 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -104,26 +104,6 @@ static DefragContext *defrag_context; RB_GENERATE(IP_FRAGMENTS, Frag_, rb, DefragRbFragCompare); /** - * Utility/debugging function to dump the frags associated with a - * tracker. Only enable when unit tests are enabled. - */ -#if 0 -#ifdef UNITTESTS -static void -DumpFrags(DefragTracker *tracker) -{ - Frag *frag; - - printf("Dumping frags for packet: ID=%d\n", tracker->id); - TAILQ_FOREACH(frag, &tracker->frags, next) { - printf("-> Frag: frag_offset=%d, frag_len=%d, data_len=%d, ltrim=%d, skip=%d\n", frag->offset, frag->len, frag->data_len, frag->ltrim, frag->skip); - PrintRawDataFp(stdout, frag->pkt, frag->len); - } -} -#endif /* UNITTESTS */ -#endif - -/** * \brief Reset a frag for reuse in a pool. */ static void @@ -266,7 +246,7 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) } /* Check that we have all the data. Relies on the fact that - * fragments are inserted if frag_offset order. */ + * fragments are inserted in frag_offset order. */ Frag *frag = NULL; size_t len = 0; RB_FOREACH_FROM(frag, IP_FRAGMENTS, first) { @@ -276,7 +256,8 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) goto done; } else { - len += frag->data_len; + /* Update the packet length to the largest known data offset. */ + len = MAX(len, frag->offset + frag->data_len); } } @@ -288,17 +269,27 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) } PKT_SET_SRC(rp, PKT_SRC_DEFRAG); rp->flags |= PKT_REBUILT_FRAGMENT; - rp->recursion_level = p->recursion_level; + rp->datalink = tracker->datalink; int fragmentable_offset = 0; uint16_t fragmentable_len = 0; uint16_t hlen = 0; int ip_hdr_offset = 0; + /* Assume more frags. */ + uint16_t prev_offset = 0; + bool more_frags = 1; + RB_FOREACH(frag, IP_FRAGMENTS, &tracker->fragment_tree) { SCLogDebug("frag %p, data_len %u, offset %u, pcap_cnt %"PRIu64, frag, frag->data_len, frag->offset, frag->pcap_cnt); + /* Previous fragment has no more fragments, and this packet + * doesn't overlap. We're done. */ + if (!more_frags && frag->offset > prev_offset) { + break; + } + if (frag->skip) continue; if (frag->ltrim >= frag->data_len) @@ -339,9 +330,16 @@ Defrag4Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) fragmentable_len = frag->offset + frag->data_len; } - if (!frag->more_frags) { - break; - } + /* Even if this fragment is flagged as having no more + * fragments, still continue. The next fragment may have the + * same offset with data that is preferred. + * + * For example, DefragBsdFragmentAfterNoMfIpv{4,6}Test + * + * This is due to not all fragments being completely trimmed, + * but relying on the copy ordering. */ + more_frags = frag->more_frags; + prev_offset = frag->offset; } SCLogDebug("ip_hdr_offset %u, hlen %" PRIu16 ", fragmentable_len %" PRIu16, ip_hdr_offset, hlen, @@ -417,7 +415,7 @@ Defrag6Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) goto done; } else { - len += frag->data_len; + len = MAX(len, frag->offset + frag->data_len); } } } @@ -430,13 +428,23 @@ Defrag6Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) goto error_remove_tracker; } PKT_SET_SRC(rp, PKT_SRC_DEFRAG); + rp->flags |= PKT_REBUILT_FRAGMENT; + rp->datalink = tracker->datalink; uint16_t unfragmentable_len = 0; int fragmentable_offset = 0; uint16_t fragmentable_len = 0; int ip_hdr_offset = 0; uint8_t next_hdr = 0; + + /* Assume more frags. */ + uint16_t prev_offset = 0; + bool more_frags = 1; + RB_FOREACH(frag, IP_FRAGMENTS, &tracker->fragment_tree) { + if (!more_frags && frag->offset > prev_offset) { + break; + } if (frag->skip) continue; if (frag->data_len - frag->ltrim <= 0) @@ -481,9 +489,16 @@ Defrag6Reassemble(ThreadVars *tv, DefragTracker *tracker, Packet *p) fragmentable_len = frag->offset + frag->data_len; } - if (!frag->more_frags) { - break; - } + /* Even if this fragment is flagged as having no more + * fragments, still continue. The next fragment may have the + * same offset with data that is preferred. + * + * For example, DefragBsdFragmentAfterNoMfIpv{4,6}Test + * + * This is due to not all fragments being completely trimmed, + * but relying on the copy ordering. */ + more_frags = frag->more_frags; + prev_offset = frag->offset; } rp->ip6h = (IPV6Hdr *)(GET_PKT_DATA(rp) + ip_hdr_offset); @@ -660,16 +675,45 @@ DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, switch (tracker->policy) { case DEFRAG_POLICY_BSD: if (frag_offset < prev->offset + prev->data_len) { - if (frag_offset >= prev->offset) { - ltrim = prev->offset + prev->data_len - frag_offset; + if (prev->offset <= frag_offset) { + /* We prefer the data from the previous + * fragment, so trim off the data in the new + * fragment that exists in the previous + * fragment. */ + uint16_t prev_end = prev->offset + prev->data_len; + if (prev_end > frag_end) { + /* Just skip. */ + /* TODO: Set overlap flag. */ + goto done; + } + ltrim = prev_end - frag_offset; + + if ((next != NULL) && (frag_end > next->offset)) { + next->ltrim = frag_end - next->offset; + } + + goto insert; } + + /* If the end of this fragment overlaps the start + * of the previous fragment, then trim up the + * start of previous fragment so this fragment is + * used. + * + * See: + * DefragBsdSubsequentOverlapsStartOfOriginal. + */ + if (frag_offset <= prev->offset && frag_end > prev->offset + prev->ltrim) { + uint16_t prev_ltrim = frag_end - prev->offset; + if (prev_ltrim > prev->ltrim) { + prev->ltrim = prev_ltrim; + } + } + if ((next != NULL) && (frag_end > next->offset)) { next->ltrim = frag_end - next->offset; } - if ((frag_offset < prev->offset) && - (frag_end >= prev->offset + prev->data_len)) { - prev->skip = 1; - } + goto insert; } break; @@ -861,6 +905,9 @@ DefragInsertFrag(ThreadVars *tv, DecodeThreadVars *dtv, DefragTracker *tracker, #ifdef DEBUG new->pcap_cnt = pcap_cnt; #endif + if (frag_offset == 0) { + tracker->datalink = p->datalink; + } IP_FRAGMENTS_RB_INSERT(&tracker->fragment_tree, new); @@ -1093,8 +1140,8 @@ void DefragDestroy(void) * Allocate a test packet. Nothing to fancy, just a simple IP packet * with some payload of no particular protocol. */ -static Packet *BuildTestPacket(uint8_t proto, uint16_t id, uint16_t off, int mf, - const char content, int content_len) +static Packet *BuildIpv4TestPacket( + uint8_t proto, uint16_t id, uint16_t off, int mf, const char content, int content_len) { Packet *p = NULL; int hlen = 20; @@ -1167,8 +1214,79 @@ error: return NULL; } -static Packet *IPV6BuildTestPacket(uint8_t proto, uint32_t id, uint16_t off, - int mf, const char content, int content_len) +/** + * Allocate a test packet, much like BuildIpv4TestPacket, but with + * the full content provided by the caller. + */ +static Packet *BuildIpv4TestPacketWithContent( + uint8_t proto, uint16_t id, uint16_t off, int mf, const uint8_t *content, int content_len) +{ + Packet *p = NULL; + int hlen = 20; + int ttl = 64; + IPV4Hdr ip4h; + + p = SCCalloc(1, sizeof(*p) + default_packet_size); + if (unlikely(p == NULL)) + return NULL; + + PacketInit(p); + + struct timeval tval; + gettimeofday(&tval, NULL); + p->ts = SCTIME_FROM_TIMEVAL(&tval); + ip4h.ip_verhl = 4 << 4; + ip4h.ip_verhl |= hlen >> 2; + ip4h.ip_len = htons(hlen + content_len); + ip4h.ip_id = htons(id); + if (mf) + ip4h.ip_off = htons(IP_MF | off); + else + ip4h.ip_off = htons(off); + ip4h.ip_ttl = ttl; + ip4h.ip_proto = proto; + + ip4h.s_ip_src.s_addr = 0x01010101; /* 1.1.1.1 */ + ip4h.s_ip_dst.s_addr = 0x02020202; /* 2.2.2.2 */ + + /* copy content_len crap, we need full length */ + PacketCopyData(p, (uint8_t *)&ip4h, sizeof(ip4h)); + p->ip4h = (IPV4Hdr *)GET_PKT_DATA(p); + SET_IPV4_SRC_ADDR(p, &p->src); + SET_IPV4_DST_ADDR(p, &p->dst); + + PacketCopyDataOffset(p, hlen, content, content_len); + SET_PKT_LEN(p, hlen + content_len); + + p->ip4h->ip_csum = IPV4Checksum((uint16_t *)GET_PKT_DATA(p), hlen, 0); + + /* Self test. */ + if (IPV4_GET_VER(p) != 4) + goto error; + if (IPV4_GET_HLEN(p) != hlen) + goto error; + if (IPV4_GET_IPLEN(p) != hlen + content_len) + goto error; + if (IPV4_GET_IPID(p) != id) + goto error; + if (IPV4_GET_IPOFFSET(p) != off) + goto error; + if (IPV4_GET_MF(p) != mf) + goto error; + if (IPV4_GET_IPTTL(p) != ttl) + goto error; + if (IPV4_GET_IPPROTO(p) != proto) + goto error; + + return p; +error: + if (p != NULL) + SCFree(p); + return NULL; +} + +static Packet *BuildIpv6TestPacket( + uint8_t proto, uint32_t id, uint16_t off, int mf, const uint8_t content, int content_len) { Packet *p = NULL; uint8_t *pcontent; @@ -1238,6 +1356,71 @@ error: return NULL; } +static Packet *BuildIpv6TestPacketWithContent( + uint8_t proto, uint32_t id, uint16_t off, int mf, const uint8_t *content, int content_len) +{ + Packet *p = NULL; + IPV6Hdr ip6h; + + p = SCCalloc(1, sizeof(*p) + default_packet_size); + if (unlikely(p == NULL)) + return NULL; + + PacketInit(p); + + struct timeval tval; + gettimeofday(&tval, NULL); + p->ts = SCTIME_FROM_TIMEVAL(&tval); + + ip6h.s_ip6_nxt = 44; + ip6h.s_ip6_hlim = 2; + + /* Source and dest address - very bogus addresses. */ + ip6h.s_ip6_src[0] = 0x01010101; + ip6h.s_ip6_src[1] = 0x01010101; + ip6h.s_ip6_src[2] = 0x01010101; + ip6h.s_ip6_src[3] = 0x01010101; + ip6h.s_ip6_dst[0] = 0x02020202; + ip6h.s_ip6_dst[1] = 0x02020202; + ip6h.s_ip6_dst[2] = 0x02020202; + ip6h.s_ip6_dst[3] = 0x02020202; + + /* copy content_len crap, we need full length */ + PacketCopyData(p, (uint8_t *)&ip6h, sizeof(IPV6Hdr)); + + p->ip6h = (IPV6Hdr *)GET_PKT_DATA(p); + IPV6_SET_RAW_VER(p->ip6h, 6); + /* Fragmentation header. */ + IPV6FragHdr *fh = (IPV6FragHdr *)(GET_PKT_DATA(p) + sizeof(IPV6Hdr)); + fh->ip6fh_nxt = proto; + fh->ip6fh_ident = htonl(id); + fh->ip6fh_offlg = htons((off << 3) | mf); + + DecodeIPV6FragHeader(p, (uint8_t *)fh, 8, 8 + content_len, 0); + + PacketCopyDataOffset(p, sizeof(IPV6Hdr) + sizeof(IPV6FragHdr), content, content_len); + SET_PKT_LEN(p, sizeof(IPV6Hdr) + sizeof(IPV6FragHdr) + content_len); + + p->ip6h->s_ip6_plen = htons(sizeof(IPV6FragHdr) + content_len); + + SET_IPV6_SRC_ADDR(p, &p->src); + SET_IPV6_DST_ADDR(p, &p->dst); + + /* Self test. */ + if (IPV6_GET_VER(p) != 6) + goto error; + if (IPV6_GET_NH(p) != 44) + goto error; + if (IPV6_GET_PLEN(p) != sizeof(IPV6FragHdr) + content_len) + goto error; + + return p; +error: + if (p != NULL) + SCFree(p); + return NULL; +} + /** * Test the simplest possible re-assembly scenario. All packet in * order and no overlaps. @@ -1251,11 +1434,11 @@ static int DefragInOrderSimpleTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = BuildTestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); + p3 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p1) != NULL); @@ -1303,11 +1486,11 @@ static int DefragReverseSimpleTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = BuildTestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); + p3 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p3) != NULL); @@ -1347,7 +1530,7 @@ static int DefragReverseSimpleTest(void) * Test the simplest possible re-assembly scenario. All packet in * order and no overlaps. */ -static int IPV6DefragInOrderSimpleTest(void) +static int DefragInOrderSimpleIpv6Test(void) { Packet *p1 = NULL, *p2 = NULL, *p3 = NULL; Packet *reassembled = NULL; @@ -1356,11 +1539,11 @@ static int IPV6DefragInOrderSimpleTest(void) DefragInit(); - p1 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); + p1 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); + p2 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); + p3 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p1) != NULL); @@ -1394,7 +1577,7 @@ static int IPV6DefragInOrderSimpleTest(void) PASS; } -static int IPV6DefragReverseSimpleTest(void) +static int DefragReverseSimpleIpv6Test(void) { DefragContext *dc = NULL; Packet *p1 = NULL, *p2 = NULL, *p3 = NULL; @@ -1407,11 +1590,11 @@ static int IPV6DefragReverseSimpleTest(void) dc = DefragContextNew(); FAIL_IF_NULL(dc); - p1 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); + p1 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); + p2 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); + p3 = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF(Defrag(NULL, NULL, p3) != NULL); @@ -1444,8 +1627,7 @@ static int IPV6DefragReverseSimpleTest(void) PASS; } -static int DefragDoSturgesNovakTest(int policy, u_char *expected, - size_t expected_len) +static int DefragDoSturgesNovakTest(int policy, uint8_t *expected, size_t expected_len) { int i; @@ -1463,60 +1645,60 @@ static int DefragDoSturgesNovakTest(int policy, u_char *expected, * Original fragments. */ - /* A*24 at 0. */ - packets[0] = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); + /* <1> A*24 at 0. */ + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); - /* B*15 at 32. */ - packets[1] = BuildTestPacket(IPPROTO_ICMP, id, 32 >> 3, 1, 'B', 16); + /* <2> B*16 at 32. */ + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 32 >> 3, 1, 'B', 16); - /* C*24 at 48. */ - packets[2] = BuildTestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'C', 24); + /* <3> C*24 at 48. */ + packets[2] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'C', 24); - /* D*8 at 80. */ - packets[3] = BuildTestPacket(IPPROTO_ICMP, id, 80 >> 3, 1, 'D', 8); + /* <3_1> D*8 at 80. */ + packets[3] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 80 >> 3, 1, 'D', 8); - /* E*16 at 104. */ - packets[4] = BuildTestPacket(IPPROTO_ICMP, id, 104 >> 3, 1, 'E', 16); + /* <3_2> E*16 at 104. */ + packets[4] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 104 >> 3, 1, 'E', 16); - /* F*24 at 120. */ - packets[5] = BuildTestPacket(IPPROTO_ICMP, id, 120 >> 3, 1, 'F', 24); + /* <3_3> F*24 at 120. */ + packets[5] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 120 >> 3, 1, 'F', 24); - /* G*16 at 144. */ - packets[6] = BuildTestPacket(IPPROTO_ICMP, id, 144 >> 3, 1, 'G', 16); + /* <3_4> G*16 at 144. */ + packets[6] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 144 >> 3, 1, 'G', 16); - /* H*16 at 160. */ - packets[7] = BuildTestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'H', 16); + /* <3_5> H*16 at 160. */ + packets[7] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'H', 16); - /* I*8 at 176. */ - packets[8] = BuildTestPacket(IPPROTO_ICMP, id, 176 >> 3, 1, 'I', 8); + /* <3_6> I*8 at 176. */ + packets[8] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 176 >> 3, 1, 'I', 8); /* * Overlapping subsequent fragments. */ - /* J*32 at 8. */ - packets[9] = BuildTestPacket(IPPROTO_ICMP, id, 8 >> 3, 1, 'J', 32); + /* <4> J*32 at 8. */ + packets[9] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 8 >> 3, 1, 'J', 32); - /* K*24 at 48. */ - packets[10] = BuildTestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'K', 24); + /* <5> K*24 at 48. */ + packets[10] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 48 >> 3, 1, 'K', 24); - /* L*24 at 72. */ - packets[11] = BuildTestPacket(IPPROTO_ICMP, id, 72 >> 3, 1, 'L', 24); + /* <6> L*24 at 72. */ + packets[11] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 72 >> 3, 1, 'L', 24); - /* M*24 at 96. */ - packets[12] = BuildTestPacket(IPPROTO_ICMP, id, 96 >> 3, 1, 'M', 24); + /* <7> M*24 at 96. */ + packets[12] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 96 >> 3, 1, 'M', 24); - /* N*8 at 128. */ - packets[13] = BuildTestPacket(IPPROTO_ICMP, id, 128 >> 3, 1, 'N', 8); + /* <8> N*8 at 128. */ + packets[13] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 128 >> 3, 1, 'N', 8); - /* O*8 at 152. */ - packets[14] = BuildTestPacket(IPPROTO_ICMP, id, 152 >> 3, 1, 'O', 8); + /* <9> O*8 at 152. */ + packets[14] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 152 >> 3, 1, 'O', 8); - /* P*8 at 160. */ - packets[15] = BuildTestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'P', 8); + /* <10> P*8 at 160. */ + packets[15] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 160 >> 3, 1, 'P', 8); - /* Q*16 at 176. */ - packets[16] = BuildTestPacket(IPPROTO_ICMP, id, 176 >> 3, 0, 'Q', 16); + /* <11> Q*16 at 176. */ + packets[16] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 176 >> 3, 0, 'Q', 16); default_policy = policy; @@ -1542,8 +1724,15 @@ static int DefragDoSturgesNovakTest(int policy, u_char *expected, FAIL_IF(IPV4_GET_HLEN(reassembled) != 20); FAIL_IF(IPV4_GET_IPLEN(reassembled) != 20 + 192); - - FAIL_IF(memcmp(GET_PKT_DATA(reassembled) + 20, expected, expected_len) != 0); + FAIL_IF(expected_len != 192); + + if (memcmp(expected, GET_PKT_DATA(reassembled) + 20, expected_len) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, expected_len); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(reassembled) + 20, GET_PKT_LEN(reassembled) - 20); + FAIL; + } SCFree(reassembled); /* Make sure all frags were returned back to the pool. */ @@ -1556,8 +1745,7 @@ static int DefragDoSturgesNovakTest(int policy, u_char *expected, PASS; } -static int IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, - size_t expected_len) +static int DefragDoSturgesNovakIpv6Test(int policy, uint8_t *expected, size_t expected_len) { int i; @@ -1575,60 +1763,60 @@ static int IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, * Original fragments. */ - /* A*24 at 0. */ - packets[0] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 24); + /* <1> A*24 at 0. */ + packets[0] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 0, 1, 'A', 24); - /* B*15 at 32. */ - packets[1] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 32 >> 3, 1, 'B', 16); + /* <2> B*16 at 32. */ + packets[1] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 32 >> 3, 1, 'B', 16); - /* C*24 at 48. */ - packets[2] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'C', 24); + /* <3> C*24 at 48. */ + packets[2] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'C', 24); - /* D*8 at 80. */ - packets[3] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 80 >> 3, 1, 'D', 8); + /* <3_1> D*8 at 80. */ + packets[3] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 80 >> 3, 1, 'D', 8); - /* E*16 at 104. */ - packets[4] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 104 >> 3, 1, 'E', 16); + /* <3_2> E*16 at 104. */ + packets[4] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 104 >> 3, 1, 'E', 16); - /* F*24 at 120. */ - packets[5] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 120 >> 3, 1, 'F', 24); + /* <3_3> F*24 at 120. */ + packets[5] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 120 >> 3, 1, 'F', 24); - /* G*16 at 144. */ - packets[6] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 144 >> 3, 1, 'G', 16); + /* <3_4> G*16 at 144. */ + packets[6] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 144 >> 3, 1, 'G', 16); - /* H*16 at 160. */ - packets[7] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'H', 16); + /* <3_5> H*16 at 160. */ + packets[7] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'H', 16); - /* I*8 at 176. */ - packets[8] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 1, 'I', 8); + /* <3_6> I*8 at 176. */ + packets[8] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 1, 'I', 8); /* * Overlapping subsequent fragments. */ - /* J*32 at 8. */ - packets[9] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 8 >> 3, 1, 'J', 32); + /* <4> J*32 at 8. */ + packets[9] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 8 >> 3, 1, 'J', 32); - /* K*24 at 48. */ - packets[10] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'K', 24); + /* <5> K*24 at 48. */ + packets[10] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 48 >> 3, 1, 'K', 24); - /* L*24 at 72. */ - packets[11] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 72 >> 3, 1, 'L', 24); + /* <6> L*24 at 72. */ + packets[11] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 72 >> 3, 1, 'L', 24); - /* M*24 at 96. */ - packets[12] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 96 >> 3, 1, 'M', 24); + /* <7> M*24 at 96. */ + packets[12] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 96 >> 3, 1, 'M', 24); - /* N*8 at 128. */ - packets[13] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 128 >> 3, 1, 'N', 8); + /* <8> N*8 at 128. */ + packets[13] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 128 >> 3, 1, 'N', 8); - /* O*8 at 152. */ - packets[14] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 152 >> 3, 1, 'O', 8); + /* <9> O*8 at 152. */ + packets[14] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 152 >> 3, 1, 'O', 8); - /* P*8 at 160. */ - packets[15] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'P', 8); + /* <10> P*8 at 160. */ + packets[15] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 160 >> 3, 1, 'P', 8); - /* Q*16 at 176. */ - packets[16] = IPV6BuildTestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 0, 'Q', 16); + /* <11> Q*16 at 176. */ + packets[16] = BuildIpv6TestPacket(IPPROTO_ICMPV6, id, 176 >> 3, 0, 'Q', 16); default_policy = policy; @@ -1667,35 +1855,61 @@ static int IPV6DefragDoSturgesNovakTest(int policy, u_char *expected, PASS; } +/* Define data that matches the naming "Target-Based Fragmentation + * Reassembly". + * + * For example, the data refers to a fragment of data as <1>, or <3_6> + * and uses these to diagram the input fragments and the resulting + * policies. We build test cases for the papers scenario but assign + * specific values to each segment. + */ +#define D_1 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A' +#define D_2 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B' +#define D_3 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C' +#define D_3_1 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D' +#define D_3_2 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E' +#define D_3_3 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F' +#define D_3_4 'G', 'G', 'G', 'G', 'G', 'G', 'G', 'G' +#define D_3_5 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'H' +#define D_3_6 'I', 'I', 'I', 'I', 'I', 'I', 'I', 'I' +#define D_4 'J', 'J', 'J', 'J', 'J', 'J', 'J', 'J' +#define D_5 'K', 'K', 'K', 'K', 'K', 'K', 'K', 'K' +#define D_6 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L' +#define D_7 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M' +#define D_8 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N' +#define D_9 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O' +#define D_10 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P' +#define D_11 'Q', 'Q', 'Q', 'Q', 'Q', 'Q', 'Q', 'Q' + static int DefragSturgesNovakBsdTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_BSD, expected, @@ -1703,69 +1917,68 @@ DefragSturgesNovakBsdTest(void) PASS; } -static int IPV6DefragSturgesNovakBsdTest(void) +static int DefragSturgesNovakBsdIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_BSD, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_BSD, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakLinuxIpv4Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_10, + D_3_5, + D_11, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_LINUX, expected, @@ -1773,69 +1986,68 @@ static int DefragSturgesNovakLinuxIpv4Test(void) PASS; } -static int IPV6DefragSturgesNovakLinuxTest(void) +static int DefragSturgesNovakLinuxIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_10, + D_3_5, + D_11, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_LINUX, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_LINUX, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakWindowsIpv4Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_WINDOWS, expected, @@ -1843,69 +2055,68 @@ static int DefragSturgesNovakWindowsIpv4Test(void) PASS; } -static int IPV6DefragSturgesNovakWindowsTest(void) +static int DefragSturgesNovakWindowsIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_WINDOWS, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_WINDOWS, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakSolarisTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_SOLARIS, expected, @@ -1913,69 +2124,68 @@ static int DefragSturgesNovakSolarisTest(void) PASS; } -static int IPV6DefragSturgesNovakSolarisTest(void) +static int DefragSturgesNovakSolarisIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_SOLARIS, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_SOLARIS, expected, sizeof(expected))); PASS; } static int DefragSturgesNovakFirstTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "DDDDDDDD" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_3_1, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_FIRST, expected, @@ -1983,69 +2193,68 @@ static int DefragSturgesNovakFirstTest(void) PASS; } -static int IPV6DefragSturgesNovakFirstTest(void) +static int DefragSturgesNovakFirstIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "JJJJJJJJ" - "BBBBBBBB" - "BBBBBBBB" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "LLLLLLLL" - "DDDDDDDD" - "LLLLLLLL" - "MMMMMMMM" - "EEEEEEEE" - "EEEEEEEE" - "FFFFFFFF" - "FFFFFFFF" - "FFFFFFFF" - "GGGGGGGG" - "GGGGGGGG" - "HHHHHHHH" - "HHHHHHHH" - "IIIIIIII" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_1, + D_1, + D_4, + D_2, + D_2, + D_3, + D_3, + D_3, + D_6, + D_3_1, + D_6, + D_7, + D_3_2, + D_3_2, + D_3_3, + D_3_3, + D_3_3, + D_3_4, + D_3_4, + D_3_5, + D_3_5, + D_3_6, + D_11, }; - return IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_FIRST, expected, - sizeof(expected)); + return DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_FIRST, expected, sizeof(expected)); } static int DefragSturgesNovakLastTest(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "NNNNNNNN" - "FFFFFFFF" - "GGGGGGGG" - "OOOOOOOO" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_4, + D_4, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_8, + D_3_3, + D_3_4, + D_9, + D_10, + D_3_5, + D_11, + D_11, }; FAIL_IF_NOT(DefragDoSturgesNovakTest(DEFRAG_POLICY_LAST, expected, @@ -2053,38 +2262,37 @@ DefragSturgesNovakLastTest(void) PASS; } -static int IPV6DefragSturgesNovakLastTest(void) +static int DefragSturgesNovakLastIpv6Test(void) { /* Expected data. */ - u_char expected[] = { - "AAAAAAAA" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "JJJJJJJJ" - "BBBBBBBB" - "KKKKKKKK" - "KKKKKKKK" - "KKKKKKKK" - "LLLLLLLL" - "LLLLLLLL" - "LLLLLLLL" - "MMMMMMMM" - "MMMMMMMM" - "MMMMMMMM" - "FFFFFFFF" - "NNNNNNNN" - "FFFFFFFF" - "GGGGGGGG" - "OOOOOOOO" - "PPPPPPPP" - "HHHHHHHH" - "QQQQQQQQ" - "QQQQQQQQ" + uint8_t expected[] = { + D_1, + D_4, + D_4, + D_4, + D_4, + D_2, + D_5, + D_5, + D_5, + D_6, + D_6, + D_6, + D_7, + D_7, + D_7, + D_3_3, + D_8, + D_3_3, + D_3_4, + D_9, + D_10, + D_3_5, + D_11, + D_11, }; - FAIL_IF_NOT(IPV6DefragDoSturgesNovakTest(DEFRAG_POLICY_LAST, expected, - sizeof(expected))); + FAIL_IF_NOT(DefragDoSturgesNovakIpv6Test(DEFRAG_POLICY_LAST, expected, sizeof(expected))); PASS; } @@ -2099,7 +2307,7 @@ static int DefragTimeoutTest(void) /* Load in 16 packets. */ for (i = 0; i < 16; i++) { - Packet *p = BuildTestPacket(IPPROTO_ICMP,i, 0, 1, 'A' + i, 16); + Packet *p = BuildIpv4TestPacket(IPPROTO_ICMP, i, 0, 1, 'A' + i, 16); FAIL_IF_NULL(p); Packet *tp = Defrag(NULL, NULL, p); @@ -2109,7 +2317,7 @@ static int DefragTimeoutTest(void) /* Build a new packet but push the timestamp out by our timeout. * This should force our previous fragments to be timed out. */ - Packet *p = BuildTestPacket(IPPROTO_ICMP, 99, 0, 1, 'A' + i, 16); + Packet *p = BuildIpv4TestPacket(IPPROTO_ICMP, 99, 0, 1, 'A' + i, 16); FAIL_IF_NULL(p); p->ts = SCTIME_ADD_SECS(p->ts, defrag_context->timeout + 1); @@ -2134,7 +2342,7 @@ static int DefragTimeoutTest(void) * fail. The fix was simple, but this unit test is just to make sure * its not introduced. */ -static int DefragIPv4NoDataTest(void) +static int DefragNoDataIpv4Test(void) { DefragContext *dc = NULL; Packet *p = NULL; @@ -2146,7 +2354,7 @@ static int DefragIPv4NoDataTest(void) FAIL_IF_NULL(dc); /* This packet has an offset > 0, more frags set to 0 and no data. */ - p = BuildTestPacket(IPPROTO_ICMP, id, 1, 0, 'A', 0); + p = BuildIpv4TestPacket(IPPROTO_ICMP, id, 1, 0, 'A', 0); FAIL_IF_NULL(p); /* We do not expect a packet returned. */ @@ -2163,7 +2371,7 @@ static int DefragIPv4NoDataTest(void) PASS; } -static int DefragIPv4TooLargeTest(void) +static int DefragTooLargeIpv4Test(void) { DefragContext *dc = NULL; Packet *p = NULL; @@ -2175,7 +2383,7 @@ static int DefragIPv4TooLargeTest(void) /* Create a fragment that would extend past the max allowable size * for an IPv4 packet. */ - p = BuildTestPacket(IPPROTO_ICMP, 1, 8183, 0, 'A', 71); + p = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 8183, 0, 'A', 71); FAIL_IF_NULL(p); /* We do not expect a packet returned. */ @@ -2206,9 +2414,9 @@ static int DefragVlanTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); FAIL_IF_NULL(p2); /* With no VLAN IDs set, packets should re-assemble. */ @@ -2238,9 +2446,9 @@ static int DefragVlanQinQTest(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); FAIL_IF_NULL(p2); /* With no VLAN IDs set, packets should re-assemble. */ @@ -2272,9 +2480,9 @@ static int DefragVlanQinQinQTest(void) DefragInit(); - Packet *p1 = BuildTestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); + Packet *p1 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - Packet *p2 = BuildTestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); + Packet *p2 = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 1, 0, 'B', 8); FAIL_IF_NULL(p2); /* With no VLAN IDs set, packets should re-assemble. */ @@ -2308,7 +2516,7 @@ static int DefragTrackerReuseTest(void) /* Build a packet, its not a fragment but shouldn't matter for * this test. */ - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 0, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 0, 'A', 8); FAIL_IF_NULL(p1); /* Get a tracker. It shouldn't look like its already in use. */ @@ -2355,9 +2563,9 @@ static int DefragMfIpv4Test(void) DefragInit(); - Packet *p1 = BuildTestPacket(IPPROTO_ICMP, ip_id, 2, 1, 'C', 8); - Packet *p2 = BuildTestPacket(IPPROTO_ICMP, ip_id, 0, 1, 'A', 8); - Packet *p3 = BuildTestPacket(IPPROTO_ICMP, ip_id, 1, 0, 'B', 8); + Packet *p1 = BuildIpv4TestPacket(IPPROTO_ICMP, ip_id, 2, 1, 'C', 8); + Packet *p2 = BuildIpv4TestPacket(IPPROTO_ICMP, ip_id, 0, 1, 'A', 8); + Packet *p3 = BuildIpv4TestPacket(IPPROTO_ICMP, ip_id, 1, 0, 'B', 8); FAIL_IF(p1 == NULL || p2 == NULL || p3 == NULL); p = Defrag(NULL, NULL, p1); @@ -2374,6 +2582,10 @@ static int DefragMfIpv4Test(void) * fragments should be in the re-assembled packet. */ FAIL_IF(IPV4_GET_IPLEN(p) != 36); + /* Verify the payload of the IPv4 packet. */ + uint8_t expected_payload[] = "AAAAAAAABBBBBBBB"; + FAIL_IF(memcmp(GET_PKT_DATA(p) + sizeof(IPV4Hdr), expected_payload, sizeof(expected_payload))); + SCFree(p1); SCFree(p2); SCFree(p3); @@ -2398,9 +2610,9 @@ static int DefragMfIpv6Test(void) DefragInit(); - Packet *p1 = IPV6BuildTestPacket(IPPROTO_ICMPV6, ip_id, 2, 1, 'C', 8); - Packet *p2 = IPV6BuildTestPacket(IPPROTO_ICMPV6, ip_id, 0, 1, 'A', 8); - Packet *p3 = IPV6BuildTestPacket(IPPROTO_ICMPV6, ip_id, 1, 0, 'B', 8); + Packet *p1 = BuildIpv6TestPacket(IPPROTO_ICMPV6, ip_id, 2, 1, 'C', 8); + Packet *p2 = BuildIpv6TestPacket(IPPROTO_ICMPV6, ip_id, 0, 1, 'A', 8); + Packet *p3 = BuildIpv6TestPacket(IPPROTO_ICMPV6, ip_id, 1, 0, 'B', 8); FAIL_IF(p1 == NULL || p2 == NULL || p3 == NULL); p = Defrag(NULL, NULL, p1); @@ -2417,6 +2629,10 @@ static int DefragMfIpv6Test(void) * of 2 fragments, so 16. */ FAIL_IF(IPV6_GET_PLEN(p) != 16); + /* Verify the payload of the IPv4 packet. */ + uint8_t expected_payload[] = "AAAAAAAABBBBBBBB"; + FAIL_IF(memcmp(GET_PKT_DATA(p) + sizeof(IPV6Hdr), expected_payload, sizeof(expected_payload))); + SCFree(p1); SCFree(p2); SCFree(p3); @@ -2436,11 +2652,11 @@ static int DefragTestBadProto(void) DefragInit(); - p1 = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); + p1 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 8); FAIL_IF_NULL(p1); - p2 = BuildTestPacket(IPPROTO_UDP, id, 1, 1, 'B', 8); + p2 = BuildIpv4TestPacket(IPPROTO_UDP, id, 1, 1, 'B', 8); FAIL_IF_NULL(p2); - p3 = BuildTestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); + p3 = BuildIpv4TestPacket(IPPROTO_ICMP, id, 2, 0, 'C', 3); FAIL_IF_NULL(p3); FAIL_IF_NOT_NULL(Defrag(NULL, NULL, p1)); @@ -2461,19 +2677,19 @@ static int DefragTestBadProto(void) */ static int DefragTestJeremyLinux(void) { - char expected[] = "AAAAAAAA" - "AAAAAAAA" - "AAAAAAAA" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "CCCCCCCC" - "BBBBBBBB" - "BBBBBBBB" - "DDDDDDDD" - "DDDDDD"; + uint8_t expected[] = "AAAAAAAA" + "AAAAAAAA" + "AAAAAAAA" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "CCCCCCCC" + "BBBBBBBB" + "BBBBBBBB" + "DDDDDDDD" + "DDDDDD"; DefragInit(); default_policy = DEFRAG_POLICY_LINUX; @@ -2482,10 +2698,10 @@ static int DefragTestJeremyLinux(void) Packet *packets[4]; int i = 0; - packets[0] = BuildTestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); - packets[1] = BuildTestPacket(IPPROTO_ICMP, id, 40 >> 3, 1, 'B', 48); - packets[2] = BuildTestPacket(IPPROTO_ICMP, id, 24 >> 3, 1, 'C', 48); - packets[3] = BuildTestPacket(IPPROTO_ICMP, id, 88 >> 3, 0, 'D', 14); + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 0, 1, 'A', 24); + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 40 >> 3, 1, 'B', 48); + packets[2] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 24 >> 3, 1, 'C', 48); + packets[3] = BuildIpv4TestPacket(IPPROTO_ICMP, id, 88 >> 3, 0, 'D', 14); Packet *r = Defrag(NULL, NULL, packets[0]); FAIL_IF_NOT_NULL(r); @@ -2510,6 +2726,401 @@ static int DefragTestJeremyLinux(void) PASS; } +/** + * | 0 | 8 | 16 | 24 | 32 | + * |----------|----------|----------|----------|----------| + * | AAAAAAAA | AAAAAAAA | + * | | BBBBBBBB | BBBBBBBB | | | + * | | | CCCCCCCC | CCCCCCCC | | + * | DDDDDDDD | | | | | + * + * | DDDDDDDD | BBBBBBBB | BBBBBBBB | CCCCCCCC | AAAAAAAA | + */ +static int DefragBsdFragmentAfterNoMfIpv4Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 24 >> 3, 0, 'A', 16); + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 8 >> 3, 1, 'B', 16); + packets[2] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 16 >> 3, 1, 'C', 16); + packets[3] = BuildIpv4TestPacket(IPPROTO_ICMP, 0x96, 0, 1, 'D', 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + uint8_t expected[] = { + 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 20, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 20, GET_PKT_LEN(r) - 20); + FAIL; + } + + DefragDestroy(); + PASS; +} + +static int DefragBsdFragmentAfterNoMfIpv6Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + packets[0] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 24 >> 3, 0, 'A', 16); + packets[1] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 8 >> 3, 1, 'B', 16); + packets[2] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 16 >> 3, 1, 'C', 16); + packets[3] = BuildIpv6TestPacket(IPPROTO_ICMP, 0x96, 0, 1, 'D', 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + uint8_t expected[] = { + 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'D', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'B', 'B', 'B', 'B', 'B', 'B', 'B', 'B', + 'C', 'C', 'C', 'C', 'C', 'C', 'C', 'C', + 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 40, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 40, GET_PKT_LEN(r) - 40); + FAIL; + } + + DefragDestroy(); + PASS; +} + +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test_2(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + /* Packet 1: off=16, mf=1 */ + packets[0] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 6, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + /* Packet 2: off=8, mf=1 */ + packets[1] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 6, 8 >> 3, 1, (uint8_t *)"AACCBBDDAACCDDBB", 16); + + /* Packet 3: off=0, mf=1: IP and ICMP header. */ + packets[2] = BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 6, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + /* Packet 4: off=8, mf=1 */ + packets[3] = + BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 6, 32 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + // AACCBBDD + // AACCDDBB + // AABBDDCC + // DDCCBBAA + 'A', 'A', 'C', 'C', 'B', 'B', 'D', 'D', + 'A', 'A', 'C', 'C', 'D', 'D', 'B', 'B', + 'A', 'A', 'B', 'B', 'D', 'D', 'C', 'C', + 'D', 'D', 'C', 'C', 'B', 'B', 'A', 'A', + }; + // clang-format on + + FAIL_IF(memcmp(expected, GET_PKT_DATA(r) + 20 + 8, sizeof(expected)) != 0); + + DefragDestroy(); + PASS; +} + +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test_2(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[4]; + + /* Packet 1: off=16, mf=1 */ + packets[0] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 6, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + /* Packet 2: off=8, mf=1 */ + packets[1] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 6, 8 >> 3, 1, (uint8_t *)"AACCBBDDAACCDDBB", 16); + + /* Packet 3: off=0, mf=1: IP and ICMP header. */ + packets[2] = BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 6, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + /* Packet 4: off=8, mf=1 */ + packets[3] = + BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 6, 32 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + // AACCBBDD + // AACCDDBB + // AABBDDCC + // DDCCBBAA + 'A', 'A', 'C', 'C', 'B', 'B', 'D', 'D', + 'A', 'A', 'C', 'C', 'D', 'D', 'B', 'B', + 'A', 'A', 'B', 'B', 'D', 'D', 'C', 'C', + 'D', 'D', 'C', 'C', 'B', 'B', 'A', 'A', + }; + // clang-format on + + FAIL_IF(memcmp(expected, GET_PKT_DATA(r) + 40 + 8, sizeof(expected)) != 0); + + DefragDestroy(); + PASS; +} + +/** + * #### Input + * + * | 96 (0) | 104 (8) | 112 (16) | 120 (24) | + * |----------|----------|----------|----------| + * | | EEEEEEEE | EEEEEEEE | EEEEEEEE | + * | MMMMMMMM | MMMMMMMM | MMMMMMMM | | + * + * #### Expected Output + * + * | MMMMMMMM | MMMMMMMM | MMMMMMMM | EEEEEEEE | + */ +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[2]; + + packets[0] = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 8 >> 3, 0, 'E', 24); + packets[1] = BuildIpv4TestPacket(IPPROTO_ICMP, 1, 0, 1, 'M', 24); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 20, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 20, GET_PKT_LEN(r) - 20); + FAIL; + } + + PASS; +} + +static int DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[2]; + + packets[0] = BuildIpv6TestPacket(IPPROTO_ICMP, 1, 8 >> 3, 0, 'E', 24); + packets[1] = BuildIpv6TestPacket(IPPROTO_ICMP, 1, 0, 1, 'M', 24); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NULL(r); + + // clang-format off + const uint8_t expected[] = { + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'M', 'M', 'M', 'M', 'M', 'M', 'M', 'M', + 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', + }; + // clang-format on + + if (memcmp(expected, GET_PKT_DATA(r) + 40, sizeof(expected)) != 0) { + printf("Expected:\n"); + PrintRawDataFp(stdout, expected, sizeof(expected)); + printf("Got:\n"); + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 40, GET_PKT_LEN(r) - 40); + FAIL; + } + + PASS; +} + +/** + * Reassembly should fail. + * + * |0 |8 |16 |24 |32 |40 |48 | + * |========|========|========|========|========|========|========| + * | | |AABBCCDD|AABBDDCC| | | | + * | | | | | |AACCBBDD| | + * | |AACCDDBB|AADDBBCC| | | | | + * |ZZZZZZZZ| | | | | | | + * | | | | | | |DDCCBBAA| + */ +static int DefragBsdMissingFragmentIpv4Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[5]; + + packets[0] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 189, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + packets[1] = + BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 189, 40 >> 3, 1, (uint8_t *)"AACCBBDD", 8); + + packets[2] = BuildIpv4TestPacketWithContent( + IPPROTO_ICMP, 189, 8 >> 3, 1, (uint8_t *)"AACCDDBBAADDBBCC", 16); + + /* ICMP header. */ + packets[3] = BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 189, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + packets[4] = + BuildIpv4TestPacketWithContent(IPPROTO_ICMP, 189, 48 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[4]); + FAIL_IF_NOT_NULL(r); + +#if 0 + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 20, GET_PKT_LEN(r) - 20); +#endif + + for (int i = 0; i < 5; i++) { + SCFree(packets[i]); + } + + DefragDestroy(); + + PASS; +} + +static int DefragBsdMissingFragmentIpv6Test(void) +{ + DefragInit(); + default_policy = DEFRAG_POLICY_BSD; + Packet *packets[5]; + + packets[0] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 189, 16 >> 3, 1, (uint8_t *)"AABBCCDDAABBDDCC", 16); + + packets[1] = + BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 189, 40 >> 3, 1, (uint8_t *)"AACCBBDD", 8); + + packets[2] = BuildIpv6TestPacketWithContent( + IPPROTO_ICMP, 189, 8 >> 3, 1, (uint8_t *)"AACCDDBBAADDBBCC", 16); + + /* ICMP header. */ + packets[3] = BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 189, 0, 1, (uint8_t *)"ZZZZZZZZ", 8); + + packets[4] = + BuildIpv6TestPacketWithContent(IPPROTO_ICMP, 189, 48 >> 3, 0, (uint8_t *)"DDCCBBAA", 8); + + Packet *r = Defrag(NULL, NULL, packets[0]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[1]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[2]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[3]); + FAIL_IF_NOT_NULL(r); + + r = Defrag(NULL, NULL, packets[4]); + FAIL_IF_NOT_NULL(r); + +#if 0 + PrintRawDataFp(stdout, GET_PKT_DATA(r) + 40, GET_PKT_LEN(r) - 40); +#endif + + for (int i = 0; i < 5; i++) { + SCFree(packets[i]); + } + + DefragDestroy(); + + PASS; +} + #endif /* UNITTESTS */ void DefragRegisterTests(void) @@ -2527,23 +3138,17 @@ void DefragRegisterTests(void) UtRegisterTest("DefragSturgesNovakFirstTest", DefragSturgesNovakFirstTest); UtRegisterTest("DefragSturgesNovakLastTest", DefragSturgesNovakLastTest); - UtRegisterTest("DefragIPv4NoDataTest", DefragIPv4NoDataTest); - UtRegisterTest("DefragIPv4TooLargeTest", DefragIPv4TooLargeTest); - - UtRegisterTest("IPV6DefragInOrderSimpleTest", IPV6DefragInOrderSimpleTest); - UtRegisterTest("IPV6DefragReverseSimpleTest", IPV6DefragReverseSimpleTest); - UtRegisterTest("IPV6DefragSturgesNovakBsdTest", - IPV6DefragSturgesNovakBsdTest); - UtRegisterTest("IPV6DefragSturgesNovakLinuxTest", - IPV6DefragSturgesNovakLinuxTest); - UtRegisterTest("IPV6DefragSturgesNovakWindowsTest", - IPV6DefragSturgesNovakWindowsTest); - UtRegisterTest("IPV6DefragSturgesNovakSolarisTest", - IPV6DefragSturgesNovakSolarisTest); - UtRegisterTest("IPV6DefragSturgesNovakFirstTest", - IPV6DefragSturgesNovakFirstTest); - UtRegisterTest("IPV6DefragSturgesNovakLastTest", - IPV6DefragSturgesNovakLastTest); + UtRegisterTest("DefragNoDataIpv4Test", DefragNoDataIpv4Test); + UtRegisterTest("DefragTooLargeIpv4Test", DefragTooLargeIpv4Test); + + UtRegisterTest("DefragInOrderSimpleIpv6Test", DefragInOrderSimpleIpv6Test); + UtRegisterTest("DefragReverseSimpleIpv6Test", DefragReverseSimpleIpv6Test); + UtRegisterTest("DefragSturgesNovakBsdIpv6Test", DefragSturgesNovakBsdIpv6Test); + UtRegisterTest("DefragSturgesNovakLinuxIpv6Test", DefragSturgesNovakLinuxIpv6Test); + UtRegisterTest("DefragSturgesNovakWindowsIpv6Test", DefragSturgesNovakWindowsIpv6Test); + UtRegisterTest("DefragSturgesNovakSolarisIpv6Test", DefragSturgesNovakSolarisIpv6Test); + UtRegisterTest("DefragSturgesNovakFirstIpv6Test", DefragSturgesNovakFirstIpv6Test); + UtRegisterTest("DefragSturgesNovakLastIpv6Test", DefragSturgesNovakLastIpv6Test); UtRegisterTest("DefragVlanTest", DefragVlanTest); UtRegisterTest("DefragVlanQinQTest", DefragVlanQinQTest); @@ -2555,5 +3160,16 @@ void DefragRegisterTests(void) UtRegisterTest("DefragTestBadProto", DefragTestBadProto); UtRegisterTest("DefragTestJeremyLinux", DefragTestJeremyLinux); + + UtRegisterTest("DefragBsdFragmentAfterNoMfIpv4Test", DefragBsdFragmentAfterNoMfIpv4Test); + UtRegisterTest("DefragBsdFragmentAfterNoMfIpv6Test", DefragBsdFragmentAfterNoMfIpv6Test); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test", + DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test", + DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test_2", DefragBsdSubsequentOverlapsStartOfOriginalIpv4Test_2); + UtRegisterTest("DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test_2", DefragBsdSubsequentOverlapsStartOfOriginalIpv6Test_2); + UtRegisterTest("DefragBsdMissingFragmentIpv4Test", DefragBsdMissingFragmentIpv4Test); + UtRegisterTest("DefragBsdMissingFragmentIpv6Test", DefragBsdMissingFragmentIpv6Test); #endif /* UNITTESTS */ } diff --git a/src/defrag.h b/src/defrag.h index 11e6a61..93fe872 100644 --- a/src/defrag.h +++ b/src/defrag.h @@ -106,6 +106,7 @@ typedef struct DefragTracker_ { Address src_addr; /**< Source address for this tracker. */ Address dst_addr; /**< Destination address for this tracker. */ + int datalink; /**< datalink for reassembled packet, set by first fragment */ SCTime_t timeout; /**< When this tracker will timeout. */ uint32_t host_timeout; /**< Host timeout, statically assigned from the yaml */ diff --git a/src/detect-dataset.c b/src/detect-dataset.c index 3d29646..69eaf81 100644 --- a/src/detect-dataset.c +++ b/src/detect-dataset.c @@ -407,10 +407,6 @@ int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst SCLogError("failed to set up dataset '%s'.", name); return -1; } - if (set->hash && SC_ATOMIC_GET(set->hash->memcap_reached)) { - SCLogError("dataset too large for set memcap"); - return -1; - } cd = SCCalloc(1, sizeof(DetectDatasetData)); if (unlikely(cd == NULL)) diff --git a/src/detect-fast-pattern.c b/src/detect-fast-pattern.c index b82f327..ef6007a 100644 --- a/src/detect-fast-pattern.c +++ b/src/detect-fast-pattern.c @@ -274,6 +274,9 @@ static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const c } } } + if (SigMatchListSMBelongsTo(s, pm) == DETECT_SM_LIST_BASE64_DATA) { + SCLogInfo("fast_pattern is ineffective with base64_data"); + } cd->flags |= DETECT_CONTENT_FAST_PATTERN; return 0; } diff --git a/src/detect-http-server-body.c b/src/detect-http-server-body.c index 98f0ec5..28833a8 100644 --- a/src/detect-http-server-body.c +++ b/src/detect-http-server-body.c @@ -124,6 +124,9 @@ static int DetectHttpServerBodySetupSticky(DetectEngineCtx *de_ctx, Signature *s return -1; if (DetectSignatureSetAppProto(s, ALPROTO_HTTP) < 0) return -1; + // file data is on both directions, but we only take the one to client here + s->flags |= SIG_FLAG_TOCLIENT; + s->flags &= ~SIG_FLAG_TOSERVER; return 0; } diff --git a/src/detect-ipopts.c b/src/detect-ipopts.c index 105751c..01b4712 100644 --- a/src/detect-ipopts.c +++ b/src/detect-ipopts.c @@ -25,23 +25,13 @@ #include "suricata-common.h" #include "suricata.h" -#include "decode.h" #include "detect.h" #include "detect-parse.h" -#include "flow-var.h" -#include "decode-events.h" - -#include "util-debug.h" - #include "detect-ipopts.h" #include "util-unittest.h" -#define PARSE_REGEX "\\S[A-z]" - -static DetectParseRegex parse_regex; - static int DetectIpOptsMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *); static int DetectIpOptsSetup (DetectEngineCtx *, Signature *, const char *); @@ -64,7 +54,6 @@ void DetectIpOptsRegister (void) #ifdef UNITTESTS sigmatch_table[DETECT_IPOPTS].RegisterTests = IpOptsRegisterTests; #endif - DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); } /** @@ -173,11 +162,7 @@ static int DetectIpOptsMatch (DetectEngineThreadCtx *det_ctx, Packet *p, if (!de || !PKT_IS_IPV4(p) || PKT_IS_PSEUDOPKT(p)) return 0; - if (p->ip4vars.opts_set & de->ipopt) { - return 1; - } - - return 0; + return (p->ip4vars.opts_set & de->ipopt) == de->ipopt; } /** @@ -191,42 +176,30 @@ static int DetectIpOptsMatch (DetectEngineThreadCtx *det_ctx, Packet *p, */ static DetectIpOptsData *DetectIpOptsParse (const char *rawstr) { - int i; - DetectIpOptsData *de = NULL; - int found = 0; - - pcre2_match_data *match = NULL; - int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0); - if (ret < 1) { - SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr); - goto error; - } + if (rawstr == NULL || strlen(rawstr) == 0) + return NULL; + int i; + bool found = false; for(i = 0; ipopts[i].ipopt_name != NULL; i++) { if((strcasecmp(ipopts[i].ipopt_name,rawstr)) == 0) { - found = 1; + found = true; break; } } - if(found == 0) - goto error; + if (!found) { + SCLogError("unknown IP option specified \"%s\"", rawstr); + return NULL; + } - de = SCMalloc(sizeof(DetectIpOptsData)); + DetectIpOptsData *de = SCMalloc(sizeof(DetectIpOptsData)); if (unlikely(de == NULL)) - goto error; + return NULL; de->ipopt = ipopts[i].code; - pcre2_match_data_free(match); return de; - -error: - if (match) { - pcre2_match_data_free(match); - } - if (de) SCFree(de); - return NULL; } /** @@ -242,10 +215,8 @@ error: */ static int DetectIpOptsSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - DetectIpOptsData *de = NULL; SigMatch *sm = NULL; - - de = DetectIpOptsParse(rawstr); + DetectIpOptsData *de = DetectIpOptsParse(rawstr); if (de == NULL) goto error; @@ -275,8 +246,9 @@ error: */ void DetectIpOptsFree(DetectEngineCtx *de_ctx, void *de_ptr) { - DetectIpOptsData *de = (DetectIpOptsData *)de_ptr; - if(de) SCFree(de); + if (de_ptr) { + SCFree(de_ptr); + } } /* @@ -381,6 +353,20 @@ static int IpOptsTestParse04 (void) } /** + * \test IpOptsTestParse05 tests the NULL and empty string + */ +static int IpOptsTestParse05(void) +{ + DetectIpOptsData *de = DetectIpOptsParse(""); + FAIL_IF_NOT_NULL(de); + + de = DetectIpOptsParse(NULL); + FAIL_IF_NOT_NULL(de); + + PASS; +} + +/** * \brief this function registers unit tests for IpOpts */ void IpOptsRegisterTests(void) @@ -389,5 +375,6 @@ void IpOptsRegisterTests(void) UtRegisterTest("IpOptsTestParse02", IpOptsTestParse02); UtRegisterTest("IpOptsTestParse03", IpOptsTestParse03); UtRegisterTest("IpOptsTestParse04", IpOptsTestParse04); + UtRegisterTest("IpOptsTestParse05", IpOptsTestParse05); } #endif /* UNITTESTS */ diff --git a/src/detect-parse.c b/src/detect-parse.c index c3232b9..5dee7e6 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -2701,7 +2701,7 @@ int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, *match = pcre2_match_data_create_from_pattern(parse_regex->regex, NULL); if (*match) return pcre2_match(parse_regex->regex, (PCRE2_SPTR8)str, strlen(str), options, start_offset, - *match, NULL); + *match, parse_regex->context); return -1; } @@ -2761,8 +2761,16 @@ bool DetectSetupParseRegexesOpts(const char *parse_str, DetectParseRegex *detect parse_str, en, errbuffer); return false; } - detect_parse->match = pcre2_match_data_create_from_pattern(detect_parse->regex, NULL); + detect_parse->context = pcre2_match_context_create(NULL); + if (detect_parse->context == NULL) { + SCLogError("pcre2 could not create match context"); + pcre2_code_free(detect_parse->regex); + detect_parse->regex = NULL; + return false; + } + pcre2_set_match_limit(detect_parse->context, SC_MATCH_LIMIT_DEFAULT); + pcre2_set_recursion_limit(detect_parse->context, SC_MATCH_LIMIT_RECURSION_DEFAULT); DetectParseRegexAddToFreeList(detect_parse); return true; diff --git a/src/flow-timeout.c b/src/flow-timeout.c index e5d2794..6efa382 100644 --- a/src/flow-timeout.c +++ b/src/flow-timeout.c @@ -341,14 +341,20 @@ int FlowForceReassemblyNeedReassembly(Flow *f) * * The function requires flow to be locked beforehand. * + * Normally, the first thread_id value should be used. This is when the flow is + * created on seeing the first packet to the server; when the flow's reversed + * flag is set, choose the second thread_id (to client/source). + * * \param f Pointer to the flow. * * \retval 0 This flow doesn't need any reassembly processing; 1 otherwise. */ void FlowForceReassemblyForFlow(Flow *f) { - const int thread_id = (int)f->thread_id[0]; - TmThreadsInjectFlowById(f, thread_id); + // Choose the thread_id based on whether the flow has been + // reversed. + int idx = f->flags & FLOW_DIR_REVERSED ? 1 : 0; + TmThreadsInjectFlowById(f, (const int)f->thread_id[idx]); } /** @@ -291,6 +291,8 @@ void FlowSwap(Flow *f) FlowSwapFlags(f); FlowSwapFileFlags(f); + SWAP_VARS(FlowThreadId, f->thread_id[0], f->thread_id[1]); + if (f->proto == IPPROTO_TCP) { TcpStreamFlowSwap(f); } diff --git a/src/output-json-stats.c b/src/output-json-stats.c index 33f98af..7cc8807 100644 --- a/src/output-json-stats.c +++ b/src/output-json-stats.c @@ -265,23 +265,30 @@ json_t *StatsToJSON(const StatsTable *st, uint8_t flags) uint32_t x; for (x = 0; x < st->ntstats; x++) { uint32_t offset = x * st->nstats; - - // Stats for for this thread. - json_t *thread = json_object(); - if (unlikely(thread == NULL)) { - json_decref(js_stats); - json_decref(threads); - return NULL; - } + const char *tm_name = NULL; + json_t *thread = NULL; /* for each counter */ for (u = offset; u < (offset + st->nstats); u++) { if (st->tstats[u].name == NULL) continue; - // Seems this holds, but assert in debug builds. - DEBUG_VALIDATE_BUG_ON( - strcmp(st->tstats[offset].tm_name, st->tstats[u].tm_name) != 0); + DEBUG_VALIDATE_BUG_ON(st->tstats[u].tm_name == NULL); + + if (tm_name == NULL) { + // First time we see a set tm_name. Remember it + // and allocate the stats object for this thread. + tm_name = st->tstats[u].tm_name; + thread = json_object(); + if (unlikely(thread == NULL)) { + json_decref(js_stats); + json_decref(threads); + return NULL; + } + } else { + DEBUG_VALIDATE_BUG_ON(strcmp(tm_name, st->tstats[u].tm_name) != 0); + DEBUG_VALIDATE_BUG_ON(thread == NULL); + } json_t *js_type = NULL; const char *stat_name = st->tstats[u].short_name; @@ -303,7 +310,10 @@ json_t *StatsToJSON(const StatsTable *st, uint8_t flags) } } } - json_object_set_new(threads, st->tstats[offset].tm_name, thread); + if (tm_name != NULL) { + DEBUG_VALIDATE_BUG_ON(thread == NULL); + json_object_set_new(threads, tm_name, thread); + } } json_object_set_new(js_stats, "threads", threads); } diff --git a/src/runmode-af-packet.c b/src/runmode-af-packet.c index b8ad0bf..742d968 100644 --- a/src/runmode-af-packet.c +++ b/src/runmode-af-packet.c @@ -90,7 +90,6 @@ static int AFPRunModeIsIPS(void) SCLogError("Problem with config file"); return 0; } - const char *copymodestr = NULL; if_root = ConfFindDeviceConfig(af_packet_node, live_dev); if (if_root == NULL) { @@ -101,7 +100,10 @@ static int AFPRunModeIsIPS(void) if_root = if_default; } - if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1) { + const char *copymodestr = NULL; + const char *copyifacestr = NULL; + if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 && + ConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) { if (strcmp(copymodestr, "ips") == 0) { has_ips = 1; } else { diff --git a/src/runmode-dpdk.c b/src/runmode-dpdk.c index 1a240aa..fb49d6e 100644 --- a/src/runmode-dpdk.c +++ b/src/runmode-dpdk.c @@ -54,9 +54,12 @@ #define RSS_HKEY_LEN 40 // General purpose RSS key for symmetric bidirectional flow distribution -uint8_t rss_hkey[] = { 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, - 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, - 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A }; +uint8_t rss_hkey[] = { + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 40 + 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, 0x6D, 0x5A, // 52 +}; // Calculates the closest multiple of y from x #define ROUNDUP(x, y) ((((x) + ((y)-1)) / (y)) * (y)) @@ -111,6 +114,7 @@ static void *ParseDpdkConfigAndConfigureDevice(const char *iface); static void DPDKDerefConfig(void *conf); #define DPDK_CONFIG_DEFAULT_THREADS "auto" +#define DPDK_CONFIG_DEFAULT_INTERRUPT_MODE false #define DPDK_CONFIG_DEFAULT_MEMPOOL_SIZE 65535 #define DPDK_CONFIG_DEFAULT_MEMPOOL_CACHE_SIZE "auto" #define DPDK_CONFIG_DEFAULT_RX_DESCRIPTORS 1024 @@ -126,6 +130,7 @@ static void DPDKDerefConfig(void *conf); DPDKIfaceConfigAttributes dpdk_yaml = { .threads = "threads", + .irq_mode = "interrupt-mode", .promisc = "promisc", .multicast = "multicast", .checksum_checks = "checksum-checks", @@ -434,6 +439,15 @@ static int ConfigSetThreads(DPDKIfaceConfig *iconf, const char *entry_str) SCReturnInt(0); } +static bool ConfigSetInterruptMode(DPDKIfaceConfig *iconf, bool enable) +{ + SCEnter(); + if (enable) + iconf->flags |= DPDK_IRQ_MODE; + + SCReturnBool(true); +} + static int ConfigSetRxQueues(DPDKIfaceConfig *iconf, uint16_t nb_queues) { SCEnter(); @@ -704,6 +718,17 @@ static int ConfigLoad(DPDKIfaceConfig *iconf, const char *iface) if (retval < 0) SCReturnInt(retval); + bool irq_enable; + retval = ConfGetChildValueBoolWithDefault(if_root, if_default, dpdk_yaml.irq_mode, &entry_bool); + if (retval != 1) { + irq_enable = DPDK_CONFIG_DEFAULT_INTERRUPT_MODE; + } else { + irq_enable = entry_bool ? true : false; + } + retval = ConfigSetInterruptMode(iconf, irq_enable); + if (retval != true) + SCReturnInt(-EINVAL); + // currently only mapping "1 thread == 1 RX (and 1 TX queue in IPS mode)" is supported retval = ConfigSetRxQueues(iconf, (uint16_t)iconf->threads); if (retval < 0) @@ -846,7 +871,7 @@ static void DeviceSetPMDSpecificRSS(struct rte_eth_rss_conf *rss_conf, const cha if (strcmp(driver_name, "net_i40e") == 0) i40eDeviceSetRSSConf(rss_conf); if (strcmp(driver_name, "net_ice") == 0) - iceDeviceSetRSSHashFunction(&rss_conf->rss_hf); + iceDeviceSetRSSConf(rss_conf); if (strcmp(driver_name, "net_ixgbe") == 0) ixgbeDeviceSetRSSHashFunction(&rss_conf->rss_hf); if (strcmp(driver_name, "net_e1000_igb") == 0) @@ -1115,6 +1140,11 @@ static void DeviceInitPortConf(const DPDKIfaceConfig *iconf, }, }; + SCLogConfig("%s: interrupt mode is %s", iconf->iface, + iconf->flags & DPDK_IRQ_MODE ? "enabled" : "disabled"); + if (iconf->flags & DPDK_IRQ_MODE) + port_conf->intr_conf.rxq = 1; + // configure RX offloads if (dev_info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_RSS_HASH) { if (iconf->nb_rx_queues > 1) { @@ -1616,7 +1646,9 @@ static int DPDKRunModeIsIPS(void) } const char *copymodestr = NULL; - if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1) { + const char *copyifacestr = NULL; + if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 && + ConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) { if (strcmp(copymodestr, "ips") == 0) { has_ips = true; } else { diff --git a/src/runmode-dpdk.h b/src/runmode-dpdk.h index a00327b..152c1d6 100644 --- a/src/runmode-dpdk.h +++ b/src/runmode-dpdk.h @@ -25,6 +25,7 @@ typedef struct DPDKIfaceConfigAttributes_ { const char *threads; + const char *irq_mode; const char *promisc; const char *multicast; const char *checksum_checks; diff --git a/src/runmode-netmap.c b/src/runmode-netmap.c index e207cf0..947b381 100644 --- a/src/runmode-netmap.c +++ b/src/runmode-netmap.c @@ -81,7 +81,6 @@ static int NetmapRunModeIsIPS(void) SCLogError("Problem with config file"); return 0; } - const char *copymodestr = NULL; if_root = ConfNodeLookupKeyValue(netmap_node, "interface", live_dev); if (if_root == NULL) { @@ -92,7 +91,10 @@ static int NetmapRunModeIsIPS(void) if_root = if_default; } - if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1) { + const char *copymodestr = NULL; + const char *copyifacestr = NULL; + if (ConfGetChildValueWithDefault(if_root, if_default, "copy-mode", ©modestr) == 1 && + ConfGetChildValue(if_root, "copy-iface", ©ifacestr) == 1) { if (strcmp(copymodestr, "ips") == 0) { has_ips = 1; } else { diff --git a/src/runmodes.c b/src/runmodes.c index 348adfa..d4b57df 100644 --- a/src/runmodes.c +++ b/src/runmodes.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2022 Open Information Security Foundation +/* Copyright (C) 2007-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -70,6 +70,7 @@ #include "counters.h" #include "suricata-plugin.h" +#include "util-device.h" int debuglog_enabled = 0; int threading_set_cpu_affinity = FALSE; @@ -408,6 +409,14 @@ void RunModeEngineIsIPS(int capture_mode, const char *runmode, const char *captu if (mode->RunModeIsIPSEnabled != NULL) { mode->RunModeIsIPSEnabled(); + + if (EngineModeIsIPS()) { + extern uint16_t g_livedev_mask; + if (g_livedev_mask != 0 && LiveGetDeviceCount() > 0) { + SCLogWarning("disabling livedev.use-for-tracking with IPS mode. See ticket #6726."); + g_livedev_mask = 0; + } + } } } diff --git a/src/source-dpdk.c b/src/source-dpdk.c index cf26af5..6a7833e 100644 --- a/src/source-dpdk.c +++ b/src/source-dpdk.c @@ -93,6 +93,13 @@ TmEcode NoDPDKSupportExit(ThreadVars *tv, const void *initdata, void **data) #define BURST_SIZE 32 static struct timeval machine_start_time = { 0, 0 }; +// interrupt mode constants +#define MIN_ZERO_POLL_COUNT 10U +#define MIN_ZERO_POLL_COUNT_TO_SLEEP 10U +#define MINIMUM_SLEEP_TIME_US 1U +#define STANDARD_SLEEP_TIME_US 100U +#define MAX_EPOLL_TIMEOUT_MS 500U +static rte_spinlock_t intr_lock[RTE_MAX_ETHPORTS]; /** * \brief Structure to hold thread specific variables. @@ -104,6 +111,7 @@ typedef struct DPDKThreadVars_ { TmSlot *slot; LiveDevice *livedev; ChecksumValidationMode checksum_mode; + bool intr_enabled; /* references to packet and drop counters */ uint16_t capture_dpdk_packets; uint16_t capture_dpdk_rx_errs; @@ -142,6 +150,40 @@ static uint64_t CyclesToSeconds(uint64_t cycles); static void DPDKFreeMbufArray(struct rte_mbuf **mbuf_array, uint16_t mbuf_cnt, uint16_t offset); static uint64_t DPDKGetSeconds(void); +static bool InterruptsRXEnable(uint16_t port_id, uint16_t queue_id) +{ + uint32_t event_data = port_id << UINT16_WIDTH | queue_id; + int32_t ret = rte_eth_dev_rx_intr_ctl_q(port_id, queue_id, RTE_EPOLL_PER_THREAD, + RTE_INTR_EVENT_ADD, (void *)((uintptr_t)event_data)); + + if (ret != 0) { + SCLogError("%s-Q%d: failed to enable interrupt mode: %s", DPDKGetPortNameByPortID(port_id), + queue_id, rte_strerror(-ret)); + return false; + } + return true; +} + +static inline uint32_t InterruptsSleepHeuristic(uint32_t no_pkt_polls_count) +{ + if (no_pkt_polls_count < MIN_ZERO_POLL_COUNT_TO_SLEEP) + return MINIMUM_SLEEP_TIME_US; + + return STANDARD_SLEEP_TIME_US; +} + +static inline void InterruptsTurnOnOff(uint16_t port_id, uint16_t queue_id, bool on) +{ + rte_spinlock_lock(&(intr_lock[port_id])); + + if (on) + rte_eth_dev_rx_intr_enable(port_id, queue_id); + else + rte_eth_dev_rx_intr_disable(port_id, queue_id); + + rte_spinlock_unlock(&(intr_lock[port_id])); +} + static void DPDKFreeMbufArray(struct rte_mbuf **mbuf_array, uint16_t mbuf_cnt, uint16_t offset) { for (int i = offset; i < mbuf_cnt; i++) { @@ -377,6 +419,11 @@ static TmEcode ReceiveDPDKLoop(ThreadVars *tv, void *data, void *slot) rte_eth_stats_reset(ptv->port_id); rte_eth_xstats_reset(ptv->port_id); + + uint32_t pwd_zero_rx_packet_polls_count = 0; + if (ptv->intr_enabled && !InterruptsRXEnable(ptv->port_id, ptv->queue_id)) + SCReturnInt(TM_ECODE_FAILED); + while (1) { if (unlikely(suricata_ctl_flags != 0)) { SCLogDebug("Stopping Suricata!"); @@ -398,7 +445,27 @@ static TmEcode ReceiveDPDKLoop(ThreadVars *tv, void *data, void *slot) TmThreadsCaptureHandleTimeout(tv, NULL); last_timeout_msec = msecs; } - continue; + + if (!ptv->intr_enabled) + continue; + + pwd_zero_rx_packet_polls_count++; + if (pwd_zero_rx_packet_polls_count <= MIN_ZERO_POLL_COUNT) + continue; + + uint32_t pwd_idle_hint = InterruptsSleepHeuristic(pwd_zero_rx_packet_polls_count); + + if (pwd_idle_hint < STANDARD_SLEEP_TIME_US) { + rte_delay_us(pwd_idle_hint); + } else { + InterruptsTurnOnOff(ptv->port_id, ptv->queue_id, true); + struct rte_epoll_event event; + rte_epoll_wait(RTE_EPOLL_PER_THREAD, &event, 1, MAX_EPOLL_TIMEOUT_MS); + InterruptsTurnOnOff(ptv->port_id, ptv->queue_id, false); + continue; + } + } else if (ptv->intr_enabled && pwd_zero_rx_packet_polls_count) { + pwd_zero_rx_packet_polls_count = 0; } ptv->pkts += (uint64_t)nb_rx; @@ -522,6 +589,7 @@ static TmEcode ReceiveDPDKThreadInit(ThreadVars *tv, const void *initdata, void ptv->checksum_mode = dpdk_config->checksum_mode; ptv->threads = dpdk_config->threads; + ptv->intr_enabled = (dpdk_config->flags & DPDK_IRQ_MODE) ? true : false; ptv->port_id = dpdk_config->port_id; ptv->out_port_id = dpdk_config->out_port_id; ptv->port_socket_id = dpdk_config->socket_id; @@ -569,6 +637,9 @@ static TmEcode ReceiveDPDKThreadInit(ThreadVars *tv, const void *initdata, void "%s: unable to determine NIC's NUMA node, degraded performance can be expected", dpdk_config->iface); } + if (ptv->intr_enabled) { + rte_spinlock_init(&intr_lock[ptv->port_id]); + } } *data = (void *)ptv; diff --git a/src/source-dpdk.h b/src/source-dpdk.h index 3fdb63c..b962d86 100644 --- a/src/source-dpdk.h +++ b/src/source-dpdk.h @@ -38,6 +38,7 @@ typedef enum { DPDK_COPY_MODE_NONE, DPDK_COPY_MODE_TAP, DPDK_COPY_MODE_IPS } Dpd // General flags #define DPDK_PROMISC (1 << 0) /**< Promiscuous mode */ #define DPDK_MULTICAST (1 << 1) /**< Enable multicast packets */ +#define DPDK_IRQ_MODE (1 << 2) /**< Interrupt mode */ // Offloads #define DPDK_RX_CHECKSUM_OFFLOAD (1 << 4) /**< Enable chsum offload */ diff --git a/src/source-pcap-file-helper.c b/src/source-pcap-file-helper.c index 936b65f..4984a44 100644 --- a/src/source-pcap-file-helper.c +++ b/src/source-pcap-file-helper.c @@ -251,6 +251,7 @@ TmEcode ValidateLinkType(int datalink, DecoderFunc *DecoderFn) *DecoderFn = DecodePPP; break; case LINKTYPE_IPV4: + case LINKTYPE_IPV6: case LINKTYPE_RAW: case LINKTYPE_RAW2: case LINKTYPE_GRE_OVER_IP: diff --git a/src/tests/detect-http-client-body.c b/src/tests/detect-http-client-body.c index c87d667..bbeb4d3 100644 --- a/src/tests/detect-http-client-body.c +++ b/src/tests/detect-http-client-body.c @@ -157,6 +157,7 @@ static int RunTest (struct TestSteps *steps, const char *sig, const char *yaml) int i = 0; while (b->input != NULL) { SCLogDebug("chunk %p %d", b, i); + (void)i; Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); FAIL_IF_NULL(p); p->flow = &f; diff --git a/src/tests/detect-http-server-body.c b/src/tests/detect-http-server-body.c index 29340fb..d9723d4 100644 --- a/src/tests/detect-http-server-body.c +++ b/src/tests/detect-http-server-body.c @@ -119,6 +119,7 @@ static int RunTest(struct TestSteps *steps, const char *sig, const char *yaml) int i = 0; while (b->input != NULL) { SCLogDebug("chunk %p %d", b, i); + (void)i; Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); FAIL_IF_NULL(p); p->flow = &f; diff --git a/src/tests/output-json-stats.c b/src/tests/output-json-stats.c index ac1336e..332a819 100644 --- a/src/tests/output-json-stats.c +++ b/src/tests/output-json-stats.c @@ -23,7 +23,7 @@ static int OutputJsonStatsTest01(void) { - StatsRecord global_records[] = { { 0 }, { 0 } }; + StatsRecord total_records[] = { { 0 }, { 0 } }; StatsRecord thread_records[2]; thread_records[0].name = "capture.kernel_packets"; thread_records[0].short_name = "kernel_packets"; @@ -36,7 +36,7 @@ static int OutputJsonStatsTest01(void) StatsTable table = { .nstats = 2, - .stats = &global_records[0], + .stats = &total_records[0], .ntstats = 1, .tstats = &thread_records[0], }; @@ -64,7 +64,72 @@ static int OutputJsonStatsTest01(void) return cmp_result == 0; } +static int OutputJsonStatsTest02(void) +{ + StatsRecord total_records[4] = { 0 }; + StatsRecord thread_records[8] = { 0 }; + + // Totals + total_records[0].name = "tcp.syn"; + total_records[0].short_name = "syn"; + total_records[0].tm_name = NULL; + total_records[0].value = 1234; + + // Worker + // thread_records[0] is a global counter + thread_records[1].name = "capture.kernel_packets"; + thread_records[1].short_name = "kernel_packets"; + thread_records[1].tm_name = "W#01-bond0.30"; + thread_records[1].value = 42; + thread_records[2].name = "capture.kernel_drops"; + thread_records[2].short_name = "kernel_drops"; + thread_records[2].tm_name = "W#01-bond0.30"; + thread_records[2].value = 4711; + // thread_records[3] is a FM specific counter + + // Flow manager + // thread_records[4] is a global counter + // thread_records[5] is a worker specific counter + // thread_records[6] is a worker specific counter + thread_records[7].name = "flow.mgr.full_hash_passes"; + thread_records[7].short_name = "full_hash_passes"; + thread_records[7].tm_name = "FM#01"; + thread_records[7].value = 10; + + StatsTable table = { + .nstats = 4, + .stats = &total_records[0], + .ntstats = 2, + .tstats = &thread_records[0], + }; + + json_t *r = StatsToJSON(&table, JSON_STATS_TOTALS | JSON_STATS_THREADS); + if (!r) + return 0; + + // Remove variable content + json_object_del(r, "uptime"); + + char *serialized = json_dumps(r, 0); + + // Cheesy comparison + const char *expected = "{\"tcp\": {\"syn\": 1234}, \"threads\": {\"W#01-bond0.30\": " + "{\"capture\": {\"kernel_packets\": " + "42, \"kernel_drops\": 4711}}, \"FM#01\": {\"flow\": {\"mgr\": " + "{\"full_hash_passes\": 10}}}}}"; + + int cmp_result = strcmp(expected, serialized); + if (cmp_result != 0) + printf("unexpected result\nexpected=%s\ngot=%s\n", expected, serialized); + + free(serialized); + json_decref(r); + + return cmp_result == 0; +} + void OutputJsonStatsRegisterTests(void) { UtRegisterTest("OutputJsonStatsTest01", OutputJsonStatsTest01); + UtRegisterTest("OutputJsonStatsTest02", OutputJsonStatsTest02); } diff --git a/src/util-base64.c b/src/util-base64.c index 4a4a5d1..d973f0e 100644 --- a/src/util-base64.c +++ b/src/util-base64.c @@ -156,6 +156,8 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, ecode = BASE64_ECODE_BUF; break; } + if (dest_size - *decoded_bytes < ASCII_BLOCK) + return BASE64_ECODE_BUF; /* Decode base-64 block into ascii block and move pointer */ DecodeBase64Block(dptr, b64); @@ -183,7 +185,7 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, /* if the destination size is not at least 3 Bytes long, it'll give a dynamic * buffer overflow while decoding, so, return and let the caller take care of the * remaining bytes to be decoded which should always be < 4 at this stage */ - if (dest_size - *decoded_bytes < 3) + if (dest_size - *decoded_bytes < ASCII_BLOCK) return BASE64_ECODE_BUF; *decoded_bytes += numDecoded_blk; DecodeBase64Block(dptr, b64); @@ -193,6 +195,8 @@ Base64Ecode DecodeBase64(uint8_t *dest, uint32_t dest_size, const uint8_t *src, /* Finish remaining b64 bytes by padding */ if (valid && bbidx > 0 && (mode != BASE64_MODE_RFC2045)) { /* Decode remaining */ + if (dest_size - *decoded_bytes < ASCII_BLOCK) + return BASE64_ECODE_BUF; *decoded_bytes += ASCII_BLOCK - (B64_BLOCK - bbidx); DecodeBase64Block(dptr, b64); } diff --git a/src/util-dpdk-ice.c b/src/util-dpdk-ice.c index 36f4481..4b714d8 100644 --- a/src/util-dpdk-ice.c +++ b/src/util-dpdk-ice.c @@ -35,7 +35,7 @@ #ifdef HAVE_DPDK -void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) +static void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) { #if RTE_VERSION < RTE_VERSION_NUM(20, 0, 0, 0) *rss_hf = RTE_ETH_RSS_FRAG_IPV4 | RTE_ETH_RSS_NONFRAG_IPV4_OTHER | RTE_ETH_RSS_FRAG_IPV6 | @@ -46,6 +46,16 @@ void iceDeviceSetRSSHashFunction(uint64_t *rss_hf) #endif } +void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf) +{ + iceDeviceSetRSSHashFunction(&rss_conf->rss_hf); +#if RTE_VERSION < RTE_VERSION_NUM(23, 11, 0, 0) + rss_conf->rss_key_len = 40; +#else + rss_conf->rss_key_len = 52; +#endif +} + #endif /* HAVE_DPDK */ /** * @} diff --git a/src/util-dpdk-ice.h b/src/util-dpdk-ice.h index cdc2185..d535fa0 100644 --- a/src/util-dpdk-ice.h +++ b/src/util-dpdk-ice.h @@ -28,7 +28,9 @@ #ifdef HAVE_DPDK -void iceDeviceSetRSSHashFunction(uint64_t *rss_conf); +#include "util-dpdk.h" + +void iceDeviceSetRSSConf(struct rte_eth_rss_conf *rss_conf); #endif /* HAVE_DPDK */ diff --git a/src/util-host-info.c b/src/util-host-info.c index c0dd93b..282b2fb 100644 --- a/src/util-host-info.c +++ b/src/util-host-info.c @@ -44,7 +44,6 @@ int SCKernelVersionIsAtLeast(int major, int minor) PCRE2_SIZE eo; int ret; int kmajor, kminor; - PCRE2_UCHAR **list; /* get local version */ if (uname(&kuname) != 0) { @@ -79,25 +78,36 @@ int SCKernelVersionIsAtLeast(int major, int minor) goto error; } - pcre2_substring_list_get(version_regex_match, &list, NULL); + char majorstr[32]; + size_t pcre2len = sizeof(majorstr); + ret = pcre2_substring_copy_bynumber( + version_regex_match, 1, (PCRE2_UCHAR8 *)majorstr, &pcre2len); + if (ret < 0) { + SCLogError("pcre2_substring_copy_bynumber failed"); + goto error; + } - bool err = false; - if (StringParseInt32(&kmajor, 10, 0, (const char *)list[1]) < 0) { - SCLogError("Invalid value for kmajor: '%s'", list[1]); - err = true; + char minorstr[32]; + pcre2len = sizeof(majorstr); + ret = pcre2_substring_copy_bynumber( + version_regex_match, 2, (PCRE2_UCHAR8 *)minorstr, &pcre2len); + if (ret < 0) { + SCLogError("pcre2_substring_copy_bynumber failed"); + goto error; } - if (StringParseInt32(&kminor, 10, 0, (const char *)list[2]) < 0) { - SCLogError("Invalid value for kminor: '%s'", list[2]); - err = true; + + if (StringParseInt32(&kmajor, 10, 0, (const char *)majorstr) < 0) { + SCLogError("Invalid value for kmajor: '%s'", minorstr); + goto error; + } + if (StringParseInt32(&kminor, 10, 0, (const char *)minorstr) < 0) { + SCLogError("Invalid value for kminor: '%s'", minorstr); + goto error; } - pcre2_substring_list_free((PCRE2_SPTR *)list); pcre2_match_data_free(version_regex_match); pcre2_code_free(version_regex); - if (err) - goto error; - if (kmajor > major) return 1; if (kmajor == major && kminor >= minor) diff --git a/src/util-streaming-buffer.c b/src/util-streaming-buffer.c index 6ff4f43..204ef2e 100644 --- a/src/util-streaming-buffer.c +++ b/src/util-streaming-buffer.c @@ -1064,7 +1064,15 @@ void StreamingBufferSlideToOffset( DEBUG_VALIDATE_BUG_ON(sb->region.stream_offset < offset); } -#define DATA_FITS(sb, len) ((sb)->region.buf_offset + (len) <= (sb)->region.buf_size) +static int DataFits(const StreamingBuffer *sb, const uint32_t len) +{ + uint64_t buf_offset64 = sb->region.buf_offset; + uint64_t len64 = len; + if (len64 + buf_offset64 > UINT32_MAX) { + return -1; + } + return sb->region.buf_offset + len <= sb->region.buf_size; +} int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len) @@ -1076,7 +1084,11 @@ int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, return -1; } - if (!DATA_FITS(sb, data_len)) { + int r = DataFits(sb, data_len); + if (r < 0) { + DEBUG_VALIDATE_BUG_ON(1); + return -1; + } else if (r == 0) { if (sb->region.buf_size == 0) { if (GrowToSize(sb, cfg, data_len) != SC_OK) return -1; @@ -1085,7 +1097,7 @@ int StreamingBufferAppend(StreamingBuffer *sb, const StreamingBufferConfig *cfg, return -1; } } - DEBUG_VALIDATE_BUG_ON(!DATA_FITS(sb, data_len)); + DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1); memcpy(sb->region.buf + sb->region.buf_offset, data, data_len); seg->stream_offset = sb->region.stream_offset + sb->region.buf_offset; @@ -1111,7 +1123,11 @@ int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfi return -1; } - if (!DATA_FITS(sb, data_len)) { + int r = DataFits(sb, data_len); + if (r < 0) { + DEBUG_VALIDATE_BUG_ON(1); + return -1; + } else if (r == 0) { if (sb->region.buf_size == 0) { if (GrowToSize(sb, cfg, data_len) != SC_OK) return -1; @@ -1120,7 +1136,7 @@ int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfi return -1; } } - DEBUG_VALIDATE_BUG_ON(!DATA_FITS(sb, data_len)); + DEBUG_VALIDATE_BUG_ON(DataFits(sb, data_len) != 1); memcpy(sb->region.buf + sb->region.buf_offset, data, data_len); uint32_t rel_offset = sb->region.buf_offset; @@ -1133,7 +1149,15 @@ int StreamingBufferAppendNoTrack(StreamingBuffer *sb, const StreamingBufferConfi } } -#define DATA_FITS_AT_OFFSET(region, len, offset) ((offset) + (len) <= (region)->buf_size) +static int DataFitsAtOffset( + const StreamingBufferRegion *region, const uint32_t len, const uint32_t offset) +{ + const uint64_t offset64 = offset; + const uint64_t len64 = len; + if (offset64 + len64 > UINT32_MAX) + return -1; + return (offset + len <= region->buf_size); +} #if defined(DEBUG) || defined(DEBUG_VALIDATION) static void Validate(const StreamingBuffer *sb) @@ -1477,8 +1501,6 @@ static StreamingBufferRegion *BufferInsertAtRegion(StreamingBuffer *sb, int StreamingBufferInsertAt(StreamingBuffer *sb, const StreamingBufferConfig *cfg, StreamingBufferSegment *seg, const uint8_t *data, uint32_t data_len, uint64_t offset) { - int r; - DEBUG_VALIDATE_BUG_ON(seg == NULL); DEBUG_VALIDATE_BUG_ON(offset < sb->region.stream_offset); if (offset < sb->region.stream_offset) { @@ -1496,11 +1518,15 @@ int StreamingBufferInsertAt(StreamingBuffer *sb, const StreamingBufferConfig *cf region == &sb->region ? "main" : "aux", region); uint32_t rel_offset = offset - region->stream_offset; - if (!DATA_FITS_AT_OFFSET(region, data_len, rel_offset)) { + int r = DataFitsAtOffset(region, data_len, rel_offset); + if (r < 0) { + DEBUG_VALIDATE_BUG_ON(1); + return SC_ELIMIT; + } else if (r == 0) { if ((r = GrowToSize(sb, cfg, (rel_offset + data_len))) != SC_OK) return r; } - DEBUG_VALIDATE_BUG_ON(!DATA_FITS_AT_OFFSET(region, data_len, rel_offset)); + DEBUG_VALIDATE_BUG_ON(DataFitsAtOffset(region, data_len, rel_offset) != 1); SCLogDebug("offset %" PRIu64 " data_len %u, rel_offset %u into region offset %" PRIu64 ", buf_offset %u, buf_size %u", @@ -2320,6 +2346,22 @@ static int StreamingBufferTest10(void) PASS; } +static int StreamingBufferTest11(void) +{ + StreamingBufferConfig cfg = { 24, 1, STREAMING_BUFFER_REGION_GAP_DEFAULT, NULL, NULL, NULL }; + StreamingBuffer *sb = StreamingBufferInit(&cfg); + FAIL_IF(sb == NULL); + + StreamingBufferSegment seg1; + FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg1, (const uint8_t *)"ABCDEFGH", 8) != 0); + StreamingBufferSegment seg2; + unsigned int data_len = 0xffffffff; + FAIL_IF(StreamingBufferAppend(sb, &cfg, &seg2, (const uint8_t *)"unused", data_len) != -1); + FAIL_IF(StreamingBufferInsertAt( + sb, &cfg, &seg2, (const uint8_t *)"abcdefghij", data_len, 100000) != SC_ELIMIT); + StreamingBufferFree(sb, &cfg); + PASS; +} #endif void StreamingBufferRegisterTests(void) @@ -2333,5 +2375,6 @@ void StreamingBufferRegisterTests(void) UtRegisterTest("StreamingBufferTest08", StreamingBufferTest08); UtRegisterTest("StreamingBufferTest09", StreamingBufferTest09); UtRegisterTest("StreamingBufferTest10", StreamingBufferTest10); + UtRegisterTest("StreamingBufferTest11 Bug 6903", StreamingBufferTest11); #endif } |