Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions src/test/test_native_wolfssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/select.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "config.h"
#include "wolfip.h"
#include <wolfssl/options.h>
Expand Down Expand Up @@ -176,6 +178,11 @@ void *pt_echoclient(void *arg)
unsigned i;
uint8_t local_buf[BUFFER_SIZE];
uint32_t *srv_addr = (uint32_t *)arg;
int old_flags = -1;
fd_set wfds, rfds;
struct timeval tv;
socklen_t errlen;
int err;
struct sockaddr_in remote_sock = {
.sin_family = AF_INET,
.sin_port = ntohs(8), /* Echo */
Expand Down Expand Up @@ -205,12 +212,71 @@ void *pt_echoclient(void *arg)
sleep(1);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
printf("Connecting to echo server\n");

/* Use non-blocking connect with select() loop for robustness */
old_flags = fcntl(fd, F_GETFL, 0);
if (old_flags < 0) {
perror("fcntl(F_GETFL)");
close(fd);
return (void *)-1;
}
if (fcntl(fd, F_SETFL, old_flags | O_NONBLOCK) < 0) {
perror("fcntl(F_SETFL)");
close(fd);
return (void *)-1;
}
ret = connect(fd, (struct sockaddr *)&remote_sock, sizeof(remote_sock));
if (ret < 0) {
printf("test client connect: %d\n", ret);
perror("connect");
return (void *)-1;
err = errno;
printf("test client connect returned %d, errno=%d (%s)\n", ret, err, strerror(err));
if (err != EINPROGRESS) {
perror("connect");
close(fd);
return (void *)-1;
}
printf("Waiting for connect to complete...\n");
while (1) {
tv.tv_sec = 5;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_SET(fd, &rfds);
FD_SET(fd, &wfds);
ret = select(fd + 1, &rfds, &wfds, NULL, &tv);
Comment thread
danielinux marked this conversation as resolved.
if (ret <= 0) {
printf("select returned %d (timeout or error)\n", ret);
if (ret < 0) {
perror("select");
close(fd);
return (void *)-1;
}
}
errlen = sizeof(err);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) < 0) {
perror("getsockopt(SO_ERROR)");
close(fd);
return (void *)-1;
}
if (err == 0) {
printf("connect completed after select()\n");
break;
}
if (ret == 0) {
printf("connect still in progress after timeout\n");
continue;
}
if (err != EINPROGRESS && err != EALREADY && err != EWOULDBLOCK && err != EAGAIN) {
printf("connect completed with error: %d (%s)\n", err, strerror(err));
close(fd);
return (void *)-1;
}
}
} else {
printf("connect returned immediately\n");
}
/* Restore blocking mode for TLS operations */
if (fcntl(fd, F_SETFL, old_flags) < 0)
perror("fcntl(restore)");
Comment thread
danielinux marked this conversation as resolved.
printf("Linux client: TCP connection established\n");
ret = wolfSSL_connect(client_ssl);
if (ret != SSL_SUCCESS) {
Expand Down
167 changes: 164 additions & 3 deletions src/test/unit/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -3069,6 +3069,160 @@ START_TEST(test_sock_accept_bound_local_ip_no_match)
}
END_TEST

START_TEST(test_sock_accept_starts_rto_timer)
{
struct wolfIP s;
int listen_sd;
int client_sd;
struct tsocket *listener;
struct tsocket *accepted;
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);

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, NULL, NULL);
ck_assert_int_gt(client_sd, 0);

accepted = &s.tcpsockets[SOCKET_UNMARK(client_sd)];
/* Accepted socket should be in SYN_RCVD state with RTO timer active */
ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD);
ck_assert_uint_eq(accepted->sock.tcp.ctrl_rto_active, 1);
ck_assert_uint_eq(accepted->sock.tcp.ctrl_rto_retries, 0);
ck_assert_int_ne(accepted->sock.tcp.tmr_rto, NO_TIMER);

/* Listening socket should have returned to LISTEN with RTO stopped */
ck_assert_int_eq(listener->sock.tcp.state, TCP_LISTEN);
ck_assert_uint_eq(listener->sock.tcp.ctrl_rto_active, 0);
}
END_TEST

START_TEST(test_sock_accept_synack_retransmission)
{
struct wolfIP s;
int listen_sd;
int client_sd;
struct tsocket *accepted;
struct wolfIP_sockaddr_in sin;
struct pkt_desc *desc;
struct wolfIP_tcp_seg *seg;

wolfIP_init(&s);
mock_link_init(&s);
wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0);

listen_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP);
ck_assert_int_gt(listen_sd, 0);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = ee16(1234);
sin.sin_addr.s_addr = ee32(0x0A000001U);
ck_assert_int_eq(wolfIP_sock_bind(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0);
ck_assert_int_eq(wolfIP_sock_listen(&s, listen_sd, 1), 0);

inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234);
client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL);
ck_assert_int_gt(client_sd, 0);

accepted = &s.tcpsockets[SOCKET_UNMARK(client_sd)];
ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD);

/* Clear tx buffer to prepare for retransmission check */
fifo_init(&accepted->sock.tcp.txbuf, accepted->txmem, TXBUF_SIZE);
s.last_tick = 10000;

/* Simulate RTO timeout by calling tcp_rto_cb */
tcp_rto_cb(accepted);

/* Verify SYN-ACK was retransmitted */
desc = fifo_peek(&accepted->sock.tcp.txbuf);
ck_assert_ptr_nonnull(desc);
seg = (struct wolfIP_tcp_seg *)(accepted->txmem + desc->pos + sizeof(*desc));
ck_assert_uint_eq(seg->flags, TCP_FLAG_SYN | TCP_FLAG_ACK);
ck_assert_uint_eq(accepted->sock.tcp.ctrl_rto_retries, 1);
ck_assert_int_ne(accepted->sock.tcp.tmr_rto, NO_TIMER);
}
END_TEST

START_TEST(test_sock_accept_ack_transitions_to_established)
{
struct wolfIP s;
int listen_sd;
int client_sd;
struct tsocket *accepted;
Comment thread
danielinux marked this conversation as resolved.
struct wolfIP_sockaddr_in sin;
struct wolfIP_tcp_seg ack;
struct wolfIP_ll_dev *ll;

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);
client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL);
ck_assert_int_gt(client_sd, 0);

accepted = &s.tcpsockets[SOCKET_UNMARK(client_sd)];
ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD);
ck_assert_uint_eq(accepted->sock.tcp.ctrl_rto_active, 1);

/* Send pure ACK to complete the handshake */
ll = wolfIP_getdev_ex(&s, TEST_PRIMARY_IF);
memset(&ack, 0, sizeof(ack));
memcpy(ack.ip.eth.dst, ll->mac, 6);
ack.ip.eth.type = ee16(ETH_TYPE_IP);
ack.ip.ver_ihl = 0x45;
ack.ip.ttl = 64;
ack.ip.proto = WI_IPPROTO_TCP;
ack.ip.len = ee16(IP_HEADER_LEN + TCP_HEADER_LEN);
ack.ip.src = ee32(0x0A0000A1U); /* Remote IP from inject_tcp_syn */
ack.ip.dst = ee32(0x0A000001U);
ack.ip.csum = 0;
iphdr_set_checksum(&ack.ip);
ack.src_port = ee16(40000); /* Remote port from inject_tcp_syn */
ack.dst_port = ee16(1234);
ack.seq = ee32(accepted->sock.tcp.ack);
ack.ack = ee32(accepted->sock.tcp.seq);
ack.hlen = TCP_HEADER_LEN << 2;
ack.flags = TCP_FLAG_ACK;
ack.win = ee16(65535);

tcp_input(&s, TEST_PRIMARY_IF, &ack,
(uint32_t)(ETH_HEADER_LEN + IP_HEADER_LEN + TCP_HEADER_LEN));

/* Verify socket transitioned to ESTABLISHED and RTO stopped */
ck_assert_int_eq(accepted->sock.tcp.state, TCP_ESTABLISHED);
ck_assert_uint_eq(accepted->sock.tcp.ctrl_rto_active, 0);
ck_assert_uint_eq(accepted->sock.tcp.ctrl_rto_retries, 0);
ck_assert_int_eq(accepted->sock.tcp.tmr_rto, NO_TIMER);
/* Should be signaled as writable */
ck_assert(accepted->events & CB_EVENT_WRITABLE);
}
END_TEST

START_TEST(test_sock_sendto_error_paths)
{
struct wolfIP s;
Expand Down Expand Up @@ -5454,7 +5608,9 @@ START_TEST(test_sock_accept_success)
new_sd = wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen);
ck_assert_int_gt(new_sd, 0);
new_ts = &s.tcpsockets[SOCKET_UNMARK(new_sd)];
ck_assert_int_eq(new_ts->sock.tcp.state, TCP_ESTABLISHED);
/* After accept(), socket stays in SYN_RCVD until final ACK completes
* the three-way handshake (SYN-ACK retransmission fix). */
ck_assert_int_eq(new_ts->sock.tcp.state, TCP_SYN_RCVD);
ck_assert_uint_eq(sin.sin_port, ee16(new_ts->dst_port));

listen_ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)];
Expand Down Expand Up @@ -14552,7 +14708,8 @@ START_TEST(test_tcp_listen_accepts_bound_interface)
client = &s.tcpsockets[SOCKET_UNMARK(client_fd)];
ck_assert_uint_eq(client->local_ip, secondary_ip);
ck_assert_uint_eq(client->bound_local_ip, secondary_ip);
ck_assert_int_eq(client->sock.tcp.state, TCP_ESTABLISHED);
/* After accept(), socket stays in SYN_RCVD until final ACK. */
ck_assert_int_eq(client->sock.tcp.state, TCP_SYN_RCVD);
}
END_TEST

Expand Down Expand Up @@ -14591,7 +14748,8 @@ START_TEST(test_tcp_listen_accepts_any_interface)
ck_assert_int_ge(client_fd, 0);
client = &s.tcpsockets[SOCKET_UNMARK(client_fd)];
ck_assert_uint_eq(client->local_ip, secondary_ip);
ck_assert_int_eq(client->sock.tcp.state, TCP_ESTABLISHED);
/* After accept(), socket stays in SYN_RCVD until final ACK. */
ck_assert_int_eq(client->sock.tcp.state, TCP_SYN_RCVD);
}
END_TEST

Expand Down Expand Up @@ -15151,6 +15309,9 @@ Suite *wolf_suite(void)
tcase_add_test(tc_utils, test_sock_accept_no_free_socket_syn_rcvd);
tcase_add_test(tc_utils, test_sock_accept_listen_no_connection);
tcase_add_test(tc_utils, test_sock_accept_bound_local_ip_no_match);
tcase_add_test(tc_utils, test_sock_accept_starts_rto_timer);
tcase_add_test(tc_utils, test_sock_accept_synack_retransmission);
tcase_add_test(tc_utils, test_sock_accept_ack_transitions_to_established);
tcase_add_test(tc_utils, test_sock_sendto_error_paths);
tcase_add_test(tc_utils, test_sock_sendto_null_buf_or_len_zero);
tcase_add_test(tc_utils, test_sock_sendto_tcp_not_established);
Expand Down
9 changes: 6 additions & 3 deletions src/wolfip.c
Original file line number Diff line number Diff line change
Expand Up @@ -3697,8 +3697,8 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add
if (!newts)
return -1;
ts->events &= ~CB_EVENT_READABLE;
if (tx_has_writable_space(newts))
newts->events |= CB_EVENT_WRITABLE;
/* Don't signal writable until connection fully established */
newts->events &= ~CB_EVENT_WRITABLE;
newts->callback = ts->callback;
newts->callback_arg = ts->callback_arg;
newts->local_ip = ts->local_ip;
Expand All @@ -3722,20 +3722,23 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add
newts->sock.tcp.ts_offer = ts->sock.tcp.ts_offer;
newts->sock.tcp.sack_offer = ts->sock.tcp.sack_offer;
newts->sock.tcp.sack_permitted = ts->sock.tcp.sack_permitted;
newts->sock.tcp.state = TCP_ESTABLISHED;
newts->sock.tcp.state = TCP_SYN_RCVD;
Comment thread
danielinux marked this conversation as resolved.
/* Send SYN-ACK to accept connection.
* Send the syn-ack from the newly established socket:
* the caller could still close the listening socket
* while we're still accepting.
*/
tcp_send_syn(newts, TCP_FLAG_SYN | TCP_FLAG_ACK);
newts->sock.tcp.seq++;
newts->sock.tcp.ctrl_rto_retries = 0;
tcp_ctrl_rto_start(newts, s->last_tick);
if (sin) {
sin->sin_family = AF_INET;
sin->sin_port = ee16(ts->dst_port);
sin->sin_addr.s_addr = ee32(ts->remote_ip);
}
ts->sock.tcp.state = TCP_LISTEN;
tcp_ctrl_rto_stop(ts);
ts->sock.tcp.seq = wolfIP_getrandom();
if (ts->bound_local_ip != IPADDR_ANY) {
int bound_match = 0;
Expand Down