ipv6 reassembly: fix detecting holes in reassembled packets

Added unit test for reassembly.

See bug #64767
This commit is contained in:
Simon Goldschmidt 2023-10-07 22:44:52 +02:00
parent 16e8ef3145
commit c8f42025ce
3 changed files with 108 additions and 2 deletions

View File

@ -447,6 +447,19 @@ ip6_reass(struct pbuf *p)
}
}
#endif /* IP_REASS_CHECK_OVERLAP */
/* Check if the fragments received so far have no gaps. */
if (iprh_prev != NULL) {
if (iprh_prev->end != start) {
/* There is a fragment missing between the current
* and the previous fragment */
valid = 0;
}
}
if (end != iprh_tmp->start) {
/* There is a fragment missing between the current
* and the following fragment */
valid = 0;
}
/* the new pbuf should be inserted before this */
next_pbuf = q;
if (iprh_prev != NULL) {
@ -658,6 +671,7 @@ ip6_reass(struct pbuf *p)
}
/* Return the pbuf chain */
MIB2_STATS_INC(mib2.ip6reasmoks);
return p;
}
/* the datagram is not (yet?) reassembled completely */

View File

@ -122,7 +122,7 @@ struct stats_sys {
/** SNMP MIB2 stats */
struct stats_mib2 {
/* IP */
/* IPv4 */
u32_t ipinhdrerrors;
u32_t ipinaddrerrors;
u32_t ipinunknownprotos;
@ -140,6 +140,9 @@ struct stats_mib2 {
u32_t ipforwdatagrams;
u32_t ipinreceives;
/* IPv6 */
u32_t ip6reasmoks;
/* TCP */
u32_t tcpactiveopens;
u32_t tcppassiveopens;

View File

@ -66,6 +66,50 @@ ip6_test_handle_timers(int count)
}
}
/* Helper functions */
static void
create_ip6_input_fragment(u32_t ip_id, u16_t start, u16_t len, int last, u8_t next_hdr)
{
struct pbuf* p;
struct netif* input_netif = netif_list; /* just use any netif */
fail_unless((start & 7) == 0);
fail_unless(((len & 7) == 0) || last);
fail_unless(input_netif != NULL);
p = pbuf_alloc(PBUF_RAW, len + sizeof(struct ip6_frag_hdr) +
sizeof(struct ip6_hdr), PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
err_t err;
struct ip6_frag_hdr* fraghdr;
struct ip6_hdr* ip6hdr = (struct ip6_hdr*)p->payload;
IP6H_VTCFL_SET(ip6hdr, 6, 0, 0);
IP6H_PLEN_SET(ip6hdr, len + sizeof(struct ip6_frag_hdr));
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
IP6H_HOPLIM_SET(ip6hdr, 64);
ip6_addr_copy_to_packed(ip6hdr->src, *netif_ip6_addr(input_netif, 0));
ip6hdr->src.addr[3]++;
ip6_addr_copy_to_packed(ip6hdr->dest, *netif_ip6_addr(input_netif, 0));
fraghdr = (struct ip6_frag_hdr*)(ip6hdr + 1);
fraghdr->_nexth = next_hdr;
fraghdr->reserved = 0;
if (last) {
fraghdr->_fragment_offset = htons(start & ~7);
} else {
fraghdr->_fragment_offset = htons((start & ~7) | 1);
}
fraghdr->_identification = htonl(ip_id);
err = ip6_input(p, input_netif);
if (err != ERR_OK) {
pbuf_free(p);
}
fail_unless(err == ERR_OK);
}
}
/* Setups/teardown functions */
static void
@ -432,6 +476,50 @@ START_TEST(test_ip6_frag)
}
END_TEST
static void test_ip6_reass_helper(u32_t ip_id, const u16_t *segments, size_t num_segs, u16_t seglen)
{
ip_addr_t my_addr = IPADDR6_INIT_HOST(0x20010db8, 0x0, 0x0, 0x1);
size_t i;
memset(&lwip_stats.mib2, 0, sizeof(lwip_stats.mib2));
memset(&lwip_stats.ip6_frag, 0, sizeof(lwip_stats.ip6_frag));
netif_set_up(&test_netif6);
netif_ip6_addr_set(&test_netif6, 0, ip_2_ip6(&my_addr));
netif_ip6_addr_set_state(&test_netif6, 0, IP6_ADDR_VALID);
for (i = 0; i < num_segs; i++) {
u16_t seg = segments[i];
int last = seg + 1U == num_segs;
create_ip6_input_fragment(ip_id, seg * seglen, seglen, last, IP6_NEXTH_UDP);
fail_unless(lwip_stats.ip6_frag.recv == i + 1);
fail_unless(lwip_stats.ip6_frag.err == 0);
fail_unless(lwip_stats.ip6_frag.memerr == 0);
fail_unless(lwip_stats.ip6_frag.drop == 0);
if (i + 1 == num_segs) {
fail_unless(lwip_stats.mib2.ip6reasmoks == 1);
}
else {
fail_unless(lwip_stats.mib2.ip6reasmoks == 0);
}
}
}
START_TEST(test_ip6_reass)
{
#define NUM_SEGS 9
const u16_t t1[NUM_SEGS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
const u16_t t2[NUM_SEGS] = { 8, 0, 1, 2, 3, 4, 7, 6, 5 };
const u16_t t3[NUM_SEGS] = { 1, 2, 3, 4, 5, 6, 7, 8, 0 };
const u16_t t4[NUM_SEGS] = { 8, 2, 4, 6, 7, 5, 3, 1, 0 };
LWIP_UNUSED_ARG(_i);
test_ip6_reass_helper(128, t1, NUM_SEGS, 200);
test_ip6_reass_helper(129, t2, NUM_SEGS, 208);
test_ip6_reass_helper(130, t3, NUM_SEGS, 8);
test_ip6_reass_helper(130, t4, NUM_SEGS, 1448);
}
/** Create the suite including all tests for this module */
Suite *
ip6_suite(void)
@ -444,7 +532,8 @@ ip6_suite(void)
TESTFUNC(test_ip6_lladdr),
TESTFUNC(test_ip6_dest_unreachable_chained_pbuf),
TESTFUNC(test_ip6_frag_pbuf_len_assert),
TESTFUNC(test_ip6_frag)
TESTFUNC(test_ip6_frag),
TESTFUNC(test_ip6_reass)
};
return create_suite("IPv6", tests, sizeof(tests)/sizeof(testfunc), ip6_setup, ip6_teardown);
}