From 67d3371d8a7b0a438aa9f46b7a26cf9eabf1e565 Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Wed, 22 Apr 2026 14:13:15 +0200 Subject: [PATCH 1/6] add test_ip_recv_drops_zero_source to cover the ipaddr_any && !dhcp_is_running branch of the ip_recv rfc 1122 3.2.1.3 source address validation --- src/test/unit/unit.c | 1 + src/test/unit/unit_tests_api.c | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 10f2f824..f9dde147 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -220,6 +220,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_syn_rcvd_rst_bad_seq_dropped); tcase_add_test(tc_utils, test_ip_recv_drops_broadcast_source); tcase_add_test(tc_utils, test_ip_recv_drops_multicast_source); + tcase_add_test(tc_utils, test_ip_recv_drops_zero_source); tcase_add_test(tc_utils, test_arp_recv_rejects_broadcast_sender); tcase_add_test(tc_utils, test_arp_recv_rejects_multicast_sender); tcase_add_test(tc_utils, test_dhcp_ack_rejects_mismatched_server_id); diff --git a/src/test/unit/unit_tests_api.c b/src/test/unit/unit_tests_api.c index 4ecb8f74..93b6e50e 100644 --- a/src/test/unit/unit_tests_api.c +++ b/src/test/unit/unit_tests_api.c @@ -3974,6 +3974,65 @@ START_TEST(test_ip_recv_drops_multicast_source) } END_TEST +START_TEST(test_ip_recv_drops_zero_source) +{ + struct wolfIP s; + int listen_sd; + struct tsocket *listener; + struct wolfIP_sockaddr_in sin; + struct wolfIP_tcp_seg seg; + struct wolfIP_ll_dev *ll; + union transport_pseudo_header ph; + static const uint8_t src_mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(listen_sd, 0); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000001U); + ck_assert_int_eq(wolfIP_sock_bind(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, listen_sd, 1), 0); + + listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + + ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF); + memset(&seg, 0, sizeof(seg)); + memcpy(seg.ip.eth.dst, ll->mac, 6); + memcpy(seg.ip.eth.src, src_mac, 6); + seg.ip.eth.type = ee16(ETH_TYPE_IP); + seg.ip.ver_ihl = 0x45; + seg.ip.ttl = 64; + seg.ip.proto = WI_IPPROTO_TCP; + seg.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN); + seg.ip.src = ee32(IPADDR_ANY); + seg.ip.dst = ee32(0x0A000001U); + seg.ip.csum = 0; + iphdr_set_checksum(&seg.ip); + seg.src_port = ee16(40000); + seg.dst_port = ee16(1234); + seg.seq = ee32(1); + seg.hlen = TCP_HEADER_LEN << 2; + seg.flags = TCP_FLAG_SYN; + seg.win = ee16(65535); + memset(&ph, 0, sizeof(ph)); + ph.ph.src = seg.ip.src; + ph.ph.dst = seg.ip.dst; + ph.ph.proto = WI_IPPROTO_TCP; + ph.ph.len = ee16(TCP_HEADER_LEN); + seg.csum = ee16(transport_checksum(&ph, &seg.src_port)); + + ip_recv(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&seg, + sizeof(struct wolfIP_eth_frame) + IP_HEADER_LEN + TCP_HEADER_LEN); + + ck_assert_int_eq(listener->sock.tcp.state, TCP_LISTEN); +} +END_TEST + /* Regression: arp_recv must not cache entries with broadcast, multicast, * zero, or own-IP sender addresses. Without validation, an ARP request * with a spoofed sender IP poisons the neighbor cache. */ From 2067c3b15d7a4dcb068b0f706a6b113a1fe9c0fd Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Wed, 22 Apr 2026 15:10:32 +0200 Subject: [PATCH 2/6] Enforce a 576-octet next-hop MTU floor in icmp_try_deliver_tcp_error so spoofed ICMP Fragmentation Needed messages cannot push peer_mss below the RFC 879 / RFC 9293 default of 536, with a new test_icmp_input_dest_unreach_frag_needed_below_floor_preserves_peer_mss covering the reject path. --- src/test/unit/unit.c | 1 + src/test/unit/unit_tests_dns_dhcp.c | 60 +++++++++++++++++++++++++++++ src/wolfip.c | 6 ++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index f9dde147..34c0c225 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -783,6 +783,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_icmp_input_filter_drop_receiving); tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_keeps_established_tcp_socket); tcase_add_test(tc_proto, test_icmp_input_dest_unreach_frag_needed_reduces_tcp_peer_mss); + tcase_add_test(tc_proto, test_icmp_input_dest_unreach_frag_needed_below_floor_preserves_peer_mss); tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_closes_syn_sent_tcp_socket); tcase_add_test(tc_proto, test_icmp_input_dest_unreach_port_unreachable_quoted_ip_options_keep_established_tcp_socket); tcase_add_test(tc_proto, test_udp_sendto_and_recvfrom); diff --git a/src/test/unit/unit_tests_dns_dhcp.c b/src/test/unit/unit_tests_dns_dhcp.c index d081a329..f49147eb 100644 --- a/src/test/unit/unit_tests_dns_dhcp.c +++ b/src/test/unit/unit_tests_dns_dhcp.c @@ -2169,6 +2169,66 @@ START_TEST(test_icmp_input_dest_unreach_frag_needed_reduces_tcp_peer_mss) } END_TEST +START_TEST(test_icmp_input_dest_unreach_frag_needed_below_floor_preserves_peer_mss) +{ + struct wolfIP s; + struct tsocket *ts; + struct wolfIP_icmp_dest_unreachable_packet icmp; + struct wolfIP_tcp_wire_prefix *orig; + uint32_t frame_len; + uint16_t next_hop_mtu; + static const uint16_t below_floor[] = { 0U, 41U, 67U, 68U, 575U }; + size_t i; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_ESTABLISHED; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + ts->src_port = 1234; + ts->dst_port = 4321; + + frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + + for (i = 0; i < sizeof(below_floor) / sizeof(below_floor[0]); i++) { + ts->sock.tcp.peer_mss = 1460U; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A0000FEU); + icmp.ip.dst = ee32(ts->local_ip); + icmp.ip.ttl = 64; + icmp.ip.proto = WI_IPPROTO_ICMP; + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_DEST_UNREACH_SIZE); + icmp.type = ICMP_DEST_UNREACH; + icmp.code = ICMP_FRAG_NEEDED; + next_hop_mtu = ee16(below_floor[i]); + memcpy(&icmp.unused[2], &next_hop_mtu, sizeof(next_hop_mtu)); + + orig = (struct wolfIP_tcp_wire_prefix *)icmp.orig_packet; + orig->ip.ver_ihl = 0x45; + orig->ip.proto = WI_IPPROTO_TCP; + orig->ip.src = ee32(ts->local_ip); + orig->ip.dst = ee32(ts->remote_ip); + orig->ip.len = ee16(IP_HEADER_LEN + 8U); + orig->src_port = ee16(ts->src_port); + orig->dst_port = ee16(ts->dst_port); + + icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, + ICMP_DEST_UNREACH_SIZE)); + + icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); + + ck_assert_uint_eq(ts->sock.tcp.peer_mss, 1460U); + } +} +END_TEST + START_TEST(test_icmp_input_dest_unreach_port_unreachable_closes_syn_sent_tcp_socket) { struct wolfIP s; diff --git a/src/wolfip.c b/src/wolfip.c index 99ec1fa3..e0cdfea1 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -2316,7 +2316,11 @@ static void icmp_try_deliver_tcp_error(struct wolfIP *s, memcpy(&next_hop_mtu, &icmp->unused[2], sizeof(next_hop_mtu)); next_hop_mtu = ee16(next_hop_mtu); - if (next_hop_mtu > (IP_HEADER_LEN + TCP_HEADER_LEN)) { + /* RFC 879 / RFC 9293 §3.7.1: IPv4 default MSS is 536; ignore + * any PTB whose next-hop MTU cannot accommodate it. Rejecting + * (rather than clamping) prevents a spoofed ICMP from dragging + * a SYN-negotiated peer_mss down to 536. */ + if (next_hop_mtu >= (IP_HEADER_LEN + TCP_HEADER_LEN + TCP_DEFAULT_MSS)) { uint16_t new_mss = (uint16_t)(next_hop_mtu - (IP_HEADER_LEN + TCP_HEADER_LEN)); From 45e9fd2a7b4c80b974cc9690030aaff9ec8cd20d Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Wed, 22 Apr 2026 15:39:39 +0200 Subject: [PATCH 3/6] Add a bind_port_in_use collision scan across tcpsockets/udpsockets/icmpsockets in wolfIP_sock_bind so duplicate binds to an already-owned (local_ip, port) pair are rejected with -1 instead of silently succeeding, with test_sock_bind_tcp_port_collision_rejected pinning the contract. --- src/test/unit/unit.c | 1 + src/test/unit/unit_tests_api.c | 27 +++++++++++++++++++++++ src/wolfip.c | 39 ++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 34c0c225..ce82bd21 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -156,6 +156,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_sock_bind_invalid_fd); tcase_add_test(tc_utils, test_sock_bind_tcp_state_not_closed); tcase_add_test(tc_utils, test_sock_bind_tcp_filter_blocks); + tcase_add_test(tc_utils, test_sock_bind_tcp_port_collision_rejected); tcase_add_test(tc_utils, test_sock_bind_udp_src_port_nonzero); tcase_add_test(tc_utils, test_sock_bind_udp_filter_blocks); tcase_add_test(tc_utils, test_sock_bind_icmp_success); diff --git a/src/test/unit/unit_tests_api.c b/src/test/unit/unit_tests_api.c index 93b6e50e..cac53498 100644 --- a/src/test/unit/unit_tests_api.c +++ b/src/test/unit/unit_tests_api.c @@ -1014,6 +1014,33 @@ START_TEST(test_sock_bind_tcp_filter_blocks) } END_TEST +START_TEST(test_sock_bind_tcp_port_collision_rejected) +{ + struct wolfIP s; + int tcp_sd1, tcp_sd2; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd1 = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd1, 0); + tcp_sd2 = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd2, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + ck_assert_int_eq(wolfIP_sock_bind(&s, tcp_sd1, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_bind(&s, tcp_sd2, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + START_TEST(test_sock_bind_udp_src_port_nonzero) { struct wolfIP s; diff --git a/src/wolfip.c b/src/wolfip.c index e0cdfea1..9d34af62 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -6252,6 +6252,30 @@ int wolfIP_sock_can_write(struct wolfIP *s, int sockfd) return -WOLFIP_EINVAL; } +/* Return non-zero if any socket in arr[0..n] other than self already claims + * (local_ip, port). IPADDR_ANY on either side overlaps with any specific + * local address, matching POSIX EADDRINUSE semantics. */ +static int bind_port_in_use(const struct tsocket *arr, int n, + const struct tsocket *self, + ip4 new_local_ip, uint16_t new_port) +{ + int i; + if (new_port == 0) + return 0; + for (i = 0; i < n; i++) { + const struct tsocket *tk = &arr[i]; + if (tk == self) + continue; + if (tk->src_port != new_port) + continue; + if (tk->local_ip != IPADDR_ANY && new_local_ip != IPADDR_ANY && + tk->local_ip != new_local_ip) + continue; + return 1; + } + return 0; +} + int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) { @@ -6329,6 +6353,11 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr else ts->local_ip = IPADDR_ANY; } + if (bind_port_in_use(s->tcpsockets, MAX_TCPSOCKETS, ts, + ts->local_ip, new_port)) { + ts->local_ip = prev_ip; + return -1; + } if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_BINDING, s, ts, ts->local_ip, new_port, IPADDR_ANY, 0) != 0) { @@ -6364,6 +6393,11 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr else ts->local_ip = IPADDR_ANY; } + if (bind_port_in_use(s->udpsockets, MAX_UDPSOCKETS, ts, + ts->local_ip, new_port)) { + ts->local_ip = prev_ip; + return -1; + } ts->src_port = new_port; if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_BINDING, s, ts, @@ -6397,6 +6431,11 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr if (primary && primary->ip != IPADDR_ANY) ts->local_ip = primary->ip; } + if (bind_port_in_use(s->icmpsockets, MAX_ICMPSOCKETS, ts, + ts->local_ip, new_id)) { + ts->local_ip = prev_ip; + return -1; + } ts->src_port = new_id; if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_BINDING, s, ts, From d27ec10da812e7a6f1c81ad6c78486379ffb47b4 Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Wed, 22 Apr 2026 19:06:19 +0200 Subject: [PATCH 4/6] Set the IP DF bit on locally-originated ICMP replies in wolfIP_send_ttl_exceeded, wolfIP_send_port_unreachable, and the icmp_input echo-reply path to match the tcp_send_reset_reply precedent, with three new *_sets_df tests pinning the contract. --- src/test/unit/unit.c | 3 +++ src/test/unit/unit_tests_dns_dhcp.c | 31 +++++++++++++++++++++++++++++ src/test/unit/unit_tests_proto.c | 24 ++++++++++++++++++++++ src/test/unit/unit_tests_tcp_ack.c | 26 ++++++++++++++++++++++++ src/wolfip.c | 3 +++ 5 files changed, 87 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index ce82bd21..a0d140aa 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -126,6 +126,7 @@ Suite *wolf_suite(void) #endif tcase_add_test(tc_utils, test_wolfip_send_port_unreachable_ignores_missing_link_sender); tcase_add_test(tc_utils, test_wolfip_send_port_unreachable_non_ethernet_skips_eth_filter); + tcase_add_test(tc_utils, test_wolfip_send_port_unreachable_sets_df); tcase_add_test(tc_utils, test_tcp_adv_win_clamps_and_applies_window_scale); tcase_add_test(tc_utils, test_tcp_segment_acceptable_zero_window_and_overlap_cases); tcase_add_test(tc_utils, test_tcp_segment_acceptable_counts_syn_in_segment_length); @@ -655,6 +656,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_send_ttl_exceeded_eth_filter_drop); tcase_add_test(tc_proto, test_send_ttl_exceeded_no_send); tcase_add_test(tc_proto, test_send_ttl_exceeded_non_ethernet_skips_eth_filter); + tcase_add_test(tc_proto, test_send_ttl_exceeded_sets_df); #if WOLFIP_ENABLE_FORWARDING tcase_add_test(tc_proto, test_wolfip_forward_ttl_exceeded_short_len_does_not_send); #endif @@ -772,6 +774,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_icmp_socket_send_recv); tcase_add_test(tc_proto, test_icmp_input_echo_reply_queues); tcase_add_test(tc_proto, test_icmp_input_echo_request_reply_sent); + tcase_add_test(tc_proto, test_icmp_input_echo_reply_sets_df); tcase_add_test(tc_proto, test_icmp_input_echo_request_bad_checksum_dropped); tcase_add_test(tc_proto, test_icmp_input_echo_request_odd_len_reply_checksum); tcase_add_test(tc_proto, test_icmp_input_echo_request_dhcp_running_no_reply); diff --git a/src/test/unit/unit_tests_dns_dhcp.c b/src/test/unit/unit_tests_dns_dhcp.c index f49147eb..af31d4ae 100644 --- a/src/test/unit/unit_tests_dns_dhcp.c +++ b/src/test/unit/unit_tests_dns_dhcp.c @@ -1758,6 +1758,37 @@ START_TEST(test_icmp_input_echo_request_reply_sent) } END_TEST +START_TEST(test_icmp_input_echo_reply_sets_df) +{ + struct wolfIP s; + struct wolfIP_icmp_packet icmp; + struct wolfIP_icmp_packet *reply; + uint32_t frame_len; + + wolfIP_init(&s); + mock_link_init(&s); + s.dhcp_state = DHCP_OFF; + wolfIP_filter_set_callback(NULL, NULL); + last_frame_sent_size = 0; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A000002U); + icmp.ip.dst = ee32(0x0A000001U); + icmp.ip.ttl = 1; + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); + icmp.ip.flags_fo = 0; + icmp.type = ICMP_ECHO_REQUEST; + icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); + frame_len = (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + ICMP_HEADER_LEN); + + icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); + ck_assert_uint_gt(last_frame_sent_size, 0); + reply = (struct wolfIP_icmp_packet *)last_frame_sent; + ck_assert_uint_eq(reply->type, ICMP_ECHO_REPLY); + ck_assert_uint_eq(ee16(reply->ip.flags_fo) & 0x4000U, 0x4000U); +} +END_TEST + START_TEST(test_icmp_input_echo_request_bad_checksum_dropped) { struct wolfIP s; diff --git a/src/test/unit/unit_tests_proto.c b/src/test/unit/unit_tests_proto.c index 2fc9600e..e6f1db7a 100644 --- a/src/test/unit/unit_tests_proto.c +++ b/src/test/unit/unit_tests_proto.c @@ -3329,6 +3329,30 @@ START_TEST(test_wolfip_send_port_unreachable_non_ethernet_skips_eth_filter) } END_TEST +START_TEST(test_wolfip_send_port_unreachable_sets_df) +{ + struct wolfIP s; + uint8_t orig_buf[ETH_HEADER_LEN + TTL_EXCEEDED_ORIG_PACKET_SIZE_DEFAULT]; + struct wolfIP_ip_packet *orig = (struct wolfIP_ip_packet *)orig_buf; + struct wolfIP_icmp_dest_unreachable_packet *reply; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + last_frame_sent_size = 0; + + memset(orig_buf, 0, sizeof(orig_buf)); + orig->ver_ihl = 0x45; + orig->src = ee32(0x0A000002U); + orig->dst = ee32(0x0A000001U); + + wolfIP_send_port_unreachable(&s, TEST_PRIMARY_IF, orig); + ck_assert_uint_gt(last_frame_sent_size, 0U); + reply = (struct wolfIP_icmp_dest_unreachable_packet *)last_frame_sent; + ck_assert_uint_eq(ee16(reply->ip.flags_fo) & 0x4000U, 0x4000U); +} +END_TEST + START_TEST(test_tcp_adv_win_clamps_and_applies_window_scale) { struct tsocket ts; diff --git a/src/test/unit/unit_tests_tcp_ack.c b/src/test/unit/unit_tests_tcp_ack.c index 0fbb60eb..2c987ccf 100644 --- a/src/test/unit/unit_tests_tcp_ack.c +++ b/src/test/unit/unit_tests_tcp_ack.c @@ -2457,6 +2457,32 @@ START_TEST(test_send_ttl_exceeded_non_ethernet_skips_eth_filter) } END_TEST +START_TEST(test_send_ttl_exceeded_sets_df) +{ + struct wolfIP s; + uint8_t ip_buf[ETH_HEADER_LEN + TTL_EXCEEDED_ORIG_PACKET_SIZE_DEFAULT]; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)ip_buf; + struct wolfIP_icmp_ttl_exceeded_packet *reply; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + wolfIP_filter_set_callback(NULL, NULL); + last_frame_sent_size = 0; + + memset(ip_buf, 0, sizeof(ip_buf)); + memcpy(ip->eth.src, "\x01\x02\x03\x04\x05\x06", 6); + ip->ver_ihl = 0x45; + ip->src = ee32(0x0A000002U); + ip->dst = ee32(0x0A000001U); + + wolfIP_send_ttl_exceeded(&s, TEST_PRIMARY_IF, ip); + ck_assert_uint_gt(last_frame_sent_size, 0U); + reply = (struct wolfIP_icmp_ttl_exceeded_packet *)last_frame_sent; + ck_assert_uint_eq(ee16(reply->ip.flags_fo) & 0x4000U, 0x4000U); +} +END_TEST + #if WOLFIP_ENABLE_FORWARDING START_TEST(test_wolfip_forward_ttl_exceeded_short_len_does_not_send) { diff --git a/src/wolfip.c b/src/wolfip.c index 9d34af62..9f74eaf3 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -1829,6 +1829,7 @@ static void wolfIP_send_ttl_exceeded(struct wolfIP *s, unsigned int if_idx, icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, icmp_data_len)); icmp.ip.ver_ihl = 0x45; + icmp.ip.flags_fo = ee16(0x4000U); icmp.ip.ttl = 64; icmp.ip.proto = WI_IPPROTO_ICMP; icmp.ip.id = ipcounter_next(s); @@ -1900,6 +1901,7 @@ static void wolfIP_send_port_unreachable(struct wolfIP *s, unsigned int if_idx, icmp.csum = ee16(icmp_checksum((struct wolfIP_icmp_packet *)&icmp, icmp_data_len)); icmp.ip.ver_ihl = 0x45; + icmp.ip.flags_fo = ee16(0x4000U); icmp.ip.ttl = 64; icmp.ip.proto = WI_IPPROTO_ICMP; icmp.ip.id = ipcounter_next(s); @@ -6573,6 +6575,7 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p ip->dst = tmp; ip->ttl = 64; ip->id = ipcounter_next(s); + ip->flags_fo = ee16(0x4000U); ip->csum = 0; iphdr_set_checksum(ip); #ifdef ETHERNET From 01c2e181003c4fb339c325db257fb0bc59341620 Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Thu, 23 Apr 2026 13:03:06 +0200 Subject: [PATCH 5/6] =?UTF-8?q?Mask=20the=20reserved=20nibble=20on=20every?= =?UTF-8?q?=20receive-side=20read=20of=20tcp->hlen=20via=20a=20new=20tcp?= =?UTF-8?q?=5Fdata=5Foffset=5Fbytes=20helper=20so=20segments=20carrying=20?= =?UTF-8?q?non-zero=20reserved=20bits=20are=20processed=20identically=20to?= =?UTF-8?q?=20those=20without,=20per=20RFC=209293=20=C2=A73.1's=20MUST-ign?= =?UTF-8?q?ore=20rule,=20with=20a=20new=20test=5Ftcp=5Finput=5Fignores=5Fr?= =?UTF-8?q?eserved=5Fbits=5Fin=5Fhlen=20pinning=20the=20contract=20and=20f?= =?UTF-8?q?our=20pre-existing=20option-parser=20tests=20re-aligned=20to=20?= =?UTF-8?q?legal=204-byte=20header=20encodings.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/unit/unit.c | 1 + src/test/unit/unit_tests_tcp_flow.c | 101 ++++++++++++++++++++++++---- src/wolfip.c | 22 ++++-- 3 files changed, 104 insertions(+), 20 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index a0d140aa..f69d4a6c 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -610,6 +610,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_recv_ooo_capacity_limit); tcase_add_test(tc_utils, test_tcp_recv_overlapping_ooo_segments_coalesce_on_consume); tcase_add_test(tc_utils, test_tcp_input_syn_with_sack_option_enables_sack); + tcase_add_test(tc_utils, test_tcp_input_ignores_reserved_bits_in_hlen); tcase_add_test(tc_utils, test_tcp_input_syn_with_sack_option_respects_local_sack_offer); tcase_add_test(tc_utils, test_tcp_input_iplen_too_big); tcase_add_test(tc_utils, test_tcp_checksum_valid_passes); diff --git a/src/test/unit/unit_tests_tcp_flow.c b/src/test/unit/unit_tests_tcp_flow.c index 596a1485..be4b70dc 100644 --- a/src/test/unit/unit_tests_tcp_flow.c +++ b/src/test/unit/unit_tests_tcp_flow.c @@ -431,6 +431,68 @@ START_TEST(test_tcp_input_syn_with_sack_option_enables_sack) } END_TEST +START_TEST(test_tcp_input_ignores_reserved_bits_in_hlen) +{ + struct wolfIP s; + int listen_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + struct { + uint8_t frame[sizeof(struct wolfIP_tcp_seg) + 4]; + } pkt; + struct wolfIP_tcp_seg *syn = (struct wolfIP_tcp_seg *)pkt.frame; + struct wolfIP_ll_dev *ll; + union transport_pseudo_header ph; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(listen_sd, 0); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000001U); + ck_assert_int_eq(wolfIP_sock_bind(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, listen_sd, 1), 0); + ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF); + ck_assert_ptr_nonnull(ll); + + memset(&pkt, 0, sizeof(pkt)); + memcpy(syn->ip.eth.dst, ll->mac, 6); + syn->ip.eth.type = ee16(ETH_TYPE_IP); + syn->ip.ver_ihl = 0x45; + syn->ip.ttl = 64; + syn->ip.proto = WI_IPPROTO_TCP; + syn->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 4); + syn->ip.src = ee32(0x0A0000A1U); + syn->ip.dst = ee32(0x0A000001U); + iphdr_set_checksum(&syn->ip); + syn->src_port = ee16(40000); + syn->dst_port = ee16(1234); + syn->seq = ee32(1); + syn->hlen = (uint8_t)(((TCP_HEADER_LEN + 4) << 2) | 0x0C); + syn->flags = TCP_FLAG_SYN; + syn->win = ee16(65535); + syn->data[0] = TCP_OPTION_SACK_PERMITTED; + syn->data[1] = TCP_OPTION_SACK_PERMITTED_LEN; + syn->data[2] = TCP_OPTION_NOP; + syn->data[3] = TCP_OPTION_NOP; + + memset(&ph, 0, sizeof(ph)); + ph.ph.src = syn->ip.src; + ph.ph.dst = syn->ip.dst; + ph.ph.proto = WI_IPPROTO_TCP; + ph.ph.len = ee16(TCP_HEADER_LEN + 4); + syn->csum = ee16(transport_checksum(&ph, &syn->src_port)); + + tcp_input(&s, TEST_PRIMARY_IF, syn, + sizeof(struct wolfIP_eth_frame) + IP_HEADER_LEN + TCP_HEADER_LEN + 4); + ck_assert_uint_eq(ts->sock.tcp.sack_permitted, 1); +} +END_TEST + START_TEST(test_tcp_input_syn_with_sack_option_respects_local_sack_offer) { struct wolfIP s; @@ -722,7 +784,7 @@ END_TEST START_TEST(test_tcp_parse_sack_wraparound_block_accepted) { - uint8_t seg_buf[sizeof(struct wolfIP_tcp_seg) + 10]; + uint8_t seg_buf[sizeof(struct wolfIP_tcp_seg) + 12]; struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)seg_buf; struct tcp_parsed_opts po; uint32_t left = 0xFFFFFFF0U; @@ -731,7 +793,7 @@ START_TEST(test_tcp_parse_sack_wraparound_block_accepted) uint32_t frame_len; memset(seg_buf, 0, sizeof(seg_buf)); - seg->hlen = (uint8_t)((TCP_HEADER_LEN + 10) << 2); + seg->hlen = (uint8_t)((TCP_HEADER_LEN + 12) << 2); opt = seg->data; opt[0] = TCP_OPTION_SACK; opt[1] = 10; @@ -741,8 +803,10 @@ START_TEST(test_tcp_parse_sack_wraparound_block_accepted) memcpy(opt + 2, &left_be, sizeof(left_be)); memcpy(opt + 6, &right_be, sizeof(right_be)); } + opt[10] = TCP_OPTION_NOP; + opt[11] = TCP_OPTION_NOP; - frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 10; + frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 12; tcp_parse_options(seg, frame_len, &po); ck_assert_int_eq(po.sack_count, 1); @@ -808,7 +872,7 @@ START_TEST(test_tcp_parse_options_parses_and_clamps_mixed_options) uint32_t frame_len; memset(seg_buf, 0, sizeof(seg_buf)); - seg->hlen = (uint8_t)((TCP_HEADER_LEN + 30) << 2); + seg->hlen = (uint8_t)((TCP_HEADER_LEN + 32) << 2); opt = seg->data; opt[0] = TCP_OPTION_WS; opt[1] = TCP_OPTION_WS_LEN; @@ -840,8 +904,10 @@ START_TEST(test_tcp_parse_options_parses_and_clamps_mixed_options) memcpy(opt + 6, &right, sizeof(right)); } opt += 10; - opt[0] = TCP_OPTION_EOO; - frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 30; + opt[0] = TCP_OPTION_NOP; + opt[1] = TCP_OPTION_NOP; + opt[2] = TCP_OPTION_EOO; + frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 32; tcp_parse_options(seg, frame_len, &po); @@ -867,7 +933,7 @@ START_TEST(test_tcp_parse_options_parses_mss_sack_permitted_timestamp_and_two_sa uint32_t frame_len; memset(seg_buf, 0, sizeof(seg_buf)); - seg->hlen = (uint8_t)((TCP_HEADER_LEN + 34) << 2); + seg->hlen = (uint8_t)((TCP_HEADER_LEN + 36) << 2); opt = seg->data; opt[0] = TCP_OPTION_MSS; opt[1] = TCP_OPTION_MSS_LEN; @@ -897,7 +963,10 @@ START_TEST(test_tcp_parse_options_parses_mss_sack_permitted_timestamp_and_two_sa memcpy(opt + 10, &left, sizeof(left)); memcpy(opt + 14, &right, sizeof(right)); } - frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 34; + opt += 18; + opt[0] = TCP_OPTION_NOP; + opt[1] = TCP_OPTION_NOP; + frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 36; tcp_parse_options(seg, frame_len, &po); @@ -942,7 +1011,11 @@ END_TEST START_TEST(test_tcp_parse_options_caps_sack_block_count) { - uint8_t seg_buf[sizeof(struct wolfIP_tcp_seg) + 48]; + /* TCP_SACK_MAX_BLOCKS equals the natural ceiling of SACK blocks that fit + * in the 40-byte legal options budget (2-byte option header + 4*8 block + * bytes = 34 bytes), so we exercise the MAX path with a compliant SACK + * option carrying exactly TCP_SACK_MAX_BLOCKS blocks. */ + uint8_t seg_buf[sizeof(struct wolfIP_tcp_seg) + 36]; struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)seg_buf; struct tcp_parsed_opts po; uint8_t *opt; @@ -950,17 +1023,19 @@ START_TEST(test_tcp_parse_options_caps_sack_block_count) int i; memset(seg_buf, 0, sizeof(seg_buf)); - seg->hlen = (uint8_t)((TCP_HEADER_LEN + 42) << 2); + seg->hlen = (uint8_t)((TCP_HEADER_LEN + 36) << 2); opt = seg->data; opt[0] = TCP_OPTION_SACK; - opt[1] = 42; - for (i = 0; i < 5; i++) { + opt[1] = (uint8_t)(2 + (TCP_SACK_MAX_BLOCKS * 8)); + for (i = 0; i < TCP_SACK_MAX_BLOCKS; i++) { uint32_t left = ee32((uint32_t)(100 + (i * 20))); uint32_t right = ee32((uint32_t)(110 + (i * 20))); memcpy(opt + 2 + (i * 8), &left, sizeof(left)); memcpy(opt + 2 + (i * 8) + 4, &right, sizeof(right)); } - frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 42; + opt[2 + (TCP_SACK_MAX_BLOCKS * 8)] = TCP_OPTION_NOP; + opt[3 + (TCP_SACK_MAX_BLOCKS * 8)] = TCP_OPTION_NOP; + frame_len = ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 36; tcp_parse_options(seg, frame_len, &po); diff --git a/src/wolfip.c b/src/wolfip.c index 9f74eaf3..90ca21d2 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -2541,11 +2541,19 @@ struct tcp_parsed_opts { uint32_t ts_val, ts_ecr; }; +/* RFC 9293 3.1: the low nibble of the Data Offset byte is Rsrvd and MUST be + * ignored by receivers. Mask it off before translating the 4-bit word count + * into bytes. */ +static inline uint32_t tcp_data_offset_bytes(uint8_t hlen) +{ + return (uint32_t)((hlen & 0xF0U) >> 2); +} + static void tcp_parse_options(const struct wolfIP_tcp_seg *tcp, uint32_t frame_len, struct tcp_parsed_opts *po) { const uint8_t *opt = tcp->data; - int claimed_opt_len = (tcp->hlen >> 2) - TCP_HEADER_LEN; + int claimed_opt_len = (int)tcp_data_offset_bytes(tcp->hlen) - TCP_HEADER_LEN; int available_bytes = (int)frame_len - (int)sizeof(struct wolfIP_tcp_seg); int opt_len; const uint8_t *opt_end; @@ -2966,7 +2974,7 @@ static void tcp_send_reset_reply(struct wolfIP *s, unsigned int if_idx, return; ip_len = ee16(in->ip.len); - tcp_hlen = (uint32_t)(in->hlen >> 2); + tcp_hlen = tcp_data_offset_bytes(in->hlen); if (tcp_hlen < TCP_HEADER_LEN) return; if (ip_len < (uint16_t)(IP_HEADER_LEN + tcp_hlen)) @@ -4035,7 +4043,7 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } tcp_process_sack(t, tcp, - (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + (tcp->hlen >> 2))); + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + tcp_data_offset_bytes(tcp->hlen))); desc = fifo_peek(&t->sock.tcp.txbuf); while ((desc) && (desc->flags & PKT_FLAG_SENT)) { struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)(t->txmem + desc->pos + sizeof(*desc)); @@ -4123,7 +4131,7 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) if (ack_count > 0) { struct pkt_desc *fresh_desc = NULL; uint32_t ack_ip_len = ee16(tcp->ip.len); - uint32_t ack_hdr_len = IP_HEADER_LEN + (uint32_t)(tcp->hlen >> 2); + uint32_t ack_hdr_len = IP_HEADER_LEN + tcp_data_offset_bytes(tcp->hlen); uint32_t ack_frame_len = 0; /* This ACK ackwnowledged some data. */ desc = fifo_peek(&t->sock.tcp.txbuf); @@ -4304,14 +4312,14 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, t->if_idx = (uint8_t)if_idx; matched = 1; /* Validate minimum TCP header length (data offset). */ - if ((tcp->hlen >> 2) < TCP_HEADER_LEN) { + if (tcp_data_offset_bytes(tcp->hlen) < TCP_HEADER_LEN) { return; /* malformed: TCP header below minimum length */ } /* Validate TCP header length fits in IP payload */ - if (iplen < (uint32_t)(IP_HEADER_LEN + (tcp->hlen >> 2))) { + if (iplen < (uint32_t)(IP_HEADER_LEN + tcp_data_offset_bytes(tcp->hlen))) { return; /* malformed: TCP header exceeds IP length */ } - tcplen = iplen - (IP_HEADER_LEN + (tcp->hlen >> 2)); + tcplen = iplen - (IP_HEADER_LEN + tcp_data_offset_bytes(tcp->hlen)); if (t->sock.tcp.state == TCP_LISTEN) { /* RFC 9293 3.10.7.2: reject ACK-bearing segments before SYN handling. */ if (tcp->flags & TCP_FLAG_RST) From 571a80efd7c6045b7c0ecf38cc43087fa7dcc25c Mon Sep 17 00:00:00 2001 From: Reda Chouk Date: Tue, 28 Apr 2026 15:35:16 +0200 Subject: [PATCH 6/6] - make payload pointer/length calculations in tcp_recv mask - correct MTU comment --- src/test/unit/unit.c | 1 + src/test/unit/unit_tests_tcp_flow.c | 43 +++++++++++++++++++++++++++++ src/wolfip.c | 11 +++++--- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index f69d4a6c..a9051fa1 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -611,6 +611,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_recv_overlapping_ooo_segments_coalesce_on_consume); tcase_add_test(tc_utils, test_tcp_input_syn_with_sack_option_enables_sack); tcase_add_test(tc_utils, test_tcp_input_ignores_reserved_bits_in_hlen); + tcase_add_test(tc_utils, test_tcp_recv_ignores_reserved_bits_in_hlen); tcase_add_test(tc_utils, test_tcp_input_syn_with_sack_option_respects_local_sack_offer); tcase_add_test(tc_utils, test_tcp_input_iplen_too_big); tcase_add_test(tc_utils, test_tcp_checksum_valid_passes); diff --git a/src/test/unit/unit_tests_tcp_flow.c b/src/test/unit/unit_tests_tcp_flow.c index be4b70dc..87ee0376 100644 --- a/src/test/unit/unit_tests_tcp_flow.c +++ b/src/test/unit/unit_tests_tcp_flow.c @@ -3065,6 +3065,49 @@ START_TEST(test_tcp_recv_queues_payload_and_advances_ack) } END_TEST +START_TEST(test_tcp_recv_ignores_reserved_bits_in_hlen) +{ + /* RFC 9293 3.1: the low nibble of the Data Offset byte is Rsrvd and MUST + * be ignored. tcp_recv must therefore base its payload pointer and length + * on the masked (high-nibble) header length, otherwise a peer that sets + * reserved bits would have its delivered bytes shifted by 4*Rsrvd octets. */ + struct wolfIP s; + struct tsocket *ts; + uint8_t payload[8] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }; + uint8_t seg_buf[sizeof(struct wolfIP_tcp_seg) + sizeof(payload)]; + struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)seg_buf; + uint32_t seq = 50; + uint8_t out[sizeof(payload) + 4]; + int ret; + + wolfIP_init(&s); + ts = &s.tcpsockets[0]; + memset(ts, 0, sizeof(*ts)); + ts->proto = WI_IPPROTO_TCP; + ts->S = &s; + ts->sock.tcp.state = TCP_ESTABLISHED; + ts->sock.tcp.ack = seq; + ts->sock.tcp.bytes_in_flight = 1; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, seq); + + memset(seg, 0, sizeof(seg_buf)); + seg->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + sizeof(payload)); + /* No options (hdr is the bare 20 bytes), but flip every reserved bit on. */ + seg->hlen = (uint8_t)((TCP_HEADER_LEN << 2) | 0x0F); + seg->seq = ee32(seq); + seg->flags = (TCP_FLAG_ACK | TCP_FLAG_PSH); + memcpy(seg->data, payload, sizeof(payload)); + + tcp_recv(ts, seg); + + ck_assert_uint_eq(ts->sock.tcp.ack, seq + sizeof(payload)); + memset(out, 0, sizeof(out)); + ret = queue_pop(&ts->sock.tcp.rxbuf, out, sizeof(out)); + ck_assert_int_eq(ret, (int)sizeof(payload)); + ck_assert_mem_eq(out, payload, sizeof(payload)); +} +END_TEST + START_TEST(test_tcp_recv_wrong_state_does_nothing) { struct wolfIP s; diff --git a/src/wolfip.c b/src/wolfip.c index 90ca21d2..94b4268a 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -2320,8 +2320,8 @@ static void icmp_try_deliver_tcp_error(struct wolfIP *s, next_hop_mtu = ee16(next_hop_mtu); /* RFC 879 / RFC 9293 §3.7.1: IPv4 default MSS is 536; ignore * any PTB whose next-hop MTU cannot accommodate it. Rejecting - * (rather than clamping) prevents a spoofed ICMP from dragging - * a SYN-negotiated peer_mss down to 536. */ + * (rather than clamping) sub-576 MTUs prevents a spoofed ICMP + * from dragging a SYN-negotiated peer_mss below 536. */ if (next_hop_mtu >= (IP_HEADER_LEN + TCP_HEADER_LEN + TCP_DEFAULT_MSS)) { uint16_t new_mss = (uint16_t)(next_hop_mtu - (IP_HEADER_LEN + TCP_HEADER_LEN)); @@ -3465,9 +3465,12 @@ static inline uint32_t tcp_seq_inc(uint32_t seq, uint32_t n) /* Add a segment to the rx buffer for the application to consume */ static void tcp_recv(struct tsocket *t, struct wolfIP_tcp_seg *seg) { - uint32_t seg_len = ee16(seg->ip.len) - (IP_HEADER_LEN + (seg->hlen >> 2)); + /* RFC 9293 3.1: mask the reserved nibble before deriving header length so a + * peer that sets reserved bits cannot shift our payload pointer/length. */ + uint32_t hdr_len = tcp_data_offset_bytes(seg->hlen); + uint32_t seg_len = ee16(seg->ip.len) - (IP_HEADER_LEN + hdr_len); uint32_t seq = ee32(seg->seq); - const uint8_t *payload = (uint8_t *)seg->ip.data + (seg->hlen >> 2); + const uint8_t *payload = (uint8_t *)seg->ip.data + hdr_len; if ((t->sock.tcp.state != TCP_ESTABLISHED) && (t->sock.tcp.state != TCP_CLOSE_WAIT) && (t->sock.tcp.state != TCP_FIN_WAIT_1) &&