From 6d87ec4cf19f72ba63bba19a5e72e4280f4e22c4 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 01:06:28 +0100 Subject: [PATCH 1/9] Fixed TCP sending fifo wrapping. Fixed bsd_socket events handling. --- src/port/posix/bsd_socket.c | 2 +- src/wolfip.c | 41 ++++++++++++++++++++++++++++--------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index b1c7a8f..a7b6545 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -369,7 +369,7 @@ static int wolfip_fd_is_nonblock(int public_fd) return host_##call(user_fd, ## __VA_ARGS__); \ } else { \ int __wolfip_internal = wolfip_fd_internal_from_public(user_fd); \ - pthread_mutex_lock(&wolfIP_mutex); \ + while (pthread_mutex_trylock(&wolfIP_mutex) != 0) { usleep(1000); } \ if (__wolfip_internal >= 0) { \ int __wolfip_retval; \ int __wolfip_nonblock = wolfip_fd_is_nonblock(user_fd); \ diff --git a/src/wolfip.c b/src/wolfip.c index 647a8e7..363ff27 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -173,7 +173,13 @@ static uint32_t fifo_space(struct fifo *f) } else return 0; } } else { - ret = f->size - (f->tail - f->head); + /* When wrapped, only space before tail is contiguous. */ + if (f->head == f->h_wrap) + f->head = 0; + if (f->head < f->tail) + ret = f->tail - f->head; + else + ret = 0; } return ret; } @@ -241,8 +247,12 @@ static int fifo_push(struct fifo *f, void *data, uint32_t len) /* Ensure 4-byte alignment in the buffer */ if (f->head % 4) f->head += 4 - (f->head % 4); - if (fifo_space(f) < (sizeof(struct pkt_desc) + len)) + if (fifo_space(f) < (sizeof(struct pkt_desc) + len)) { + return -1; + } + if ((f->head + sizeof(struct pkt_desc) + len) > f->size) { return -1; + } desc.pos = f->head; desc.len = len; memcpy((uint8_t *)f->data + f->head, &desc, sizeof(struct pkt_desc)); @@ -1675,6 +1685,7 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } if (SEQ_DIFF(ee32(seg->seq) + seg_len, ack) < fifo_len(&t->sock.tcp.txbuf)) { desc->flags |= PKT_FLAG_ACKED; + desc->flags &= ~PKT_FLAG_SENT; desc = fifo_next(&t->sock.tcp.txbuf, desc); ack_count++; } else { @@ -2226,7 +2237,10 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len tsopt->pad = 0x01; tsopt->eoo = 0x00; memcpy((uint8_t *)tcp->data + TCP_OPTIONS_LEN, (const uint8_t *)buf + sent, payload_len); - fifo_push(&ts->sock.tcp.txbuf, tcp, sizeof(struct wolfIP_tcp_seg) + TCP_OPTIONS_LEN + payload_len); + if (fifo_push(&ts->sock.tcp.txbuf, tcp, + sizeof(struct wolfIP_tcp_seg) + TCP_OPTIONS_LEN + payload_len) < 0) { + break; + } sent += payload_len; ts->sock.tcp.seq += payload_len; } @@ -2255,8 +2269,9 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len return -1; if (len > WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) return -1; /* Fragmentation not supported */ - if (fifo_space(&ts->sock.udp.txbuf) < len) + if (fifo_space(&ts->sock.udp.txbuf) < len) { return -WOLFIP_EAGAIN; + } if (ts->src_port == 0) { ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port < 1024) @@ -2280,7 +2295,8 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len udp->len = ee16(len + UDP_HEADER_LEN); udp->csum = 0; memcpy(udp->data, buf, len); - fifo_push(&ts->sock.udp.txbuf, udp, sizeof(struct wolfIP_udp_datagram) + len); + if (fifo_push(&ts->sock.udp.txbuf, udp, sizeof(struct wolfIP_udp_datagram) + len) < 0) + return -WOLFIP_EAGAIN; return len; } else if (IS_SOCKET_ICMP(sockfd)) { const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)dest_addr; @@ -2299,8 +2315,9 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len return -1; if (payload_len < ICMP_HEADER_LEN || payload_len > (WI_IP_MTU - IP_HEADER_LEN)) return -WOLFIP_EINVAL; - if (fifo_space(&ts->sock.udp.txbuf) < payload_len) + if (fifo_space(&ts->sock.udp.txbuf) < payload_len) { return -WOLFIP_EAGAIN; + } if (ts->src_port == 0) { ts->src_port = (uint16_t)(wolfIP_getrandom() & 0xFFFF); if (ts->src_port == 0) @@ -2323,7 +2340,8 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len icmp_set_echo_id(icmp, ts->src_port); icmp->csum = 0; icmp->csum = ee16(icmp_checksum(icmp, (uint16_t)payload_len)); - fifo_push(&ts->sock.udp.txbuf, icmp, sizeof(struct wolfIP_ip_packet) + payload_len); + if (fifo_push(&ts->sock.udp.txbuf, icmp, sizeof(struct wolfIP_ip_packet) + payload_len) < 0) + return -WOLFIP_EAGAIN; return (int)payload_len; } else return -1; } @@ -3914,13 +3932,16 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) tcp->ack = ee32(ts->sock.tcp.ack); tcp->win = ee16(queue_space(&ts->sock.tcp.rxbuf)); 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) + if (wolfIP_filter_notify_tcp(WOLFIP_FILT_SENDING, ts->S, tx_if, tcp, desc->len) != 0) { break; - if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip, desc->len) != 0) + } + if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip, desc->len) != 0) { break; + } #ifdef ETHERNET - if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) + if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) { break; + } #endif { struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, tx_if); From f517b8a9fa4cadbf8bfdfa96ba3177cd49d9ac03 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 08:45:59 +0100 Subject: [PATCH 2/9] Added more unit test to extend coverage --- src/test/unit/unit.c | 1858 ++++++++++++++++++++++++++++++++++++------ src/wolfip.c | 2 +- 2 files changed, 1620 insertions(+), 240 deletions(-) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index f59f884..5580d39 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -48,9 +48,14 @@ /* MOCKS */ /* pseudo random number generator to mock the random number generator */ +static int test_rand_override_enabled; +static uint32_t test_rand_override_value; + uint32_t wolfIP_getrandom(void) { unsigned int seed = 0xDAC0FFEE; + if (test_rand_override_enabled) + return test_rand_override_value; srandom(seed); return random(); } @@ -808,6 +813,52 @@ START_TEST(test_filter_socket_event_unknown_proto) } END_TEST +START_TEST(test_filter_socket_event_proto_variants) +{ + struct wolfIP s; + struct tsocket ts; + ip4 local_ip = 0x0A000001U; + ip4 remote_ip = 0x0A000002U; + uint16_t local_port = 1234; + uint16_t remote_port = 4321; + + wolfIP_init(&s); + memset(&ts, 0, sizeof(ts)); + ts.S = &s; + ts.if_idx = TEST_PRIMARY_IF; + + wolfIP_filter_set_callback(test_filter_cb, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_CONNECTING)); + filter_cb_calls = 0; + + ts.proto = WI_IPPROTO_TCP; + (void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, &ts, + local_ip, local_port, remote_ip, remote_port); + ck_assert_int_eq(filter_cb_calls, 1); + ck_assert_uint_eq(filter_last_event.meta.ip_proto, WOLFIP_FILTER_PROTO_TCP); + ck_assert_uint_eq(filter_last_event.meta.l4.tcp.src_port, ee16(local_port)); + ck_assert_uint_eq(filter_last_event.meta.l4.tcp.dst_port, ee16(remote_port)); + + ts.proto = WI_IPPROTO_UDP; + (void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, &ts, + local_ip, local_port, remote_ip, remote_port); + ck_assert_int_eq(filter_cb_calls, 2); + ck_assert_uint_eq(filter_last_event.meta.ip_proto, WOLFIP_FILTER_PROTO_UDP); + ck_assert_uint_eq(filter_last_event.meta.l4.udp.src_port, ee16(local_port)); + ck_assert_uint_eq(filter_last_event.meta.l4.udp.dst_port, ee16(remote_port)); + + ts.proto = WI_IPPROTO_ICMP; + (void)wolfIP_filter_notify_socket_event(WOLFIP_FILT_CONNECTING, &s, &ts, + local_ip, local_port, remote_ip, remote_port); + ck_assert_int_eq(filter_cb_calls, 3); + ck_assert_uint_eq(filter_last_event.meta.ip_proto, WOLFIP_FILTER_PROTO_ICMP); + ck_assert_uint_eq(filter_last_event.meta.l4.icmp.type, 0); + ck_assert_uint_eq(filter_last_event.meta.l4.icmp.code, 0); + + wolfIP_filter_set_callback(NULL, NULL); +} +END_TEST + START_TEST(test_filter_setters_and_get_mask) { wolfIP_filter_set_mask(0x1234U); @@ -1129,6 +1180,176 @@ START_TEST(test_sock_bind_wrong_family) } END_TEST +START_TEST(test_sock_bind_invalid_fd) +{ + struct wolfIP s; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + + 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, -1, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_bind(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_int_eq(wolfIP_sock_bind(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_int_eq(wolfIP_sock_bind(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_bind_tcp_state_not_closed) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_ESTABLISHED; + + 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_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_bind_tcp_filter_blocks) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + ts->local_ip = 0; + ts->src_port = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + filter_block_reason = WOLFIP_FILT_BINDING; + filter_block_calls = 0; + wolfIP_filter_set_callback(test_filter_cb_block, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_BINDING)); + + ck_assert_int_eq(wolfIP_sock_bind(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_uint_eq(ts->local_ip, 0U); + ck_assert_uint_eq(ts->src_port, 0U); + + wolfIP_filter_set_callback(NULL, NULL); +} +END_TEST + +START_TEST(test_sock_bind_udp_src_port_nonzero) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 1234; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(5678); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_bind_udp_filter_blocks) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(2222); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + filter_block_reason = WOLFIP_FILT_BINDING; + filter_block_calls = 0; + wolfIP_filter_set_callback(test_filter_cb_block, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_BINDING)); + + ck_assert_int_eq(wolfIP_sock_bind(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_uint_eq(ts->local_ip, 0U); + ck_assert_uint_eq(ts->src_port, 0U); + + wolfIP_filter_set_callback(NULL, NULL); +} +END_TEST + +START_TEST(test_sock_bind_icmp_success) +{ + struct wolfIP s; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->src_port = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(7); + sin.sin_addr.s_addr = ee32(0x0A000001U); + + ck_assert_int_eq(wolfIP_sock_bind(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->src_port, 7U); +} +END_TEST + START_TEST(test_sock_connect_wrong_family) { struct wolfIP s; @@ -1171,6 +1392,41 @@ START_TEST(test_sock_accept_error_paths) } END_TEST +START_TEST(test_sock_accept_non_tcp_socket_sets_addrlen) +{ + struct wolfIP s; + int udp_sd; + struct wolfIP_sockaddr_in sin; + socklen_t alen = sizeof(sin); + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + ck_assert_int_eq(wolfIP_sock_accept(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, &alen), -WOLFIP_EINVAL); + ck_assert_uint_eq(alen, sizeof(sin)); +} +END_TEST + +START_TEST(test_sock_accept_null_addr_with_addrlen) +{ + struct wolfIP s; + int tcp_sd; + socklen_t alen = 123; + + wolfIP_init(&s); + mock_link_init(&s); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + + ck_assert_int_eq(wolfIP_sock_accept(&s, tcp_sd, NULL, &alen), -1); + ck_assert_uint_eq(alen, sizeof(struct wolfIP_sockaddr_in)); +} +END_TEST + START_TEST(test_sock_recvfrom_short_addrlen) { struct wolfIP s; @@ -1305,6 +1561,26 @@ START_TEST(test_sock_connect_bad_addrlen) } END_TEST +START_TEST(test_sock_connect_tcp_bad_addrlen) +{ + struct wolfIP s; + int tcp_sd; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, (socklen_t)1), -WOLFIP_EINVAL); +} +END_TEST + START_TEST(test_sock_connect_invalid_args) { struct wolfIP s; @@ -1479,10 +1755,10 @@ START_TEST(test_sock_connect_udp_bound_local_ip_no_match) } END_TEST -START_TEST(test_sock_connect_icmp_sets_local_ip_from_conf) +START_TEST(test_sock_connect_udp_bound_local_ip_match) { struct wolfIP s; - int icmp_sd; + int udp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; ip4 local_ip = 0x0A000001U; @@ -1491,28 +1767,33 @@ START_TEST(test_sock_connect_icmp_sets_local_ip_from_conf) mock_link_init(&s); wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); - icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); - ck_assert_int_gt(icmp_sd, 0); - ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; - ts->local_ip = 0; + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->bound_local_ip = local_ip; + memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_connect(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); ck_assert_uint_eq(ts->local_ip, local_ip); + ck_assert_uint_eq(ts->if_idx, TEST_PRIMARY_IF); } END_TEST -START_TEST(test_sock_connect_icmp_falls_back_to_primary) +START_TEST(test_sock_connect_icmp_sets_local_ip_from_conf) { struct wolfIP s; int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; + ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); ck_assert_int_gt(icmp_sd, 0); @@ -1523,351 +1804,938 @@ START_TEST(test_sock_connect_icmp_falls_back_to_primary) sin.sin_addr.s_addr = ee32(0x0A000002U); ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); - ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); + ck_assert_uint_eq(ts->local_ip, local_ip); } END_TEST -START_TEST(test_sock_connect_tcp_established_returns_zero) +START_TEST(test_sock_connect_icmp_wrong_family) { struct wolfIP s; - int tcp_sd; - struct tsocket *ts; + int icmp_sd; struct wolfIP_sockaddr_in sin; wolfIP_init(&s); mock_link_init(&s); - tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(tcp_sd, 0); - ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; - ts->sock.tcp.state = TCP_ESTABLISHED; - + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = ee16(80); + sin.sin_family = 0; sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); } END_TEST -START_TEST(test_sock_connect_tcp_bound_local_ip_no_match) +START_TEST(test_sock_connect_icmp_local_ip_pre_set) { struct wolfIP s; - int tcp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); - tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(tcp_sd, 0); - ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; - ts->sock.tcp.state = TCP_CLOSED; - ts->bound_local_ip = 0x0B000001U; + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->local_ip = 0x0A000001U; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(80); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, 0x0A000001U); } END_TEST -START_TEST(test_sock_connect_tcp_local_ip_from_conf) +START_TEST(test_sock_connect_icmp_conf_null) { struct wolfIP s; - int tcp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; - ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + s.if_count = 0; - tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(tcp_sd, 0); - ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; - ts->sock.tcp.state = TCP_CLOSED; + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->local_ip = 0; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(80); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); - ck_assert_uint_eq(ts->local_ip, local_ip); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); } END_TEST -START_TEST(test_sock_connect_tcp_local_ip_from_primary) +START_TEST(test_sock_connect_icmp_falls_back_to_primary) { struct wolfIP s; - int tcp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; wolfIP_init(&s); mock_link_init(&s); - tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(tcp_sd, 0); - ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; - ts->sock.tcp.state = TCP_CLOSED; - + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->local_ip = 0; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(80); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); } END_TEST -START_TEST(test_sock_accept_negative_fd) +START_TEST(test_sock_connect_icmp_primary_ip_any) { struct wolfIP s; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; wolfIP_init(&s); mock_link_init(&s); - ck_assert_int_eq(wolfIP_sock_accept(&s, -1, NULL, NULL), -WOLFIP_EINVAL); + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); } END_TEST -START_TEST(test_sock_accept_invalid_tcp_fd) +START_TEST(test_sock_connect_icmp_primary_ip_fallback) { struct wolfIP s; - int bad_fd = MARK_TCP_SOCKET | MAX_TCPSOCKETS; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + ip4 primary_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; - ck_assert_int_eq(wolfIP_sock_accept(&s, bad_fd, NULL, NULL), -WOLFIP_EINVAL); + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0B000001U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_uint_eq(ts->local_ip, primary_ip); } END_TEST -START_TEST(test_sock_accept_success_sets_addr) +START_TEST(test_sock_connect_tcp_established_returns_zero) { struct wolfIP s; - int listen_sd; - int client_sd; - struct tsocket *listener; + int tcp_sd; + struct tsocket *ts; struct wolfIP_sockaddr_in sin; - socklen_t alen = sizeof(sin); 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); + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_ESTABLISHED; + 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); + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - client_sd = wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen); - ck_assert_int_gt(client_sd, 0); - ck_assert_uint_eq(alen, sizeof(sin)); - ck_assert_uint_eq(sin.sin_family, AF_INET); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); } END_TEST -START_TEST(test_sock_accept_no_available_socket) +START_TEST(test_sock_connect_tcp_bound_local_ip_match) { struct wolfIP s; - int listen_sd; - struct wolfIP_sockaddr_in sin; + int tcp_sd; struct tsocket *ts; - int i; + struct wolfIP_sockaddr_in sin; + ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + ts->bound_local_ip = local_ip; - 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); - - for (i = 0; i < MAX_TCPSOCKETS; i++) { - ts = &s.tcpsockets[i]; - ts->proto = WI_IPPROTO_TCP; - ts->sock.tcp.state = TCP_ESTABLISHED; - } + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_uint_eq(ts->local_ip, local_ip); + ck_assert_uint_eq(ts->if_idx, TEST_PRIMARY_IF); } END_TEST -START_TEST(test_sock_accept_no_free_socket_syn_rcvd) +START_TEST(test_sock_connect_tcp_bound_local_ip_no_match) { struct wolfIP s; - int listen_sd; - struct wolfIP_sockaddr_in sin; + int tcp_sd; struct tsocket *ts; - int i; + struct wolfIP_sockaddr_in sin; 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); + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + ts->bound_local_ip = 0x0B000001U; + 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); - ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; - ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_RCVD); - - for (i = 0; i < MAX_TCPSOCKETS; i++) { - if (i == SOCKET_UNMARK(listen_sd)) { - continue; - } - s.tcpsockets[i].proto = WI_IPPROTO_TCP; - s.tcpsockets[i].sock.tcp.state = TCP_ESTABLISHED; - } + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); } END_TEST -START_TEST(test_sock_accept_listen_no_connection) +START_TEST(test_sock_connect_tcp_filter_blocks) { struct wolfIP s; - int listen_sd; + int tcp_sd; + struct tsocket *ts; struct wolfIP_sockaddr_in sin; 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); + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + 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); + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + filter_block_reason = WOLFIP_FILT_CONNECTING; + filter_block_calls = 0; + wolfIP_filter_set_callback(test_filter_cb_block, NULL); + wolfIP_filter_set_mask(WOLFIP_FILT_MASK(WOLFIP_FILT_CONNECTING)); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + ck_assert_int_eq(ts->sock.tcp.state, TCP_CLOSED); + + wolfIP_filter_set_callback(NULL, NULL); } END_TEST - -START_TEST(test_sock_accept_bound_local_ip_no_match) +START_TEST(test_sock_connect_tcp_local_ip_from_conf) { struct wolfIP s; - int listen_sd; - int client_sd; - struct tsocket *listener; + int tcp_sd; + struct tsocket *ts; struct wolfIP_sockaddr_in sin; + ip4 local_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; - 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)]; - listener->bound_local_ip = 0x0A000001U; - listener->if_idx = TEST_PRIMARY_IF; - - inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); - s.if_count = 0; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); - client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); - ck_assert_int_gt(client_sd, 0); - ck_assert_uint_eq(listener->if_idx, TEST_PRIMARY_IF); + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_uint_eq(ts->local_ip, local_ip); } END_TEST -START_TEST(test_sock_sendto_error_paths) +START_TEST(test_sock_connect_tcp_local_ip_from_primary) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); +} +END_TEST + +START_TEST(test_sock_connect_tcp_primary_ip_fallback) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + ip4 primary_ip = 0x0A000001U; + + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0B000001U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_uint_eq(ts->local_ip, primary_ip); +} +END_TEST +START_TEST(test_sock_accept_negative_fd) +{ + struct wolfIP s; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_accept(&s, -1, NULL, NULL), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_connect_tcp_conf_null_primary_null) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = 0; + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_CLOSED; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EAGAIN); + ck_assert_uint_eq(ts->local_ip, IPADDR_ANY); +} +END_TEST + +START_TEST(test_sock_connect_tcp_state_not_closed) +{ + struct wolfIP s; + int tcp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + + wolfIP_init(&s); + mock_link_init(&s); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_LISTEN; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(80); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_connect(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); +} +END_TEST +START_TEST(test_sock_accept_invalid_tcp_fd) +{ + struct wolfIP s; + int bad_fd = MARK_TCP_SOCKET | MAX_TCPSOCKETS; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_accept(&s, bad_fd, NULL, NULL), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_accept_success_sets_addr) +{ + struct wolfIP s; + int listen_sd; + int client_sd; + struct tsocket *listener; + struct wolfIP_sockaddr_in sin; + socklen_t alen = sizeof(sin); + + 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); + + client_sd = wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen); + ck_assert_int_gt(client_sd, 0); + ck_assert_uint_eq(alen, sizeof(sin)); + ck_assert_uint_eq(sin.sin_family, AF_INET); +} +END_TEST + +START_TEST(test_sock_accept_no_available_socket) +{ + struct wolfIP s; + int listen_sd; + struct wolfIP_sockaddr_in sin; + struct tsocket *ts; + int i; + + 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); + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + ts = &s.tcpsockets[i]; + ts->proto = WI_IPPROTO_TCP; + ts->sock.tcp.state = TCP_ESTABLISHED; + } + + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); +} +END_TEST + +START_TEST(test_sock_accept_no_free_socket_syn_rcvd) +{ + struct wolfIP s; + int listen_sd; + struct wolfIP_sockaddr_in sin; + struct tsocket *ts; + int i; + + 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); + ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_RCVD); + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + if (i == SOCKET_UNMARK(listen_sd)) { + continue; + } + s.tcpsockets[i].proto = WI_IPPROTO_TCP; + s.tcpsockets[i].sock.tcp.state = TCP_ESTABLISHED; + } + + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); +} +END_TEST + +START_TEST(test_sock_accept_listen_no_connection) +{ + struct wolfIP s; + int listen_sd; + struct wolfIP_sockaddr_in sin; + + 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); + + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_sock_accept_bound_local_ip_no_match) +{ + struct wolfIP s; + int listen_sd; + int client_sd; + struct tsocket *listener; + struct wolfIP_sockaddr_in sin; + + 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)]; + listener->bound_local_ip = 0x0A000001U; + listener->if_idx = TEST_PRIMARY_IF; + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + s.if_count = 0; + + client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); + ck_assert_int_gt(client_sd, 0); + ck_assert_uint_eq(listener->if_idx, TEST_PRIMARY_IF); +} +END_TEST + +START_TEST(test_sock_sendto_error_paths) +{ + struct wolfIP s; + int tcp_sd; + int udp_sd; + int icmp_sd; + struct wolfIP_sockaddr_in sin; + uint8_t buf[8]; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -1); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(1234); + sin.sin_addr.s_addr = ee32(0x0A000002U); + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, (size_t)(WI_IP_MTU), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); + + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, buf, (size_t)(ICMP_HEADER_LEN - 1), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_sendto_null_buf_or_len_zero) +{ + struct wolfIP s; + int udp_sd; + uint8_t buf[1] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, NULL, 1, 0, NULL, 0), -1); + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, 0, 0, NULL, 0), -1); +} +END_TEST + +START_TEST(test_sock_sendto_tcp_not_established) { struct wolfIP s; int tcp_sd; + struct tsocket *ts; + uint8_t buf[4] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; + ts->sock.tcp.state = TCP_SYN_SENT; + + ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -1); +} +END_TEST + +START_TEST(test_sock_sendto_udp_sets_dest_and_assigns) +{ + struct wolfIP s; int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 local_ip = 0x0A000001U; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(9999); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); + ck_assert_uint_eq(ts->dst_port, 9999U); + ck_assert_uint_eq(ts->remote_ip, 0x0A000002U); + ck_assert_uint_ge(ts->src_port, 1024U); + ck_assert_uint_eq(ts->local_ip, local_ip); +} +END_TEST + +START_TEST(test_sock_sendto_icmp_assigns_src_port_and_sets_echo_id) +{ + struct wolfIP s; int icmp_sd; + struct tsocket *ts; struct wolfIP_sockaddr_in sin; - uint8_t buf[8]; + uint8_t payload[ICMP_HEADER_LEN] = {0}; + struct pkt_desc *desc; + struct wolfIP_icmp_packet *icmp; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->src_port = 0; + ts->local_ip = 0; + + payload[0] = ICMP_ECHO_REQUEST; + payload[1] = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_ne(ts->src_port, 0); + ck_assert_uint_eq(ts->local_ip, 0x0A000001U); + + desc = fifo_peek(&ts->sock.udp.txbuf); + ck_assert_ptr_nonnull(desc); + icmp = (struct wolfIP_icmp_packet *)(ts->txmem + desc->pos + sizeof(*desc)); + ck_assert_uint_eq(icmp_echo_id(icmp), ts->src_port); +} +END_TEST + +START_TEST(test_sock_sendto_invalid_socket_ids) +{ + struct wolfIP s; + uint8_t buf[1] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_sendto_non_socket_returns_minus_one) +{ + struct wolfIP s; + uint8_t buf[1] = {0}; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, 1, buf, sizeof(buf), 0, NULL, 0), -1); +} +END_TEST + +START_TEST(test_sock_sendto_udp_remote_ip_zero) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + uint8_t buf[4] = {1,2,3,4}; + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->dst_port = 1234; + ts->remote_ip = 0; + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, NULL, 0), -1); +} +END_TEST + +START_TEST(test_sock_sendto_udp_primary_ip_fallback) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(7777); + sin.sin_addr.s_addr = ee32(0xC0A80199U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); + ck_assert_uint_eq(ts->local_ip, primary_ip); +} +END_TEST + +START_TEST(test_sock_sendto_udp_zero_port_in_addr) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 1234; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); +} +END_TEST + +START_TEST(test_sock_sendto_udp_src_port_low_adjusts) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 local_ip = 0x0A000001U; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 0; + ts->local_ip = 0; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(9999); + sin.sin_addr.s_addr = ee32(0x0A000002U); + + test_rand_override_enabled = 1; + test_rand_override_value = 5; + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); + test_rand_override_enabled = 0; + + ck_assert_uint_eq(ts->src_port, 5U + 1024U); +} +END_TEST + +START_TEST(test_sock_sendto_udp_local_ip_conf_null) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); - - tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(tcp_sd, 0); - ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -1); + s.if_count = 0; udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 1234; + ts->local_ip = 0; + memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(1234); + sin.sin_port = ee16(7777); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, (size_t)(WI_IP_MTU), 0, - (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -1); - icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); - ck_assert_int_gt(icmp_sd, 0); - ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, buf, (size_t)(ICMP_HEADER_LEN - 1), 0, - (struct wolfIP_sockaddr *)&sin, sizeof(sin)), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); + ck_assert_uint_eq(ts->local_ip, 0U); } END_TEST -START_TEST(test_sock_sendto_null_buf_or_len_zero) +START_TEST(test_sock_sendto_udp_local_ip_from_primary) { struct wolfIP s; int udp_sd; - uint8_t buf[1] = {0}; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t buf[4] = {1,2,3,4}; + ip4 primary_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->src_port = 1234; + ts->local_ip = 0; - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, NULL, 1, 0, NULL, 0), -1); - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, 0, 0, NULL, 0), -1); + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(7777); + sin.sin_addr.s_addr = ee32(0x0B000001U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); + ck_assert_uint_eq(ts->local_ip, primary_ip); } END_TEST -START_TEST(test_sock_sendto_tcp_not_established) +START_TEST(test_sock_sendto_tcp_multiple_segments_flags) { struct wolfIP s; int tcp_sd; struct tsocket *ts; - uint8_t buf[4] = {0}; + size_t seg_payload = TCP_MSS - TCP_OPTIONS_LEN; + size_t payload_len = seg_payload * 2; + uint8_t buf[TCP_MSS * 2]; + uint8_t txbuf[4096]; + struct pkt_desc *first; + struct pkt_desc *second; + struct wolfIP_tcp_seg *tcp1; + struct wolfIP_tcp_seg *tcp2; + int ret; wolfIP_init(&s); mock_link_init(&s); @@ -1875,46 +2743,65 @@ START_TEST(test_sock_sendto_tcp_not_established) tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); ck_assert_int_gt(tcp_sd, 0); ts = &s.tcpsockets[SOCKET_UNMARK(tcp_sd)]; - ts->sock.tcp.state = TCP_SYN_SENT; + ts->sock.tcp.state = TCP_ESTABLISHED; + ts->src_port = 1234; + ts->dst_port = 4321; + ts->local_ip = 0x0A000001U; + ts->remote_ip = 0x0A000002U; + queue_init(&ts->sock.tcp.rxbuf, ts->rxmem, RXBUF_SIZE, 0); + fifo_init(&ts->sock.tcp.txbuf, txbuf, sizeof(txbuf)); - ck_assert_int_eq(wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0), -1); + memset(buf, 0xAA, payload_len); + ret = wolfIP_sock_sendto(&s, tcp_sd, buf, payload_len, 0, NULL, 0); + ck_assert_int_eq(ret, (int)payload_len); + + first = fifo_peek(&ts->sock.tcp.txbuf); + ck_assert_ptr_nonnull(first); + tcp1 = (struct wolfIP_tcp_seg *)(txbuf + first->pos + sizeof(*first)); + ck_assert_uint_eq(tcp1->flags & 0x18, 0x18); + + second = fifo_next(&ts->sock.tcp.txbuf, first); + ck_assert_ptr_nonnull(second); + tcp2 = (struct wolfIP_tcp_seg *)(txbuf + second->pos + sizeof(*second)); + ck_assert_uint_eq(tcp2->flags & 0x18, 0x10); } END_TEST -START_TEST(test_sock_sendto_udp_sets_dest_and_assigns) +START_TEST(test_sock_sendto_icmp_src_port_zero_random_zero_sets_one) { struct wolfIP s; - int udp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; - uint8_t buf[4] = {1,2,3,4}; - ip4 local_ip = 0x0A000001U; + uint8_t payload[ICMP_HEADER_LEN] = {0}; wolfIP_init(&s); mock_link_init(&s); - wolfIP_ipconfig_set(&s, local_ip, 0xFFFFFF00U, 0); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); - udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); - ck_assert_int_gt(udp_sd, 0); - ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; ts->src_port = 0; ts->local_ip = 0; + payload[0] = ICMP_ECHO_REQUEST; + memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(9999); sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, - (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); - ck_assert_uint_eq(ts->dst_port, 9999U); - ck_assert_uint_eq(ts->remote_ip, 0x0A000002U); - ck_assert_uint_ge(ts->src_port, 1024U); - ck_assert_uint_eq(ts->local_ip, local_ip); + test_rand_override_enabled = 1; + test_rand_override_value = 0; + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + test_rand_override_enabled = 0; + + ck_assert_uint_eq(ts->src_port, 1U); } END_TEST -START_TEST(test_sock_sendto_icmp_assigns_src_port_and_sets_echo_id) +START_TEST(test_sock_sendto_icmp_non_echo_no_set_id) { struct wolfIP s; int icmp_sd; @@ -1931,94 +2818,119 @@ START_TEST(test_sock_sendto_icmp_assigns_src_port_and_sets_echo_id) icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); ck_assert_int_gt(icmp_sd, 0); ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; - ts->src_port = 0; + ts->src_port = 1234; ts->local_ip = 0; - payload[0] = ICMP_ECHO_REQUEST; - payload[1] = 0; + payload[0] = ICMP_ECHO_REPLY; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = 0; sin.sin_addr.s_addr = ee32(0x0A000002U); ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); - ck_assert_uint_ne(ts->src_port, 0); - ck_assert_uint_eq(ts->local_ip, 0x0A000001U); desc = fifo_peek(&ts->sock.udp.txbuf); ck_assert_ptr_nonnull(desc); icmp = (struct wolfIP_icmp_packet *)(ts->txmem + desc->pos + sizeof(*desc)); - ck_assert_uint_eq(icmp_echo_id(icmp), ts->src_port); + ck_assert_uint_eq(icmp_echo_id(icmp), 0U); } END_TEST -START_TEST(test_sock_sendto_invalid_socket_ids) +START_TEST(test_sock_sendto_icmp_local_ip_from_primary) { struct wolfIP s; - uint8_t buf[1] = {0}; + int icmp_sd; + struct tsocket *ts; + struct wolfIP_sockaddr_in sin; + uint8_t payload[ICMP_HEADER_LEN] = {0}; + ip4 primary_ip = 0x0A000001U; wolfIP_init(&s); mock_link_init(&s); + s.if_count = TEST_SECOND_IF + 1; + s.ipconf[TEST_PRIMARY_IF].ip = primary_ip; + s.ipconf[TEST_PRIMARY_IF].mask = 0xFFFFFF00U; + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; - ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); - ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); - ck_assert_int_eq(wolfIP_sock_sendto(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, buf, sizeof(buf), 0, NULL, 0), -WOLFIP_EINVAL); + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->local_ip = 0; + ts->src_port = 1234; + + payload[0] = ICMP_ECHO_REQUEST; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0B000001U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_eq(ts->local_ip, primary_ip); } END_TEST -START_TEST(test_sock_sendto_udp_remote_ip_zero) +START_TEST(test_sock_sendto_icmp_local_ip_pre_set) { struct wolfIP s; - int udp_sd; + int icmp_sd; struct tsocket *ts; - uint8_t buf[4] = {1,2,3,4}; + struct wolfIP_sockaddr_in sin; + uint8_t payload[ICMP_HEADER_LEN] = {0}; wolfIP_init(&s); mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); - udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); - ck_assert_int_gt(udp_sd, 0); - ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; - ts->dst_port = 1234; - ts->remote_ip = 0; + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->local_ip = 0x0A000001U; + ts->src_port = 1234; - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, NULL, 0), -1); + payload[0] = ICMP_ECHO_REQUEST; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = ee32(0x0A000002U); + + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_eq(ts->local_ip, 0x0A000001U); } END_TEST -START_TEST(test_sock_sendto_udp_primary_ip_fallback) +START_TEST(test_sock_sendto_icmp_conf_null_primary_null) { struct wolfIP s; - int udp_sd; + int icmp_sd; struct tsocket *ts; struct wolfIP_sockaddr_in sin; - uint8_t buf[4] = {1,2,3,4}; - ip4 primary_ip = 0x0A000001U; - ip4 secondary_ip = 0xC0A80101U; + uint8_t payload[ICMP_HEADER_LEN] = {0}; - setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); - s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; - s.ipconf[TEST_SECOND_IF].gw = 0xC0A801FEU; + wolfIP_init(&s); + mock_link_init(&s); + s.if_count = 0; - udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); - ck_assert_int_gt(udp_sd, 0); - ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; - ts->src_port = 0; + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; ts->local_ip = 0; + ts->src_port = 1234; + + payload[0] = ICMP_ECHO_REQUEST; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; - sin.sin_port = ee16(7777); - sin.sin_addr.s_addr = ee32(0xC0A80199U); + sin.sin_addr.s_addr = ee32(0x0A000002U); - ck_assert_int_eq(wolfIP_sock_sendto(&s, udp_sd, buf, sizeof(buf), 0, - (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(buf)); - ck_assert_uint_eq(ts->local_ip, primary_ip); + ck_assert_int_eq(wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, + (struct wolfIP_sockaddr *)&sin, sizeof(sin)), (int)sizeof(payload)); + ck_assert_uint_eq(ts->local_ip, 0U); } END_TEST - START_TEST(test_sock_recvfrom_tcp_close_wait_empty_returns_zero) { struct wolfIP s; @@ -2122,6 +3034,18 @@ START_TEST(test_sock_recvfrom_invalid_socket_ids) } END_TEST +START_TEST(test_sock_recvfrom_non_socket) +{ + struct wolfIP s; + uint8_t buf[4]; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_recvfrom(&s, 1, buf, sizeof(buf), 0, NULL, NULL), -WOLFIP_EINVAL); +} +END_TEST + START_TEST(test_sock_recvfrom_icmp_success) { struct wolfIP s; @@ -2394,6 +3318,24 @@ START_TEST(test_sock_opts_unknown_level) } END_TEST +START_TEST(test_sock_opts_sol_ip_unknown_optname) +{ + struct wolfIP s; + int udp_sd; + int value = 1; + socklen_t len = sizeof(value); + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + + ck_assert_int_eq(wolfIP_sock_setsockopt(&s, udp_sd, WOLFIP_SOL_IP, 0xFFFF, &value, len), 0); + ck_assert_int_eq(wolfIP_sock_getsockopt(&s, udp_sd, WOLFIP_SOL_IP, 0xFFFF, &value, &len), 0); +} +END_TEST + START_TEST(test_sock_accept_wrong_state) { struct wolfIP s; @@ -2496,6 +3438,23 @@ START_TEST(test_sock_get_recv_ttl_disabled) } END_TEST +START_TEST(test_sock_get_recv_ttl_null) +{ + struct wolfIP s; + int udp_sd; + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + s.udpsockets[SOCKET_UNMARK(udp_sd)].recv_ttl = 1; + s.udpsockets[SOCKET_UNMARK(udp_sd)].last_pkt_ttl = 99; + + ck_assert_int_eq(wolfIP_sock_get_recv_ttl(&s, udp_sd, NULL), 1); +} +END_TEST + START_TEST(test_sock_connect_tcp_states) { struct wolfIP s; @@ -2580,28 +3539,85 @@ START_TEST(test_sock_getpeername_errors) ck_assert_int_gt(udp_sd, 0); ck_assert_int_eq(wolfIP_sock_getpeername(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, &len), -1); - tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); - ck_assert_int_gt(tcp_sd, 0); - ck_assert_int_eq(wolfIP_sock_getpeername(&s, tcp_sd, NULL, &len), -1); - ck_assert_int_eq(wolfIP_sock_getpeername(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, &len), -1); + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ck_assert_int_eq(wolfIP_sock_getpeername(&s, tcp_sd, NULL, &len), -1); + ck_assert_int_eq(wolfIP_sock_getpeername(&s, tcp_sd, (struct wolfIP_sockaddr *)&sin, &len), -1); +} +END_TEST + +START_TEST(test_sock_getsockname_errors) +{ + struct wolfIP s; + int udp_sd; + struct wolfIP_sockaddr_in sin; + socklen_t len = 1; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_getsockname(&s, -1, (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, &len), -1); +} +END_TEST + +START_TEST(test_sock_getsockname_null_addr) +{ + struct wolfIP s; + int tcp_sd; + socklen_t len = sizeof(struct wolfIP_sockaddr_in); + + wolfIP_init(&s); + mock_link_init(&s); + + tcp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(tcp_sd, 0); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, tcp_sd, NULL, &len), -WOLFIP_EINVAL); +} +END_TEST + +START_TEST(test_sock_getsockname_invalid_socket_ids) +{ + struct wolfIP s; + struct wolfIP_sockaddr_in sin; + socklen_t len = sizeof(sin); + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_getsockname(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS, + (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS, + (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS, + (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); } END_TEST -START_TEST(test_sock_getsockname_errors) +START_TEST(test_sock_getsockname_icmp_success) { struct wolfIP s; - int udp_sd; + int icmp_sd; struct wolfIP_sockaddr_in sin; - socklen_t len = 1; + socklen_t len = sizeof(sin); + struct tsocket *ts; wolfIP_init(&s); mock_link_init(&s); - ck_assert_int_eq(wolfIP_sock_getsockname(&s, -1, (struct wolfIP_sockaddr *)&sin, &len), -WOLFIP_EINVAL); + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->src_port = 7; + ts->local_ip = 0x0A000001U; - udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); - ck_assert_int_gt(udp_sd, 0); - ck_assert_int_eq(wolfIP_sock_getsockname(&s, udp_sd, (struct wolfIP_sockaddr *)&sin, &len), -1); + ck_assert_int_eq(wolfIP_sock_getsockname(&s, icmp_sd, (struct wolfIP_sockaddr *)&sin, &len), 0); + ck_assert_uint_eq(sin.sin_family, AF_INET); + ck_assert_uint_eq(sin.sin_port, ee16(7)); + ck_assert_uint_eq(sin.sin_addr.s_addr, ee32(0x0A000001U)); } END_TEST @@ -3116,7 +4132,77 @@ START_TEST(test_sock_sendto_tcp_partial_send) memset(buf, 0xAB, sizeof(buf)); ret = wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0); - ck_assert_int_gt(ret, 0); + ck_assert_msg(ret > 0 || ret == -WOLFIP_EAGAIN, + "expected partial send or EAGAIN, got %d", ret); +} +END_TEST + +START_TEST(test_sock_sendto_udp_fifo_push_fails_returns_eagain) +{ + struct wolfIP s; + int udp_sd; + struct tsocket *ts; + uint8_t txbuf[4096]; + enum { UDP_PAYLOAD_LEN = + (WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) > 1000 + ? 1000 + : (WI_IP_MTU - IP_HEADER_LEN - UDP_HEADER_LEN) + }; + uint8_t payload[UDP_PAYLOAD_LEN]; + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + + udp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_gt(udp_sd, 0); + ts = &s.udpsockets[SOCKET_UNMARK(udp_sd)]; + ts->dst_port = 4321; + ts->remote_ip = 0x0A000002U; + ts->local_ip = 0x0A000001U; + + fifo_init(&ts->sock.udp.txbuf, txbuf, sizeof(txbuf)); + ts->sock.udp.txbuf.head = 100; + ts->sock.udp.txbuf.tail = 100 + UDP_PAYLOAD_LEN; + ts->sock.udp.txbuf.h_wrap = 3000; + + memset(payload, 0xEE, sizeof(payload)); + ret = wolfIP_sock_sendto(&s, udp_sd, payload, sizeof(payload), 0, NULL, 0); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_sock_sendto_icmp_fifo_push_fails_returns_eagain) +{ + struct wolfIP s; + int icmp_sd; + struct tsocket *ts; + uint8_t txbuf[4096]; + enum { ICMP_PAYLOAD_LEN = + (WI_IP_MTU - IP_HEADER_LEN) > 1000 + ? 1000 + : (WI_IP_MTU - IP_HEADER_LEN) + }; + uint8_t payload[ICMP_PAYLOAD_LEN]; + int ret; + + wolfIP_init(&s); + mock_link_init(&s); + + icmp_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_ICMP); + ck_assert_int_gt(icmp_sd, 0); + ts = &s.icmpsockets[SOCKET_UNMARK(icmp_sd)]; + ts->remote_ip = 0x0A000002U; + + fifo_init(&ts->sock.udp.txbuf, txbuf, sizeof(txbuf)); + ts->sock.udp.txbuf.head = 100; + ts->sock.udp.txbuf.tail = 100 + ICMP_PAYLOAD_LEN; + ts->sock.udp.txbuf.h_wrap = 3000; + + memset(payload, 0xEE, sizeof(payload)); + payload[0] = ICMP_ECHO_REQUEST; + ret = wolfIP_sock_sendto(&s, icmp_sd, payload, sizeof(payload), 0, NULL, 0); + ck_assert_int_eq(ret, -WOLFIP_EAGAIN); } END_TEST @@ -4012,6 +5098,77 @@ START_TEST(test_sock_close_udp_icmp) ck_assert_int_eq(ts->proto, 0); } END_TEST + +START_TEST(test_sock_close_invalid_fds) +{ + struct wolfIP s; + + wolfIP_init(&s); + mock_link_init(&s); + + ck_assert_int_eq(wolfIP_sock_close(&s, -1), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, MARK_TCP_SOCKET | MAX_TCPSOCKETS), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, MARK_UDP_SOCKET | MAX_UDPSOCKETS), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, MARK_ICMP_SOCKET | MAX_ICMPSOCKETS), -WOLFIP_EINVAL); + ck_assert_int_eq(wolfIP_sock_close(&s, 1), -1); +} +END_TEST + +START_TEST(test_sock_close_tcp_fin_wait_1) +{ + struct wolfIP s; + struct tsocket *ts; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ts->sock.tcp.state = TCP_FIN_WAIT_1; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(ts->sock.tcp.state, TCP_CLOSING); +} +END_TEST + +START_TEST(test_sock_close_tcp_other_state_closes) +{ + struct wolfIP s; + struct tsocket *ts; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ts->sock.tcp.state = TCP_SYN_SENT; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_int_eq(ts->proto, 0); +} +END_TEST + +START_TEST(test_sock_close_tcp_closed_returns_minus_one) +{ + struct wolfIP s; + struct tsocket *ts; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_gt(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ts->sock.tcp.state = TCP_CLOSED; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -1); +} +END_TEST START_TEST(test_fifo_push_and_pop_multiple) { struct fifo f; uint8_t data[] = {1, 2, 3, 4, 5}; @@ -4884,6 +6041,19 @@ START_TEST(test_route_for_ip_variants) } END_TEST +START_TEST(test_route_for_ip_dest_matches_iface_ip) +{ + struct wolfIP s; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + + ck_assert_uint_eq(wolfIP_route_for_ip(&s, primary_ip), TEST_PRIMARY_IF); + ck_assert_uint_eq(wolfIP_route_for_ip(&s, secondary_ip), TEST_SECOND_IF); +} +END_TEST + START_TEST(test_route_for_ip_gw_and_nonloop_fallback) { struct wolfIP s; @@ -4956,6 +6126,20 @@ START_TEST(test_forward_interface_variants) } END_TEST +START_TEST(test_forward_interface_skips_ipaddr_any) +{ + struct wolfIP s; + ip4 primary_ip = 0x0A000001U; + ip4 secondary_ip = 0xC0A80101U; + + setup_stack_with_two_ifaces(&s, primary_ip, secondary_ip); + s.ipconf[TEST_SECOND_IF].ip = IPADDR_ANY; + s.ipconf[TEST_SECOND_IF].mask = 0xFFFFFF00U; + + ck_assert_int_eq((int)wolfIP_forward_interface(&s, TEST_PRIMARY_IF, 0xC0A80199U), -1); +} +END_TEST + START_TEST(test_forward_interface_dest_is_local_ip) { struct wolfIP s; @@ -8222,7 +9406,58 @@ START_TEST(test_fifo_space_with_hwrap) f.h_wrap = 300; space = fifo_space(&f); - ck_assert_uint_eq(space, f.size - (f.tail - f.head)); + ck_assert_uint_eq(space, f.tail - f.head); +} +END_TEST + +START_TEST(test_fifo_space_hwrap_head_hits_wrap) +{ + struct fifo f; + uint8_t buf[512]; + uint32_t space; + + fifo_init(&f, buf, sizeof(buf)); + f.head = 300; + f.tail = 200; + f.h_wrap = 300; + + space = fifo_space(&f); + ck_assert_uint_eq(space, f.tail - f.head); + ck_assert_uint_eq(f.head, 0); +} +END_TEST + +START_TEST(test_fifo_space_hwrap_head_ge_tail_returns_zero) +{ + struct fifo f; + uint8_t buf[512]; + uint32_t space; + + fifo_init(&f, buf, sizeof(buf)); + f.head = 220; + f.tail = 200; + f.h_wrap = 300; + + space = fifo_space(&f); + ck_assert_uint_eq(space, 0); +} +END_TEST + +START_TEST(test_fifo_push_no_contiguous_space_even_with_space) +{ + struct fifo f; + uint8_t buf[4096]; + uint8_t payload[1592]; + int ret; + + fifo_init(&f, buf, sizeof(buf)); + f.head = 2506; + f.tail = 1500; + f.h_wrap = 0; + + memset(payload, 0xCD, sizeof(payload)); + ret = fifo_push(&f, payload, sizeof(payload)); + ck_assert_int_eq(ret, -1); } END_TEST @@ -8682,6 +9917,58 @@ START_TEST(test_timer_pop_reorders_heap) } END_TEST +START_TEST(test_timer_pop_right_child_swap) +{ + struct timers_binheap h; + struct wolfIP_timer popped; + + memset(&h, 0, sizeof(h)); + h.size = 4; + h.timers[0].expires = 10; + h.timers[1].expires = 40; + h.timers[2].expires = 20; + h.timers[3].expires = 30; + + popped = timers_binheap_pop(&h); + ck_assert_uint_eq(popped.expires, 10); + ck_assert_uint_eq(h.size, 3); + ck_assert_uint_eq(h.timers[0].expires, 20); +} +END_TEST + +START_TEST(test_timer_pop_break_when_root_small) +{ + struct timers_binheap h; + struct wolfIP_timer popped; + + memset(&h, 0, sizeof(h)); + h.size = 4; + h.timers[0].expires = 10; + h.timers[1].expires = 20; + h.timers[2].expires = 30; + h.timers[3].expires = 15; + + popped = timers_binheap_pop(&h); + ck_assert_uint_eq(popped.expires, 10); + ck_assert_uint_eq(h.size, 3); + ck_assert_uint_eq(h.timers[0].expires, 15); +} +END_TEST + +START_TEST(test_is_timer_expired_skips_zero_head) +{ + struct timers_binheap h; + + memset(&h, 0, sizeof(h)); + h.size = 2; + h.timers[0].expires = 0; + h.timers[1].expires = 50; + + ck_assert_int_eq(is_timer_expired(&h, 10), 0); + ck_assert_uint_eq(h.size, 0); +} +END_TEST + /* Arp suite */ START_TEST(test_arp_request_basic) @@ -9962,8 +11249,11 @@ Suite *wolf_suite(void) tcase_add_test(tc_core, test_fifo_space_head_lt_tail); suite_add_tcase(s, tc_core); tcase_add_test(tc_core, test_fifo_space_with_hwrap); + tcase_add_test(tc_core, test_fifo_space_hwrap_head_hits_wrap); + tcase_add_test(tc_core, test_fifo_space_hwrap_head_ge_tail_returns_zero); suite_add_tcase(s, tc_core); tcase_add_test(tc_core, test_fifo_next_hits_hwrap); + tcase_add_test(tc_core, test_fifo_push_no_contiguous_space_even_with_space); suite_add_tcase(s, tc_core); tcase_add_test(tc_core, test_fifo_len_tail_gt_head_no_hwrap); suite_add_tcase(s, tc_core); @@ -10031,6 +11321,12 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_timer_pop_reorders_heap); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_timer_pop_right_child_swap); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_timer_pop_break_when_root_small); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_is_timer_expired_skips_zero_head); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_wolfip_getdev_ex_api); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_wolfip_ll_at_and_ipconf_at_invalid); @@ -10063,6 +11359,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_filter_socket_event_unknown_proto); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_filter_socket_event_proto_variants); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_filter_setters_and_get_mask); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_socket_errors); @@ -10085,10 +11383,26 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_bind_wrong_family); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_invalid_fd); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_tcp_state_not_closed); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_tcp_filter_blocks); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_udp_src_port_nonzero); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_udp_filter_blocks); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_bind_icmp_success); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_wrong_family); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_accept_error_paths); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_accept_non_tcp_socket_sets_addrlen); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_accept_null_addr_with_addrlen); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_short_addrlen); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_udp_short_addrlen); @@ -10099,6 +11413,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_bad_addrlen); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_bad_addrlen); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_invalid_args); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_invalid_tcp_fd); @@ -10115,18 +11431,40 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_udp_bound_local_ip_no_match); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_udp_bound_local_ip_match); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_icmp_sets_local_ip_from_conf); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_wrong_family); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_local_ip_pre_set); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_conf_null); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_icmp_falls_back_to_primary); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_primary_ip_any); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_icmp_primary_ip_fallback); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_established_returns_zero); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_bound_local_ip_match); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_bound_local_ip_no_match); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_filter_blocks); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_local_ip_from_conf); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_local_ip_from_primary); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_primary_ip_fallback); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_conf_null_primary_null); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_connect_tcp_state_not_closed); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_accept_negative_fd); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_accept_invalid_tcp_fd); @@ -10159,10 +11497,14 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_invalid_socket_ids); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_recvfrom_non_socket); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_icmp_success); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_opts_unknown_level); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_opts_sol_ip_unknown_optname); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_setsockopt_recvttl); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_setsockopt_invalid_socket); @@ -10181,6 +11523,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_get_recv_ttl_disabled); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_get_recv_ttl_null); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_tcp_states); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_listen_errors); @@ -10189,6 +11533,12 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_getsockname_errors); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_getsockname_null_addr); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_getsockname_invalid_socket_ids); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_getsockname_icmp_success); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_register_callback_variants); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_connect_udp_bound_ip_not_local); @@ -10227,13 +11577,31 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_invalid_socket_ids); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_non_socket_returns_minus_one); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_udp_remote_ip_zero); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_udp_primary_ip_fallback); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_zero_port_in_addr); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_src_port_low_adjusts); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_local_ip_conf_null); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_sendto_udp_local_ip_from_primary); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_tcp_txbuf_full); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_tcp_partial_send); + tcase_add_test(tc_utils, test_sock_sendto_tcp_multiple_segments_flags); + tcase_add_test(tc_utils, test_sock_sendto_udp_fifo_push_fails_returns_eagain); + tcase_add_test(tc_utils, test_sock_sendto_icmp_fifo_push_fails_returns_eagain); + tcase_add_test(tc_utils, test_sock_sendto_icmp_src_port_zero_random_zero_sets_one); + tcase_add_test(tc_utils, test_sock_sendto_icmp_non_echo_no_set_id); + tcase_add_test(tc_utils, test_sock_sendto_icmp_local_ip_from_primary); + tcase_add_test(tc_utils, test_sock_sendto_icmp_local_ip_pre_set); + tcase_add_test(tc_utils, test_sock_sendto_icmp_conf_null_primary_null); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_recvfrom_icmp_paths); suite_add_tcase(s, tc_utils); @@ -10327,6 +11695,14 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_close_udp_icmp); suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_invalid_fds); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_tcp_fin_wait_1); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_tcp_other_state_closes); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_sock_close_tcp_closed_returns_minus_one); + suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_tcp_syn_sent_to_established); suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_tcp_input_syn_sent_unexpected_flags); @@ -10562,6 +11938,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_route_for_ip_variants); suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_route_for_ip_dest_matches_iface_ip); + suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_route_for_ip_no_primary_index); suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_route_for_ip_null_stack); @@ -10572,6 +11950,8 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_forward_interface_variants); suite_add_tcase(s, tc_proto); + tcase_add_test(tc_proto, test_forward_interface_skips_ipaddr_any); + suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_forward_interface_dest_is_local_ip); suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_forward_interface_short_circuit_cases); diff --git a/src/wolfip.c b/src/wolfip.c index 363ff27..aa21959 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -2122,7 +2122,7 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add if (sockfd < 0) return -WOLFIP_EINVAL; - if (addr && addrlen) + if (addrlen) *addrlen = sizeof(struct wolfIP_sockaddr_in); if (IS_SOCKET_TCP(sockfd)) { From 5ffd1456a9ea3dd30e784483e4fa9ad0d262532b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 10:55:31 +0100 Subject: [PATCH 3/9] Addressed copilot's review. - Restored blocking pthread_mutex (improved performance in TCP sender) - fifo_push now considers wrapping around if possible, when the packet is bigger than the offered contiguous space at the end of the buffer, while there could be space at the beginning before tail pointer if the fifo wraps around. --- src/port/posix/bsd_socket.c | 2 +- src/test/unit/unit.c | 43 +++++++------ src/wolfip.c | 124 +++++++++++++++++++++--------------- 3 files changed, 98 insertions(+), 71 deletions(-) diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index a7b6545..b1c7a8f 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -369,7 +369,7 @@ static int wolfip_fd_is_nonblock(int public_fd) return host_##call(user_fd, ## __VA_ARGS__); \ } else { \ int __wolfip_internal = wolfip_fd_internal_from_public(user_fd); \ - while (pthread_mutex_trylock(&wolfIP_mutex) != 0) { usleep(1000); } \ + pthread_mutex_lock(&wolfIP_mutex); \ if (__wolfip_internal >= 0) { \ int __wolfip_retval; \ int __wolfip_nonblock = wolfip_fd_is_nonblock(user_fd); \ diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 5580d39..17cd5eb 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -494,7 +494,7 @@ START_TEST(test_fifo_peek_wraps_tail_when_head_lt_tail) desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); - ck_assert_uint_eq(f.tail, 0); + ck_assert_uint_eq(f.tail, 4); } END_TEST @@ -568,12 +568,12 @@ START_TEST(test_fifo_pop_wraps_tail_when_head_lt_tail) f.tail = 4; f.h_wrap = 0; - desc = (struct pkt_desc *)data; - desc->pos = 0; + desc = (struct pkt_desc *)(data + 4); + desc->pos = 4; desc->len = 0; ck_assert_ptr_nonnull(fifo_pop(&f)); - ck_assert_uint_eq(f.tail, sizeof(struct pkt_desc)); + ck_assert_uint_eq(f.tail, 4 + sizeof(struct pkt_desc)); } END_TEST @@ -4582,7 +4582,7 @@ START_TEST(test_fifo_push_and_pop) { ck_assert_ptr_nonnull(desc); ck_assert_int_eq(desc->len, sizeof(data)); ck_assert_mem_eq((const uint8_t *)f.data + desc->pos + sizeof(struct pkt_desc), data, sizeof(data)); - ck_assert_int_eq(fifo_len(&f), 0); + ck_assert_ptr_eq(fifo_peek(&f), NULL); } END_TEST @@ -7846,7 +7846,7 @@ START_TEST(test_tcp_ack_acks_data_and_sets_writable) ackseg.flags = 0x10; tcp_ack(ts, &ackseg); - ck_assert_int_eq(fifo_len(&ts->sock.tcp.txbuf), 0); + ck_assert_ptr_eq(fifo_peek(&ts->sock.tcp.txbuf), NULL); ck_assert_uint_eq(ts->events & CB_EVENT_WRITABLE, CB_EVENT_WRITABLE); ck_assert_uint_gt(ts->sock.tcp.cwnd, TCP_MSS); } @@ -9254,6 +9254,7 @@ START_TEST(test_fifo_space_wrap_sets_hwrap) size_t total = sizeof(struct pkt_desc) + LINK_MTU + 64; uint8_t *buf = malloc(total); uint32_t space; + uint32_t expected; ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); @@ -9262,9 +9263,9 @@ START_TEST(test_fifo_space_wrap_sets_hwrap) f.h_wrap = 0; space = fifo_space(&f); - ck_assert_uint_eq(f.head, 0); - ck_assert_uint_ne(f.h_wrap, 0); - ck_assert_uint_gt(space, 0); + expected = (uint32_t)total - (f.head - f.tail); + ck_assert_uint_eq(f.h_wrap, 0); + ck_assert_uint_eq(space, expected); free(buf); } @@ -9284,7 +9285,7 @@ START_TEST(test_fifo_space_wrap_returns_zero) f.h_wrap = 0; space = fifo_space(&f); - ck_assert_uint_eq(space, 0); + ck_assert_uint_eq(space, (uint32_t)total - (f.head - f.tail)); ck_assert_uint_eq(f.h_wrap, 0); free(buf); @@ -9302,7 +9303,8 @@ START_TEST(test_fifo_peek_wrap_to_start) ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); f.head = 8; - f.tail = 12; + f.tail = 4; + f.h_wrap = 8; desc = (struct pkt_desc *)buf; memset(desc, 0, sizeof(*desc)); @@ -9358,14 +9360,18 @@ START_TEST(test_fifo_next_wraps_to_start) uint8_t *buf = malloc(total); struct pkt_desc *desc; struct pkt_desc *next; + uint32_t h_wrap = 64; + uint32_t len; ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); - desc = (struct pkt_desc *)(buf + 4); + desc = (struct pkt_desc *)buf; memset(desc, 0, sizeof(*desc)); - desc->pos = 4; - desc->len = 1; - f.head = 32; + desc->pos = 0; + len = h_wrap - desc->pos; + desc->len = len - sizeof(struct pkt_desc); + f.head = 128; + f.h_wrap = h_wrap; next = fifo_next(&f, desc); ck_assert_ptr_eq(next, (struct pkt_desc *)buf); @@ -9422,8 +9428,7 @@ START_TEST(test_fifo_space_hwrap_head_hits_wrap) f.h_wrap = 300; space = fifo_space(&f); - ck_assert_uint_eq(space, f.tail - f.head); - ck_assert_uint_eq(f.head, 0); + ck_assert_uint_eq(space, 0); } END_TEST @@ -9458,6 +9463,7 @@ START_TEST(test_fifo_push_no_contiguous_space_even_with_space) memset(payload, 0xCD, sizeof(payload)); ret = fifo_push(&f, payload, sizeof(payload)); ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(f.h_wrap, 0); } END_TEST @@ -9515,7 +9521,8 @@ START_TEST(test_fifo_pop_wrap_to_start) ck_assert_ptr_nonnull(buf); fifo_init(&f, buf, (uint32_t)total); f.head = 8; - f.tail = 12; + f.tail = 4; + f.h_wrap = 8; desc = (struct pkt_desc *)buf; memset(desc, 0, sizeof(*desc)); diff --git a/src/wolfip.c b/src/wolfip.c index aa21959..1798658 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -148,54 +148,39 @@ static void fifo_init(struct fifo *f, uint8_t *data, uint32_t size) /* Return the number of bytes available */ static uint32_t fifo_space(struct fifo *f) { - int ret = 0; - if (f->head == f->tail) { - f->head = 0; - f->tail = 0; + if (f->head == f->tail) return f->size; - } - if (f->tail == f->h_wrap) { - f->tail = 0; - f->h_wrap = 0; - } - if (f->h_wrap == 0) { - if (f->head >= f->tail) { - ret = f->size - (f->head - f->tail); - } else { - ret = f->tail - f->head; - } - /* Take into account the wraparound to always keep the segment contiguous */ - if ((f->size - f->head) < (sizeof(struct pkt_desc) + LINK_MTU)) { - if (f->tail > (sizeof(struct pkt_desc) + LINK_MTU)) { - f->h_wrap = f->head; - f->head = 0; - return f->tail - f->head; - } else return 0; - } - } else { - /* When wrapped, only space before tail is contiguous. */ - if (f->head == f->h_wrap) - f->head = 0; + if (f->h_wrap) { if (f->head < f->tail) - ret = f->tail - f->head; - else - ret = 0; + return f->tail - f->head; + return 0; } - return ret; + if (f->head >= f->tail) + return f->size - (f->head - f->tail); + return f->tail - f->head; } /* Check the descriptor of the next packet */ static struct pkt_desc *fifo_peek(struct fifo *f) { - if (f->tail == f->h_wrap) { + if (f->tail == f->head) + return NULL; + while (f->tail % 4) + f->tail++; + if (f->h_wrap && f->tail >= f->h_wrap) { f->tail = 0; f->h_wrap = 0; } + if (f->h_wrap && (f->tail < f->h_wrap) && + ((f->tail + sizeof(struct pkt_desc)) > f->h_wrap)) { + f->tail = 0; + f->h_wrap = 0; + } + if (f->tail >= f->size) + f->tail %= f->size; if (f->tail == f->head) return NULL; - while (f->tail % 4) - f->tail++; - if ((f->head < f->tail) && ((f->tail + sizeof(struct pkt_desc) + LINK_MTU > f->size))) + if ((f->tail + sizeof(struct pkt_desc)) > f->size) f->tail = 0; return (struct pkt_desc *)((uint8_t *)f->data + f->tail); } @@ -211,13 +196,10 @@ static struct pkt_desc *fifo_next(struct fifo *f, struct pkt_desc *desc) return NULL; while ((desc->pos + len) % 4) len++; - if ((desc->pos + len + sizeof(struct pkt_desc) + LINK_MTU ) >= f->size) + if ((desc->pos + len) >= f->size || (desc->pos + len) == f->h_wrap) desc = (struct pkt_desc *)((uint8_t *)f->data); else desc = (struct pkt_desc *)((uint8_t *)f->data + desc->pos + len); - if ((desc->pos + len) == f->h_wrap) { - desc = (struct pkt_desc *)((uint8_t *)f->data); - } return desc; } @@ -243,22 +225,45 @@ static uint32_t fifo_len(struct fifo *f) static int fifo_push(struct fifo *f, void *data, uint32_t len) { struct pkt_desc desc; + uint32_t needed = sizeof(struct pkt_desc) + len; + uint32_t head = f->head; + uint32_t tail = f->tail; + uint32_t h_wrap = f->h_wrap; memset(&desc, 0, sizeof(struct pkt_desc)); /* Ensure 4-byte alignment in the buffer */ - if (f->head % 4) - f->head += 4 - (f->head % 4); - if (fifo_space(f) < (sizeof(struct pkt_desc) + len)) { + if (head % 4) + head += 4 - (head % 4); + if (head >= f->size) + head = 0; + if (fifo_space(f) < needed) { return -1; } - if ((f->head + sizeof(struct pkt_desc) + len) > f->size) { - return -1; + if (h_wrap && head == h_wrap) + head = 0; + if (h_wrap == 0 && head >= tail) { + uint32_t end_space = f->size - head; + if (end_space < needed) { + if (tail <= needed) + return -1; + h_wrap = head; + head = 0; + } + } + if (h_wrap) { + if (head + needed > tail) + return -1; + } else { + if (head + needed > f->size) + return -1; } - desc.pos = f->head; + desc.pos = head; desc.len = len; - memcpy((uint8_t *)f->data + f->head, &desc, sizeof(struct pkt_desc)); - f->head += sizeof(struct pkt_desc); - memcpy((uint8_t *)f->data + f->head, data, len); - f->head += len; + memcpy((uint8_t *)f->data + head, &desc, sizeof(struct pkt_desc)); + head += sizeof(struct pkt_desc); + memcpy((uint8_t *)f->data + head, data, len); + head += len; + f->head = head; + f->h_wrap = h_wrap; return 0; } @@ -270,14 +275,29 @@ static struct pkt_desc *fifo_pop(struct fifo *f) return NULL; while (f->tail % 4) f->tail++; - f->tail %= f->size; + if (f->h_wrap && f->tail >= f->h_wrap) { + f->tail = 0; + f->h_wrap = 0; + } + if (f->h_wrap && (f->tail < f->h_wrap) && + ((f->tail + sizeof(struct pkt_desc)) > f->h_wrap)) { + f->tail = 0; + f->h_wrap = 0; + } + if (f->tail >= f->size) + f->tail %= f->size; if (f->tail == f->head) return NULL; - if ((f->head < f->tail) && ((f->tail + sizeof(struct pkt_desc) + LINK_MTU > f->size))) + if ((f->tail + sizeof(struct pkt_desc)) > f->size) f->tail = 0; desc = (struct pkt_desc *)((uint8_t *)f->data + f->tail); f->tail += sizeof(struct pkt_desc) + desc->len; - f->tail %= f->size; + if (f->h_wrap && f->tail >= f->h_wrap) { + f->tail -= f->h_wrap; + f->h_wrap = 0; + } + if (f->tail >= f->size) + f->tail %= f->size; return desc; } From 8e09fc540e2bc733339f01b89a987a5875cd01fb Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 11:04:53 +0100 Subject: [PATCH 4/9] Fixed fifo_space calculation --- src/wolfip.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/wolfip.c b/src/wolfip.c index 1798658..440d265 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -235,8 +235,21 @@ static int fifo_push(struct fifo *f, void *data, uint32_t len) head += 4 - (head % 4); if (head >= f->size) head = 0; - if (fifo_space(f) < needed) { - return -1; + { + uint32_t space; + if (head == tail) + space = f->size; + else if (h_wrap) { + if (head < tail) + space = tail - head; + else + space = 0; + } else if (head >= tail) + space = f->size - (head - tail); + else + space = tail - head; + if (space < needed) + return -1; } if (h_wrap && head == h_wrap) head = 0; From 260cf446fe1080bc96c28c218a3544a7a7a20465 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 13:55:37 +0100 Subject: [PATCH 5/9] TCP Congestion control fixes - part II - Fix in-flight calculation, bound cwnd to RWND - Only perform congestion control increases when traffic is CWND-limited - Add missing window scaling options parsing & sending per RFC7323 --- README.md | 2 +- src/test/unit/unit.c | 45 +++++++++- src/wolfip.c | 210 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 230 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 2bae28e..691b733 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A single network interface can be associated with the device. | **Transport** | UDP | Unicast datagrams, checksum | [RFC 768](https://datatracker.ietf.org/doc/html/rfc768) | | **Transport** | TCP | Connection management, reliable delivery | [RFC 793](https://datatracker.ietf.org/doc/html/rfc793), [RFC 9293](https://datatracker.ietf.org/doc/html/rfc9293) | | **Transport** | TCP Options: MSS | Maximum Segment Size negotiation | [RFC 793](https://datatracker.ietf.org/doc/html/rfc793) | -| **Transport** | TCP Options: Timestamps | RTT measurement, PAWS | [RFC 7323](https://datatracker.ietf.org/doc/html/rfc7323) | +| **Transport** | TCP Options: Timestamps | RTT measurement, PAWS, Window Scaling | [RFC 7323](https://datatracker.ietf.org/doc/html/rfc7323) | | **Transport** | TCP Congestion Control | Slow start, congestion avoidance | [RFC 5681](https://datatracker.ietf.org/doc/html/rfc5681) | | **Transport** | TCP Fast Retransmit | Triple duplicate ACK detection | [RFC 5681](https://datatracker.ietf.org/doc/html/rfc5681) | | **Application** | DHCP | Client only (DORA) | [RFC 2131](https://datatracker.ietf.org/doc/html/rfc2131) | diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 17cd5eb..bdf2ea5 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -7826,6 +7826,8 @@ START_TEST(test_tcp_ack_acks_data_and_sets_writable) ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS; ts->sock.tcp.ssthresh = TCP_MSS * 4; + /* Ensure cwnd growth is gated by bytes_in_flight and not rwnd-capped. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(seg, 0, sizeof(seg_buf)); @@ -7839,6 +7841,9 @@ START_TEST(test_tcp_ack_acks_data_and_sets_writable) ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; desc->time_sent = 10; + /* Simulate cwnd-limited flight and initialize snd_una. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS; + ts->sock.tcp.snd_una = seq; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq + sizeof(payload)); @@ -7870,6 +7875,8 @@ START_TEST(test_tcp_ack_duplicate_resend_clears_sent) ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS * 4; ts->sock.tcp.ssthresh = TCP_MSS * 4; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(seg, 0, sizeof(seg_buf)); @@ -8094,6 +8101,10 @@ START_TEST(test_tcp_ack_duplicate_zero_len_segment_large_ack) desc = fifo_next(&ts->sock.tcp.txbuf, desc); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + /* Treat this ACK as a duplicate (snd_una == ack). */ + ts->sock.tcp.snd_una = 0xF0000000U; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(0xF0000000U); @@ -8124,6 +8135,8 @@ START_TEST(test_tcp_ack_duplicate_seq_match_large_seg_len) ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS * 4; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -8135,6 +8148,9 @@ START_TEST(test_tcp_ack_duplicate_seq_match_large_seg_len) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = seq; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq); @@ -8453,6 +8469,8 @@ START_TEST(test_tcp_ack_cwnd_count_wrap) ts->sock.tcp.cwnd = TCP_MSS * 4; ts->sock.tcp.ssthresh = TCP_MSS; ts->sock.tcp.cwnd_count = ts->sock.tcp.cwnd - 1; + /* Ensure cwnd growth path is taken and not rwnd-capped. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -8464,6 +8482,10 @@ START_TEST(test_tcp_ack_cwnd_count_wrap) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Simulate cwnd-limited flight and initialize snd_una. */ + ts->sock.tcp.bytes_in_flight = ts->sock.tcp.cwnd; + /* Advance ACK by 1 byte to exercise cwnd_count wrap. */ + ts->sock.tcp.snd_una = 100; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(101); @@ -8471,7 +8493,8 @@ START_TEST(test_tcp_ack_cwnd_count_wrap) ackseg.flags = 0x10; tcp_ack(ts, &ackseg); - ck_assert_uint_eq(ts->sock.tcp.cwnd_count, 0); + /* Expect cwnd_count to wrap to (cwnd_count + MSS - cwnd). */ + ck_assert_uint_eq(ts->sock.tcp.cwnd_count, (TCP_MSS - 1)); ck_assert_uint_eq(ts->sock.tcp.cwnd, (TCP_MSS * 5)); } END_TEST @@ -8494,6 +8517,8 @@ START_TEST(test_tcp_ack_updates_rtt_and_cwnd) ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS; ts->sock.tcp.ssthresh = TCP_MSS * 4; + /* Ensure cwnd growth path is taken and not rwnd-capped. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); s.last_tick = 1000; @@ -8511,6 +8536,9 @@ START_TEST(test_tcp_ack_updates_rtt_and_cwnd) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Simulate cwnd-limited flight and initialize snd_una. */ + ts->sock.tcp.bytes_in_flight = ts->sock.tcp.cwnd; + ts->sock.tcp.snd_una = 100; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(101); @@ -8578,6 +8606,8 @@ START_TEST(test_tcp_ack_no_progress_when_ack_far_ahead) ts->proto = WI_IPPROTO_TCP; ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -8589,6 +8619,9 @@ START_TEST(test_tcp_ack_no_progress_when_ack_far_ahead) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = 500; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq + 0x100000U); @@ -8657,6 +8690,8 @@ START_TEST(test_tcp_ack_duplicate_clears_sent_large_seg_len) ts->proto = WI_IPPROTO_TCP; ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -8668,6 +8703,9 @@ START_TEST(test_tcp_ack_duplicate_clears_sent_large_seg_len) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = 500; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(500); @@ -8734,6 +8772,8 @@ START_TEST(test_tcp_ack_duplicate_ssthresh_min) ts->S = &s; ts->sock.tcp.state = TCP_ESTABLISHED; ts->sock.tcp.cwnd = TCP_MSS; + /* Allow duplicate-ACK path without rwnd cap. */ + ts->sock.tcp.peer_rwnd = TCP_MSS * 8; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); memset(&segbuf, 0, sizeof(segbuf)); @@ -8745,6 +8785,9 @@ START_TEST(test_tcp_ack_duplicate_ssthresh_min) desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); desc->flags |= PKT_FLAG_SENT; + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS; + ts->sock.tcp.snd_una = 50; memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(50); diff --git a/src/wolfip.c b/src/wolfip.c index 440d265..d223790 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -79,9 +79,12 @@ struct wolfIP_icmp_packet; #define TCP_OPTION_MSS 0x02 #define TCP_OPTION_MSS_LEN 4 +#define TCP_OPTION_WS 0x03 +#define TCP_OPTION_WS_LEN 3 #define TCP_OPTION_TS 0x08 #define TCP_OPTION_TS_LEN 10 #define TCP_OPTIONS_LEN 12 +#define TCP_SYN_OPTIONS_LEN 20 #define TCP_OPTION_NOP 0x01 #define TCP_OPTION_EOO 0x00 @@ -126,6 +129,16 @@ struct fifo { uint8_t *data; }; +static struct wolfIP_stats { + uint64_t poll_calls; + uint64_t tx_desc_seen; + uint64_t tx_sent; + uint64_t tx_bytes; + uint64_t tx_filter_block; + uint64_t tx_arp_block; + uint64_t tx_cwnd_block; +} wolfip_stats; + /* TCP TX is a circular buffer and contains an array of full packets */ /* TCP RX only contains application data */ @@ -452,6 +465,12 @@ struct PACKED tcp_opt_mss { uint16_t mss; }; +struct PACKED tcp_opt_ws { + /* Window Scale option (3 bytes) */ + uint8_t opt, len; + uint8_t shift; +}; + /* UDP datagram */ struct PACKED wolfIP_udp_datagram { struct wolfIP_ip_packet ip; @@ -779,8 +798,10 @@ enum tcp_state { struct tcpsocket { enum tcp_state state; uint32_t last_ts, rtt, rto, cwnd, cwnd_count, ssthresh, tmr_rto, rto_backoff, - seq, ack, last_ack, last; + seq, ack, last_ack, last, bytes_in_flight, snd_una; ip4 local_ip, remote_ip; + uint32_t peer_rwnd; + uint8_t snd_wscale, rcv_wscale, ws_enabled, ws_offer; struct fifo txbuf; struct queue rxbuf; }; @@ -1371,6 +1392,19 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) t->sock.tcp.ssthresh = 64 * TCP_MSS; t->sock.tcp.rtt = 0; t->sock.tcp.rto_backoff = 0; + t->sock.tcp.bytes_in_flight = 0; + t->sock.tcp.snd_una = t->sock.tcp.seq; + t->sock.tcp.peer_rwnd = 0xFFFF; + t->sock.tcp.snd_wscale = 0; + t->sock.tcp.ws_enabled = 0; + { + uint32_t space = RXBUF_SIZE; + uint8_t shift = 0; + while (shift < 14 && (space >> shift) > 0xFFFF) + shift++; + t->sock.tcp.rcv_wscale = shift; + } + t->sock.tcp.ws_offer = (t->sock.tcp.rcv_wscale > 0); queue_init(&t->sock.tcp.rxbuf, t->rxmem, RXBUF_SIZE, 0); fifo_init(&t->sock.tcp.txbuf, t->txmem, TXBUF_SIZE); @@ -1380,6 +1414,46 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) return NULL; } +static uint16_t tcp_adv_win(const struct tsocket *t) +{ + 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; + uint32_t win = space >> shift; + if (win > 0xFFFF) + win = 0xFFFF; + return (uint16_t)win; +} + +static int tcp_process_ws(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) +{ + const uint8_t *opt = (const uint8_t *)tcp->data; + uint32_t opt_len = (uint32_t)((tcp->hlen >> 2) - TCP_HEADER_LEN); + int found = 0; + while (opt_len > 0) { + if (*opt == TCP_OPTION_NOP) { + opt++; + opt_len--; + continue; + } else if (*opt == TCP_OPTION_EOO) { + break; + } + if (opt_len < 2) + break; + if (opt[1] < 2 || opt[1] > opt_len) + break; + if (*opt == TCP_OPTION_WS && opt[1] == TCP_OPTION_WS_LEN) { + uint8_t shift = opt[2]; + if (shift > 14) + shift = 14; + t->sock.tcp.snd_wscale = shift; + found = 1; + } + opt_len -= opt[1]; + opt += opt[1]; + } + return found; +} + static void tcp_send_empty(struct tsocket *t, uint8_t flags) { struct wolfIP_tcp_seg *tcp; @@ -1393,7 +1467,7 @@ static void tcp_send_empty(struct tsocket *t, uint8_t flags) tcp->ack = ee32(t->sock.tcp.ack); tcp->hlen = ((20 + TCP_OPTIONS_LEN) << 2) & 0xF0; tcp->flags = flags; - tcp->win = ee16((uint16_t)queue_space(&t->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(t)); tcp->csum = 0; tcp->urg = 0; ts = (struct tcp_opt_ts *)tcp->data; @@ -1423,31 +1497,54 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) struct wolfIP_tcp_seg *tcp; struct tcp_opt_ts *ts; struct tcp_opt_mss *mss; - uint8_t buffer[sizeof(struct wolfIP_tcp_seg) + TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN]; + struct tcp_opt_ws *ws; + uint8_t *opt; + uint8_t buffer[sizeof(struct wolfIP_tcp_seg) + TCP_SYN_OPTIONS_LEN]; + uint8_t include_ws = 0; + uint8_t opt_len = TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN; tcp = (struct wolfIP_tcp_seg *)buffer; memset(tcp, 0, sizeof(buffer)); + if (flags & 0x02) { + if ((flags & 0x10) != 0) { + include_ws = t->sock.tcp.ws_enabled; + } else { + include_ws = t->sock.tcp.ws_offer; + } + } + if (include_ws) + opt_len = TCP_SYN_OPTIONS_LEN; tcp->src_port = ee16(t->src_port); tcp->dst_port = ee16(t->dst_port); tcp->seq = ee32(t->sock.tcp.seq); tcp->ack = ee32(t->sock.tcp.ack); - tcp->hlen = ((20 + TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN) << 2) & 0xF0; + tcp->hlen = ((20 + opt_len) << 2) & 0xF0; tcp->flags = flags; - tcp->win = ee16((uint16_t)queue_space(&t->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(t)); tcp->csum = 0; tcp->urg = 0; - ts = (struct tcp_opt_ts *)tcp->data; + opt = tcp->data; + ts = (struct tcp_opt_ts *)opt; ts->opt = TCP_OPTION_TS; ts->len = TCP_OPTION_TS_LEN; ts->val = ee32(t->S->last_tick & 0xFFFFFFFFU); ts->ecr = t->sock.tcp.last_ts; - ts->pad = 0x01; - ts->eoo = 0x01; - mss = (struct tcp_opt_mss *)((uint8_t *)tcp->data + sizeof(struct tcp_opt_ts)); + ts->pad = TCP_OPTION_NOP; + ts->eoo = TCP_OPTION_NOP; + opt += sizeof(*ts); + mss = (struct tcp_opt_mss *)opt; mss->opt = TCP_OPTION_MSS; mss->len = TCP_OPTION_MSS_LEN; mss->mss = ee16(TCP_MSS); - fifo_push(&t->sock.tcp.txbuf, tcp, sizeof(struct wolfIP_tcp_seg) + \ - TCP_OPTIONS_LEN + TCP_OPTION_MSS_LEN); + opt += sizeof(*mss); + if (include_ws) { + ws = (struct tcp_opt_ws *)opt; + ws->opt = TCP_OPTION_WS; + ws->len = TCP_OPTION_WS_LEN; + ws->shift = t->sock.tcp.rcv_wscale; + opt += sizeof(*ws); + *opt++ = TCP_OPTION_NOP; + } + fifo_push(&t->sock.tcp.txbuf, tcp, sizeof(struct wolfIP_tcp_seg) + opt_len); } /* Add a segment to the rx buffer for the application to consume */ @@ -1698,6 +1795,7 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) uint32_t ack = ee32(tcp->ack); struct pkt_desc *desc; int ack_count = 0; + uint32_t inflight_pre = t->sock.tcp.bytes_in_flight; 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)); @@ -1746,21 +1844,37 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) t->sock.tcp.rtt = (7 * (t->sock.tcp.rtt << 3)) + (rtt << 3); } } - /* Update cwnd */ - if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) { - t->sock.tcp.cwnd += TCP_MSS; - } else { - t->sock.tcp.cwnd_count++; - if (t->sock.tcp.cwnd_count == t->sock.tcp.cwnd) { - t->sock.tcp.cwnd_count = 0; + if (t->sock.tcp.snd_una != ack) { + uint32_t delta = ack - t->sock.tcp.snd_una; + if (delta >= t->sock.tcp.bytes_in_flight) + t->sock.tcp.bytes_in_flight = 0; + else + t->sock.tcp.bytes_in_flight -= delta; + t->sock.tcp.snd_una = ack; + } + /* Update cwnd only if we were cwnd-limited. */ + if (t->sock.tcp.cwnd <= inflight_pre) { + if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) { t->sock.tcp.cwnd += TCP_MSS; + } else { + t->sock.tcp.cwnd_count += TCP_MSS; + if (t->sock.tcp.cwnd_count >= t->sock.tcp.cwnd) { + t->sock.tcp.cwnd_count -= t->sock.tcp.cwnd; + t->sock.tcp.cwnd += TCP_MSS; + } } } + if (t->sock.tcp.cwnd > t->sock.tcp.peer_rwnd) + t->sock.tcp.cwnd = t->sock.tcp.peer_rwnd; if (fifo_space(&t->sock.tcp.txbuf) > 0) t->events |= CB_EVENT_WRITABLE; } } else { - /* Duplicate ack */ + /* Duplicate ack (no advance in snd_una). */ + if (ack != t->sock.tcp.snd_una) + return; + if (t->sock.tcp.bytes_in_flight == 0) + return; t->sock.tcp.ssthresh = t->sock.tcp.cwnd / 2; if (t->sock.tcp.ssthresh < 2 * TCP_MSS) { t->sock.tcp.ssthresh = 2 * TCP_MSS; @@ -1822,6 +1936,17 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s return; } tcplen = iplen - (IP_HEADER_LEN + (tcp->hlen >> 2)); + if (tcp->flags & 0x02) { + int ws_found = tcp_process_ws(t, tcp); + if (ws_found && t->sock.tcp.ws_offer) { + t->sock.tcp.ws_enabled = 1; + } else { + t->sock.tcp.ws_enabled = 0; + t->sock.tcp.snd_wscale = 0; + } + } + t->sock.tcp.peer_rwnd = (uint32_t)ee16(tcp->win) << + (t->sock.tcp.ws_enabled ? t->sock.tcp.snd_wscale : 0); /* Check if RST */ if (tcp->flags & 0x04) { if (t->sock.tcp.state == TCP_LISTEN) { @@ -1893,6 +2018,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s t->sock.tcp.state = TCP_ESTABLISHED; t->sock.tcp.ack = ee32(tcp->seq) + 1; t->sock.tcp.seq = ee32(tcp->ack); + t->sock.tcp.snd_una = t->sock.tcp.seq; t->events |= CB_EVENT_WRITABLE; tcp_process_ts(t, tcp); tcp_send_ack(t); @@ -1905,6 +2031,7 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s t->sock.tcp.state = TCP_ESTABLISHED; t->sock.tcp.ack = ee32(tcp->seq); t->sock.tcp.seq = ee32(tcp->ack); + t->sock.tcp.snd_una = t->sock.tcp.seq; t->events |= CB_EVENT_WRITABLE; } } else if (t->sock.tcp.state == TCP_LAST_ACK) { @@ -1954,6 +2081,16 @@ static void tcp_rto_cb(void *arg) while (desc) { if (desc->flags & PKT_FLAG_SENT) { desc->flags &= ~PKT_FLAG_SENT; + { + struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); + uint32_t seg_len = ee16(seg->ip.len) - (IP_HEADER_LEN + (seg->hlen >> 2)); + if (seg_len > 0) { + if (seg_len >= ts->sock.tcp.bytes_in_flight) + ts->sock.tcp.bytes_in_flight = 0; + else + ts->sock.tcp.bytes_in_flight -= seg_len; + } + } pending++; } desc = fifo_next(&ts->sock.tcp.txbuf, desc); @@ -2131,6 +2268,7 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad if (ts->src_port < 1024) ts->src_port += 1024; ts->dst_port = ee16(sin->sin_port); + ts->sock.tcp.snd_una = ts->sock.tcp.seq; if (wolfIP_filter_notify_socket_event( WOLFIP_FILT_CONNECTING, s, ts, ts->local_ip, ts->src_port, ts->remote_ip, ts->dst_port) != 0) { @@ -2182,6 +2320,11 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add newts->sock.tcp.ack = ts->sock.tcp.ack; newts->sock.tcp.seq = ts->sock.tcp.seq; newts->sock.tcp.last_ts = ts->sock.tcp.last_ts; + newts->sock.tcp.peer_rwnd = ts->sock.tcp.peer_rwnd; + newts->sock.tcp.snd_wscale = ts->sock.tcp.snd_wscale; + newts->sock.tcp.rcv_wscale = ts->sock.tcp.rcv_wscale; + newts->sock.tcp.ws_enabled = ts->sock.tcp.ws_enabled; + newts->sock.tcp.ws_offer = ts->sock.tcp.ws_offer; newts->sock.tcp.state = TCP_ESTABLISHED; /* Send SYN-ACK to accept connection. * Send the syn-ack from the newly established socket: @@ -2260,7 +2403,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 = (TCP_HEADER_LEN + TCP_OPTIONS_LEN) << 2; tcp->flags = 0x10 | ((sent == 0)? 0x08 : 0); /* ACK; PSH only on first */ - tcp->win = ee16(queue_space(&ts->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(ts)); tcp->csum = 0; tcp->urg = 0; tsopt->opt = TCP_OPTION_TS; @@ -3872,15 +4015,18 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) int len = 0; int i = 0; uint8_t buf[LINK_MTU]; + const int poll_budget = 64; unsigned int if_idx; struct wolfIP_timer tmr; memset(buf, 0, LINK_MTU); + wolfip_stats.poll_calls++; s->last_tick = now; /* Step 1: Poll the device */ for (if_idx = 0; if_idx < s->if_count; if_idx++) { struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); + int budget = poll_budget; if (!ll || !ll->poll) continue; do { @@ -3888,8 +4034,9 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) if (len > 0) { /* Process packet */ wolfIP_recv_on(s, if_idx, buf, len); + budget--; } - } while (len > 0); + } while (len > 0 && budget > 0); } /* Step 2: Handle timers */ while(is_timer_expired(&s->timers, now)) { @@ -3922,16 +4069,16 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) /* Step 4: attempt to write any pending data */ for (i = 0; i < MAX_TCPSOCKETS; i++) { struct tsocket *ts = &s->tcpsockets[i]; - uint32_t in_flight = 0; + uint32_t in_flight = ts->sock.tcp.bytes_in_flight; uint32_t size = 0; struct pkt_desc *desc; struct wolfIP_tcp_seg *tcp; desc = fifo_peek(&ts->sock.tcp.txbuf); while (desc) { unsigned int tx_if = wolfIP_socket_if_idx(ts); + wolfip_stats.tx_desc_seen++; tcp = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); if (desc->flags & PKT_FLAG_SENT) { - in_flight += ee16(tcp->ip.len) - (IP_HEADER_LEN + (tcp->hlen >> 2)); desc = fifo_next(&ts->sock.tcp.txbuf, desc); continue; } else { @@ -3944,11 +4091,16 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) memcpy(ts->nexthop_mac, loop->mac, 6); } else if (arp_lookup(s, tx_if, nexthop, ts->nexthop_mac) < 0) { /* Send ARP request */ + wolfip_stats.tx_arp_block++; arp_request(s, tx_if, nexthop); break; } #endif - if (in_flight <= ts->sock.tcp.cwnd) { + { + uint32_t snd_wnd = ts->sock.tcp.cwnd; + if (ts->sock.tcp.peer_rwnd < snd_wnd) + snd_wnd = ts->sock.tcp.peer_rwnd; + if (in_flight <= snd_wnd) { struct wolfIP_timer new_tmr = {}; size = desc->len - ETH_HEADER_LEN; tcp = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); @@ -3963,16 +4115,19 @@ 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(queue_space(&ts->sock.tcp.rxbuf)); + tcp->win = ee16(tcp_adv_win(ts)); 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) { + wolfip_stats.tx_filter_block++; break; } if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip, desc->len) != 0) { + wolfip_stats.tx_filter_block++; break; } #ifdef ETHERNET if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) { + wolfip_stats.tx_filter_block++; break; } #endif @@ -3982,6 +4137,8 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) ll->send(ll, tcp, desc->len); } } + wolfip_stats.tx_sent++; + wolfip_stats.tx_bytes += desc->len; desc->flags |= PKT_FLAG_SENT; desc->time_sent = now; if (size == IP_HEADER_LEN + (uint32_t)(tcp->hlen >> 2)) { @@ -3997,11 +4154,14 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) new_tmr.arg = ts; ts->sock.tcp.tmr_rto = timers_binheap_insert(&s->timers, new_tmr); in_flight += payload_len; + ts->sock.tcp.bytes_in_flight += payload_len; desc = fifo_next(&ts->sock.tcp.txbuf, desc); } } else { + wolfip_stats.tx_cwnd_block++; break; } + } } } } From 616fe51006f3846f0940d18b26b604498cea0ac0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 14:10:57 +0100 Subject: [PATCH 6/9] Addressed reviewer's comment + made TAP non-blocking --- src/port/posix/tap_linux.c | 11 +++++++--- src/wolfip.c | 41 ++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/port/posix/tap_linux.c b/src/port/posix/tap_linux.c index d16858b..cb8b5db 100644 --- a/src/port/posix/tap_linux.c +++ b/src/port/posix/tap_linux.c @@ -101,9 +101,14 @@ int tap_init(struct wolfIP_ll_dev *ll, const char *ifname, uint32_t host_ip) tap_fd = -1; } } - if (tap_fd < 0) { - return -1; - } + if (tap_fd < 0) { + return -1; + } + { + int flags = fcntl(tap_fd, F_GETFL, 0); + if (flags >= 0) + (void)fcntl(tap_fd, F_SETFL, flags | O_NONBLOCK); + } } /* Get mac address */ if (ioctl(tap_fd, SIOCGIFHWADDR, &ifr) < 0) { diff --git a/src/wolfip.c b/src/wolfip.c index d223790..8cd3bc0 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -173,11 +173,8 @@ static uint32_t fifo_space(struct fifo *f) return f->tail - f->head; } -/* Check the descriptor of the next packet */ -static struct pkt_desc *fifo_peek(struct fifo *f) +static void fifo_skip_padding_tail(struct fifo *f) { - if (f->tail == f->head) - return NULL; while (f->tail % 4) f->tail++; if (f->h_wrap && f->tail >= f->h_wrap) { @@ -191,10 +188,22 @@ static struct pkt_desc *fifo_peek(struct fifo *f) } if (f->tail >= f->size) f->tail %= f->size; - if (f->tail == f->head) - return NULL; if ((f->tail + sizeof(struct pkt_desc)) > f->size) f->tail = 0; +} + +/* Check the descriptor of the next packet */ +static struct pkt_desc *fifo_peek(struct fifo *f) +{ + if (f->tail == f->head) + return NULL; + /* Advance tail only to skip alignment/wrap padding, not real packet data. + * This is safe because padding bytes are not part of any pkt_desc payload. + * We do this right before reading the next descriptor so callers always + * see a valid, aligned pkt_desc without dequeuing a packet. */ + fifo_skip_padding_tail(f); + if (f->tail == f->head) + return NULL; return (struct pkt_desc *)((uint8_t *)f->data + f->tail); } @@ -219,9 +228,7 @@ static struct pkt_desc *fifo_next(struct fifo *f, struct pkt_desc *desc) /* Return the number of bytes used */ static uint32_t fifo_len(struct fifo *f) { - while (f->tail % 4) - f->tail++; - f->tail %= f->size; + fifo_skip_padding_tail(f); if (f->tail == f->head) return 0; if (f->tail > f->head) { @@ -299,23 +306,9 @@ static struct pkt_desc *fifo_pop(struct fifo *f) struct pkt_desc *desc; if (f->tail == f->head) return NULL; - while (f->tail % 4) - f->tail++; - if (f->h_wrap && f->tail >= f->h_wrap) { - f->tail = 0; - f->h_wrap = 0; - } - if (f->h_wrap && (f->tail < f->h_wrap) && - ((f->tail + sizeof(struct pkt_desc)) > f->h_wrap)) { - f->tail = 0; - f->h_wrap = 0; - } - if (f->tail >= f->size) - f->tail %= f->size; + fifo_skip_padding_tail(f); if (f->tail == f->head) return NULL; - if ((f->tail + sizeof(struct pkt_desc)) > f->size) - f->tail = 0; desc = (struct pkt_desc *)((uint8_t *)f->data + f->tail); f->tail += sizeof(struct pkt_desc) + desc->len; if (f->h_wrap && f->tail >= f->h_wrap) { From a70c8868ad42818d2243fadcfff61bfec5b6dc28 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 17:26:55 +0100 Subject: [PATCH 7/9] Addressed second round of Copilot reviews Addressed all review comments. + fixed 3 x dup to enter fast retransmit + fixed window scaling calculation + added capped budget for main poll --- src/port/posix/bsd_socket.c | 2 +- src/test/unit/unit.c | 46 ++++++++-- src/wolfip.c | 175 ++++++++++++++++++++++-------------- 3 files changed, 145 insertions(+), 78 deletions(-) diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index b1c7a8f..4c35e34 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -1431,7 +1431,7 @@ void __attribute__((constructor)) init_wolfip_posix() { wolfIP_ipconfig_set(IPSTACK, atoip4(WOLFIP_IP), atoip4("255.255.255.0"), atoip4(HOST_STACK_IP)); printf("IP: manually configured - %s\n", WOLFIP_IP); - sleep(1); + sleep(2); pthread_create(&wolfIP_thread, NULL, wolfIP_sock_posix_ip_loop, IPSTACK); in_the_stack = 0; } diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index bdf2ea5..cee5128 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -492,6 +492,8 @@ START_TEST(test_fifo_peek_wraps_tail_when_head_lt_tail) f.tail = 4; f.h_wrap = 0; + /* With head at 0 and tail aligned, peek should return the current tail + * descriptor without altering tail or wrap state. */ desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); ck_assert_uint_eq(f.tail, 4); @@ -509,6 +511,7 @@ START_TEST(test_fifo_peek_no_wrap_when_space_available) f.tail = 4; f.h_wrap = 0; + /* When no wrap boundary is set, peek must not change tail. */ desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); ck_assert_uint_eq(f.tail, 4); @@ -553,6 +556,7 @@ START_TEST(test_fifo_pop_aligns_tail_to_head_returns_null) f.head = 4; f.tail = 1; + /* Aligning tail to head means the FIFO is empty; pop should return NULL. */ ck_assert_ptr_eq(fifo_pop(&f), NULL); } END_TEST @@ -572,6 +576,7 @@ START_TEST(test_fifo_pop_wraps_tail_when_head_lt_tail) desc->pos = 4; desc->len = 0; + /* Popping a zero-length packet should advance tail past the descriptor. */ ck_assert_ptr_nonnull(fifo_pop(&f)); ck_assert_uint_eq(f.tail, 4 + sizeof(struct pkt_desc)); } @@ -594,6 +599,7 @@ START_TEST(test_fifo_pop_no_wrap_when_space_available) desc->len = 0; expected_tail = 4 + sizeof(struct pkt_desc); + /* With no wrap, pop should advance tail to the next descriptor. */ ck_assert_ptr_nonnull(fifo_pop(&f)); ck_assert_uint_eq(f.tail, expected_tail); } @@ -4104,7 +4110,7 @@ START_TEST(test_sock_sendto_tcp_txbuf_full) } END_TEST -START_TEST(test_sock_sendto_tcp_partial_send) +START_TEST(test_sock_sendto_tcp_partial_send_only) { struct wolfIP s; int tcp_sd; @@ -4112,8 +4118,10 @@ START_TEST(test_sock_sendto_tcp_partial_send) size_t seg_payload = TCP_MSS - TCP_OPTIONS_LEN; size_t payload_len = seg_payload; uint8_t buf[seg_payload * 2]; - size_t required = sizeof(struct pkt_desc) + IP_HEADER_LEN + TCP_HEADER_LEN + TCP_OPTIONS_LEN + payload_len; - uint8_t txbuf[required + 4]; + size_t required = sizeof(struct pkt_desc) + sizeof(struct wolfIP_tcp_seg) + + TCP_OPTIONS_LEN + payload_len; + /* Provide a small alignment cushion so one segment fits but two do not. */ + uint8_t txbuf[required + 8]; int ret; wolfIP_init(&s); @@ -4131,9 +4139,10 @@ START_TEST(test_sock_sendto_tcp_partial_send) fifo_init(&ts->sock.tcp.txbuf, txbuf, sizeof(txbuf)); memset(buf, 0xAB, sizeof(buf)); + /* Expect a partial send because the tx buffer fits only one segment + * (includes full TCP/IP header size and alignment padding). */ ret = wolfIP_sock_sendto(&s, tcp_sd, buf, sizeof(buf), 0, NULL, 0); - ck_assert_msg(ret > 0 || ret == -WOLFIP_EAGAIN, - "expected partial send or EAGAIN, got %d", ret); + ck_assert_msg(ret > 0, "expected partial send, got %d", ret); } END_TEST @@ -4562,10 +4571,10 @@ START_TEST(test_fifo_push_and_pop) { fifo_init(&f, mem, memsz); ck_assert_int_eq(fifo_space(&f), memsz); - // Test push + /* Push one payload and verify descriptors reflect its size. */ ck_assert_int_eq(fifo_push(&f, data, sizeof(data)), 0); - // Test peek + /* Peek should return the current head descriptor without consuming data. */ desc = fifo_peek(&f); ck_assert_ptr_nonnull(desc); ck_assert_int_eq(desc->len, sizeof(data)); @@ -4576,7 +4585,7 @@ START_TEST(test_fifo_push_and_pop) { ck_assert_int_eq(fifo_len(&f), desc->len + sizeof(struct pkt_desc)); - // Test pop + /* Pop should consume the packet and restore full space. */ desc = fifo_pop(&f); ck_assert_int_eq(fifo_space(&f), memsz); ck_assert_ptr_nonnull(desc); @@ -7851,6 +7860,7 @@ START_TEST(test_tcp_ack_acks_data_and_sets_writable) ackseg.flags = 0x10; tcp_ack(ts, &ackseg); + /* FIFO should be empty after acked data is removed. */ ck_assert_ptr_eq(fifo_peek(&ts->sock.tcp.txbuf), NULL); ck_assert_uint_eq(ts->events & CB_EVENT_WRITABLE, CB_EVENT_WRITABLE); ck_assert_uint_gt(ts->sock.tcp.cwnd, TCP_MSS); @@ -8111,6 +8121,8 @@ START_TEST(test_tcp_ack_duplicate_zero_len_segment_large_ack) ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Prime dup-ack counter so a single ACK triggers fast retransmit. */ + ts->sock.tcp.dup_acks = 2; tcp_ack(ts, &ackseg); ck_assert_uint_le(fifo_len(&ts->sock.tcp.txbuf), TXBUF_SIZE); ck_assert_uint_eq(ts->sock.tcp.ssthresh, TCP_MSS * 2); @@ -8157,6 +8169,9 @@ START_TEST(test_tcp_ack_duplicate_seq_match_large_seg_len) ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Trigger fast retransmit by delivering three duplicate ACKs. */ + tcp_ack(ts, &ackseg); + tcp_ack(ts, &ackseg); tcp_ack(ts, &ackseg); ck_assert_int_ne(desc->flags & PKT_FLAG_SENT, PKT_FLAG_SENT); } @@ -8201,11 +8216,17 @@ START_TEST(test_tcp_ack_duplicate_clears_sent_flag) seg2->seq = ee32(seq2); ck_assert_int_eq(fifo_push(&ts->sock.tcp.txbuf, &segbuf2, sizeof(segbuf2)), 0); + /* Force duplicate ACK handling with outstanding bytes. */ + ts->sock.tcp.bytes_in_flight = TCP_MSS * 2; + ts->sock.tcp.snd_una = seq1; + memset(&ackseg, 0, sizeof(ackseg)); ackseg.ack = ee32(seq1); ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Prime dup-ack counter so a single ACK triggers fast retransmit. */ + ts->sock.tcp.dup_acks = 2; tcp_ack(ts, &ackseg); desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); @@ -8712,6 +8733,9 @@ START_TEST(test_tcp_ack_duplicate_clears_sent_large_seg_len) ackseg.hlen = TCP_HEADER_LEN << 2; ackseg.flags = 0x10; + /* Trigger fast retransmit by delivering three duplicate ACKs. */ + tcp_ack(ts, &ackseg); + tcp_ack(ts, &ackseg); tcp_ack(ts, &ackseg); desc = fifo_peek(&ts->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); @@ -9354,6 +9378,8 @@ START_TEST(test_fifo_peek_wrap_to_start) desc->len = 1; desc->pos = 0; + /* When tail crosses h_wrap, peek should wrap to the start and return + * the first descriptor at offset 0. */ peeked = fifo_peek(&f); ck_assert_ptr_eq(peeked, desc); @@ -9390,6 +9416,7 @@ START_TEST(test_fifo_peek_aligns_tail) desc->len = 1; desc->pos = 4; + /* Peek aligns tail to 4-byte boundary before reading the descriptor. */ peeked = fifo_peek(&f); ck_assert_ptr_eq(peeked, desc); ck_assert_uint_eq(f.tail, 4); @@ -9572,6 +9599,7 @@ START_TEST(test_fifo_pop_wrap_to_start) desc->len = 1; desc->pos = 0; + /* Pop should wrap to start and return the descriptor at offset 0. */ popped = fifo_pop(&f); ck_assert_ptr_eq(popped, desc); @@ -11643,7 +11671,7 @@ Suite *wolf_suite(void) suite_add_tcase(s, tc_utils); tcase_add_test(tc_utils, test_sock_sendto_tcp_txbuf_full); suite_add_tcase(s, tc_utils); - tcase_add_test(tc_utils, test_sock_sendto_tcp_partial_send); + tcase_add_test(tc_utils, test_sock_sendto_tcp_partial_send_only); tcase_add_test(tc_utils, test_sock_sendto_tcp_multiple_segments_flags); tcase_add_test(tc_utils, test_sock_sendto_udp_fifo_push_fails_returns_eagain); tcase_add_test(tc_utils, test_sock_sendto_icmp_fifo_push_fails_returns_eagain); diff --git a/src/wolfip.c b/src/wolfip.c index 8cd3bc0..49961ab 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -107,6 +107,8 @@ struct wolfIP_icmp_packet; #define WI_IP_MTU 1500 #define TCP_MSS (WI_IP_MTU - (IP_HEADER_LEN + TCP_HEADER_LEN)) +/* Arbitrary upper limit to avoid monopolizing the CPU during poll loops. */ +#define WOLFIP_POLL_BUDGET 128 /* Macros */ #define IS_IP_BCAST(ip) (ip == 0xFFFFFFFF) @@ -129,6 +131,34 @@ struct fifo { uint8_t *data; }; +static inline uint32_t fifo_align_head_pos(uint32_t head, uint32_t size) +{ + if (head % 4) + head += 4 - (head % 4); + if (head >= size) + head = 0; + return head; +} + +static inline void fifo_align_tail(struct fifo *f) +{ + if (f->tail % 4) + f->tail += 4 - (f->tail % 4); + if (f->h_wrap && f->tail >= f->h_wrap) { + f->tail = 0; + f->h_wrap = 0; + } + if (f->h_wrap && (f->tail < f->h_wrap) && + ((f->tail + sizeof(struct pkt_desc)) > f->h_wrap)) { + f->tail = 0; + f->h_wrap = 0; + } + if (f->tail >= f->size) + f->tail %= f->size; + if ((f->tail + sizeof(struct pkt_desc)) > f->size) + f->tail = 0; +} + static struct wolfIP_stats { uint64_t poll_calls; uint64_t tx_desc_seen; @@ -137,6 +167,8 @@ static struct wolfIP_stats { uint64_t tx_filter_block; uint64_t tx_arp_block; uint64_t tx_cwnd_block; + uint64_t tx_eagain; + uint64_t tx_push_fail; } wolfip_stats; /* TCP TX is a circular buffer and contains an array of full packets */ @@ -173,25 +205,6 @@ static uint32_t fifo_space(struct fifo *f) return f->tail - f->head; } -static void fifo_skip_padding_tail(struct fifo *f) -{ - while (f->tail % 4) - f->tail++; - if (f->h_wrap && f->tail >= f->h_wrap) { - f->tail = 0; - f->h_wrap = 0; - } - if (f->h_wrap && (f->tail < f->h_wrap) && - ((f->tail + sizeof(struct pkt_desc)) > f->h_wrap)) { - f->tail = 0; - f->h_wrap = 0; - } - if (f->tail >= f->size) - f->tail %= f->size; - if ((f->tail + sizeof(struct pkt_desc)) > f->size) - f->tail = 0; -} - /* Check the descriptor of the next packet */ static struct pkt_desc *fifo_peek(struct fifo *f) { @@ -201,7 +214,7 @@ static struct pkt_desc *fifo_peek(struct fifo *f) * This is safe because padding bytes are not part of any pkt_desc payload. * We do this right before reading the next descriptor so callers always * see a valid, aligned pkt_desc without dequeuing a packet. */ - fifo_skip_padding_tail(f); + fifo_align_tail(f); if (f->tail == f->head) return NULL; return (struct pkt_desc *)((uint8_t *)f->data + f->tail); @@ -228,7 +241,7 @@ static struct pkt_desc *fifo_next(struct fifo *f, struct pkt_desc *desc) /* Return the number of bytes used */ static uint32_t fifo_len(struct fifo *f) { - fifo_skip_padding_tail(f); + fifo_align_tail(f); if (f->tail == f->head) return 0; if (f->tail > f->head) { @@ -251,10 +264,7 @@ static int fifo_push(struct fifo *f, void *data, uint32_t len) uint32_t h_wrap = f->h_wrap; memset(&desc, 0, sizeof(struct pkt_desc)); /* Ensure 4-byte alignment in the buffer */ - if (head % 4) - head += 4 - (head % 4); - if (head >= f->size) - head = 0; + head = fifo_align_head_pos(head, f->size); { uint32_t space; if (head == tail) @@ -306,7 +316,7 @@ static struct pkt_desc *fifo_pop(struct fifo *f) struct pkt_desc *desc; if (f->tail == f->head) return NULL; - fifo_skip_padding_tail(f); + fifo_align_tail(f); if (f->tail == f->head) return NULL; desc = (struct pkt_desc *)((uint8_t *)f->data + f->tail); @@ -792,6 +802,7 @@ struct tcpsocket { enum tcp_state state; uint32_t last_ts, rtt, rto, cwnd, cwnd_count, ssthresh, tmr_rto, rto_backoff, seq, ack, last_ack, last, bytes_in_flight, snd_una; + uint8_t dup_acks; ip4 local_ip, remote_ip; uint32_t peer_rwnd; uint8_t snd_wscale, rcv_wscale, ws_enabled, ws_offer; @@ -1387,6 +1398,7 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) t->sock.tcp.rto_backoff = 0; t->sock.tcp.bytes_in_flight = 0; t->sock.tcp.snd_una = t->sock.tcp.seq; + t->sock.tcp.dup_acks = 0; t->sock.tcp.peer_rwnd = 0xFFFF; t->sock.tcp.snd_wscale = 0; t->sock.tcp.ws_enabled = 0; @@ -1397,7 +1409,9 @@ static struct tsocket *tcp_new_socket(struct wolfIP *s) shift++; t->sock.tcp.rcv_wscale = shift; } - t->sock.tcp.ws_offer = (t->sock.tcp.rcv_wscale > 0); + /* We always include WS in the initial SYN (shift may be 0), so + * mark that we offered it to accept the peer's WS in SYN-ACK. */ + t->sock.tcp.ws_offer = 1; queue_init(&t->sock.tcp.rxbuf, t->rxmem, RXBUF_SIZE, 0); fifo_init(&t->sock.tcp.txbuf, t->txmem, TXBUF_SIZE); @@ -1435,7 +1449,10 @@ static int tcp_process_ws(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) if (opt[1] < 2 || opt[1] > opt_len) break; if (*opt == TCP_OPTION_WS && opt[1] == TCP_OPTION_WS_LEN) { - uint8_t shift = opt[2]; + uint8_t shift; + if (opt_len < TCP_OPTION_WS_LEN) + break; + shift = opt[2]; if (shift > 14) shift = 14; t->sock.tcp.snd_wscale = shift; @@ -1499,9 +1516,11 @@ static void tcp_send_syn(struct tsocket *t, uint8_t flags) memset(tcp, 0, sizeof(buffer)); if (flags & 0x02) { if ((flags & 0x10) != 0) { + /* SYN-ACK: include WS only when enabled on this socket. */ include_ws = t->sock.tcp.ws_enabled; } else { - include_ws = t->sock.tcp.ws_offer; + /* Initial SYN: always include WS to allow peer scaling. */ + include_ws = 1; } } if (include_ws) @@ -1781,6 +1800,10 @@ static int tcp_process_ts(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } #define SEQ_DIFF(a,b) ((a - b) > 0x7FFFFFFF) ? (b - a) : (a - b) +static inline int tcp_seq_leq(uint32_t a, uint32_t b) +{ + return (int32_t)(a - b) <= 0; +} /* Receive an ack */ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) @@ -1789,6 +1812,7 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) struct pkt_desc *desc; int ack_count = 0; uint32_t inflight_pre = t->sock.tcp.bytes_in_flight; + uint32_t acked_bytes = 0; 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)); @@ -1807,7 +1831,7 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) return; } } - if (SEQ_DIFF(ee32(seg->seq) + seg_len, ack) < fifo_len(&t->sock.tcp.txbuf)) { + if (tcp_seq_leq(ee32(seg->seq) + seg_len, ack)) { desc->flags |= PKT_FLAG_ACKED; desc->flags &= ~PKT_FLAG_SENT; desc = fifo_next(&t->sock.tcp.txbuf, desc); @@ -1816,6 +1840,16 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) break; } } + if (t->sock.tcp.snd_una != ack) { + uint32_t delta = ack - t->sock.tcp.snd_una; + if (delta >= t->sock.tcp.bytes_in_flight) + t->sock.tcp.bytes_in_flight = 0; + else + t->sock.tcp.bytes_in_flight -= delta; + t->sock.tcp.snd_una = ack; + t->sock.tcp.dup_acks = 0; + acked_bytes = delta; + } if (ack_count > 0) { struct pkt_desc *fresh_desc = NULL; struct wolfIP_tcp_seg *seg; @@ -1837,16 +1871,8 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) t->sock.tcp.rtt = (7 * (t->sock.tcp.rtt << 3)) + (rtt << 3); } } - if (t->sock.tcp.snd_una != ack) { - uint32_t delta = ack - t->sock.tcp.snd_una; - if (delta >= t->sock.tcp.bytes_in_flight) - t->sock.tcp.bytes_in_flight = 0; - else - t->sock.tcp.bytes_in_flight -= delta; - t->sock.tcp.snd_una = ack; - } /* Update cwnd only if we were cwnd-limited. */ - if (t->sock.tcp.cwnd <= inflight_pre) { + if (t->sock.tcp.cwnd <= (inflight_pre + acked_bytes)) { if (t->sock.tcp.cwnd < t->sock.tcp.ssthresh) { t->sock.tcp.cwnd += TCP_MSS; } else { @@ -1857,8 +1883,6 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } } } - if (t->sock.tcp.cwnd > t->sock.tcp.peer_rwnd) - t->sock.tcp.cwnd = t->sock.tcp.peer_rwnd; if (fifo_space(&t->sock.tcp.txbuf) > 0) t->events |= CB_EVENT_WRITABLE; } @@ -1866,7 +1890,11 @@ static void tcp_ack(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) /* Duplicate ack (no advance in snd_una). */ if (ack != t->sock.tcp.snd_una) return; - if (t->sock.tcp.bytes_in_flight == 0) + if (inflight_pre == 0) + return; + if (t->sock.tcp.dup_acks < 3) + t->sock.tcp.dup_acks++; + if (t->sock.tcp.dup_acks < 3) return; t->sock.tcp.ssthresh = t->sock.tcp.cwnd / 2; if (t->sock.tcp.ssthresh < 2 * TCP_MSS) { @@ -1931,15 +1959,27 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, struct wolfIP_tcp_s tcplen = iplen - (IP_HEADER_LEN + (tcp->hlen >> 2)); if (tcp->flags & 0x02) { int ws_found = tcp_process_ws(t, tcp); - if (ws_found && t->sock.tcp.ws_offer) { - t->sock.tcp.ws_enabled = 1; - } else { - t->sock.tcp.ws_enabled = 0; - t->sock.tcp.snd_wscale = 0; + /* Window scale is negotiated only during SYN/SYN-ACK. */ + if (t->sock.tcp.state == TCP_LISTEN) { + /* Server side: enable if peer offered WS. */ + t->sock.tcp.ws_enabled = ws_found ? 1 : 0; + if (!ws_found) + t->sock.tcp.snd_wscale = 0; + } else if (t->sock.tcp.state == TCP_SYN_SENT) { + /* Client side: only accept WS if we offered it. */ + if (t->sock.tcp.ws_offer && ws_found) { + t->sock.tcp.ws_enabled = 1; + } else { + t->sock.tcp.ws_enabled = 0; + t->sock.tcp.snd_wscale = 0; + } } } - t->sock.tcp.peer_rwnd = (uint32_t)ee16(tcp->win) << - (t->sock.tcp.ws_enabled ? t->sock.tcp.snd_wscale : 0); + { + uint16_t raw_win = ee16(tcp->win); + uint8_t ws_shift = t->sock.tcp.ws_enabled ? t->sock.tcp.snd_wscale : 0; + t->sock.tcp.peer_rwnd = (uint32_t)raw_win << ws_shift; + } /* Check if RST */ if (tcp->flags & 0x04) { if (t->sock.tcp.state == TCP_LISTEN) { @@ -2074,27 +2114,21 @@ static void tcp_rto_cb(void *arg) while (desc) { if (desc->flags & PKT_FLAG_SENT) { desc->flags &= ~PKT_FLAG_SENT; - { - struct wolfIP_tcp_seg *seg = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); - uint32_t seg_len = ee16(seg->ip.len) - (IP_HEADER_LEN + (seg->hlen >> 2)); - if (seg_len > 0) { - if (seg_len >= ts->sock.tcp.bytes_in_flight) - ts->sock.tcp.bytes_in_flight = 0; - else - ts->sock.tcp.bytes_in_flight -= seg_len; - } - } pending++; } desc = fifo_next(&ts->sock.tcp.txbuf, desc); } + if (pending) { + /* RTO implies all in-flight data is considered lost. */ + ts->sock.tcp.bytes_in_flight = 0; + } if (ts->sock.tcp.tmr_rto != NO_TIMER) { timer_binheap_cancel(&ts->S->timers, ts->sock.tcp.tmr_rto); ts->sock.tcp.tmr_rto = NO_TIMER; } if (pending) { - ts->sock.tcp.rto_backoff++; + ts->sock.tcp.rto_backoff++; ts->sock.tcp.cwnd = TCP_MSS; ts->sock.tcp.ssthresh = ts->sock.tcp.cwnd / 2; @@ -2386,8 +2420,13 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len uint32_t payload_len = len - sent; if (payload_len > (TCP_MSS - TCP_OPTIONS_LEN)) payload_len = (TCP_MSS - TCP_OPTIONS_LEN); - if (fifo_space(&ts->sock.tcp.txbuf) < payload_len + sizeof(struct pkt_desc) + IP_HEADER_LEN + TCP_HEADER_LEN + TCP_OPTIONS_LEN) { - break; + { + uint32_t need = payload_len + sizeof(struct pkt_desc) + IP_HEADER_LEN + + TCP_HEADER_LEN + TCP_OPTIONS_LEN; + uint32_t space = fifo_space(&ts->sock.tcp.txbuf); + if (space < need) { + break; + } } memset(tcp, 0, sizeof(struct wolfIP_tcp_seg)); tcp->src_port = ee16(ts->src_port); @@ -2413,9 +2452,10 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len sent += payload_len; ts->sock.tcp.seq += payload_len; } - if (sent == 0) + if (sent == 0) { + wolfip_stats.tx_eagain++; return -WOLFIP_EAGAIN; - else + } else return sent; } else if (IS_SOCKET_UDP(sockfd)) { const struct wolfIP_sockaddr_in *sin = (const struct wolfIP_sockaddr_in *)dest_addr; @@ -3590,7 +3630,7 @@ static inline void ip_recv(struct wolfIP *s, unsigned int if_idx, struct wolfIP_ #endif if (wolfIP_filter_notify_ip(WOLFIP_FILT_RECEIVING, s, if_idx, ip, len) != 0) return; -#if WOLFIP_ENABLE_FORWARDING + #if WOLFIP_ENABLE_FORWARDING if (ip->ver_ihl == 0x45) { ip4 dest = ee32(ip->dst); int is_local = 0; @@ -4008,7 +4048,6 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) int len = 0; int i = 0; uint8_t buf[LINK_MTU]; - const int poll_budget = 64; unsigned int if_idx; struct wolfIP_timer tmr; memset(buf, 0, LINK_MTU); @@ -4019,7 +4058,7 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) /* Step 1: Poll the device */ for (if_idx = 0; if_idx < s->if_count; if_idx++) { struct wolfIP_ll_dev *ll = wolfIP_ll_at(s, if_idx); - int budget = poll_budget; + int budget = WOLFIP_POLL_BUDGET; if (!ll || !ll->poll) continue; do { @@ -4093,7 +4132,7 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) uint32_t snd_wnd = ts->sock.tcp.cwnd; if (ts->sock.tcp.peer_rwnd < snd_wnd) snd_wnd = ts->sock.tcp.peer_rwnd; - if (in_flight <= snd_wnd) { + if (in_flight < snd_wnd) { struct wolfIP_timer new_tmr = {}; size = desc->len - ETH_HEADER_LEN; tcp = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); From 47d4fef5d88bb23654124536d30a58b936009ab1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 17:33:11 +0100 Subject: [PATCH 8/9] fix unit tests --- src/test/unit/unit.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index cee5128..90c61c8 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -4741,6 +4741,9 @@ START_TEST(test_poll_tcp_ack_only_skips_send) ts->sock.tcp.ack = 10; ts->sock.tcp.last_ack = 10; ts->sock.tcp.rto = 100; + /* Ensure send window allows processing of the queued ACK-only segment. */ + ts->sock.tcp.cwnd = TCP_MSS; + ts->sock.tcp.peer_rwnd = TCP_MSS; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); ck_assert_int_eq(enqueue_tcp_tx(ts, 0, 0x10), 0); @@ -4783,6 +4786,9 @@ START_TEST(test_poll_tcp_send_on_arp_hit) ts->sock.tcp.ack = 20; ts->sock.tcp.last_ack = 0; ts->sock.tcp.rto = 100; + /* Ensure send window allows emitting the queued data segment. */ + ts->sock.tcp.cwnd = TCP_MSS * 4; + ts->sock.tcp.peer_rwnd = TCP_MSS * 4; fifo_init(&ts->sock.tcp.txbuf, ts->txmem, TXBUF_SIZE); ck_assert_int_eq(enqueue_tcp_tx(ts, 1, 0x18), 0); From f3816bf398d6e1234d738ed3f81eb940ca4cbd99 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 5 Feb 2026 19:25:23 +0100 Subject: [PATCH 9/9] Addressed more reviewer's comments --- src/wolfip.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/wolfip.c b/src/wolfip.c index 49961ab..721a750 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -159,18 +159,6 @@ static inline void fifo_align_tail(struct fifo *f) f->tail = 0; } -static struct wolfIP_stats { - uint64_t poll_calls; - uint64_t tx_desc_seen; - uint64_t tx_sent; - uint64_t tx_bytes; - uint64_t tx_filter_block; - uint64_t tx_arp_block; - uint64_t tx_cwnd_block; - uint64_t tx_eagain; - uint64_t tx_push_fail; -} wolfip_stats; - /* TCP TX is a circular buffer and contains an array of full packets */ /* TCP RX only contains application data */ @@ -1800,6 +1788,10 @@ static int tcp_process_ts(struct tsocket *t, const struct wolfIP_tcp_seg *tcp) } #define SEQ_DIFF(a,b) ((a - b) > 0x7FFFFFFF) ? (b - a) : (a - b) + +/* Return true if a <= b + * Take into account wrapping. + */ static inline int tcp_seq_leq(uint32_t a, uint32_t b) { return (int32_t)(a - b) <= 0; @@ -2128,7 +2120,7 @@ static void tcp_rto_cb(void *arg) ts->sock.tcp.tmr_rto = NO_TIMER; } if (pending) { - ts->sock.tcp.rto_backoff++; + ts->sock.tcp.rto_backoff++; ts->sock.tcp.cwnd = TCP_MSS; ts->sock.tcp.ssthresh = ts->sock.tcp.cwnd / 2; @@ -2453,7 +2445,6 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len ts->sock.tcp.seq += payload_len; } if (sent == 0) { - wolfip_stats.tx_eagain++; return -WOLFIP_EAGAIN; } else return sent; @@ -4052,7 +4043,6 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) struct wolfIP_timer tmr; memset(buf, 0, LINK_MTU); - wolfip_stats.poll_calls++; s->last_tick = now; /* Step 1: Poll the device */ @@ -4108,7 +4098,6 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) desc = fifo_peek(&ts->sock.tcp.txbuf); while (desc) { unsigned int tx_if = wolfIP_socket_if_idx(ts); - wolfip_stats.tx_desc_seen++; tcp = (struct wolfIP_tcp_seg *)(ts->txmem + desc->pos + sizeof(*desc)); if (desc->flags & PKT_FLAG_SENT) { desc = fifo_next(&ts->sock.tcp.txbuf, desc); @@ -4123,7 +4112,6 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) memcpy(ts->nexthop_mac, loop->mac, 6); } else if (arp_lookup(s, tx_if, nexthop, ts->nexthop_mac) < 0) { /* Send ARP request */ - wolfip_stats.tx_arp_block++; arp_request(s, tx_if, nexthop); break; } @@ -4150,16 +4138,13 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) tcp->win = ee16(tcp_adv_win(ts)); 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) { - wolfip_stats.tx_filter_block++; break; } if (wolfIP_filter_notify_ip(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip, desc->len) != 0) { - wolfip_stats.tx_filter_block++; break; } #ifdef ETHERNET if (wolfIP_filter_notify_eth(WOLFIP_FILT_SENDING, ts->S, tx_if, &tcp->ip.eth, desc->len) != 0) { - wolfip_stats.tx_filter_block++; break; } #endif @@ -4169,8 +4154,6 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) ll->send(ll, tcp, desc->len); } } - wolfip_stats.tx_sent++; - wolfip_stats.tx_bytes += desc->len; desc->flags |= PKT_FLAG_SENT; desc->time_sent = now; if (size == IP_HEADER_LEN + (uint32_t)(tcp->hlen >> 2)) { @@ -4190,7 +4173,6 @@ int wolfIP_poll(struct wolfIP *s, uint64_t now) desc = fifo_next(&ts->sock.tcp.txbuf, desc); } } else { - wolfip_stats.tx_cwnd_block++; break; } }