summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Furdyna <[email protected]>2026-06-10 16:25:34 +0200
committerJerome Forissier <[email protected]>2026-06-23 13:13:16 +0200
commit8382c2ad5284fdfffb6848fb429c40fdf169bf3a (patch)
treeb3a9d2c25f21926dea96b215696b820f714a5da0
parentb1aec609bb5e0d08c25c888c91935287ab4ee5fa (diff)
test: net: add IP defragmentation duplicate-fragment regression test
Add a unit test for the IP datagram reassembler (CONFIG_IP_DEFRAG) that covers the duplicate-last-fragment scenario. Without the fix the last fragment will re-trigger datagram delivery, increasing udp_rx_count to 2 and effectively failing the test; with it applied the test passes with udp_rx_count == 1. Signed-off-by: Mateusz Furdyna <[email protected]> Reviewed-by: Simon Glass <[email protected]>
-rw-r--r--test/dm/Makefile1
-rw-r--r--test/dm/net_defrag.c82
2 files changed, 83 insertions, 0 deletions
diff --git a/test/dm/Makefile b/test/dm/Makefile
index d69b0e08d66..0e3c63568dd 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_MULTIPLEXER) += mux-emul.o
obj-$(CONFIG_MUX_MMIO) += mux-mmio.o
obj-y += fdtdec.o
obj-$(CONFIG_MTD_RAW_NAND) += nand.o
+obj-$(CONFIG_IP_DEFRAG) += net_defrag.o
obj-$(CONFIG_UT_DM) += nop.o
obj-y += ofnode.o
obj-y += ofread.o
diff --git a/test/dm/net_defrag.c b/test/dm/net_defrag.c
new file mode 100644
index 00000000000..3fd40de90cd
--- /dev/null
+++ b/test/dm/net_defrag.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Regression test for IP fragment reassembly.
+ *
+ * The test drives the real RX path via net_process_received_packet(). Final IP
+ * fragment (MF=0) is duplicated, crafted payload triggers redelivery of the datagram,
+ * which fails the test for the unfixed code.
+ */
+
+#include <net.h>
+#include <string.h>
+#include <test/ut.h>
+#include <dm/test.h>
+
+#define FRAG_LEN (8)
+#define PAYLOAD_OFFSET (ETHER_HDR_SIZE + IP_HDR_SIZE)
+#define FRAME_LEN (PAYLOAD_OFFSET + FRAG_LEN)
+
+static int udp_rx_count;
+
+static void defrag_udp_handler(uchar *pkt, unsigned int dport,
+ struct in_addr sip, unsigned int sport,
+ unsigned int len)
+{
+ udp_rx_count++;
+}
+
+static int build_frag(uchar *buf, u16 off_flags, const u16 *payload)
+{
+ struct ethernet_hdr *et = (struct ethernet_hdr *)buf;
+ struct ip_udp_hdr *ip = (struct ip_udp_hdr *)(buf + ETHER_HDR_SIZE);
+
+ memset(buf, 0, FRAME_LEN);
+ et->et_protlen = htons(PROT_IP);
+
+ ip->ip_hl_v = 0x45;
+ ip->ip_len = htons(IP_HDR_SIZE + FRAG_LEN);
+ ip->ip_id = htons(0x4321);
+ ip->ip_off = htons(off_flags);
+ ip->ip_ttl = 64;
+ ip->ip_p = IPPROTO_UDP;
+ /* Broadcast destination is accepted regardless of net_ip. */
+ ip->ip_dst.s_addr = 0xffffffff;
+ ip->ip_sum = compute_ip_checksum(ip, IP_HDR_SIZE);
+
+ memcpy(buf + PAYLOAD_OFFSET, payload, FRAG_LEN);
+
+ return FRAME_LEN;
+}
+
+static int dm_test_net_ip_defrag_dup_last(struct unit_test_state *uts)
+{
+ rxhand_f *saved_handler = net_get_udp_handler();
+ uchar frame[FRAME_LEN];
+ /* UDP header, carried by first fragment. */
+ u16 udp_hdr[4] = { htons(5000), htons(5001),
+ htons(UDP_HDR_SIZE + FRAG_LEN), 0 };
+ /*
+ * Second fragment's payload doubles as a fake hole
+ * {last_byte >= FRAG_LEN, next_hole = 0, prev_hole = 0}, so that the
+ * buggy code re-reading it on a duplicate re-delivers the datagram.
+ */
+ u16 frag_b[4] = { 2 * FRAG_LEN, 0, 0, 0 };
+
+ udp_rx_count = 0;
+ net_set_udp_handler(defrag_udp_handler);
+
+ /* UDP header, offset 0, MF=1; then data, offset 1, MF=0 */
+ net_process_received_packet(frame, build_frag(frame, IP_FLAGS_MFRAG, udp_hdr));
+ net_process_received_packet(frame, build_frag(frame, 1, frag_b));
+ ut_asserteq(1, udp_rx_count);
+
+ /* Duplicate the final fragment: UDP datagram must not be delivered again. */
+ net_process_received_packet(frame, build_frag(frame, 1, frag_b));
+ ut_asserteq(1, udp_rx_count);
+
+ net_set_udp_handler(saved_handler);
+
+ return 0;
+}
+
+DM_TEST(dm_test_net_ip_defrag_dup_last, 0);