From c01d51fd1bb093fbb18c46c2853bba6f591f7b67 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 10:42:43 +0100 Subject: [PATCH 01/17] Allow TCP receive in FIN_WAIT states F/782 --- src/test/unit/unit.c | 67 ++++++++++++++++++++++++++++++++++++++++---- src/wolfip.c | 5 +++- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index e8dce7f..2ddca07 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -8393,6 +8393,7 @@ START_TEST(test_tcp_input_fin_wait_2_ack_with_payload_receives) struct tsocket *ts; uint8_t buf[sizeof(struct wolfIP_tcp_seg) + 1]; struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)buf; + uint8_t out = 0; wolfIP_init(&s); mock_link_init(&s); @@ -8424,8 +8425,10 @@ START_TEST(test_tcp_input_fin_wait_2_ack_with_payload_receives) fix_tcp_checksums(seg); tcp_input(&s, TEST_PRIMARY_IF, seg, (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 1)); - ck_assert_uint_eq(ts->sock.tcp.ack, 50); - ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, 0); + ck_assert_uint_eq(ts->sock.tcp.ack, 51); + ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, CB_EVENT_READABLE); + ck_assert_int_eq(queue_pop(&ts->sock.tcp.rxbuf, &out, sizeof(out)), 1); + ck_assert_uint_eq(out, TCP_OPTION_EOO); } END_TEST @@ -14760,6 +14763,56 @@ START_TEST(test_tcp_recv_close_wait_ack_match) } END_TEST +START_TEST(test_tcp_recv_fin_wait_1_ack_match) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t buf[sizeof(struct wolfIP_tcp_seg) + 1]; + struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)buf; + + wolfIP_init(&s); + ts = tcp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->sock.tcp.state = TCP_FIN_WAIT_1; + ts->sock.tcp.ack = 100; + + memset(buf, 0, sizeof(buf)); + seg->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 1); + seg->hlen = TCP_HEADER_LEN << 2; + seg->seq = ee32(100); + seg->data[0] = 0x5a; + + tcp_recv(ts, seg); + ck_assert_uint_eq(ts->sock.tcp.ack, 101); + ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, CB_EVENT_READABLE); +} +END_TEST + +START_TEST(test_tcp_recv_fin_wait_2_ack_match) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t buf[sizeof(struct wolfIP_tcp_seg) + 1]; + struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)buf; + + wolfIP_init(&s); + ts = tcp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->sock.tcp.state = TCP_FIN_WAIT_2; + ts->sock.tcp.ack = 100; + + memset(buf, 0, sizeof(buf)); + seg->ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN + 1); + seg->hlen = TCP_HEADER_LEN << 2; + seg->seq = ee32(100); + seg->data[0] = 0x5a; + + tcp_recv(ts, seg); + ck_assert_uint_eq(ts->sock.tcp.ack, 101); + ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, CB_EVENT_READABLE); +} +END_TEST + START_TEST(test_tcp_recv_queue_full_sends_ack) { struct wolfIP s; @@ -15974,6 +16027,7 @@ START_TEST(test_tcp_input_fin_wait_2_fin_with_payload_queues) uint8_t buf[sizeof(struct wolfIP_tcp_seg) + 4]; struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)buf; uint8_t payload[4] = {9, 8, 7, 6}; + uint8_t out[4] = {0}; wolfIP_init(&s); mock_link_init(&s); @@ -15986,7 +16040,7 @@ START_TEST(test_tcp_input_fin_wait_2_fin_with_payload_queues) ts->dst_port = 4321; ts->local_ip = 0x0A000001U; ts->remote_ip = 0x0A000002U; - ts->sock.tcp.ack = 104; + ts->sock.tcp.ack = 100; memset(buf, 0, sizeof(buf)); seg->ip.ver_ihl = 0x45; @@ -16005,7 +16059,8 @@ START_TEST(test_tcp_input_fin_wait_2_fin_with_payload_queues) fix_tcp_checksums(seg); tcp_input(&s, TEST_PRIMARY_IF, seg, (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + sizeof(payload))); - ck_assert_int_eq(queue_pop(&ts->sock.tcp.rxbuf, NULL, 0), -WOLFIP_EAGAIN); + ck_assert_int_eq(queue_pop(&ts->sock.tcp.rxbuf, out, sizeof(out)), (int)sizeof(payload)); + ck_assert_mem_eq(out, payload, sizeof(payload)); ck_assert_uint_eq(ts->sock.tcp.ack, 105); ck_assert_int_eq(ts->sock.tcp.state, TCP_TIME_WAIT); ck_assert_uint_eq(ts->events & CB_EVENT_CLOSED, CB_EVENT_CLOSED); @@ -16020,7 +16075,7 @@ START_TEST(test_tcp_input_fin_wait_2_fin_payload_ack_mismatch_no_transition) struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)buf; uint8_t payload[4] = {9, 8, 7, 6}; uint32_t ack = 100; - uint32_t seq = 100; + uint32_t seq = 101; wolfIP_init(&s); mock_link_init(&s); @@ -19937,6 +19992,8 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_recv_ack_mismatch_does_nothing); tcase_add_test(tc_utils, test_tcp_recv_wrap_seq_ahead_not_trimmed); tcase_add_test(tc_utils, test_tcp_recv_close_wait_ack_match); + tcase_add_test(tc_utils, test_tcp_recv_fin_wait_1_ack_match); + tcase_add_test(tc_utils, test_tcp_recv_fin_wait_2_ack_match); tcase_add_test(tc_utils, test_tcp_recv_queue_full_sends_ack); tcase_add_test(tc_utils, test_tcp_process_ts_uses_ecr); tcase_add_test(tc_utils, test_tcp_rto_update_second_sample_rfc6298); diff --git a/src/wolfip.c b/src/wolfip.c index ecfac8d..c8dcb07 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -2743,7 +2743,10 @@ 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)); uint32_t seq = ee32(seg->seq); const uint8_t *payload = (uint8_t *)seg->ip.data + (seg->hlen >> 2); - if ((t->sock.tcp.state != TCP_ESTABLISHED) && (t->sock.tcp.state != TCP_CLOSE_WAIT)) { + if ((t->sock.tcp.state != TCP_ESTABLISHED) && + (t->sock.tcp.state != TCP_CLOSE_WAIT) && + (t->sock.tcp.state != TCP_FIN_WAIT_1) && + (t->sock.tcp.state != TCP_FIN_WAIT_2)) { return; } if (seg_len == 0) From fd082b374132e80dc9f67d3a3b900c2813c933bd Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 10:51:00 +0100 Subject: [PATCH 02/17] Fix ESP anti-replay window updates F/779 --- src/test/unit/unit_esp.c | 11 +++++++++++ src/wolfesp.c | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/test/unit/unit_esp.c b/src/test/unit/unit_esp.c index f648c30..606cefb 100644 --- a/src/test/unit/unit_esp.c +++ b/src/test/unit/unit_esp.c @@ -463,6 +463,16 @@ START_TEST(test_replay_advance_hi_seq) } END_TEST +/* The newly advanced hi_seq must be marked as seen immediately. */ +START_TEST(test_replay_advanced_hi_seq_duplicate_rejected) +{ + replay_t r; + esp_replay_init(r); /* hi_seq=32 */ + ck_assert_int_eq(esp_check_replay(&r, 33U), 0); + ck_assert_int_ne(esp_check_replay(&r, 33U), 0); +} +END_TEST + /* A corrupted low hi_seq should not underflow the window floor. */ START_TEST(test_replay_low_hi_seq_accepts_seq_one) { @@ -1169,6 +1179,7 @@ static Suite *esp_suite(void) tcase_add_test(tc, test_replay_multiple_in_window); tcase_add_test(tc, test_replay_below_window_rejected); tcase_add_test(tc, test_replay_advance_hi_seq); + tcase_add_test(tc, test_replay_advanced_hi_seq_duplicate_rejected); tcase_add_test(tc, test_replay_low_hi_seq_accepts_seq_one); tcase_add_test(tc, test_replay_jump_resets_bitmap); tcase_add_test(tc, test_replay_old_seqs_after_jump); diff --git a/src/wolfesp.c b/src/wolfesp.c index 4ada1d7..0315add 100644 --- a/src/wolfesp.c +++ b/src/wolfesp.c @@ -1188,7 +1188,7 @@ esp_check_replay(struct replay_t * replay, uint32_t seq) * seq_low - - - - - - - seq - - - - - - hi_seq * |<----------- ESP_REPLAY_WIN --------------| * */ - if (seq < replay->hi_seq) { + if (seq <= replay->hi_seq) { /* seq number within window. */ bitn = 1U << (replay->hi_seq - seq); @@ -1207,7 +1207,7 @@ esp_check_replay(struct replay_t * replay, uint32_t seq) diff = seq - replay->hi_seq; if (diff < ESP_REPLAY_WIN) { /* within a window width, slide up. */ - replay->bitmap = replay->bitmap << diff; + replay->bitmap = (replay->bitmap << diff) | 1U; } else { /* reset window. */ From 00ce5fc520650b81b4c1742fa9cd988cf45af062 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 10:54:12 +0100 Subject: [PATCH 03/17] Fix SYN-ACK advertised window scaling F/783 --- src/test/unit/unit.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 22 +++++++++++----------- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 2ddca07..2790318 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -3396,6 +3396,49 @@ START_TEST(test_sock_accept_synack_retransmission) } END_TEST +START_TEST(test_sock_accept_synack_window_not_scaled) +{ + struct wolfIP s; + int listen_sd; + int client_sd; + struct tsocket *listener; + struct tsocket *accepted; + struct wolfIP_sockaddr_in sin; + struct pkt_desc *desc; + struct wolfIP_tcp_seg *seg; + + 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); + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + listener = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD); + listener->sock.tcp.ws_enabled = 1; + listener->sock.tcp.rcv_wscale = 3; + + client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); + ck_assert_int_gt(client_sd, 0); + + accepted = &s.tcpsockets[SOCKET_UNMARK(client_sd)]; + desc = fifo_peek(&accepted->sock.tcp.txbuf); + ck_assert_ptr_nonnull(desc); + seg = (struct wolfIP_tcp_seg *)(accepted->txmem + desc->pos + sizeof(*desc)); + + ck_assert_uint_eq(seg->flags, (TCP_FLAG_SYN | TCP_FLAG_ACK)); + ck_assert_uint_eq(ee16(seg->win), queue_space((struct queue *)&accepted->sock.tcp.rxbuf)); +} +END_TEST + START_TEST(test_sock_accept_ack_transitions_to_established) { struct wolfIP s; @@ -19736,6 +19779,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_sock_accept_starts_rto_timer); tcase_add_test(tc_utils, test_sock_accept_initializes_snd_una); tcase_add_test(tc_utils, test_sock_accept_synack_retransmission); + tcase_add_test(tc_utils, test_sock_accept_synack_window_not_scaled); tcase_add_test(tc_utils, test_sock_accept_ack_transitions_to_established); tcase_add_test(tc_utils, test_sock_sendto_error_paths); tcase_add_test(tc_utils, test_sock_sendto_null_buf_or_len_zero); diff --git a/src/wolfip.c b/src/wolfip.c index c8dcb07..763b277 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -1991,10 +1991,10 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) return NULL; } -static uint16_t tcp_adv_win(const struct tsocket *t) +static uint16_t tcp_adv_win(const struct tsocket *t, uint8_t apply_wscale) { uint32_t space = queue_space((struct queue *)&t->sock.tcp.rxbuf); - uint8_t shift = t->sock.tcp.ws_enabled ? t->sock.tcp.rcv_wscale : 0; + uint8_t shift = (apply_wscale && t->sock.tcp.ws_enabled) ? t->sock.tcp.rcv_wscale : 0; uint32_t win = space >> shift; if (win > 0xFFFF) win = 0xFFFF; @@ -2323,7 +2323,7 @@ static void tcp_send_empty(struct tsocket *t, uint8_t flags) tcp->ack = ee32(t->sock.tcp.ack); tcp->hlen = ((20 + opt_len) << 2) & 0xF0; tcp->flags = flags; - tcp->win = ee16(tcp_adv_win(t)); + tcp->win = ee16(tcp_adv_win(t, 1)); tcp->csum = 0; tcp->urg = 0; fifo_push(&t->sock.tcp.txbuf, tcp, @@ -2455,7 +2455,7 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) tcp->seq = ee32(t->sock.tcp.seq); tcp->ack = ee32(t->sock.tcp.ack); tcp->flags = flags; - tcp->win = ee16(tcp_adv_win(t)); + tcp->win = ee16(tcp_adv_win(t, 0)); tcp->csum = 0; tcp->urg = 0; opt = tcp->data; @@ -2675,7 +2675,7 @@ static int tcp_send_zero_wnd_probe(struct tsocket *t) probe->ack = ee32(t->sock.tcp.ack); probe->hlen = TCP_HEADER_LEN << 2; probe->flags = TCP_FLAG_ACK; - probe->win = ee16(tcp_adv_win(t)); + probe->win = ee16(tcp_adv_win(t, 1)); probe->data[0] = probe_byte; tx_if = wolfIP_socket_if_idx(t); @@ -4301,7 +4301,7 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len tcp->ack = ee32(ts->sock.tcp.ack); tcp->hlen = (uint8_t)((TCP_HEADER_LEN + opt_len) << 2); tcp->flags = TCP_FLAG_ACK; - tcp->win = ee16(tcp_adv_win(ts)); + tcp->win = ee16(tcp_adv_win(ts, 1)); tcp->csum = 0; tcp->urg = 0; if (ts->sock.tcp.ts_enabled) { @@ -4489,10 +4489,10 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in if (queue_len(&ts->sock.tcp.rxbuf) == 0) return 0; { - uint16_t win_before = tcp_adv_win(ts); + uint16_t win_before = tcp_adv_win(ts, 1); int ret = queue_pop(&ts->sock.tcp.rxbuf, buf, len); if (ret > 0) { - uint16_t win_after = tcp_adv_win(ts); + uint16_t win_after = tcp_adv_win(ts, 1); if (queue_len(&ts->sock.tcp.rxbuf) > 0) ts->events |= CB_EVENT_READABLE; if (win_after > win_before) @@ -4501,10 +4501,10 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in return ret; } } else if (ts->sock.tcp.state == TCP_ESTABLISHED) { - uint16_t win_before = tcp_adv_win(ts); + uint16_t win_before = tcp_adv_win(ts, 1); int ret = queue_pop(&ts->sock.tcp.rxbuf, buf, len); if (ret > 0) { - uint16_t win_after = tcp_adv_win(ts); + uint16_t win_after = tcp_adv_win(ts, 1); if (queue_len(&ts->sock.tcp.rxbuf) > 0) ts->events |= CB_EVENT_READABLE; if (win_after > win_before) @@ -6554,7 +6554,7 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) /* Refresh ack counter */ ts->sock.tcp.last_ack = ts->sock.tcp.ack; tcp->ack = ee32(ts->sock.tcp.ack); - tcp->win = ee16(tcp_adv_win(ts)); + tcp->win = ee16(tcp_adv_win(ts, 1)); ip_output_add_header(ts, (struct wolfIP_ip_packet *)tcp, WI_IPPROTO_TCP, size); if (wolfIP_filter_notify_tcp(WOLFIP_FILT_SENDING, ts->S, tx_if, tcp, desc->len) != 0) { break; From ffb252c01f36c575b83540061ebee0dcedcf4d61 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 10:57:00 +0100 Subject: [PATCH 04/17] Drop fragmented IPv4 packets F/784 --- src/test/unit/unit.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 3 +++ 2 files changed, 51 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 2790318..0246ebf 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -9447,6 +9447,53 @@ START_TEST(test_ip_recv_udp_with_ip_options_delivers_payload) } END_TEST +START_TEST(test_ip_recv_fragmented_udp_dropped) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t frame[ETH_HEADER_LEN + IP_HEADER_LEN + UDP_HEADER_LEN + 4]; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)frame; + uint8_t *udp_hdr = frame + ETH_HEADER_LEN + IP_HEADER_LEN; + uint16_t udp_len = UDP_HEADER_LEN + 4; + uint32_t local_ip = 0x0A000001U; + uint32_t remote_ip = 0x0A000002U; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + ts = udp_new_socket(&s); + ck_assert_ptr_nonnull(ts); + ts->src_port = 1234; + ts->local_ip = local_ip; + + memset(frame, 0, sizeof(frame)); + memcpy(ip->eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(ip->eth.src, "\x01\x02\x03\x04\x05\x06", 6); + ip->eth.type = ee16(ETH_TYPE_IP); + ip->ver_ihl = 0x45; + ip->ttl = 64; + ip->proto = WI_IPPROTO_UDP; + ip->len = ee16(IP_HEADER_LEN + udp_len); + ip->flags_fo = ee16(0x2000U); /* MF=1, offset=0 */ + ip->src = ee32(remote_ip); + ip->dst = ee32(local_ip); + + ((uint16_t *)udp_hdr)[0] = ee16(4321); + ((uint16_t *)udp_hdr)[1] = ee16(1234); + ((uint16_t *)udp_hdr)[2] = ee16(udp_len); + memcpy(udp_hdr + UDP_HEADER_LEN, "frag", 4); + + fix_udp_checksum_raw(ip, udp_hdr, udp_len); + fix_ip_checksum(ip); + + ip_recv(&s, TEST_PRIMARY_IF, ip, (uint32_t)sizeof(frame)); + + ck_assert_ptr_eq(fifo_peek(&ts->sock.udp.rxbuf), NULL); + ck_assert_uint_eq(ts->events & CB_EVENT_READABLE, 0); +} +END_TEST + START_TEST(test_ip_recv_forward_arp_queue_and_flush) { struct wolfIP s; @@ -20189,6 +20236,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_proto, test_forward_interface_short_circuit_cases); tcase_add_test(tc_proto, test_ip_recv_forward_ttl_exceeded); tcase_add_test(tc_proto, test_ip_recv_udp_with_ip_options_delivers_payload); + tcase_add_test(tc_proto, test_ip_recv_fragmented_udp_dropped); tcase_add_test(tc_proto, test_ip_recv_forward_arp_queue_and_flush); tcase_add_test(tc_proto, test_arp_flush_pending_ttl_expired); tcase_add_test(tc_proto, test_wolfip_forwarding_basic); diff --git a/src/wolfip.c b/src/wolfip.c index 763b277..e93b4d6 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -5889,6 +5889,9 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, /* validate IP header checksum per RFC 1122 */ if (iphdr_verify_checksum(ip) != 0) return; + /* Fragment reassembly is not implemented; drop all fragments. */ + if ((ee16(ip->flags_fo) & 0x3FFFU) != 0U) + return; #if WOLFIP_ENABLE_LOOPBACK if (!wolfIP_is_loopback_if(if_idx)) { ip4 dest = ee32(ip->dst); From 165d1c3d401c7c89d2be533939e17a208cf58998 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 10:58:36 +0100 Subject: [PATCH 05/17] Set DF on outgoing TCP packets F/790 --- src/test/unit/unit.c | 1 + src/wolfip.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 0246ebf..62a577c 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -19093,6 +19093,7 @@ START_TEST(test_ip_output_add_header) { ck_assert_uint_eq(ip->proto, WI_IPPROTO_TCP); ck_assert_uint_eq(ip->src, ee32(t.local_ip)); ck_assert_uint_eq(ip->dst, ee32(t.remote_ip)); + ck_assert_uint_eq(ip->flags_fo, ee16(0x4000)); ck_assert_msg(ip->csum != 0, "IP header checksum should not be zero"); // Check the pseudo-header checksum calculation for TCP segment diff --git a/src/wolfip.c b/src/wolfip.c index e93b4d6..c97a928 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -2994,7 +2994,7 @@ static int ip_output_add_header(struct tsocket *t, struct wolfIP_ip_packet *ip, ip->ver_ihl = 0x45; ip->tos = 0; ip->len = ee16(len); - ip->flags_fo = 0; + ip->flags_fo = (proto == WI_IPPROTO_TCP) ? ee16(0x4000U) : 0; ip->ttl = 64; ip->proto = proto; ip->id = ee16(t->S->ipcounter); From f478a0965abf0dd9c214c6ae115742f5ad445b99 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:03:34 +0100 Subject: [PATCH 06/17] Reject out-of-window TCP segments F/786 --- src/test/unit/unit.c | 72 ++++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 29 ++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 62a577c..cdd2d56 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -8364,6 +8364,7 @@ START_TEST(test_tcp_input_fin_wait_2_fin_sets_ack) ts->local_ip = 0x0A000001U; ts->remote_ip = 0x0A000002U; ts->sock.tcp.ack = seq; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, ts->sock.tcp.ack); memset(&seg, 0, sizeof(seg)); seg.ip.ver_ihl = 0x45; @@ -9092,6 +9093,7 @@ START_TEST(test_tcp_fin_wait_1_to_closing) ts->remote_ip = remote_ip; ts->src_port = local_port; ts->dst_port = remote_port; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, ts->sock.tcp.ack); inject_tcp_segment(&s, TEST_PRIMARY_IF, remote_ip, local_ip, remote_port, local_port, 9, 0, TCP_FLAG_FIN); @@ -13055,6 +13057,73 @@ START_TEST(test_tcp_input_rst_out_of_window_does_not_update_peer_rwnd) } END_TEST +START_TEST(test_tcp_input_out_of_window_payload_not_cached) +{ + struct wolfIP s; + struct tsocket *ts; + uint8_t seg_buf[sizeof(struct wolfIP_tcp_seg) + 1]; + struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)seg_buf; + union transport_pseudo_header ph; + uint32_t seq; + uint8_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->sock.tcp.ack = 100; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, ts->sock.tcp.ack); + fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); + ts->src_port = 1234; + ts->dst_port = 4321; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + + seq = ts->sock.tcp.ack + queue_space(&ts->sock.tcp.rxbuf); + + memset(seg_buf, 0, sizeof(seg_buf)); + memcpy(seg->ip.eth.dst, s.ll_dev[TEST_PRIMARY_IF].mac, 6); + memcpy(seg->ip.eth.src, "\x20\x21\x22\x23\x24\x25", 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 + 1); + seg->ip.src = ee32(ts->remote_ip); + seg->ip.dst = ee32(ts->local_ip); + fix_ip_checksum(&seg->ip); + + seg->src_port = ee16(ts->dst_port); + seg->dst_port = ee16(ts->src_port); + seg->seq = ee32(seq); + seg->ack = 0; + seg->hlen = TCP_HEADER_LEN << 2; + seg->flags = TCP_FLAG_PSH; + seg->win = ee16(65535); + seg->data[0] = 0x5a; + + 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 + 1); + seg->csum = ee16(transport_checksum(&ph, &seg->src_port)); + + tcp_input(&s, TEST_PRIMARY_IF, seg, + (uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN + 1)); + + ck_assert_uint_eq(queue_len(&ts->sock.tcp.rxbuf), 0U); + for (i = 0; i < TCP_OOO_MAX_SEGS; i++) { + ck_assert_uint_eq(ts->sock.tcp.ooo[i].used, 0); + } +} +END_TEST + START_TEST(test_tcp_input_rst_exact_seq_closes) { struct wolfIP s; @@ -15700,6 +15769,7 @@ START_TEST(test_tcp_input_established_fin_sets_close_wait) ts->remote_ip = remote_ip; ts->src_port = 1234; ts->dst_port = 4321; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, ts->sock.tcp.ack); memset(&seg, 0, sizeof(seg)); seg.ip.ver_ihl = 0x45; @@ -15893,6 +15963,7 @@ START_TEST(test_tcp_input_fin_wait_1_fin_with_payload_returns) ts->dst_port = 4321; ts->local_ip = 0x0A000001U; ts->remote_ip = 0x0A000002U; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, ts->sock.tcp.ack); memset(buf, 0, sizeof(buf)); seg->ip.ver_ihl = 0x45; @@ -20019,6 +20090,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_input_rst_seq_in_window_sends_ack); tcase_add_test(tc_utils, test_tcp_input_rst_seq_in_scaled_window_sends_ack); tcase_add_test(tc_utils, test_tcp_input_rst_out_of_window_does_not_update_peer_rwnd); + tcase_add_test(tc_utils, test_tcp_input_out_of_window_payload_not_cached); tcase_add_test(tc_utils, test_tcp_input_rst_exact_seq_closes); tcase_add_test(tc_utils, test_tcp_input_syn_listen_mismatch); tcase_add_test(tc_utils, test_tcp_input_syn_rcvd_ack_established); diff --git a/src/wolfip.c b/src/wolfip.c index c97a928..444899e 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -2001,6 +2001,31 @@ static uint16_t tcp_adv_win(const struct tsocket *t, uint8_t apply_wscale) return (uint16_t)win; } +static int tcp_segment_acceptable(const struct tsocket *t, + const struct wolfIP_tcp_seg *tcp, uint32_t tcplen) +{ + uint32_t rcv_nxt = t->sock.tcp.ack; + uint32_t rcv_wnd = queue_space((struct queue *)&t->sock.tcp.rxbuf); + uint32_t seg_seq = ee32(tcp->seq); + uint32_t seg_len = tcplen + ((tcp->flags & TCP_FLAG_FIN) ? 1U : 0U); + + if (seg_len == 0U) { + if (rcv_wnd == 0U) + return seg_seq == rcv_nxt; + return tcp_seq_leq(rcv_nxt, seg_seq) && + tcp_seq_lt(seg_seq, tcp_seq_inc(rcv_nxt, rcv_wnd)); + } + + if (rcv_wnd == 0U) + return 0; + + return ((tcp_seq_leq(rcv_nxt, seg_seq) && + tcp_seq_lt(seg_seq, tcp_seq_inc(rcv_nxt, rcv_wnd))) || + (tcp_seq_leq(rcv_nxt, tcp_seq_inc(seg_seq, seg_len - 1U)) && + tcp_seq_lt(tcp_seq_inc(seg_seq, seg_len - 1U), + tcp_seq_inc(rcv_nxt, rcv_wnd)))); +} + struct tcp_parsed_opts { uint8_t mss_found; uint16_t mss; @@ -3687,6 +3712,10 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, (t->sock.tcp.state == TCP_FIN_WAIT_1) || (t->sock.tcp.state == TCP_FIN_WAIT_2) || (t->sock.tcp.state == TCP_CLOSING)) { + if (!tcp_segment_acceptable(t, tcp, tcplen)) { + tcp_send_ack(t); + continue; + } if (tcp->flags & TCP_FLAG_ACK) { tcp_ack(t, tcp); From c0e35f83fea678d5615c076f7ce7cfb55476757f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:05:26 +0100 Subject: [PATCH 07/17] Reset TTL on ICMP echo replies F/787 --- src/test/unit/unit.c | 7 +++++-- src/wolfip.c | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index cdd2d56..02dddb8 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -5869,6 +5869,7 @@ START_TEST(test_icmp_input_echo_request_reply_sent) { struct wolfIP s; struct wolfIP_icmp_packet icmp; + struct wolfIP_icmp_packet *reply; uint32_t frame_len; wolfIP_init(&s); @@ -5880,7 +5881,7 @@ START_TEST(test_icmp_input_echo_request_reply_sent) memset(&icmp, 0, sizeof(icmp)); icmp.ip.src = ee32(0x0A000002U); icmp.ip.dst = ee32(0x0A000001U); - icmp.ip.ttl = 64; + icmp.ip.ttl = 1; icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); icmp.type = ICMP_ECHO_REQUEST; icmp.csum = ee16(icmp_checksum(&icmp, ICMP_HEADER_LEN)); @@ -5888,7 +5889,9 @@ START_TEST(test_icmp_input_echo_request_reply_sent) icmp_input(&s, TEST_PRIMARY_IF, (struct wolfIP_ip_packet *)&icmp, frame_len); ck_assert_uint_gt(last_frame_sent_size, 0); - ck_assert_uint_eq(((struct wolfIP_icmp_packet *)last_frame_sent)->type, ICMP_ECHO_REPLY); + reply = (struct wolfIP_icmp_packet *)last_frame_sent; + ck_assert_uint_eq(reply->type, ICMP_ECHO_REPLY); + ck_assert_uint_eq(reply->ip.ttl, 64); } END_TEST diff --git a/src/wolfip.c b/src/wolfip.c index 444899e..683734b 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -5026,6 +5026,7 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p tmp = ip->src; ip->src = ip->dst; ip->dst = tmp; + ip->ttl = 64; ip->id = ipcounter_next(s); ip->csum = 0; iphdr_set_checksum(ip); From 11e421b0036ca0458cf6ea30b8408595b8e11ee0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:12:38 +0100 Subject: [PATCH 08/17] Drop ICMP replies to broadcast pings F/788 --- src/test/unit/unit.c | 25 +++++++++++++++++++++++++ src/wolfip.c | 3 +++ 2 files changed, 28 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 02dddb8..d5f282d 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -6007,6 +6007,30 @@ START_TEST(test_icmp_input_echo_request_dhcp_running_no_reply) } END_TEST +START_TEST(test_icmp_input_echo_request_broadcast_no_reply) +{ + struct wolfIP s; + struct wolfIP_icmp_packet icmp; + uint32_t frame_len; + + wolfIP_init(&s); + mock_link_init(&s); + s.dhcp_state = DHCP_OFF; + last_frame_sent_size = 0; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A000002U); + icmp.ip.dst = ee32(0xFFFFFFFFU); + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); + 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_eq(last_frame_sent_size, 0U); +} +END_TEST + START_TEST(test_icmp_input_echo_request_filter_drop) { struct wolfIP s; @@ -20330,6 +20354,7 @@ Suite *wolf_suite(void) 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); + tcase_add_test(tc_proto, test_icmp_input_echo_request_broadcast_no_reply); tcase_add_test(tc_proto, test_icmp_input_echo_request_filter_drop); tcase_add_test(tc_proto, test_icmp_input_echo_request_ip_filter_drop); tcase_add_test(tc_proto, test_icmp_input_echo_request_eth_filter_drop); diff --git a/src/wolfip.c b/src/wolfip.c index 683734b..2749fb0 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -5019,6 +5019,9 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p return; } if (!DHCP_IS_RUNNING(s) && (icmp->type == ICMP_ECHO_REQUEST)) { + ip4 dst = ee32(ip->dst); + if (IS_IP_BCAST(dst) || IS_IP_MCAST(dst)) + return; icmp->type = ICMP_ECHO_REPLY; /* Recompute full ICMP checksum for portability */ icmp->csum = 0; From 3e895373fd695bb46ff1893ecf27e63d1ce39946 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:14:27 +0100 Subject: [PATCH 09/17] Remove dead SEQ_DIFF macro F/780 --- src/wolfip.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wolfip.c b/src/wolfip.c index 2749fb0..d3cea3f 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -3127,8 +3127,6 @@ static void tcp_rto_update_from_sample(struct tsocket *t, uint32_t sample_ms) t->sock.tcp.rto = tcp_rto_clamp(rto_ms); } -#define SEQ_DIFF(a,b) ((a - b) > 0x7FFFFFFF) ? (b - a) : (a - b) - /* Return true if a <= b * Take into account wrapping. */ From 5e271efeb3c7298b8371581d9f4b78e30b09c6d7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:16:27 +0100 Subject: [PATCH 10/17] Validate ESP padding on unwrap F/789 --- src/test/unit/unit_esp.c | 62 ++++++++++++++++++++++++++++++++++++++++ src/wolfesp.c | 14 +++++++++ 2 files changed, 76 insertions(+) diff --git a/src/test/unit/unit_esp.c b/src/test/unit/unit_esp.c index 606cefb..f37c827 100644 --- a/src/test/unit/unit_esp.c +++ b/src/test/unit/unit_esp.c @@ -733,6 +733,67 @@ START_TEST(test_unwrap_pad_too_big) } END_TEST +START_TEST(test_unwrap_invalid_pad_pattern) +{ + uint8_t buf[LINK_MTU + 128]; + uint32_t frame_len; + uint8_t ref[64]; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; + int ret; + uint32_t i = 0; + uint16_t ip_len; + uint8_t *pad_len = NULL; + uint8_t *padding = NULL; + wolfIP_esp_sa *esp_sa = NULL; + uint8_t *icv = NULL; + + for (i = 0U; i < sizeof(ref); i++) { + ref[i] = (uint8_t)(i & 0xFFU); + } + esp_setup(); + + ret = wolfIP_esp_sa_new_hmac(0, (uint8_t *)spi_rt, + atoip4(T_SRC), atoip4(T_DST), + ESP_AUTH_SHA256_RFC4868, k_auth16, sizeof(k_auth16), + ESP_ICVLEN_HMAC_128); + ck_assert_int_eq(ret, 0); + esp_sa = esp_sa_get(0, (uint8_t *)spi_rt); + ck_assert_ptr_nonnull(esp_sa); + + ret = wolfIP_esp_sa_new_hmac(1, (uint8_t *)spi_rt, + atoip4(T_SRC), atoip4(T_DST), + ESP_AUTH_SHA256_RFC4868, k_auth16, sizeof(k_auth16), + ESP_ICVLEN_HMAC_128); + ck_assert_int_eq(ret, 0); + + frame_len = build_ip_packet(buf, sizeof(buf), WI_IPPROTO_UDP, + ref, sizeof(ref)); + ip_len = (uint16_t)(frame_len - ETH_HEADER_LEN); + + ret = esp_transport_wrap(ip, &ip_len); + ck_assert_int_eq(ret, 0); + + pad_len = ip->data + ip_len - IP_HEADER_LEN - ESP_ICVLEN_HMAC_128 + - ESP_NEXT_HEADER_LEN - ESP_PADDING_LEN; + ck_assert_uint_eq(*pad_len, 2U); + padding = pad_len - *pad_len; + padding[0] ^= 0x7FU; + + icv = ip->data + ip_len - IP_HEADER_LEN - ESP_ICVLEN_HMAC_128; + ret = esp_calc_icv_hmac(icv, esp_sa, ip->data, ip_len - IP_HEADER_LEN); + ck_assert_int_eq(ret, 0); + + frame_len = (uint32_t)ip_len + ETH_HEADER_LEN; + ip->proto = 0x32U; + ip->len = ee16(ip_len); + ip->csum = 0U; + iphdr_set_checksum(ip); + + ret = esp_transport_unwrap(ip, &frame_len); + ck_assert_int_eq(ret, -1); +} +END_TEST + /* * full enc/dec round-trips * */ @@ -1193,6 +1254,7 @@ static Suite *esp_suite(void) tcase_add_test(tc, test_unwrap_unknown_spi); tcase_add_test(tc, test_unwrap_below_min_len); tcase_add_test(tc, test_unwrap_pad_too_big); + tcase_add_test(tc, test_unwrap_invalid_pad_pattern); suite_add_tcase(s, tc); /* Crypto round-trips */ diff --git a/src/wolfesp.c b/src/wolfesp.c index 0315add..fb30028 100644 --- a/src/wolfesp.c +++ b/src/wolfesp.c @@ -1400,6 +1400,20 @@ esp_transport_unwrap(struct wolfIP_ip_packet *ip, uint32_t * frame_len) return -1; } } + if (pad_len > 0) { + const uint8_t *padding = ip->data + esp_len - esp_sa->icv_len + - ESP_NEXT_HEADER_LEN - ESP_PADDING_LEN + - pad_len; + uint8_t i; + + for (i = 0; i < pad_len; i++) { + if (padding[i] != (uint8_t)(i + 1U)) { + ESP_LOG("error: esp invalid padding at %u: got %u expected %u\n", + i, padding[i], (uint8_t)(i + 1U)); + return -1; + } + } + } #ifdef DEBUG_ESP wolfIP_print_esp(esp_sa, ip->data, esp_len, pad_len, nxt_hdr); From 4d0cf3013f282cd89093323ccbfaabbca1c93f33 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:18:34 +0100 Subject: [PATCH 11/17] Reject short IP lengths in ESP wrap F/791 --- src/test/unit/unit_esp.c | 27 +++++++++++++++++++++++++++ src/wolfesp.c | 5 +++++ 2 files changed, 32 insertions(+) diff --git a/src/test/unit/unit_esp.c b/src/test/unit/unit_esp.c index f37c827..62ac239 100644 --- a/src/test/unit/unit_esp.c +++ b/src/test/unit/unit_esp.c @@ -1210,6 +1210,32 @@ START_TEST(test_wrap_no_matching_sa) } END_TEST +START_TEST(test_wrap_rejects_ip_len_below_header) +{ + static uint8_t buf[70000]; + struct wolfIP_ip_packet *ip = (struct wolfIP_ip_packet *)buf; + uint16_t ip_len = (uint16_t)(IP_HEADER_LEN - 1U); + int ret; + + memset(buf, 0, sizeof(buf)); + esp_setup(); + + ret = wolfIP_esp_sa_new_hmac(0, (uint8_t *)spi_rt, + atoip4(T_SRC), atoip4(T_DST), + ESP_AUTH_SHA256_RFC4868, k_auth16, sizeof(k_auth16), + ESP_ICVLEN_HMAC_128); + ck_assert_int_eq(ret, 0); + + ip->dst = ee32(atoip4(T_DST)); + ip->src = ee32(atoip4(T_SRC)); + ip->proto = WI_IPPROTO_UDP; + ip->len = ee16(ip_len); + + ret = esp_transport_wrap(ip, &ip_len); + ck_assert_int_eq(ret, -1); +} +END_TEST + static Suite *esp_suite(void) { Suite *s; @@ -1283,6 +1309,7 @@ static Suite *esp_suite(void) /* No-SA outbound path */ tc = tcase_create("no_sa"); tcase_add_test(tc, test_wrap_no_matching_sa); + tcase_add_test(tc, test_wrap_rejects_ip_len_below_header); suite_add_tcase(s, tc); return s; diff --git a/src/wolfesp.c b/src/wolfesp.c index fb30028..ceb76b3 100644 --- a/src/wolfesp.c +++ b/src/wolfesp.c @@ -1477,6 +1477,11 @@ esp_transport_wrap(struct wolfIP_ip_packet *ip, uint16_t * ip_len) wolfIP_esp_sa * esp_sa = NULL; uint8_t iv_len = 0; + if (orig_ip_len < IP_HEADER_LEN) { + ESP_LOG("error: ip_len below header: %u\n", orig_ip_len); + return -1; + } + /* todo: priority, proto / port filtering. currently this grabs * the first dst match. */ for (size_t i = 0; i < out_sa_num; ++i) { From df6393d3c91d7de3de54d71fcc63d3843c008d00 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:20:38 +0100 Subject: [PATCH 12/17] Parenthesize MAX_TIMERS macro F/781 --- src/wolfip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wolfip.c b/src/wolfip.c index d3cea3f..d588ffc 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -70,7 +70,7 @@ struct wolfIP_udp_datagram; struct wolfIP_icmp_packet; /* Fixed size binary heap: each element is a timer. */ -#define MAX_TIMERS MAX_TCPSOCKETS * 3 +#define MAX_TIMERS (MAX_TCPSOCKETS * 3) /* Constants */ #define ICMP_ECHO_REPLY 0 From 5067faecb3b40a2fb9119d366dce9ae5ff0de4c5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:21:48 +0100 Subject: [PATCH 13/17] Parenthesize esp_rfc4106_salt macro F/792 --- src/wolfesp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wolfesp.c b/src/wolfesp.c index ceb76b3..ea2bef7 100644 --- a/src/wolfesp.c +++ b/src/wolfesp.c @@ -848,9 +848,9 @@ esp_des3_rfc2451_enc(const wolfIP_esp_sa * esp_sa, uint8_t * esp_data, * N octets are the AES key, and the remaining four octets are used as the * salt value in the nonce. * */ -#define esp_rfc4106_salt(esp_sa) (esp_sa)->enc_key \ +#define esp_rfc4106_salt(esp_sa) ((esp_sa)->enc_key \ + (esp_sa)->enc_key_len \ - - ESP_GCM_RFC4106_SALT_LEN + - ESP_GCM_RFC4106_SALT_LEN) /* Deterministic iv construction using pre-iv salt and sequence number. * NIST SP 800-38D, section 8.2.1 Deterministic Construction, using From 6ab41685a984456f3285d21941fd470fd6e745eb Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:24:59 +0100 Subject: [PATCH 14/17] Cap initial TCP congestion window F/785 --- src/test/unit/unit.c | 8 ++++++++ src/wolfip.c | 16 +++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index d5f282d..8053523 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -17467,6 +17467,13 @@ START_TEST(test_tcp_persist_cb_sends_one_byte_probe) } END_TEST +START_TEST(test_tcp_initial_cwnd_caps_to_iw10_and_half_rwnd) +{ + ck_assert_uint_eq(tcp_initial_cwnd(65535U, 1460U), 14600U); + ck_assert_uint_eq(tcp_initial_cwnd(4000U, 1460U), 2000U); +} +END_TEST + START_TEST(test_tcp_persist_probe_byte_matches_snd_una_offset) { struct wolfIP s; @@ -20012,6 +20019,7 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_poll_tcp_residual_window_gates_data_segment); tcase_add_test(tc_utils, test_poll_tcp_residual_window_allows_exact_fit); tcase_add_test(tc_utils, test_poll_tcp_zero_window_arms_persist); + tcase_add_test(tc_utils, test_tcp_initial_cwnd_caps_to_iw10_and_half_rwnd); tcase_add_test(tc_utils, test_tcp_persist_cb_sends_one_byte_probe); tcase_add_test(tc_utils, test_tcp_persist_probe_byte_matches_snd_una_offset); tcase_add_test(tc_utils, test_tcp_input_window_reopen_stops_persist); diff --git a/src/wolfip.c b/src/wolfip.c index d588ffc..f2590e9 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -1916,15 +1916,13 @@ static void icmp_try_recv(struct wolfIP *s, unsigned int if_idx, /* TCP */ static uint32_t tcp_initial_cwnd(uint32_t peer_rwnd, uint32_t smss) { - uint32_t cwnd = peer_rwnd / 2U; - uint32_t tx_half = TXBUF_SIZE / 2U; - uint32_t min_cwnd = 2U * smss; - - if (cwnd > tx_half) - cwnd = tx_half; - if (cwnd < min_cwnd) - cwnd = min_cwnd; - return cwnd; + uint32_t iw10 = smss * 10U; + uint32_t rwnd_cap = peer_rwnd / 2U; + + /* Intentional deviation from pure RFC 6928 IW10: cap the initial cwnd to + * min(10*SMSS, peer_rwnd/2) so startup remains bounded by a fraction of the + * peer's advertised receive window. */ + return (rwnd_cap < iw10) ? rwnd_cap : iw10; } static uint32_t tcp_initial_ssthresh(uint32_t peer_rwnd) From 12ac0a9bc1aab5cd3e919130c814a80912261534 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:30:55 +0100 Subject: [PATCH 15/17] Guard recvmsg scatter on positive length CID 1686341 --- src/port/posix/bsd_socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index 7ecd5ac..da3bde6 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -933,7 +933,7 @@ int wolfIP_sock_recvmsg(struct wolfIP *ipstack, int sockfd, struct msghdr *msg, break; (void)wolfIP_sock_poll(ipstack, &pfd, 1, -1); } - if (ret >= 0 && msg->msg_iovlen > 1) { + if (ret > 0 && msg->msg_iovlen > 1 && buf) { wolfip_scatter_iov(msg, buf, (size_t)ret); } if (heap_buf) From a17bcbf69c984fa591e75c5efc36735e8cb92ace Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 11:44:04 +0100 Subject: [PATCH 16/17] Restored cwnd min as suggested in copilot's comment --- src/test/unit/unit.c | 3 ++- src/wolfip.c | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 8053523..3ad8ae8 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -17470,7 +17470,8 @@ END_TEST START_TEST(test_tcp_initial_cwnd_caps_to_iw10_and_half_rwnd) { ck_assert_uint_eq(tcp_initial_cwnd(65535U, 1460U), 14600U); - ck_assert_uint_eq(tcp_initial_cwnd(4000U, 1460U), 2000U); + ck_assert_uint_eq(tcp_initial_cwnd(4000U, 1460U), 2920U); + ck_assert_uint_eq(tcp_initial_cwnd(0U, 1460U), 2920U); } END_TEST diff --git a/src/wolfip.c b/src/wolfip.c index f2590e9..e98b9ad 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -1918,10 +1918,14 @@ static uint32_t tcp_initial_cwnd(uint32_t peer_rwnd, uint32_t smss) { uint32_t iw10 = smss * 10U; uint32_t rwnd_cap = peer_rwnd / 2U; + uint32_t min_cwnd = smss * 2U; /* Intentional deviation from pure RFC 6928 IW10: cap the initial cwnd to - * min(10*SMSS, peer_rwnd/2) so startup remains bounded by a fraction of the - * peer's advertised receive window. */ + * min(10*SMSS, max(2*SMSS, peer_rwnd/2)). This retains an IW10 upper bound + * while avoiding a dead cwnd when the peer advertises a zero or tiny + * receive window; actual sending remains separately bounded by peer_rwnd. */ + if (rwnd_cap < min_cwnd) + rwnd_cap = min_cwnd; return (rwnd_cap < iw10) ? rwnd_cap : iw10; } From 238db2ea70abaf541ca7aab0dff62c44573484b3 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 16 Mar 2026 14:15:28 +0100 Subject: [PATCH 17/17] Handle directed broadcast destinations F/788 --- src/test/unit/unit.c | 70 ++++++++++++++++++++++++++++++++++++++++++++ src/wolfip.c | 63 +++++++++++++++++++++++++++++---------- 2 files changed, 118 insertions(+), 15 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 3ad8ae8..4a7d3f1 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -6031,6 +6031,31 @@ START_TEST(test_icmp_input_echo_request_broadcast_no_reply) } END_TEST +START_TEST(test_icmp_input_echo_request_directed_broadcast_no_reply) +{ + struct wolfIP s; + struct wolfIP_icmp_packet icmp; + uint32_t frame_len; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + s.dhcp_state = DHCP_OFF; + last_frame_sent_size = 0; + + memset(&icmp, 0, sizeof(icmp)); + icmp.ip.src = ee32(0x0A000002U); + icmp.ip.dst = ee32(0x0A0000FFU); + icmp.ip.len = ee16(IP_HEADER_LEN + ICMP_HEADER_LEN); + 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_eq(last_frame_sent_size, 0U); +} +END_TEST + START_TEST(test_icmp_input_echo_request_filter_drop) { struct wolfIP s; @@ -9164,6 +9189,11 @@ START_TEST(test_forward_prepare_paths) ck_assert_int_eq(ret, 1); ck_assert_int_eq(broadcast, 1); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + ret = wolfIP_forward_prepare(&s, TEST_PRIMARY_IF, 0x0A0000FFU, mac, &broadcast); + ck_assert_int_eq(ret, 1); + ck_assert_int_eq(broadcast, 1); + last_frame_sent_size = 0; s.last_tick = 2000; ret = wolfIP_forward_prepare(&s, TEST_PRIMARY_IF, 0x0A000002U, mac, &broadcast); @@ -18318,6 +18348,42 @@ START_TEST(test_ip_is_local_conf_variants) } END_TEST +START_TEST(test_wolfip_ip_is_multicast_variants) +{ + ck_assert_int_eq(wolfIP_ip_is_multicast(0xE0000001U), 1); + ck_assert_int_eq(wolfIP_ip_is_multicast(0x0A000001U), 0); +} +END_TEST + +START_TEST(test_wolfip_ip_is_broadcast_variants) +{ + struct wolfIP s; + + ck_assert_int_eq(wolfIP_ip_is_broadcast(NULL, 0xFFFFFFFFU), 1); + ck_assert_int_eq(wolfIP_ip_is_broadcast(NULL, 0x0A000001U), 0); + + setup_stack_with_two_ifaces(&s, 0x0A000001U, 0xC0A80101U); + ck_assert_int_eq(wolfIP_ip_is_broadcast(&s, 0x0A0000FFU), 1); + ck_assert_int_eq(wolfIP_ip_is_broadcast(&s, 0xC0A801FFU), 1); + ck_assert_int_eq(wolfIP_ip_is_broadcast(&s, 0x0A000001U), 0); +} +END_TEST + +START_TEST(test_wolfip_ip_is_broadcast_skips_unsuitable_configs) +{ + struct wolfIP s; + + wolfIP_init(&s); + s.if_count = 2; + s.ipconf[0].ip = 0x0A000001U; + s.ipconf[0].mask = 0xFFFFFFFFU; + s.ipconf[1].ip = IPADDR_ANY; + s.ipconf[1].mask = 0xFFFFFF00U; + + ck_assert_int_eq(wolfIP_ip_is_broadcast(&s, 0x0A0000FFU), 0); +} +END_TEST + #if WOLFIP_ENABLE_LOOPBACK START_TEST(test_wolfip_loopback_defaults) { @@ -20092,6 +20158,9 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_dns_send_query_invalid_name); tcase_add_test(tc_utils, test_dns_wrapper_apis); tcase_add_test(tc_utils, test_wolfip_static_instance_apis); + tcase_add_test(tc_utils, test_wolfip_ip_is_multicast_variants); + tcase_add_test(tc_utils, test_wolfip_ip_is_broadcast_variants); + tcase_add_test(tc_utils, test_wolfip_ip_is_broadcast_skips_unsuitable_configs); tcase_add_test(tc_utils, test_tcp_rto_cb_resets_flags_and_arms_timer); tcase_add_test(tc_utils, test_tcp_rto_cb_no_pending_resets_backoff); tcase_add_test(tc_utils, test_tcp_rto_cb_skips_unsent_desc); @@ -20364,6 +20433,7 @@ Suite *wolf_suite(void) 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); tcase_add_test(tc_proto, test_icmp_input_echo_request_broadcast_no_reply); + tcase_add_test(tc_proto, test_icmp_input_echo_request_directed_broadcast_no_reply); tcase_add_test(tc_proto, test_icmp_input_echo_request_filter_drop); tcase_add_test(tc_proto, test_icmp_input_echo_request_ip_filter_drop); tcase_add_test(tc_proto, test_icmp_input_echo_request_eth_filter_drop); diff --git a/src/wolfip.c b/src/wolfip.c index e98b9ad..e229ea2 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -135,9 +135,6 @@ struct wolfIP_icmp_packet; #define WOLFIP_POLL_BUDGET 128 /* Macros */ -#define IS_IP_BCAST(ip) ((ip) == 0xFFFFFFFFU) -#define IS_IP_MCAST(ip) (((ip) & 0xF0000000U) == 0xE0000000U) - #define PKT_FLAG_SENT 0x01U #define PKT_FLAG_ACKED 0x02U #define PKT_FLAG_FIN 0x04U @@ -1402,6 +1399,36 @@ static inline int ip_is_local_conf(const struct ipconf *conf, ip4 addr) return ((addr & conf->mask) == (conf->ip & conf->mask)); } +static inline int wolfIP_ip_is_multicast(ip4 addr) +{ + return ((addr & 0xF0000000U) == 0xE0000000U); +} + +static int wolfIP_ip_is_broadcast(const struct wolfIP *s, ip4 addr) +{ + unsigned int i; + + if (addr == 0xFFFFFFFFU) + return 1; + if (!s) + return 0; + + for (i = 0; i < s->if_count; i++) { + const struct ipconf *conf = &s->ipconf[i]; + ip4 directed_bcast; + + if (conf->ip == IPADDR_ANY) + continue; + if (conf->mask == 0 || conf->mask == 0xFFFFFFFFU) + continue; + + directed_bcast = (conf->ip & conf->mask) | (~conf->mask); + if (addr == directed_bcast) + return 1; + } + return 0; +} + #if WOLFIP_ENABLE_FORWARDING static int wolfIP_forward_interface(struct wolfIP *s, unsigned int in_if, ip4 dest) { @@ -1426,7 +1453,7 @@ static int wolfIP_forward_interface(struct wolfIP *s, unsigned int in_if, ip4 de static inline ip4 wolfIP_select_nexthop(const struct ipconf *conf, ip4 dest) { - if (IS_IP_BCAST(dest)) + if (dest == 0xFFFFFFFFU) return dest; if (!conf) return dest; @@ -1452,7 +1479,7 @@ static unsigned int wolfIP_route_for_ip(struct wolfIP *s, ip4 dest) if (WOLFIP_PRIMARY_IF_IDX < s->if_count) default_if = WOLFIP_PRIMARY_IF_IDX; - if (dest == IPADDR_ANY || IS_IP_BCAST(dest)) + if (dest == IPADDR_ANY || wolfIP_ip_is_broadcast(s, dest)) return default_if; for (i = 0; i < s->if_count; i++) { @@ -1855,8 +1882,10 @@ static void udp_try_recv(struct wolfIP *s, unsigned int if_idx, int dst_match = 0; if (dst_ip != IPADDR_ANY && src_ip != IPADDR_ANY && - !IS_IP_BCAST(dst_ip) && !IS_IP_BCAST(src_ip) && - !IS_IP_MCAST(dst_ip) && !IS_IP_MCAST(src_ip)) { + !wolfIP_ip_is_broadcast(s, dst_ip) && + !wolfIP_ip_is_broadcast(s, src_ip) && + !wolfIP_ip_is_multicast(dst_ip) && + !wolfIP_ip_is_multicast(src_ip)) { (void)wolfIP_if_for_local_ip(s, dst_ip, &dst_match); if (dst_match) wolfIP_send_port_unreachable(s, if_idx, &udp->ip); @@ -2949,7 +2978,7 @@ static int wolfIP_forward_prepare(struct wolfIP *s, unsigned int out_if, *broadcast = 0; return 1; } - if (IS_IP_BCAST(dest)) { + if (wolfIP_ip_is_broadcast(s, dest)) { *broadcast = 1; return 1; } @@ -3768,7 +3797,9 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, ip4 dst = ee32(tcp->ip.dst); int dst_match = 0; - if (dst != IPADDR_ANY && !IS_IP_BCAST(dst) && !IS_IP_MCAST(dst)) { + if (dst != IPADDR_ANY && + !wolfIP_ip_is_broadcast(S, dst) && + !wolfIP_ip_is_multicast(dst)) { (void)wolfIP_if_for_local_ip(S, dst, &dst_match); if (dst_match) tcp_send_reset_reply(S, if_idx, tcp); @@ -5020,7 +5051,7 @@ static void icmp_input(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ip_p } if (!DHCP_IS_RUNNING(s) && (icmp->type == ICMP_ECHO_REQUEST)) { ip4 dst = ee32(ip->dst); - if (IS_IP_BCAST(dst) || IS_IP_MCAST(dst)) + if (wolfIP_ip_is_broadcast(s, dst) || wolfIP_ip_is_multicast(dst)) return; icmp->type = ICMP_ECHO_REPLY; /* Recompute full ICMP checksum for portability */ @@ -5939,7 +5970,7 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, if (version == 4 && ip_hlen >= IP_HEADER_LEN) { ip4 dest = ee32(ip->dst); int is_local = 0; - if (dest == IPADDR_ANY || IS_IP_BCAST(dest)) { + if (dest == IPADDR_ANY || wolfIP_ip_is_broadcast(s, dest)) { is_local = 1; } else { for (i = 0; i < s->if_count; i++) { @@ -6685,12 +6716,13 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) if (loop) memcpy(t->nexthop_mac, loop->mac, 6); } else if (!wolfIP_ll_is_non_ethernet(s, tx_if)) { - if ((!IS_IP_BCAST(nexthop) && (arp_lookup(s, tx_if, nexthop, t->nexthop_mac) < 0))) { + if ((!wolfIP_ip_is_broadcast(s, nexthop) && + (arp_lookup(s, tx_if, nexthop, t->nexthop_mac) < 0))) { /* Send ARP request */ arp_request(s, tx_if, nexthop); break; } - if (IS_IP_BCAST(nexthop)) + if (wolfIP_ip_is_broadcast(s, nexthop)) memset(t->nexthop_mac, 0xFF, 6); } #endif @@ -6743,11 +6775,12 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) if (loop) memcpy(t->nexthop_mac, loop->mac, 6); } else if (!wolfIP_ll_is_non_ethernet(s, tx_if)) { - if ((!IS_IP_BCAST(nexthop) && (arp_lookup(s, tx_if, nexthop, t->nexthop_mac) < 0))) { + if ((!wolfIP_ip_is_broadcast(s, nexthop) && + (arp_lookup(s, tx_if, nexthop, t->nexthop_mac) < 0))) { arp_request(s, tx_if, nexthop); break; } - if (IS_IP_BCAST(nexthop)) + if (wolfIP_ip_is_broadcast(s, nexthop)) memset(t->nexthop_mac, 0xFF, 6); } #endif