diff --git a/Makefile b/Makefile index 6a42f664..5803bc9b 100644 --- a/Makefile +++ b/Makefile @@ -207,6 +207,8 @@ EXE=build/tcpecho build/tcp_netcat_poll build/tcp_netcat_select \ build/test-http-headers \ build/test-http-close-notify \ build/test-freertos-close-last-ack \ + build/test-freertos-bsd-semantics \ + build/test-freertos-bsd-semantics-2khz \ build/test-posix-errno \ build/ipfilter-logger \ build/test-esp build/esp-server @@ -922,6 +924,17 @@ build/test-freertos-close-last-ack: src/test/test_freertos_close_last_ack.c src/ @echo "[LD] $@" @$(CC) -Isrc/test/freertos_mocks $(CFLAGS) -o $@ src/test/test_freertos_close_last_ack.c $(LDFLAGS) +build/test-freertos-bsd-semantics: src/test/test_freertos_bsd_semantics.c src/port/freeRTOS/bsd_socket.c + @mkdir -p build || true + @echo "[LD] $@" + @$(CC) -Isrc/test/freertos_mocks $(CFLAGS) -o $@ src/test/test_freertos_bsd_semantics.c $(LDFLAGS) + +# Same test above 1000 Hz, where portTICK_PERIOD_MS is 0. +build/test-freertos-bsd-semantics-2khz: src/test/test_freertos_bsd_semantics.c src/port/freeRTOS/bsd_socket.c + @mkdir -p build || true + @echo "[LD] $@" + @$(CC) -Isrc/test/freertos_mocks -DconfigTICK_RATE_HZ=2000u $(CFLAGS) -o $@ src/test/test_freertos_bsd_semantics.c $(LDFLAGS) + build/%.o: src/%.c @mkdir -p `dirname $@` || true @echo "[CC] $<" diff --git a/docs/API.md b/docs/API.md index ef7f2512..0f938d3c 100644 --- a/docs/API.md +++ b/docs/API.md @@ -151,22 +151,22 @@ Initiates a connection on a socket. ```c int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen); ``` -Accepts a connection on a listening socket. +Accepts a connection on a listening socket. Only a connection whose handshake has completed (`ESTABLISHED`, or `CLOSE_WAIT` when the peer already sent its FIN) is returned. A call while the handshake is still running moves it to a child socket the stack holds back and returns `-WOLFIP_EAGAIN`; the listener reports `CB_EVENT_READABLE` again once the child is established, and the next call returns it. So `-WOLFIP_EAGAIN` can follow `CB_EVENT_READABLE`, and the caller must not treat it as a connection. A held-back child that is reset or gets no answer after `TCP_SYNACK_MAXRTX` SYN-ACK retransmissions (default 3) is released silently, and closing the listener resets the ones still held. When the socket table is full, one still in its handshake gives up its slot before a socket the application closed in `FIN_WAIT_1`, `CLOSING` or `LAST_ACK` does. One that completes but is not accepted within `TCP_PREACCEPT_TIMEOUT_MS` (default 5 s) is reset, as a connection the listener completed itself would be. - Parameters: - s: wolfIP instance - sockfd: Listening socket descriptor - addr: Address of connecting peer - addrlen: Length of address structure -- Returns: New socket descriptor or negative error code +- Returns: New socket descriptor, `-WOLFIP_EAGAIN` when no connection is ready, or another negative error code ```c int wolfIP_sock_abort(struct wolfIP *s, int sockfd); ``` -Abortive close, like `SO_LINGER` with a zero timeout: sends an RST in `SYN_RCVD`, `ESTABLISHED`, `CLOSE_WAIT`, `FIN_WAIT_1` and `FIN_WAIT_2` (other states, such as `CLOSING` and `LAST_ACK`, are released without one), and releases the socket at once instead of waiting for a FIN exchange the peer may never complete. Also valid on a socket whose `wolfIP_sock_close()` returned `-WOLFIP_EAGAIN`, as long as no socket has been created or accepted since (see the return values under Data Transfer). +Abortive close, like `SO_LINGER` with a zero timeout: sends an RST in `SYN_RCVD`, `ESTABLISHED`, `CLOSE_WAIT`, `FIN_WAIT_1` and `FIN_WAIT_2` (other states, such as `CLOSING` and `LAST_ACK`, are released without one), and releases the socket at once instead of waiting for a FIN exchange the peer may never complete. Also valid on a socket that `wolfIP_sock_close()` is still closing, until the stack releases it and its slot is handed out again (see the return values under Data Transfer). - Parameters: - s: wolfIP instance - sockfd: TCP socket descriptor -- Returns: 0 on success, `-WOLFIP_EINVAL` for a bad or non-TCP descriptor +- Returns: 0 on success, `-WOLFIP_EINVAL` for a bad or non-TCP descriptor, `-WOLFIP_EBADF` for a stale one ### Data Transfer ```c @@ -197,9 +197,10 @@ wolfIP never blocks, so every call above can ask the caller to retry. On a TCP s | `0` | End of stream: the peer closed and nothing is left to read | | `-WOLFIP_EAGAIN` | Retry later: no data queued, no transmit space, or the socket is still connecting (`SYN_SENT`/`SYN_RCVD`) | | `-WOLFIP_EINVAL` | Bad descriptor or arguments | +| `-WOLFIP_EBADF` | Stale descriptor: its socket was released and the slot handed out again | | `-1` | The operation cannot succeed on this socket (a listener, or a closing state) | -`wolfIP_sock_close()` follows the same convention: on a connected socket it starts the FIN exchange and returns `-WOLFIP_EAGAIN`. The stack then releases the descriptor by itself, without notification, once the exchange completes, the peer resets, or the close times out, and the next `wolfIP_sock_socket()` or `wolfIP_sock_accept()` can hand out the same number. Calling `wolfIP_sock_close()` or `wolfIP_sock_abort()` on it again is therefore only safe while no socket has been created or accepted since; after that, the call acts on the new socket. +On a connected socket `wolfIP_sock_close()` queues a FIN and returns 0, or returns `-WOLFIP_EAGAIN` when the transmit buffer has no room for the FIN yet, in which case the caller retries. Once the FIN is queued, the stack finishes the exchange and releases the socket by itself, without notification, once the exchange completes, the peer resets, or the close times out. Until then, calling `wolfIP_sock_close()` again returns 0 without effect; after that, the next `wolfIP_sock_socket()` or `wolfIP_sock_accept()` can reuse its slot. When every TCP slot is taken, `wolfIP_sock_socket()` and `wolfIP_sock_accept()` take the slot of a socket the application has already closed or has not accepted yet: one in `TIME_WAIT` first, then `FIN_WAIT_2`, then a held-back connection still in its handshake, then `FIN_WAIT_1`, `CLOSING` or `LAST_ACK`, resetting the peer where the exchange has not finished. A descriptor carries the generation of its slot in bits 16-30, so once the slot has been handed out again, every call on the old descriptor returns `-WOLFIP_EBADF` instead of acting on the new socket. The generation wraps after 32768 reuses of the same slot. A slot the stack released on its own (peer reset, retransmission timeout) while the application still holds the descriptor is reused only when no other slot is free; from then on that descriptor, too, answers `-WOLFIP_EBADF` instead of reporting the connection as closed. ## Stack Interface Functions diff --git a/src/port/amd/common/wolfip_config.h b/src/port/amd/common/wolfip_config.h index 7fbf0a84..8d6f7228 100644 --- a/src/port/amd/common/wolfip_config.h +++ b/src/port/amd/common/wolfip_config.h @@ -58,7 +58,7 @@ #define TXBUF_SIZE (LINK_MTU * 6) #else #define MAX_TCPSOCKETS 2 -#define MAX_UDPSOCKETS 4 +#define MAX_UDPSOCKETS 3 #define MAX_ICMPSOCKETS 1 #define RXBUF_SIZE (LINK_MTU * 4) #define TXBUF_SIZE (LINK_MTU * 4) diff --git a/src/port/freeRTOS/README.md b/src/port/freeRTOS/README.md index e751d1c3..9ea0a996 100644 --- a/src/port/freeRTOS/README.md +++ b/src/port/freeRTOS/README.md @@ -107,6 +107,7 @@ Defined in `bsd_socket.c`: - `WOLFIP_FREERTOS_POLL_MIN_MS` (default: `1`) - `WOLFIP_FREERTOS_POLL_MAX_MS` (default: `5`) - `WOLFIP_BSD_DEBUG_CALLBACK` (default: `0`) - set to `1` to log socket callbacks from the poll task +- `WOLFIP_BSD_CLOSE_LINGER_MS` (default: `10000`) - how long `close()` waits for room to queue its FIN before it aborts the connection Override via compiler flags, for example: @@ -119,3 +120,9 @@ CFLAGS += -DWOLFIP_FREERTOS_BSD_MAX_FDS=32 - `wolfip_freertos_socket_init()` should be called once after wolfIP/device init and before socket usage. - File descriptors returned by this layer are wrapper FDs, not raw wolfIP internal FDs. - The wrapper is intended for task context (not ISR context). +- Blocking calls wait indefinitely by default. `setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, sizeof(tv))` bounds `accept`, `recv` and `recvfrom`, and `WOLFIP_SO_SNDTIMEO` bounds `connect`, `send` and `sendto`, with `tv` a `struct wolfIP_timeval` and `optlen` exactly its size; each bounds the whole call, not each wait inside it. An expired wait returns -1 with `socket_last_error()` set to `WOLFIP_EAGAIN`; as on Linux, an all-zero `tv` or one too long for `TickType_t` waits without bound, a negative `tv_sec` does not wait, and a `tv_usec` outside [0, 999999] fails with `WOLFIP_EDOM`. A `connect()` that runs out of time fails with `WOLFIP_EINPROGRESS` instead, while the handshake goes on. `getsockopt()` reads the current values back. +- `accept()` returns only connections whose handshake has completed; until then wolfIP holds them back (see `wolfIP_sock_accept()` in `docs/API.md`), and `accept()` waits. +- `close()` returns once its FIN is queued, and wolfIP finishes the FIN exchange on its own. It waits only while the transmit buffer has no room for the FIN, for at most `WOLFIP_BSD_CLOSE_LINGER_MS`, and then resets the connection with `wolfIP_sock_abort()`. +- `close()` on a descriptor that other tasks are blocked on wakes them, and their calls return -1 with `socket_last_error()` set to `WOLFIP_EBADF`. The descriptor is reissued only after the last of them has returned, and a call that finds its descriptor closed or reissued when it takes the wrapper's lock fails the same way. As in POSIX, a call made at the same moment as another task's `close()` of that descriptor is still a race in the application: until it reaches the lock, it can end up on whatever socket the descriptor names by then. +- `socket_last_error()` is one value for all tasks, so when calls in several tasks fail at the same time each may read another's error. +- Blocking calls on one descriptor share its wake-up, so do not leave another task blocked in `recv()` or `send()` on a descriptor while `close()` waits for room to queue its FIN: the close can miss its wake-up and reset the connection after `WOLFIP_BSD_CLOSE_LINGER_MS`. diff --git a/src/port/freeRTOS/bsd_socket.c b/src/port/freeRTOS/bsd_socket.c index f14b76ca..af958b6b 100644 --- a/src/port/freeRTOS/bsd_socket.c +++ b/src/port/freeRTOS/bsd_socket.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "FreeRTOS.h" #include "task.h" @@ -45,12 +46,22 @@ #define WOLFIP_FREERTOS_POLL_MIN_MS 1u #endif +#ifndef WOLFIP_BSD_CLOSE_LINGER_MS +#define WOLFIP_BSD_CLOSE_LINGER_MS 10000u +#endif + typedef struct { int in_use; + /* close() ran while tasks were blocked on it; the last one frees the slot. */ + int closing; + int waiters; int internal_fd; SemaphoreHandle_t ready_sem; volatile uint16_t wait_events; volatile uint16_t seen_events; + /* SO_RCVTIMEO / SO_SNDTIMEO in ticks; portMAX_DELAY means none. */ + TickType_t rx_timeout; + TickType_t tx_timeout; } wolfip_bsd_fd_entry; static struct wolfIP *g_ipstack; @@ -137,10 +148,9 @@ int socket_last_error(void) static int wolfip_bsd_fd_valid(int public_fd) { - if (public_fd < 0 || public_fd >= WOLFIP_FREERTOS_BSD_MAX_FDS) { - return 0; - } - if (!g_fds[public_fd].in_use) { + if ((public_fd < 0) || (public_fd >= WOLFIP_FREERTOS_BSD_MAX_FDS) || + !g_fds[public_fd].in_use || g_fds[public_fd].closing) { + wolfip_bsd_set_error(WOLFIP_EBADF); return 0; } return 1; @@ -156,10 +166,14 @@ static int wolfip_bsd_fd_alloc(int internal_fd) return -WOLFIP_ENOMEM; } g_fds[i].in_use = 1; + g_fds[i].closing = 0; + g_fds[i].waiters = 0; g_fds[i].internal_fd = internal_fd; g_fds[i].ready_sem = sem; g_fds[i].wait_events = 0; g_fds[i].seen_events = 0; + g_fds[i].rx_timeout = portMAX_DELAY; + g_fds[i].tx_timeout = portMAX_DELAY; return i; } } @@ -168,18 +182,39 @@ static int wolfip_bsd_fd_alloc(int internal_fd) static void wolfip_bsd_fd_free(int public_fd) { - if (!wolfip_bsd_fd_valid(public_fd)) { + if (public_fd < 0 || public_fd >= WOLFIP_FREERTOS_BSD_MAX_FDS || + !g_fds[public_fd].in_use) { return; } vSemaphoreDelete(g_fds[public_fd].ready_sem); g_fds[public_fd].in_use = 0; + g_fds[public_fd].closing = 0; g_fds[public_fd].internal_fd = -1; g_fds[public_fd].ready_sem = NULL; g_fds[public_fd].wait_events = 0; g_fds[public_fd].seen_events = 0; } +/* With g_lock held: the descriptor was closed, or reissued to another socket. */ +static int wolfip_bsd_entry_lost(const wolfip_bsd_fd_entry *entry, int internal_fd) +{ + return !entry->in_use || entry->closing || (entry->internal_fd != internal_fd); +} + +/* Takes g_lock, or fails with WOLFIP_EBADF and leaves it free if the + * descriptor no longer names internal_fd. */ +static int wolfip_bsd_lock(int public_fd, int internal_fd) +{ + xSemaphoreTake(g_lock, portMAX_DELAY); + if (wolfip_bsd_entry_lost(&g_fds[public_fd], internal_fd)) { + xSemaphoreGive(g_lock); + wolfip_bsd_set_error(WOLFIP_EBADF); + return -1; + } + return 0; +} + static void wolfip_bsd_socket_cb(int internal_fd, uint16_t events, void *arg) { wolfip_bsd_fd_entry *entry = (wolfip_bsd_fd_entry *)arg; @@ -213,25 +248,41 @@ static void wolfip_bsd_prepare_wait_locked(wolfip_bsd_fd_entry *entry, uint16_t wolfIP_register_callback(g_ipstack, entry->internal_fd, wolfip_bsd_socket_cb, entry); } -static int wolfip_bsd_wait_unlocked(wolfip_bsd_fd_entry *entry) +/* Called with g_lock held, returns with it released: 0 when woken, + * -WOLFIP_EAGAIN on timeout, -WOLFIP_EBADF when the descriptor was closed. */ +static int wolfip_bsd_wait(wolfip_bsd_fd_entry *entry, int public_fd, + TickType_t timeout) { - if (xSemaphoreTake(entry->ready_sem, portMAX_DELAY) != pdTRUE) { - return -1; + int ret; + + entry->waiters++; + xSemaphoreGive(g_lock); + ret = (xSemaphoreTake(entry->ready_sem, timeout) == pdTRUE) ? 0 : + -WOLFIP_EAGAIN; + xSemaphoreTake(g_lock, portMAX_DELAY); + entry->waiters--; + if (entry->closing) { + if (entry->waiters > 0) { + (void)xSemaphoreGive(entry->ready_sem); + } + else { + wolfip_bsd_fd_free(public_fd); + } + ret = -WOLFIP_EBADF; } - return 0; + xSemaphoreGive(g_lock); + return ret; } -/* Some TCP core calls surface a temporary "not established yet" as -1 on a - * freshly accepted stream socket before the final ACK promotes it to - * ESTABLISHED. Allow a single wait/retry for that case without turning all - * bare -1 returns into infinite retry loops. */ -static int wolfip_bsd_tcp_stream_retryable_once(int internal_fd, int ret, int *used) +static TickType_t wolfip_bsd_remaining(TickType_t timeout, TickType_t start) { - if (ret != -1 || used == NULL || *used || !IS_SOCKET_TCP(internal_fd)) { - return 0; + TickType_t elapsed; + + if (timeout == portMAX_DELAY) { + return portMAX_DELAY; } - *used = 1; - return 1; + elapsed = (TickType_t)(xTaskGetTickCount() - start); + return (elapsed < timeout) ? (TickType_t)(timeout - elapsed) : 0; } static int wolfip_bsd_tcp_recv_should_wait_locked(int internal_fd, int ret) @@ -327,11 +378,16 @@ int socket(int domain, int type, int protocol) int bind(int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) { int ret; + int internal_fd; + if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } - xSemaphoreTake(g_lock, portMAX_DELAY); - ret = wolfIP_sock_bind(g_ipstack, g_fds[sockfd].internal_fd, addr, addrlen); + internal_fd = g_fds[sockfd].internal_fd; + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + ret = wolfIP_sock_bind(g_ipstack, internal_fd, addr, addrlen); xSemaphoreGive(g_lock); if (ret < 0) { wolfip_bsd_set_error(ret); @@ -343,11 +399,16 @@ int bind(int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) int listen(int sockfd, int backlog) { int ret; + int internal_fd; + if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } - xSemaphoreTake(g_lock, portMAX_DELAY); - ret = wolfIP_sock_listen(g_ipstack, g_fds[sockfd].internal_fd, backlog); + internal_fd = g_fds[sockfd].internal_fd; + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + ret = wolfIP_sock_listen(g_ipstack, internal_fd, backlog); xSemaphoreGive(g_lock); if (ret < 0) { wolfip_bsd_set_error(ret); @@ -360,16 +421,23 @@ int accept(int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) { int ret; int public_fd; - int retried_minus_one = 0; + int listen_fd; wolfip_bsd_fd_entry *entry; + TickType_t start; + TickType_t left; if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } entry = &g_fds[sockfd]; + listen_fd = entry->internal_fd; + start = xTaskGetTickCount(); for (;;) { - xSemaphoreTake(g_lock, portMAX_DELAY); + if (wolfip_bsd_lock(sockfd, listen_fd) < 0) { + return -1; + } + left = wolfip_bsd_remaining(entry->rx_timeout, start); ret = wolfIP_sock_accept(g_ipstack, entry->internal_fd, addr, addrlen); if (ret >= 0) { public_fd = wolfip_bsd_fd_alloc(ret); @@ -383,27 +451,15 @@ int accept(int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) } return public_fd; } - if (wolfip_bsd_tcp_stream_retryable_once(entry->internal_fd, ret, - &retried_minus_one)) { - wolfip_bsd_prepare_wait_locked(entry, - (uint16_t)(CB_EVENT_READABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); - return -1; - } - continue; - } - if (ret != -WOLFIP_EAGAIN) { + if ((ret != -WOLFIP_EAGAIN) || (left == 0)) { xSemaphoreGive(g_lock); wolfip_bsd_set_error(ret); return -1; } wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_READABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + if (wolfip_bsd_wait(entry, sockfd, left) == -WOLFIP_EBADF) { + wolfip_bsd_set_error(WOLFIP_EBADF); return -1; } } @@ -412,15 +468,23 @@ int accept(int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) int connect(int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) { int ret; + int internal_fd; wolfip_bsd_fd_entry *entry; + TickType_t start; + TickType_t left; if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } entry = &g_fds[sockfd]; + internal_fd = entry->internal_fd; + start = xTaskGetTickCount(); for (;;) { - xSemaphoreTake(g_lock, portMAX_DELAY); + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + left = wolfip_bsd_remaining(entry->tx_timeout, start); ret = wolfIP_sock_connect(g_ipstack, entry->internal_fd, addr, addrlen); if (ret == 0) { xSemaphoreGive(g_lock); @@ -433,9 +497,9 @@ int connect(int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) } wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_WRITABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + ret = wolfip_bsd_wait(entry, sockfd, left); + if (ret < 0) { + wolfip_bsd_set_error((ret == -WOLFIP_EAGAIN) ? WOLFIP_EINPROGRESS : ret); return -1; } } @@ -444,32 +508,28 @@ int connect(int sockfd, const struct wolfIP_sockaddr *addr, socklen_t addrlen) int send(int sockfd, const void *buf, size_t len, int flags) { int ret; - int retried_minus_one = 0; + int internal_fd; wolfip_bsd_fd_entry *entry; + TickType_t start; + TickType_t left; if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } entry = &g_fds[sockfd]; + internal_fd = entry->internal_fd; + start = xTaskGetTickCount(); for (;;) { - xSemaphoreTake(g_lock, portMAX_DELAY); + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + left = wolfip_bsd_remaining(entry->tx_timeout, start); ret = wolfIP_sock_send(g_ipstack, entry->internal_fd, buf, len, flags); if (ret >= 0) { xSemaphoreGive(g_lock); return ret; } - if (wolfip_bsd_tcp_stream_retryable_once(entry->internal_fd, ret, - &retried_minus_one)) { - wolfip_bsd_prepare_wait_locked(entry, - (uint16_t)(CB_EVENT_WRITABLE | CB_EVENT_READABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); - return -1; - } - continue; - } if (ret != -WOLFIP_EAGAIN) { xSemaphoreGive(g_lock); wolfip_bsd_set_error(ret); @@ -477,9 +537,9 @@ int send(int sockfd, const void *buf, size_t len, int flags) } wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_WRITABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + ret = wolfip_bsd_wait(entry, sockfd, left); + if (ret < 0) { + wolfip_bsd_set_error(ret); return -1; } } @@ -489,32 +549,28 @@ int sendto(int sockfd, const void *buf, size_t len, int flags, const struct wolfIP_sockaddr *dest_addr, socklen_t addrlen) { int ret; - int retried_minus_one = 0; + int internal_fd; wolfip_bsd_fd_entry *entry; + TickType_t start; + TickType_t left; if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } entry = &g_fds[sockfd]; + internal_fd = entry->internal_fd; + start = xTaskGetTickCount(); for (;;) { - xSemaphoreTake(g_lock, portMAX_DELAY); + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + left = wolfip_bsd_remaining(entry->tx_timeout, start); ret = wolfIP_sock_sendto(g_ipstack, entry->internal_fd, buf, len, flags, dest_addr, addrlen); if (ret >= 0) { xSemaphoreGive(g_lock); return ret; } - if (wolfip_bsd_tcp_stream_retryable_once(entry->internal_fd, ret, - &retried_minus_one)) { - wolfip_bsd_prepare_wait_locked(entry, - (uint16_t)(CB_EVENT_WRITABLE | CB_EVENT_READABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); - return -1; - } - continue; - } if (ret != -WOLFIP_EAGAIN) { xSemaphoreGive(g_lock); wolfip_bsd_set_error(ret); @@ -522,9 +578,9 @@ int sendto(int sockfd, const void *buf, size_t len, int flags, } wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_WRITABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + ret = wolfip_bsd_wait(entry, sockfd, left); + if (ret < 0) { + wolfip_bsd_set_error(ret); return -1; } } @@ -533,15 +589,23 @@ int sendto(int sockfd, const void *buf, size_t len, int flags, int recv(int sockfd, void *buf, size_t len, int flags) { int ret; + int internal_fd; wolfip_bsd_fd_entry *entry; + TickType_t start; + TickType_t left; if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } entry = &g_fds[sockfd]; + internal_fd = entry->internal_fd; + start = xTaskGetTickCount(); for (;;) { - xSemaphoreTake(g_lock, portMAX_DELAY); + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + left = wolfip_bsd_remaining(entry->rx_timeout, start); ret = wolfIP_sock_recv(g_ipstack, entry->internal_fd, buf, len, flags); if (ret >= 0) { xSemaphoreGive(g_lock); @@ -550,9 +614,9 @@ int recv(int sockfd, void *buf, size_t len, int flags) if (wolfip_bsd_tcp_recv_should_wait_locked(entry->internal_fd, ret)) { wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_READABLE | CB_EVENT_WRITABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + ret = wolfip_bsd_wait(entry, sockfd, left); + if (ret < 0) { + wolfip_bsd_set_error(ret); return -1; } continue; @@ -564,9 +628,9 @@ int recv(int sockfd, void *buf, size_t len, int flags) } wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_READABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + ret = wolfip_bsd_wait(entry, sockfd, left); + if (ret < 0) { + wolfip_bsd_set_error(ret); return -1; } } @@ -576,15 +640,23 @@ int recvfrom(int sockfd, void *buf, size_t len, int flags, struct wolfIP_sockaddr *src_addr, socklen_t *addrlen) { int ret; + int internal_fd; wolfip_bsd_fd_entry *entry; + TickType_t start; + TickType_t left; if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } entry = &g_fds[sockfd]; + internal_fd = entry->internal_fd; + start = xTaskGetTickCount(); for (;;) { - xSemaphoreTake(g_lock, portMAX_DELAY); + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + left = wolfip_bsd_remaining(entry->rx_timeout, start); ret = wolfIP_sock_recvfrom(g_ipstack, entry->internal_fd, buf, len, flags, src_addr, addrlen); if (ret >= 0) { xSemaphoreGive(g_lock); @@ -593,9 +665,9 @@ int recvfrom(int sockfd, void *buf, size_t len, int flags, if (wolfip_bsd_tcp_recv_should_wait_locked(entry->internal_fd, ret)) { wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_READABLE | CB_EVENT_WRITABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + ret = wolfip_bsd_wait(entry, sockfd, left); + if (ret < 0) { + wolfip_bsd_set_error(ret); return -1; } continue; @@ -607,23 +679,87 @@ int recvfrom(int sockfd, void *buf, size_t len, int flags, } wolfip_bsd_prepare_wait_locked(entry, (uint16_t)(CB_EVENT_READABLE | CB_EVENT_CLOSED)); - xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + ret = wolfip_bsd_wait(entry, sockfd, left); + if (ret < 0) { + wolfip_bsd_set_error(ret); return -1; } } } +/* As on Linux: {0, 0} or a huge value waits forever, a negative one does not + * wait; anything else rounds up to whole ticks. */ +static TickType_t wolfip_bsd_timeout_ticks(const struct wolfIP_timeval *tv) +{ + uint64_t ticks; + + if (tv->tv_sec < 0) { + return 0; + } + if (((tv->tv_sec == 0) && (tv->tv_usec == 0)) || + ((uint64_t)tv->tv_sec >= (uint64_t)portMAX_DELAY / configTICK_RATE_HZ)) { + return portMAX_DELAY; + } + ticks = ((uint64_t)tv->tv_sec * configTICK_RATE_HZ) + + (((uint64_t)tv->tv_usec * configTICK_RATE_HZ + 999999u) / 1000000u); + if (ticks >= (uint64_t)portMAX_DELAY) { + return portMAX_DELAY; + } + return (TickType_t)ticks; +} + +static void wolfip_bsd_timeout_timeval(TickType_t ticks, + struct wolfIP_timeval *tv) +{ + if (ticks == portMAX_DELAY) { + tv->tv_sec = 0; + tv->tv_usec = 0; + return; + } + tv->tv_sec = (long)(ticks / configTICK_RATE_HZ); + tv->tv_usec = (long)(((uint64_t)(ticks % configTICK_RATE_HZ) * 1000000u) / + configTICK_RATE_HZ); +} + int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { int ret; + int internal_fd; + if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } - xSemaphoreTake(g_lock, portMAX_DELAY); - ret = wolfIP_sock_setsockopt(g_ipstack, g_fds[sockfd].internal_fd, + internal_fd = g_fds[sockfd].internal_fd; + if ((level == WOLFIP_SOL_SOCKET) && + ((optname == WOLFIP_SO_RCVTIMEO) || (optname == WOLFIP_SO_SNDTIMEO))) { + struct wolfIP_timeval tv; + + if ((optval == NULL) || (optlen != (socklen_t)sizeof(tv))) { + wolfip_bsd_set_error(WOLFIP_EINVAL); + return -1; + } + memcpy(&tv, optval, sizeof(tv)); + if ((tv.tv_usec < 0) || (tv.tv_usec >= 1000000L)) { + wolfip_bsd_set_error(WOLFIP_EDOM); + return -1; + } + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + if (optname == WOLFIP_SO_RCVTIMEO) { + g_fds[sockfd].rx_timeout = wolfip_bsd_timeout_ticks(&tv); + } + else { + g_fds[sockfd].tx_timeout = wolfip_bsd_timeout_ticks(&tv); + } + xSemaphoreGive(g_lock); + return 0; + } + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + ret = wolfIP_sock_setsockopt(g_ipstack, internal_fd, level, optname, optval, optlen); xSemaphoreGive(g_lock); if (ret < 0) { @@ -637,11 +773,37 @@ int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { int ret; + int internal_fd; + if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } - xSemaphoreTake(g_lock, portMAX_DELAY); - ret = wolfIP_sock_getsockopt(g_ipstack, g_fds[sockfd].internal_fd, + internal_fd = g_fds[sockfd].internal_fd; + if ((level == WOLFIP_SOL_SOCKET) && + ((optname == WOLFIP_SO_RCVTIMEO) || (optname == WOLFIP_SO_SNDTIMEO))) { + struct wolfIP_timeval tv; + TickType_t ticks; + + if ((optval == NULL) || (optlen == NULL) || + (*optlen < (socklen_t)sizeof(tv))) { + wolfip_bsd_set_error(WOLFIP_EINVAL); + return -1; + } + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + ticks = (optname == WOLFIP_SO_RCVTIMEO) ? g_fds[sockfd].rx_timeout : + g_fds[sockfd].tx_timeout; + xSemaphoreGive(g_lock); + wolfip_bsd_timeout_timeval(ticks, &tv); + memcpy(optval, &tv, sizeof(tv)); + *optlen = (socklen_t)sizeof(tv); + return 0; + } + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + ret = wolfIP_sock_getsockopt(g_ipstack, internal_fd, level, optname, optval, optlen); xSemaphoreGive(g_lock); if (ret < 0) { @@ -654,11 +816,16 @@ int getsockopt(int sockfd, int level, int optname, int getsockname(int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) { int ret; + int internal_fd; + if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } - xSemaphoreTake(g_lock, portMAX_DELAY); - ret = wolfIP_sock_getsockname(g_ipstack, g_fds[sockfd].internal_fd, addr, addrlen); + internal_fd = g_fds[sockfd].internal_fd; + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + ret = wolfIP_sock_getsockname(g_ipstack, internal_fd, addr, addrlen); xSemaphoreGive(g_lock); if (ret < 0) { wolfip_bsd_set_error(ret); @@ -670,11 +837,16 @@ int getsockname(int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) int getpeername(int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) { int ret; + int internal_fd; + if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } - xSemaphoreTake(g_lock, portMAX_DELAY); - ret = wolfIP_sock_getpeername(g_ipstack, g_fds[sockfd].internal_fd, addr, addrlen); + internal_fd = g_fds[sockfd].internal_fd; + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; + } + ret = wolfIP_sock_getpeername(g_ipstack, internal_fd, addr, addrlen); xSemaphoreGive(g_lock); if (ret < 0) { wolfip_bsd_set_error(ret); @@ -686,44 +858,51 @@ int getpeername(int sockfd, struct wolfIP_sockaddr *addr, socklen_t *addrlen) int close(int sockfd) { int ret; + int internal_fd; wolfip_bsd_fd_entry *entry; + TickType_t start; + TickType_t left; if (!wolfip_bsd_fd_valid(sockfd)) { return -1; } entry = &g_fds[sockfd]; + internal_fd = entry->internal_fd; + start = xTaskGetTickCount(); for (;;) { - xSemaphoreTake(g_lock, portMAX_DELAY); - ret = wolfIP_sock_close(g_ipstack, entry->internal_fd); - if (ret >= 0) { - wolfIP_register_callback(g_ipstack, entry->internal_fd, NULL, NULL); - wolfip_bsd_fd_free(sockfd); - xSemaphoreGive(g_lock); - return ret; + if (wolfip_bsd_lock(sockfd, internal_fd) < 0) { + return -1; } - if ((ret == -1) && IS_SOCKET_TCP(entry->internal_fd) && - ((entry->seen_events & CB_EVENT_CLOSED) != 0u)) { - /* The TCP core can destroy the socket immediately after delivering - * CB_EVENT_CLOSED (e.g. final ACK in LAST_ACK), so the retry sees - * the already-zeroed descriptor and wolfIP_sock_close() returns -1. - * Treat that as a completed close and release the wrapper slot. */ - wolfIP_register_callback(g_ipstack, entry->internal_fd, NULL, NULL); - wolfip_bsd_fd_free(sockfd); - xSemaphoreGive(g_lock); - return 0; + left = wolfip_bsd_remaining(pdMS_TO_TICKS(WOLFIP_BSD_CLOSE_LINGER_MS), start); + ret = wolfIP_sock_close(g_ipstack, internal_fd); + /* -EAGAIN: no room for the FIN yet. */ + if ((ret == -WOLFIP_EAGAIN) && (left > 0)) { + wolfip_bsd_prepare_wait_locked(entry, + (uint16_t)(CB_EVENT_WRITABLE | CB_EVENT_CLOSED)); + if (wolfip_bsd_wait(entry, sockfd, left) == -WOLFIP_EBADF) { + wolfip_bsd_set_error(WOLFIP_EBADF); + return -1; + } + continue; } - if (ret != -WOLFIP_EAGAIN) { - xSemaphoreGive(g_lock); - wolfip_bsd_set_error(ret); - return -1; + if (ret == -WOLFIP_EAGAIN) { + (void)wolfIP_sock_abort(g_ipstack, internal_fd); + } + wolfIP_register_callback(g_ipstack, internal_fd, NULL, NULL); + if (entry->waiters > 0) { + entry->closing = 1; + (void)xSemaphoreGive(entry->ready_sem); + } + else { + wolfip_bsd_fd_free(sockfd); } - - wolfip_bsd_prepare_wait_locked(entry, CB_EVENT_CLOSED); xSemaphoreGive(g_lock); - if (wolfip_bsd_wait_unlocked(entry) < 0) { - wolfip_bsd_set_error(WOLFIP_EAGAIN); + /* -EBADF: the stack already released the socket. */ + if ((ret < 0) && (ret != -WOLFIP_EAGAIN) && (ret != -WOLFIP_EBADF)) { + wolfip_bsd_set_error(ret); return -1; } + return 0; } } diff --git a/src/port/lpc54s018/main.c b/src/port/lpc54s018/main.c index 8bba6da1..f24301d6 100644 --- a/src/port/lpc54s018/main.c +++ b/src/port/lpc54s018/main.c @@ -259,6 +259,8 @@ static void echo_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) wolfIP_register_callback(s, client_fd, echo_cb, s); return; diff --git a/src/port/pic32mz/main.c b/src/port/pic32mz/main.c index 5645f0d7..b85307f7 100644 --- a/src/port/pic32mz/main.c +++ b/src/port/pic32mz/main.c @@ -94,6 +94,8 @@ static void app_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { wolfIP_register_callback(s, client_fd, app_cb, s); rx_bytes = 0; @@ -143,6 +145,8 @@ static void app_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) wolfIP_register_callback(s, client_fd, app_cb, s); return; diff --git a/src/port/posix/bsd_socket.c b/src/port/posix/bsd_socket.c index 2fc169a1..99cff2fd 100644 --- a/src/port/posix/bsd_socket.c +++ b/src/port/posix/bsd_socket.c @@ -292,6 +292,9 @@ static struct wolfip_fd_entry *wolfip_entry_from_internal(int internal_fd) return NULL; if (!wolfip_fd_entries[idx].in_use) return NULL; + /* A reused slot maps to the descriptor generation that replaced this one. */ + if (wolfip_fd_entries[idx].internal_fd != internal_fd) + return NULL; return &wolfip_fd_entries[idx]; } @@ -306,6 +309,8 @@ static struct wolfip_fd_entry *wolfip_entry_from_public(int public_fd) static void wolfip_fd_detach_internal(int internal_fd) { + if (wolfip_entry_from_internal(internal_fd) == NULL) + return; if (IS_SOCKET_TCP(internal_fd)) { int pos = SOCKET_UNMARK(internal_fd); if (pos >= 0 && pos < MAX_TCPSOCKETS) diff --git a/src/port/raspberry-pico-usb-server/src/main.c b/src/port/raspberry-pico-usb-server/src/main.c index fbf177c6..d6a1a991 100644 --- a/src/port/raspberry-pico-usb-server/src/main.c +++ b/src/port/raspberry-pico-usb-server/src/main.c @@ -189,6 +189,8 @@ static void telnet_cb(int fd, uint16_t event, void *arg) char ipaddr[16]; char welcome_msg[32]; tel_c = wolfIP_sock_accept(IPStack, tel_s, (struct wolfIP_sockaddr*)&addr, &socklen); + if (tel_c < 0) + tel_c = -1; if (tel_c > 0) { iptoa(ee32(addr.sin_addr.s_addr), ipaddr); snprintf(welcome_msg, sizeof(welcome_msg), "Welcome %s!\n", ipaddr); diff --git a/src/port/stm32c5a3/main.c b/src/port/stm32c5a3/main.c index f4c53b49..73f730bf 100644 --- a/src/port/stm32c5a3/main.c +++ b/src/port/stm32c5a3/main.c @@ -453,6 +453,8 @@ static void echo_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { wolfIP_register_callback(s, client_fd, echo_cb, s); } diff --git a/src/port/stm32f439/main.c b/src/port/stm32f439/main.c index c3703cfd..5c115498 100644 --- a/src/port/stm32f439/main.c +++ b/src/port/stm32f439/main.c @@ -577,6 +577,8 @@ static void echo_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { printf("Echo: client connected (fd=%d)\n", client_fd); wolfIP_register_callback(s, client_fd, echo_cb, s); diff --git a/src/port/stm32h563/main.c b/src/port/stm32h563/main.c index 542b46b2..69bc5ab4 100644 --- a/src/port/stm32h563/main.c +++ b/src/port/stm32h563/main.c @@ -781,6 +781,8 @@ static void echo_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { wolfIP_register_callback(s, client_fd, echo_cb, s); } diff --git a/src/port/stm32h753/main.c b/src/port/stm32h753/main.c index b75dc475..38eb0987 100644 --- a/src/port/stm32h753/main.c +++ b/src/port/stm32h753/main.c @@ -773,6 +773,8 @@ static void echo_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { wolfIP_register_callback(s, client_fd, echo_cb, s); led_blue_on(); /* Client connected */ diff --git a/src/port/stm32n6/main.c b/src/port/stm32n6/main.c index 54ac0511..3c2af3ac 100644 --- a/src/port/stm32n6/main.c +++ b/src/port/stm32n6/main.c @@ -647,6 +647,8 @@ static void echo_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { wolfIP_register_callback(s, client_fd, echo_cb, s); } diff --git a/src/port/va416xx/main.c b/src/port/va416xx/main.c index 4d94e03e..80adf344 100644 --- a/src/port/va416xx/main.c +++ b/src/port/va416xx/main.c @@ -231,6 +231,8 @@ static void speed_cb(int fd, uint16_t event, void *arg) if ((fd == speed_listen_fd) && (event & CB_EVENT_READABLE) && (speed_client_fd == -1)) { speed_client_fd = wolfIP_sock_accept(s, speed_listen_fd, NULL, NULL); + if (speed_client_fd < 0) + speed_client_fd = -1; if (speed_client_fd > 0) { printf("Speed: client connected (fd=%d)\n", speed_client_fd); wolfIP_register_callback(s, speed_client_fd, speed_cb, s); @@ -296,6 +298,8 @@ static void echo_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { printf("Echo: client connected (fd=%d)\n", client_fd); wolfIP_register_callback(s, client_fd, echo_cb, s); diff --git a/src/test/esp/test_esp.c b/src/test/esp/test_esp.c index 1e23f7ae..318dc03e 100644 --- a/src/test/esp/test_esp.c +++ b/src/test/esp/test_esp.c @@ -66,6 +66,8 @@ static void server_cb(int fd, uint16_t event, void *arg) if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept((struct wolfIP *)arg, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { printf("accept: %04x\n", client_fd); } diff --git a/src/test/freertos_mocks/FreeRTOS.h b/src/test/freertos_mocks/FreeRTOS.h index 48b20d86..c10af40a 100644 --- a/src/test/freertos_mocks/FreeRTOS.h +++ b/src/test/freertos_mocks/FreeRTOS.h @@ -14,8 +14,9 @@ typedef uint32_t TickType_t; #ifndef configTICK_RATE_HZ #define configTICK_RATE_HZ 1000u #endif -#define portTICK_PERIOD_MS 1u -#define pdMS_TO_TICKS(ms) ((TickType_t)(ms)) +#define portTICK_PERIOD_MS ((TickType_t)1000u / configTICK_RATE_HZ) +#define pdMS_TO_TICKS(ms) \ + ((TickType_t)(((uint64_t)(ms) * configTICK_RATE_HZ) / 1000u)) #define portYIELD_FROM_ISR(woken) ((void)(woken)) #endif diff --git a/src/test/freertos_mocks/wolfip.h b/src/test/freertos_mocks/wolfip.h index a80a417a..8d2e0470 100644 --- a/src/test/freertos_mocks/wolfip.h +++ b/src/test/freertos_mocks/wolfip.h @@ -25,6 +25,9 @@ typedef void (*wolfIP_wake_cb)(void *arg); #define WOLFIP_EAGAIN 11 #define WOLFIP_EINVAL 22 #define WOLFIP_ENOMEM 12 +#define WOLFIP_EDOM 33 +#define WOLFIP_EINPROGRESS 115 +#define WOLFIP_EBADF 9 #define MARK_TCP_SOCKET 0x100 #define IS_SOCKET_TCP(fd) (((fd) & MARK_TCP_SOCKET) == MARK_TCP_SOCKET) @@ -33,6 +36,15 @@ typedef void (*wolfIP_wake_cb)(void *arg); #define CB_EVENT_WRITABLE 0x0002 #define CB_EVENT_CLOSED 0x0004 +#define WOLFIP_SOL_SOCKET 1 +#define WOLFIP_SO_RCVTIMEO 20 +#define WOLFIP_SO_SNDTIMEO 21 + +struct wolfIP_timeval { + long tv_sec; + long tv_usec; +}; + int wolfIP_poll(struct wolfIP *ipstack, uint64_t now_ms); int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol); int wolfIP_sock_bind(struct wolfIP *s, int fd, const struct wolfIP_sockaddr *addr, socklen_t len); @@ -54,6 +66,7 @@ int wolfIP_sock_getpeername(struct wolfIP *s, int fd, struct wolfIP_sockaddr *ad int wolfIP_sock_can_write(struct wolfIP *s, int fd); int wolfIP_sock_can_read(struct wolfIP *s, int fd); int wolfIP_sock_close(struct wolfIP *s, int fd); +int wolfIP_sock_abort(struct wolfIP *s, int fd); void wolfIP_register_callback(struct wolfIP *s, int fd, tsocket_cb cb, void *arg); void wolfIP_set_wake_cb(struct wolfIP *s, wolfIP_wake_cb cb, void *arg); diff --git a/src/test/ipfilter_logger.c b/src/test/ipfilter_logger.c index b1fe4b78..bcd75c4f 100644 --- a/src/test/ipfilter_logger.c +++ b/src/test/ipfilter_logger.c @@ -161,6 +161,8 @@ static void server_cb(int fd, uint16_t event, void *arg) int ret = 0; if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept((struct wolfIP *)arg, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { printf("accept: Client FD is 0x%04x\n", client_fd); server_ssl = wolfSSL_new(server_ctx); diff --git a/src/test/test_eventloop.c b/src/test/test_eventloop.c index b584e6df..8e8a7c8b 100644 --- a/src/test/test_eventloop.c +++ b/src/test/test_eventloop.c @@ -59,6 +59,8 @@ static void server_cb(int fd, uint16_t event, void *arg) int ret = 0; if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept((struct wolfIP *)arg, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { printf("accept: %04x\n", client_fd); } diff --git a/src/test/test_eventloop_tun.c b/src/test/test_eventloop_tun.c index 34fb90c6..4d1b0c8f 100644 --- a/src/test/test_eventloop_tun.c +++ b/src/test/test_eventloop_tun.c @@ -59,6 +59,8 @@ static void server_cb(int fd, uint16_t event, void *arg) int ret = 0; if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept((struct wolfIP *)arg, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { printf("accept: %04x\n", client_fd); } diff --git a/src/test/test_freertos_bsd_semantics.c b/src/test/test_freertos_bsd_semantics.c new file mode 100644 index 00000000..ad996d64 --- /dev/null +++ b/src/test/test_freertos_bsd_semantics.c @@ -0,0 +1,687 @@ +/* test_freertos_bsd_semantics.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Blocking semantics of the FreeRTOS BSD wrapper: timeouts, accept() and + * close(), against a scripted wolfIP core. */ + +#include +#include +#include +#include + +#include "FreeRTOS.h" +#include "semphr.h" +#include "task.h" +#include "wolfip.h" + +struct MockSemaphore { + int count; + int deleted; +}; + +/* Deleted semaphores are kept so a later use is counted, not undefined. */ +static int use_after_delete; +static SemaphoreHandle_t mock_mutex; +/* Runs, in place of another task, at the lock_countdown-th take of g_lock. */ +static void (*lock_hook)(void); +static int lock_countdown; + +#define LISTEN_FD (MARK_TCP_SOCKET | 0) +#define CHILD_FD (MARK_TCP_SOCKET | 1) + +static int next_socket_fd = LISTEN_FD; +static TickType_t last_wait_ticks; +static int recv_ret = -WOLFIP_EAGAIN; +static int can_read_ret; +static TickType_t now_ticks; +static tsocket_cb registered_cb; +static void *registered_arg; +/* Runs once, in place of another task, when a caller first blocks. */ +static void (*wait_hook)(void); + +SemaphoreHandle_t xSemaphoreCreateBinary(void) +{ + return calloc(1, sizeof(struct MockSemaphore)); +} + +SemaphoreHandle_t xSemaphoreCreateMutex(void) +{ + struct MockSemaphore *sem = calloc(1, sizeof(*sem)); + + if (sem != NULL) + sem->count = 1; + mock_mutex = sem; + return sem; +} + +/* Nothing else runs, so a wait that is not already satisfied times out. */ +BaseType_t xSemaphoreTake(SemaphoreHandle_t sem, TickType_t ticks) +{ + if (sem == NULL) + return pdFALSE; + if (sem == mock_mutex && lock_countdown > 0 && --lock_countdown == 0) + lock_hook(); + if (sem->count == 0 && ticks != 0 && wait_hook != NULL) { + void (*hook)(void) = wait_hook; + + wait_hook = NULL; + hook(); + } + if (sem->deleted) + use_after_delete++; + if (sem->count > 0) { + sem->count--; + return pdTRUE; + } + last_wait_ticks = ticks; + if (ticks != 0 && ticks != portMAX_DELAY) + now_ticks += ticks; + return pdFALSE; +} + +BaseType_t xSemaphoreGive(SemaphoreHandle_t sem) +{ + if (sem == NULL) + return pdFALSE; + if (sem->deleted) + use_after_delete++; + sem->count++; + return pdTRUE; +} + +void vSemaphoreDelete(SemaphoreHandle_t sem) +{ + sem->deleted = 1; +} + +BaseType_t xTaskCreate(TaskFunction_t task, const char *name, + uint16_t stack_words, void *arg, UBaseType_t priority, TaskHandle_t *handle) +{ + (void)task; (void)name; (void)stack_words; (void)arg; (void)priority; + (void)handle; + return pdPASS; +} + +void vTaskDelay(TickType_t ticks) +{ + now_ticks += ticks; +} + +TickType_t xTaskGetTickCount(void) +{ + return now_ticks; +} + +void vTaskDelete(TaskHandle_t handle) +{ + (void)handle; +} + +int wolfIP_poll(struct wolfIP *ipstack, uint64_t now_ms) +{ + (void)ipstack; (void)now_ms; + return 0; +} + +int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol) +{ + (void)s; (void)domain; (void)type; (void)protocol; + return next_socket_fd++; +} + +int wolfIP_sock_bind(struct wolfIP *s, int fd, const struct wolfIP_sockaddr *addr, socklen_t len) +{ + (void)s; (void)fd; (void)addr; (void)len; + return 0; +} + +int wolfIP_sock_listen(struct wolfIP *s, int fd, int backlog) +{ + (void)s; (void)fd; (void)backlog; + return 0; +} + +/* Successive results of wolfIP_sock_accept() and wolfIP_sock_close(); once + * a script runs out, accept() keeps returning -EAGAIN and close() 0. */ +static int accept_script[4]; +static int accept_steps; +static int accept_calls; +static int accept_last_fd; +static int close_script[4]; +static int close_steps; +static int close_calls; +static int abort_calls; +static int abort_fd; + +int wolfIP_sock_accept(struct wolfIP *s, int fd, struct wolfIP_sockaddr *addr, socklen_t *len) +{ + (void)s; (void)addr; (void)len; + accept_last_fd = fd; + if (accept_calls < accept_steps) + return accept_script[accept_calls++]; + accept_calls++; + return -WOLFIP_EAGAIN; +} + +int wolfIP_sock_connect(struct wolfIP *s, int fd, const struct wolfIP_sockaddr *addr, socklen_t len) +{ + (void)s; (void)fd; (void)addr; (void)len; + return -WOLFIP_EAGAIN; +} + +int wolfIP_sock_send(struct wolfIP *s, int fd, const void *buf, size_t len, int flags) +{ + (void)s; (void)fd; (void)buf; (void)len; (void)flags; + return -WOLFIP_EAGAIN; +} + +int wolfIP_sock_sendto(struct wolfIP *s, int fd, const void *buf, size_t len, int flags, + const struct wolfIP_sockaddr *dest_addr, socklen_t len2) +{ + (void)s; (void)fd; (void)buf; (void)len; (void)flags; (void)dest_addr; (void)len2; + return -WOLFIP_EAGAIN; +} + +int wolfIP_sock_recv(struct wolfIP *s, int fd, void *buf, size_t len, int flags) +{ + (void)s; (void)fd; (void)buf; (void)len; (void)flags; + return recv_ret; +} + +int wolfIP_sock_recvfrom(struct wolfIP *s, int fd, void *buf, size_t len, int flags, + struct wolfIP_sockaddr *src_addr, socklen_t *len2) +{ + (void)s; (void)fd; (void)buf; (void)len; (void)flags; (void)src_addr; (void)len2; + return recv_ret; +} + +int wolfIP_sock_setsockopt(struct wolfIP *s, int fd, int level, int optname, + const void *optval, socklen_t optlen) +{ + (void)s; (void)fd; (void)level; (void)optname; (void)optval; (void)optlen; + return 0; +} + +int wolfIP_sock_getsockopt(struct wolfIP *s, int fd, int level, int optname, + void *optval, socklen_t *optlen) +{ + (void)s; (void)fd; (void)level; (void)optname; (void)optval; (void)optlen; + return 0; +} + +int wolfIP_sock_getsockname(struct wolfIP *s, int fd, struct wolfIP_sockaddr *addr, socklen_t *len) +{ + (void)s; (void)fd; (void)addr; (void)len; + return 0; +} + +int wolfIP_sock_getpeername(struct wolfIP *s, int fd, struct wolfIP_sockaddr *addr, socklen_t *len) +{ + (void)s; (void)fd; (void)addr; (void)len; + return 0; +} + +int wolfIP_sock_can_write(struct wolfIP *s, int fd) +{ + (void)s; (void)fd; + return 0; +} + +int wolfIP_sock_can_read(struct wolfIP *s, int fd) +{ + (void)s; (void)fd; + return can_read_ret; +} + +int wolfIP_sock_close(struct wolfIP *s, int fd) +{ + (void)s; (void)fd; + if (close_calls < close_steps) + return close_script[close_calls++]; + close_calls++; + return 0; +} + +int wolfIP_sock_abort(struct wolfIP *s, int fd) +{ + (void)s; + abort_calls++; + abort_fd = fd; + return 0; +} + +void wolfIP_register_callback(struct wolfIP *s, int fd, tsocket_cb cb, void *arg) +{ + (void)s; (void)fd; + registered_cb = cb; + registered_arg = arg; +} + +void wolfIP_set_wake_cb(struct wolfIP *s, wolfIP_wake_cb cb, void *arg) +{ + (void)s; (void)cb; (void)arg; +} + +#include "../port/freeRTOS/bsd_socket.c" + +static int failures; + +#define CHECK(cond) do { \ + if (!(cond)) { \ + printf("%s:%d: check failed: %s\n", __FILE__, __LINE__, #cond); \ + failures++; \ + } \ + } while (0) + +static int spurious_wakes; + +/* Another task's activity: time passes and a non-completing event fires. */ +static void spurious_wake(void) +{ + now_ticks += pdMS_TO_TICKS(100); + spurious_wakes++; + registered_cb(0, CB_EVENT_WRITABLE, registered_arg); + if (spurious_wakes < 10) + wait_hook = spurious_wake; +} + +static void test_timeouts(void) +{ + struct wolfIP_timeval tv; + struct wolfIP_timeval out; + socklen_t outlen; + char wide[2 * sizeof(struct wolfIP_timeval)]; + char buf[8]; + TickType_t start; + int fd; + + fd = socket(AF_INET, SOCK_STREAM, 0); + CHECK(fd >= 0); + recv_ret = -WOLFIP_EAGAIN; + + /* Default: the wait is unbounded. */ + CHECK(recv(fd, buf, sizeof(buf), 0) == -1); + CHECK(last_wait_ticks == portMAX_DELAY); + + tv.tv_sec = 1; + tv.tv_usec = 500000; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == 0); + CHECK(recv(fd, buf, sizeof(buf), 0) == -1); + CHECK(last_wait_ticks == pdMS_TO_TICKS(1500)); + CHECK(socket_last_error() == WOLFIP_EAGAIN); + + /* A sub-millisecond timeout still waits, rather than meaning "none". */ + tv.tv_sec = 0; + tv.tv_usec = 1; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_SNDTIMEO, &tv, + sizeof(tv)) == 0); + CHECK(send(fd, buf, sizeof(buf), 0) == -1); + CHECK(last_wait_ticks == 1); + + /* Zero restores "no timeout". */ + tv.tv_usec = 0; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == 0); + CHECK(recv(fd, buf, sizeof(buf), 0) == -1); + CHECK(last_wait_ticks == portMAX_DELAY); + + /* Every blocking call honours its timeout. */ + tv.tv_sec = 0; + tv.tv_usec = 250000; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_SNDTIMEO, &tv, + sizeof(tv)) == 0); + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == 0); + last_wait_ticks = 0; + CHECK(connect(fd, NULL, 0) == -1); + CHECK(last_wait_ticks == pdMS_TO_TICKS(250)); + /* The handshake goes on: connect() reports it as in progress. */ + CHECK(socket_last_error() == WOLFIP_EINPROGRESS); + last_wait_ticks = 0; + CHECK(sendto(fd, buf, sizeof(buf), 0, NULL, 0) == -1); + CHECK(last_wait_ticks == pdMS_TO_TICKS(250)); + last_wait_ticks = 0; + CHECK(recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL) == -1); + CHECK(last_wait_ticks == pdMS_TO_TICKS(250)); + + /* Wakes that do not complete the call spend its timeout, not restart it. */ + spurious_wakes = 0; + wait_hook = spurious_wake; + start = now_ticks; + CHECK(send(fd, buf, sizeof(buf), 0) == -1); + CHECK(socket_last_error() == WOLFIP_EAGAIN); + CHECK((TickType_t)(now_ticks - start) <= pdMS_TO_TICKS(300)); + CHECK(spurious_wakes == 3); + wait_hook = NULL; + + /* getsockopt() reads back what setsockopt() stored. */ + tv.tv_sec = 1; + tv.tv_usec = 500000; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == 0); + memset(&out, 0xff, sizeof(out)); + outlen = sizeof(out); + CHECK(getsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &out, + &outlen) == 0); + CHECK(outlen == sizeof(out)); + CHECK(out.tv_sec == 1 && out.tv_usec == 500000); + tv.tv_sec = 0; + tv.tv_usec = 0; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_SNDTIMEO, &tv, + sizeof(tv)) == 0); + memset(&out, 0xff, sizeof(out)); + CHECK(getsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_SNDTIMEO, &out, + &outlen) == 0); + CHECK(out.tv_sec == 0 && out.tv_usec == 0); + outlen = sizeof(out) - 1; + CHECK(getsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_SNDTIMEO, &out, + &outlen) == -1); + + /* A timeout too long for TickType_t waits forever, as on Linux. */ + tv.tv_sec = LONG_MAX; + tv.tv_usec = 999999; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == 0); + CHECK(recv(fd, buf, sizeof(buf), 0) == -1); + CHECK(last_wait_ticks == portMAX_DELAY); + + /* A negative timeout does not wait. */ + tv.tv_sec = -1; + tv.tv_usec = 0; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == 0); + last_wait_ticks = 1234; + CHECK(recv(fd, buf, sizeof(buf), 0) == -1); + CHECK(last_wait_ticks == 0); + CHECK(socket_last_error() == WOLFIP_EAGAIN); + + tv.tv_sec = 0; + tv.tv_usec = 1000000; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == -1); + CHECK(socket_last_error() == WOLFIP_EDOM); + tv.tv_usec = -1; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == -1); + CHECK(socket_last_error() == WOLFIP_EDOM); + /* A larger struct timeval (64-bit time_t on a 32-bit target) is refused. */ + memset(wide, 0, sizeof(wide)); + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, wide, + sizeof(wide)) == -1); + tv.tv_sec = 1; + tv.tv_usec = 0; + CHECK(setsockopt(fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv) - 1) == -1); + + CHECK(close(fd) == 0); +} + +static void script_reset(void) +{ + accept_steps = accept_calls = 0; + close_steps = close_calls = 0; + abort_calls = 0; + abort_fd = -1; + wait_hook = NULL; +} + +static int hook_lfd; + +/* The listener's connection completes while accept() waits. */ +static void child_established(void) +{ + registered_cb(0, CB_EVENT_READABLE, registered_arg); +} + +/* Another task closes the listener; its slot is not reused while accept() + * still waits on it. */ +static void listener_replaced(void) +{ + int fd; + + CHECK(close(hook_lfd) == 0); + fd = socket(AF_INET, SOCK_STREAM, 0); + CHECK(fd >= 0 && fd != hook_lfd); + CHECK(close(fd) == 0); +} + +static void test_accept(void) +{ + struct wolfIP_timeval tv; + int lfd; + int fd; + + script_reset(); + lfd = socket(AF_INET, SOCK_STREAM, 0); + CHECK(lfd >= 0); + + /* -EAGAIN while the handshake runs: wait for the listener, then retry. */ + accept_script[0] = -WOLFIP_EAGAIN; + accept_script[1] = CHILD_FD; + accept_steps = 2; + wait_hook = child_established; + fd = accept(lfd, NULL, NULL); + CHECK(fd >= 0 && fd != lfd); + CHECK(accept_calls == 2); + CHECK(close(fd) == 0); + + tv.tv_sec = 0; + tv.tv_usec = 250000; + CHECK(setsockopt(lfd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == 0); + script_reset(); + CHECK(accept(lfd, NULL, NULL) == -1); + CHECK(socket_last_error() == WOLFIP_EAGAIN); + CHECK(last_wait_ticks == pdMS_TO_TICKS(250)); + + script_reset(); + hook_lfd = lfd; + wait_hook = listener_replaced; + CHECK(accept(lfd, NULL, NULL) == -1); + CHECK(socket_last_error() == WOLFIP_EBADF); + CHECK(accept_calls == 1); + CHECK(close(lfd) == -1); +} + +/* Room for the FIN frees up while close() waits. */ +static void tx_space(void) +{ + registered_cb(0, CB_EVENT_WRITABLE, registered_arg); + wait_hook = tx_space; +} + +static void test_close(void) +{ + TickType_t start; + int fd; + + /* The FIN is queued at once: nothing to wait for. */ + script_reset(); + fd = socket(AF_INET, SOCK_STREAM, 0); + last_wait_ticks = 1234; + CHECK(close(fd) == 0); + CHECK(close_calls == 1); + CHECK(last_wait_ticks == 1234); + wolfip_bsd_set_error(WOLFIP_EAGAIN); + CHECK(close(fd) == -1); + CHECK(socket_last_error() == WOLFIP_EBADF); + + /* A full TX FIFO: retry as room frees up. */ + script_reset(); + fd = socket(AF_INET, SOCK_STREAM, 0); + close_script[0] = -WOLFIP_EAGAIN; + close_script[1] = -WOLFIP_EAGAIN; + close_steps = 2; + wait_hook = tx_space; + CHECK(close(fd) == 0); + CHECK(close_calls == 3); + CHECK(abort_calls == 0); + + /* Never any room: abort once the linger runs out. */ + script_reset(); + fd = socket(AF_INET, SOCK_STREAM, 0); + close_script[0] = close_script[1] = close_script[2] = -WOLFIP_EAGAIN; + close_steps = 3; + start = now_ticks; + CHECK(close(fd) == 0); + CHECK(abort_calls == 1); + CHECK(abort_fd == next_socket_fd - 1); + CHECK((TickType_t)(now_ticks - start) == pdMS_TO_TICKS(WOLFIP_BSD_CLOSE_LINGER_MS)); + CHECK(close(fd) == -1); + + /* The stack released the socket and reissued its descriptor. */ + script_reset(); + fd = socket(AF_INET, SOCK_STREAM, 0); + close_script[0] = -WOLFIP_EBADF; + close_steps = 1; + CHECK(close(fd) == 0); + CHECK(abort_calls == 0); + CHECK(close(fd) == -1); +} + +static int blocked_fd; +static int other_waiter; + +/* Another task closes the descriptor this one is blocked on. */ +static void close_under_waiter(void) +{ + int fd; + + if (other_waiter) + g_fds[blocked_fd].waiters++; + CHECK(close(blocked_fd) == 0); + /* The slot stays taken until every waiter has left it. */ + fd = socket(AF_INET, SOCK_STREAM, 0); + CHECK(fd >= 0 && fd != blocked_fd); + CHECK(close(fd) == 0); +} + +static void test_close_while_blocked(void) +{ + char buf[8]; + + script_reset(); + use_after_delete = 0; + recv_ret = -WOLFIP_EAGAIN; + blocked_fd = socket(AF_INET, SOCK_STREAM, 0); + other_waiter = 0; + wait_hook = close_under_waiter; + CHECK(recv(blocked_fd, buf, sizeof(buf), 0) == -1); + CHECK(socket_last_error() == WOLFIP_EBADF); + CHECK(use_after_delete == 0); + CHECK(!g_fds[blocked_fd].in_use); + CHECK(socket(AF_INET, SOCK_STREAM, 0) == blocked_fd); + + /* With a second waiter the first one passes the wake on, and the + * second one frees the slot. */ + other_waiter = 1; + wait_hook = close_under_waiter; + CHECK(recv(blocked_fd, buf, sizeof(buf), 0) == -1); + CHECK(g_fds[blocked_fd].in_use && g_fds[blocked_fd].closing); + /* A closing descriptor refuses new calls without blocking. */ + last_wait_ticks = 1234; + CHECK(recv(blocked_fd, buf, sizeof(buf), 0) == -1); + CHECK(close(blocked_fd) == -1); + CHECK(last_wait_ticks == 1234); + xSemaphoreTake(g_lock, portMAX_DELAY); + g_fds[blocked_fd].waiters--; + CHECK(wolfip_bsd_wait(&g_fds[blocked_fd], blocked_fd, 0) == -WOLFIP_EBADF); + CHECK(!g_fds[blocked_fd].in_use); + CHECK(use_after_delete == 0); + CHECK(recv(blocked_fd, buf, sizeof(buf), 0) == -1); +} + +static int reissued_fd; + +/* Another task closes the descriptor and a third gets its slot back. */ +static void close_and_reissue(void) +{ + CHECK(close(blocked_fd) == 0); + reissued_fd = socket(AF_INET, SOCK_STREAM, 0); + CHECK(reissued_fd == blocked_fd); +} + +/* Data arrives; the close lands before the woken task relocks. */ +static void wake_then_reissue(void) +{ + registered_cb(0, CB_EVENT_READABLE, registered_arg); + lock_hook = close_and_reissue; + lock_countdown = 2; +} + +static void test_reissue_between_calls(void) +{ + struct wolfIP_timeval tv; + char buf[8]; + + script_reset(); + recv_ret = -WOLFIP_EAGAIN; + blocked_fd = socket(AF_INET, SOCK_STREAM, 0); + wait_hook = wake_then_reissue; + CHECK(recv(blocked_fd, buf, sizeof(buf), 0) == -1); + CHECK(socket_last_error() == WOLFIP_EBADF); + CHECK(close(reissued_fd) == 0); + + /* The same for an option set between the validity check and the lock. */ + blocked_fd = socket(AF_INET, SOCK_STREAM, 0); + lock_hook = close_and_reissue; + lock_countdown = 1; + tv.tv_sec = 1; + tv.tv_usec = 0; + CHECK(setsockopt(blocked_fd, WOLFIP_SOL_SOCKET, WOLFIP_SO_RCVTIMEO, &tv, + sizeof(tv)) == -1); + CHECK(socket_last_error() == WOLFIP_EBADF); + CHECK(g_fds[reissued_fd].rx_timeout == portMAX_DELAY); + CHECK(close(reissued_fd) == 0); + + /* And for the calls that never block. */ + blocked_fd = socket(AF_INET, SOCK_STREAM, 0); + lock_hook = close_and_reissue; + lock_countdown = 1; + CHECK(listen(blocked_fd, 1) == -1); + CHECK(socket_last_error() == WOLFIP_EBADF); + CHECK(close(reissued_fd) == 0); +} + +int main(void) +{ + struct wolfIP stack; + + memset(&stack, 0, sizeof(stack)); + if (wolfip_freertos_socket_init(&stack, 1, 128) != 0) { + printf("init failed\n"); + return 1; + } + + test_timeouts(); + test_accept(); + test_close(); + test_close_while_blocked(); + test_reissue_between_calls(); + + if (failures != 0) { + printf("test_freertos_bsd_semantics: %d FAILED\n", failures); + return 1; + } + printf("test_freertos_bsd_semantics: passed\n"); + return 0; +} diff --git a/src/test/test_freertos_close_last_ack.c b/src/test/test_freertos_close_last_ack.c index 536f932d..8808d123 100644 --- a/src/test/test_freertos_close_last_ack.c +++ b/src/test/test_freertos_close_last_ack.c @@ -1,5 +1,5 @@ -/* Regression test for the FreeRTOS BSD close() wrapper when the core delivers - * CB_EVENT_CLOSED synchronously during LAST_ACK teardown. */ +/* Regression test for the FreeRTOS BSD close() wrapper when the core releases + * the socket while close() waits for room to queue its FIN. */ #include #include @@ -212,7 +212,13 @@ int wolfIP_sock_close(struct wolfIP *s, int fd) close_calls++; if (close_calls == 1) return -WOLFIP_EAGAIN; - return -1; + return -WOLFIP_EBADF; +} + +int wolfIP_sock_abort(struct wolfIP *s, int fd) +{ + (void)s; (void)fd; + return 0; } void wolfIP_register_callback(struct wolfIP *s, int fd, tsocket_cb cb, void *arg) diff --git a/src/test/test_native_wolfssl.c b/src/test/test_native_wolfssl.c index 97a97ed9..f9fabfa7 100644 --- a/src/test/test_native_wolfssl.c +++ b/src/test/test_native_wolfssl.c @@ -67,6 +67,8 @@ static void server_cb(int fd, uint16_t event, void *arg) int ret = 0; if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { client_fd = wolfIP_sock_accept((struct wolfIP *)arg, listen_fd, NULL, NULL); + if (client_fd < 0) + client_fd = -1; if (client_fd > 0) { printf("accept: Client FD is 0x%04x\n", client_fd); /* Create the wolfSSL object */ diff --git a/src/test/test_posix_errno.c b/src/test/test_posix_errno.c index 6010747a..a3e3fabb 100644 --- a/src/test/test_posix_errno.c +++ b/src/test/test_posix_errno.c @@ -191,6 +191,26 @@ static void test_fd_lookup_synchronization(void) assert(close(fd) == 0); } +/* The stack reused a TCP slot while the application still held its old + * descriptor: closing the old one must not unmap the new one. */ +static void test_reused_slot_keeps_new_mapping(void) +{ + const int old_internal = MARK_TCP_SOCKET | 1; + const int new_internal = MARK_TCP_SOCKET | 1 | (1 << 16); + int old_fd; + int new_fd; + + old_fd = wolfip_fd_alloc(old_internal, 0); + new_fd = wolfip_fd_alloc(new_internal, 0); + assert(old_fd >= 0 && new_fd >= 0 && old_fd != new_fd); + assert(wolfip_entry_from_internal(new_internal) == &wolfip_fd_entries[new_fd]); + assert(wolfip_entry_from_internal(old_internal) == NULL); + wolfip_fd_release(old_fd); + assert(wolfip_entry_from_internal(new_internal) == &wolfip_fd_entries[new_fd]); + wolfip_fd_release(new_fd); + assert(wolfip_entry_from_internal(new_internal) == NULL); +} + int main(void) { struct sockaddr_in peer; @@ -225,6 +245,7 @@ int main(void) printf("F-4950 regression test passed\n"); test_host_call_publication(); test_fd_lookup_synchronization(); + test_reused_slot_keeps_new_mapping(); printf("POSIX concurrency regression tests passed\n"); return 0; } diff --git a/src/test/test_wolfssl_forwarding.c b/src/test/test_wolfssl_forwarding.c index 1c9c23c5..3bd0c616 100644 --- a/src/test/test_wolfssl_forwarding.c +++ b/src/test/test_wolfssl_forwarding.c @@ -232,6 +232,8 @@ static void server_cb(int fd, uint16_t events, void *arg) (void)events; if (fd == server_listen_fd && (events & CB_EVENT_READABLE) && server_client_fd == -1) { server_client_fd = wolfIP_sock_accept(s, server_listen_fd, NULL, NULL); + if (server_client_fd < 0) + server_client_fd = -1; if (server_client_fd > 0) { wolfIP_register_callback(s, server_client_fd, server_cb, s); server_ssl = wolfSSL_new(server_ctx); diff --git a/src/test/unit/unit.c b/src/test/unit/unit.c index 887909fc..ee896ec3 100644 --- a/src/test/unit/unit.c +++ b/src/test/unit/unit.c @@ -655,17 +655,17 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_tcp_listener_closed_while_pending_is_not_readable); tcase_add_test(tc_utils, test_tcp_listener_preaccept_timeout_reverts_port); tcase_add_test(tc_utils, test_tcp_listener_preaccept_timeout_resets_peer); - tcase_add_test(tc_utils, test_tcp_listener_preaccept_accept_no_socket_resets_peer); + tcase_add_test(tc_utils, test_tcp_listener_preaccept_accept_no_socket_keeps_connection); tcase_add_test(tc_utils, test_tcp_listener_preaccept_rst_keeps_listener); tcase_add_test(tc_utils, test_tcp_listener_closed_preaccept_rst_frees_fin_wait_1); tcase_add_test(tc_utils, test_tcp_listener_closed_preaccept_rst_frees_last_ack); tcase_add_test(tc_utils, test_tcp_listener_preaccept_close_wait_timeout_reverts_port); tcase_add_test(tc_utils, test_sock_abort_established_resets_and_frees); - tcase_add_test(tc_utils, test_sock_abort_after_close_eagain_frees); + tcase_add_test(tc_utils, test_sock_abort_after_close_frees); tcase_add_test(tc_utils, test_sock_abort_without_peer_sends_nothing); tcase_add_test(tc_utils, test_sock_abort_fin_wait_1_rst_covers_sent_fin); tcase_add_test(tc_utils, test_sock_abort_fin_wait_2_rst_covers_acked_fin); - tcase_add_test(tc_utils, test_sock_abort_syn_rcvd_rst_covers_syn); + tcase_add_test(tc_utils, test_listener_close_resets_parked_syn_rcvd_child); tcase_add_test(tc_utils, test_sock_abort_queued_data_rst_at_snd_una); tcase_add_test(tc_utils, test_sock_abort_preaccept_listener_stops_listening); tcase_add_test(tc_utils, test_sock_abort_after_data_rto_rst_covers_sent_data); @@ -673,6 +673,25 @@ Suite *wolf_suite(void) tcase_add_test(tc_utils, test_sock_abort_requeued_fin_covers_fin); tcase_add_test(tc_utils, test_sock_abort_close_wait_resets_peer); tcase_add_test(tc_utils, test_sock_abort_last_ack_sends_nothing); + tcase_add_test(tc_utils, test_sock_close_repeated_in_last_ack_keeps_socket); + tcase_add_test(tc_utils, test_sock_close_repeated_in_time_wait_keeps_socket); + tcase_add_test(tc_utils, test_sock_abort_after_close_stale_once_slot_reused); + tcase_add_test(tc_utils, test_tcp_full_table_reclaims_time_wait_first); + tcase_add_test(tc_utils, test_tcp_full_table_reclaims_fin_wait_2_with_rst); + tcase_add_test(tc_utils, test_tcp_full_table_keeps_open_sockets); + tcase_add_test(tc_utils, test_tcp_full_table_reclaims_fin_wait_1_with_rst); + tcase_add_test(tc_utils, test_tcp_full_table_reclaims_last_ack_silently); + tcase_add_test(tc_utils, test_accept_parks_child_until_established); + tcase_add_test(tc_utils, test_accept_hands_out_parked_close_wait); + tcase_add_test(tc_utils, test_accept_drops_reset_parked_child_silently); + tcase_add_test(tc_utils, test_accept_parked_child_gives_up_after_synack_cap); + tcase_add_test(tc_utils, test_full_table_reclaims_parked_syn_rcvd_child); + tcase_add_test(tc_utils, test_parked_child_of_replaced_listener_not_handed_out); + tcase_add_test(tc_utils, test_parked_child_not_accepted_is_reset_after_timeout); + tcase_add_test(tc_utils, test_accept_reannounces_second_ready_child); + tcase_add_test(tc_utils, test_parked_child_handed_out_with_wide_listener_generation); + tcase_add_test(tc_utils, test_parked_child_in_freed_slot_retires_old_descriptor); + tcase_add_test(tc_utils, test_parked_child_in_held_slot_stays_silent); tcase_add_test(tc_utils, test_sock_abort_syn_rcvd_listener_resets_and_stops_listening); tcase_add_test(tc_utils, test_tcp_listener_preaccept_revert_drains_connection_state); tcase_add_test(tc_utils, test_tcp_listener_preaccept_close_rto_retransmits_finack); @@ -1386,6 +1405,13 @@ Suite *wolf_suite(void) tcase_add_test(tc_core, test_notify_loopback_tcp_sets_writable); tcase_add_test(tc_core, test_notify_loopback_tcp_non_loopback_not_notified); tcase_add_test(tc_core, test_notify_loopback_null_stack_no_crash); + tcase_add_test(tc_core, test_sock_fd_reissued_slot_rejects_old_descriptor); + tcase_add_test(tc_core, test_sock_fd_slot_freed_by_stack_gets_new_generation); + tcase_add_test(tc_core, test_sock_fd_callback_carries_generation); + tcase_add_test(tc_core, test_sock_fd_internal_reuse_retires_open_descriptor); + tcase_add_test(tc_core, test_sock_fd_held_slot_is_reused_last); + tcase_add_test(tc_core, test_sock_fd_reused_held_slot_drops_old_callback); + tcase_add_test(tc_core, test_sock_fd_generation_wraps_positive); /* === Branch-coverage tests from fleet ===*/ /* --- unit_tests_tcp_state.c (72 tests) --- */ @@ -1542,6 +1568,8 @@ Suite *wolf_suite(void) #if WOLFIP_ENABLE_LOOPBACK tcase_add_test(tc_core, test_poll_returns_zero_with_loopback_frame_queued); #endif + tcase_add_test(tc_core, test_poll_rebased_timer_stays_within_max_wait); + tcase_add_test(tc_core, test_poll_by_rebased_deadline_keeps_earlier_one); tcase_add_test(tc_core, test_poll_returns_zero_while_flush_events_undelivered); tcase_add_test(tc_core, test_wake_cb_on_socket_tx); tcase_add_test(tc_core, test_wake_cb_on_register_with_pending_events); diff --git a/src/test/unit/unit_shared.c b/src/test/unit/unit_shared.c index a6c51c09..47ce051c 100644 --- a/src/test/unit/unit_shared.c +++ b/src/test/unit/unit_shared.c @@ -496,6 +496,17 @@ static void inject_tcp_syn(struct wolfIP *s, unsigned int if_idx, ip4 dst_ip, ui tcp_input(s, if_idx, &syn, sizeof(struct wolfIP_eth_frame) + IP_HEADER_LEN + TCP_HEADER_LEN); } +/* Marks every other free TCP slot used, so the next allocation must take skip. */ +static void take_tcp_slots_except(struct wolfIP *s, int skip) +{ + int i; + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + if (i != skip && s->tcpsockets[i].proto == 0) + s->tcpsockets[i].proto = WI_IPPROTO_TCP; + } +} + static void inject_tcp_segment(struct wolfIP *s, unsigned int if_idx, ip4 src_ip, ip4 dst_ip, uint16_t src_port, uint16_t dst_port, uint32_t seq, uint32_t ack, uint8_t flags) { @@ -538,6 +549,35 @@ static void inject_tcp_segment(struct wolfIP *s, unsigned int if_idx, ip4 src_ip tcp_input(s, if_idx, &seg, sizeof(struct wolfIP_eth_frame) + IP_HEADER_LEN + TCP_HEADER_LEN); } +static struct tsocket *parked_child(struct wolfIP *s, int listen_sd) +{ + int i; + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + struct tsocket *t = &s->tcpsockets[i]; + + if ((t->proto == WI_IPPROTO_TCP) && + (tcp_parked_parent(t) == &s->tcpsockets[SOCKET_UNMARK(listen_sd)])) + return t; + } + return NULL; +} + +/* accept() parks the child, the peer ACKs its SYN-ACK, accept() hands it out. */ +static int accept_after_handshake(struct wolfIP *s, int listen_sd, + struct wolfIP_sockaddr *addr, socklen_t *addrlen) +{ + struct tsocket *t; + + ck_assert_int_eq(wolfIP_sock_accept(s, listen_sd, addr, addrlen), -WOLFIP_EAGAIN); + t = parked_child(s, listen_sd); + ck_assert_ptr_nonnull(t); + inject_tcp_segment(s, t->if_idx, t->remote_ip, t->local_ip, t->dst_port, + t->src_port, t->sock.tcp.ack, t->sock.tcp.seq + 1, TCP_FLAG_ACK); + ck_assert_int_eq(t->sock.tcp.state, TCP_ESTABLISHED); + return wolfIP_sock_accept(s, listen_sd, addr, addrlen); +} + static int tcp_option_find(const struct wolfIP_tcp_seg *tcp, uint8_t kind) { const uint8_t *opt = tcp->data; diff --git a/src/test/unit/unit_tests_api.c b/src/test/unit/unit_tests_api.c index 1d6eec25..3dad241f 100644 --- a/src/test/unit/unit_tests_api.c +++ b/src/test/unit/unit_tests_api.c @@ -1092,7 +1092,7 @@ START_TEST(test_tcp_listen_before_ipconfig_accepts_after) 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, + client_sd = accept_after_handshake(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen); ck_assert_int_gt(client_sd, 0); } @@ -3044,7 +3044,7 @@ START_TEST(test_sock_accept_success_sets_addr) 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); + client_sd = accept_after_handshake(&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); @@ -3257,7 +3257,8 @@ START_TEST(test_sock_accept_no_available_socket) ts->sock.tcp.state = TCP_ESTABLISHED; } - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); + /* No slot for the child: the connection stays on the listener. */ + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); } END_TEST @@ -3294,7 +3295,9 @@ START_TEST(test_sock_accept_no_free_socket_syn_rcvd) s.tcpsockets[i].sock.tcp.state = TCP_ESTABLISHED; } - ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -1); + /* No slot to park the child in: the listener keeps the handshake. */ + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + ck_assert_int_eq(ts->sock.tcp.state, TCP_SYN_RCVD); } END_TEST @@ -3325,7 +3328,6 @@ 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; @@ -3349,8 +3351,8 @@ START_TEST(test_sock_accept_bound_local_ip_no_match) 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_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + ck_assert_ptr_nonnull(parked_child(&s, listen_sd)); ck_assert_uint_eq(listener->if_idx, TEST_PRIMARY_IF); } END_TEST @@ -3359,7 +3361,6 @@ 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; @@ -3381,10 +3382,9 @@ START_TEST(test_sock_accept_starts_rto_timer) 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)]; + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); /* 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); @@ -3401,7 +3401,6 @@ START_TEST(test_sock_accept_initializes_snd_una) { struct wolfIP s; int listen_sd; - int client_sd; struct tsocket *listener; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; @@ -3430,10 +3429,9 @@ START_TEST(test_sock_accept_initializes_snd_una) listener->sock.tcp.snd_una = isn; } - 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(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); /* While in SYN_RCVD the socket's seq stays at the ISN; the final ACK * handler advances it to ISN+1 on establishment. */ ck_assert_uint_eq(accepted->sock.tcp.seq, 0x80000000U); @@ -3446,7 +3444,6 @@ START_TEST(test_sock_accept_clones_half_open_state_and_queues_synack) { struct wolfIP s; int listen_sd; - int client_sd; struct tsocket *listener; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; @@ -3527,14 +3524,12 @@ START_TEST(test_sock_accept_clones_half_open_state_and_queues_synack) pre_accept_sack_permitted = listener->sock.tcp.sack_permitted; /* Accept should fork the half-open state into a child socket and queue a SYN-ACK there. */ - 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(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); /* The child socket should inherit the negotiated transport parameters verbatim. */ ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); - ck_assert_ptr_eq(accepted->callback, test_socket_cb); - ck_assert_ptr_eq(accepted->callback_arg, cb_arg); + ck_assert_ptr_null(accepted->callback); ck_assert_uint_eq(accepted->local_ip, pre_accept_local_ip); ck_assert_uint_eq(accepted->bound_local_ip, listener->bound_local_ip); ck_assert_uint_eq(accepted->if_idx, TEST_PRIMARY_IF); @@ -3580,7 +3575,6 @@ START_TEST(test_sock_accept_synack_rto_txbuf_full_does_not_consume_retry) { struct wolfIP s; int listen_sd; - int client_sd; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; uint8_t tiny_txbuf[32]; @@ -3599,10 +3593,9 @@ START_TEST(test_sock_accept_synack_rto_txbuf_full_does_not_consume_retry) 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(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); fifo_init(&accepted->sock.tcp.txbuf, tiny_txbuf, sizeof(tiny_txbuf)); @@ -3622,7 +3615,6 @@ 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; @@ -3642,10 +3634,9 @@ START_TEST(test_sock_accept_synack_retransmission) 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(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); /* Clear tx buffer to prepare for retransmission check */ @@ -3669,7 +3660,6 @@ START_TEST(test_sock_accept_synack_window_not_scaled) { struct wolfIP s; int listen_sd; - int client_sd; struct tsocket *listener; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; @@ -3695,10 +3685,9 @@ START_TEST(test_sock_accept_synack_window_not_scaled) listener->sock.tcp.ws_enabled = 1; listener->sock.tcp.rcv_wscale = 3; - client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); - ck_assert_int_gt(client_sd, 0); - - accepted = &s.tcpsockets[SOCKET_UNMARK(client_sd)]; + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); desc = fifo_peek(&accepted->sock.tcp.txbuf); ck_assert_ptr_nonnull(desc); seg = (struct wolfIP_tcp_seg *)(accepted->txmem + desc->pos + sizeof(*desc)); @@ -3712,7 +3701,6 @@ START_TEST(test_sock_accept_ack_transitions_to_established) { struct wolfIP s; int listen_sd; - int client_sd; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; struct wolfIP_tcp_seg ack; @@ -3732,10 +3720,9 @@ START_TEST(test_sock_accept_ack_transitions_to_established) 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(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); ck_assert_uint_eq(accepted->sock.tcp.ctrl_rto_active, 1); @@ -3769,7 +3756,8 @@ START_TEST(test_sock_accept_ack_transitions_to_established) 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); + /* Still held back from accept(), so the pre-accept timeout runs. */ + ck_assert_uint_eq(accepted->sock.tcp.preaccept_timeout_active, 1); /* Should be signaled as writable */ ck_assert(accepted->events & CB_EVENT_WRITABLE); } @@ -5018,7 +5006,7 @@ END_TEST START_TEST(test_accepted_socket_destroyed_on_synrcvd_rto_expiry) { struct wolfIP s; - int listen_sd, client_sd; + int listen_sd; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; @@ -5036,10 +5024,9 @@ START_TEST(test_accepted_socket_destroyed_on_synrcvd_rto_expiry) 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(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); /* Exhaust all ctrl-RTO retries */ @@ -5956,7 +5943,7 @@ END_TEST START_TEST(test_syn_rcvd_bad_ack_sends_rst) { struct wolfIP s; - int listen_sd, client_sd; + int listen_sd; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; @@ -5975,10 +5962,9 @@ START_TEST(test_syn_rcvd_bad_ack_sends_rst) /* SYN puts listen socket in SYN_RCVD, accept creates new socket */ 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(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); /* Inject ACK with wrong ack value (99 instead of snd_una+1) */ diff --git a/src/test/unit/unit_tests_dns_dhcp.c b/src/test/unit/unit_tests_dns_dhcp.c index 8ba16055..cdc4a945 100644 --- a/src/test/unit/unit_tests_dns_dhcp.c +++ b/src/test/unit/unit_tests_dns_dhcp.c @@ -4313,16 +4313,18 @@ START_TEST(test_sock_accept_success) inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); - 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(wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen), -WOLFIP_EAGAIN); + new_ts = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(new_ts); /* 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)]; ck_assert_int_eq(listen_ts->sock.tcp.state, TCP_LISTEN); + + new_sd = accept_after_handshake(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen); + ck_assert_int_eq(SOCKET_UNMARK(new_sd), (int)(new_ts - s.tcpsockets)); + ck_assert_uint_eq(sin.sin_port, ee16(new_ts->dst_port)); } END_TEST @@ -4330,7 +4332,6 @@ START_TEST(test_sock_accept_ack_with_payload_completes_handshake) { struct wolfIP s; int listen_sd; - int new_sd; struct wolfIP_sockaddr_in sin; socklen_t alen = sizeof(sin); struct tsocket *new_ts; @@ -4359,9 +4360,9 @@ START_TEST(test_sock_accept_ack_with_payload_completes_handshake) inject_tcp_syn(&s, TEST_PRIMARY_IF, local_ip, local_port); - 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(wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen), -WOLFIP_EAGAIN); + new_ts = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(new_ts); ck_assert_int_eq(new_ts->sock.tcp.state, TCP_SYN_RCVD); memset(buf, 0, sizeof(buf)); @@ -4396,7 +4397,6 @@ START_TEST(test_sock_accept_ack_at_snd_nxt_completes_handshake) { struct wolfIP s; int listen_sd; - int new_sd; struct wolfIP_sockaddr_in sin; socklen_t alen = sizeof(sin); struct tsocket *new_ts; @@ -4421,9 +4421,9 @@ START_TEST(test_sock_accept_ack_at_snd_nxt_completes_handshake) inject_tcp_syn(&s, TEST_PRIMARY_IF, local_ip, local_port); - 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(wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen), -WOLFIP_EAGAIN); + new_ts = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(new_ts); ck_assert_int_eq(new_ts->sock.tcp.state, TCP_SYN_RCVD); memset(&ackseg, 0, sizeof(ackseg)); @@ -4453,7 +4453,6 @@ START_TEST(test_sock_accept_ack_psh_with_payload_completes_handshake) { struct wolfIP s; int listen_sd; - int new_sd; struct wolfIP_sockaddr_in sin; socklen_t alen = sizeof(sin); struct tsocket *new_ts; @@ -4482,9 +4481,9 @@ START_TEST(test_sock_accept_ack_psh_with_payload_completes_handshake) inject_tcp_syn(&s, TEST_PRIMARY_IF, local_ip, local_port); - 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(wolfIP_sock_accept(&s, listen_sd, (struct wolfIP_sockaddr *)&sin, &alen), -WOLFIP_EAGAIN); + new_ts = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(new_ts); ck_assert_int_eq(new_ts->sock.tcp.state, TCP_SYN_RCVD); memset(buf, 0, sizeof(buf)); @@ -6307,7 +6306,7 @@ START_TEST(test_sock_close_tcp_fin_wait_1) 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(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); } END_TEST @@ -6404,7 +6403,7 @@ START_TEST(test_sock_close_tcp_fin_wait_1_repeated_close_keeps_fin_wait_2_path) ts->sock.tcp.tmr_rto = timers_binheap_insert(&s.timers, tmr); ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER); - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); memset(&ackseg, 0, sizeof(ackseg)); @@ -6423,7 +6422,7 @@ START_TEST(test_sock_close_tcp_fin_wait_1_repeated_close_keeps_fin_wait_2_path) timeout_at = find_timer_expiry(&s, ts->sock.tcp.tmr_rto); ck_assert_uint_eq(timeout_at, s.last_tick + TCP_FIN_WAIT_2_TIMEOUT_MS); - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_2); ck_assert_int_ne(ts->sock.tcp.tmr_rto, NO_TIMER); ck_assert_uint_eq(ts->sock.tcp.fin_wait_2_timeout_active, 1); diff --git a/src/test/unit/unit_tests_poll_dispatcher.c b/src/test/unit/unit_tests_poll_dispatcher.c index e1dce78b..e40060ae 100644 --- a/src/test/unit/unit_tests_poll_dispatcher.c +++ b/src/test/unit/unit_tests_poll_dispatcher.c @@ -530,7 +530,7 @@ START_TEST(test_poll_raw_socket_callback_reraised_event_survives) /* The reopened socket reuses slot 0; the event raised during dispatch * must not be wiped by the old iteration's clear. */ ck_assert_int_ge(f10259_reopen_fd, 0); - ck_assert_int_eq(f10259_reopen_fd, raw_sd); + ck_assert_int_eq(SOCKET_UNMARK(f10259_reopen_fd), SOCKET_UNMARK(raw_sd)); ck_assert(s.rawsockets[SOCKET_UNMARK(f10259_reopen_fd)].events & CB_EVENT_WRITABLE); @@ -582,7 +582,7 @@ START_TEST(test_poll_packet_socket_callback_reraised_event_survives) (void)wolfIP_poll(&s, 100); ck_assert_int_ge(f10259_pkt_reopen_fd, 0); - ck_assert_int_eq(f10259_pkt_reopen_fd, pkt_sd); + ck_assert_int_eq(SOCKET_UNMARK(f10259_pkt_reopen_fd), SOCKET_UNMARK(pkt_sd)); ck_assert(s.packetsockets[SOCKET_UNMARK(f10259_pkt_reopen_fd)].events & CB_EVENT_WRITABLE); @@ -1893,6 +1893,38 @@ START_TEST(test_poll_returns_zero_with_loopback_frame_queued) END_TEST #endif +START_TEST(test_poll_rebased_timer_stays_within_max_wait) +{ + struct wolfIP s; + struct wolfIP_timer tmr = {0}; + const uint64_t base = 0x100000000ULL; + + wolfIP_init(&s); + mock_link_init(&s); + (void)wolfIP_poll(&s, base + 20000); + tmr.expires = base + 25000; + tmr.cb = test_timer_cb; + ck_assert_uint_ne(timers_binheap_insert(&s.timers, tmr), 0U); + + /* The clock steps back: the timer is rebased to a 32-bit value. */ + ck_assert_int_eq(wolfIP_poll(&s, base + 10000), WOLFIP_POLL_MAX_WAIT_MS); +} +END_TEST + +START_TEST(test_poll_by_rebased_deadline_keeps_earlier_one) +{ + struct wolfIP s; + const uint64_t now = 0x100000000ULL + 10000; + + wolfIP_init(&s); + s.poll_next_at = now + WOLFIP_POLL_MAX_WAIT_MS; + wolfIP_poll_by(&s, now); + /* A rebased timer 15 s ahead is smaller as a raw 64-bit value. */ + wolfIP_poll_by(&s, 25000); + ck_assert_uint_eq(s.poll_next_at, now); +} +END_TEST + START_TEST(test_poll_returns_zero_while_flush_events_undelivered) { struct wolfIP s; diff --git a/src/test/unit/unit_tests_proto.c b/src/test/unit/unit_tests_proto.c index 2b916a2e..81a9714d 100644 --- a/src/test/unit/unit_tests_proto.c +++ b/src/test/unit/unit_tests_proto.c @@ -705,19 +705,19 @@ START_TEST(test_tcp_sock_close_state_transitions) ck_assert_int_gt(sd, 0); ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; ts->sock.tcp.state = TCP_ESTABLISHED; - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); ts->sock.tcp.state = TCP_CLOSE_WAIT; - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_LAST_ACK); ts->sock.tcp.state = TCP_FIN_WAIT_2; - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_2); ts->sock.tcp.state = TCP_CLOSING; - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_CLOSING); ts->sock.tcp.state = TCP_LISTEN; @@ -2132,7 +2132,7 @@ START_TEST(test_tcp_persist_close_resets_retry_budget) (TCP_FLAG_ACK | TCP_FLAG_PSH)), 0); s.last_tick = 500; - ck_assert_int_eq(wolfIP_sock_close(&s, tcp_sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, tcp_sd), 0); ck_assert_uint_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); /* Two unanswered teardown probes must not exhaust a fresh budget. */ @@ -5512,7 +5512,6 @@ START_TEST(test_tcp_listen_accepts_bound_interface) const ip4 secondary_ip = 0xC0A80101U; const uint16_t listen_port = 23456; int listen_fd; - int client_fd; struct wolfIP_sockaddr_in addr; struct tsocket *listener; struct tsocket *client; @@ -5536,9 +5535,9 @@ START_TEST(test_tcp_listen_accepts_bound_interface) ck_assert_uint_eq(listener->local_ip, secondary_ip); ck_assert_uint_eq(listener->if_idx, TEST_SECOND_IF); - client_fd = wolfIP_sock_accept(&s, listen_fd, NULL, NULL); - ck_assert_int_ge(client_fd, 0); - client = &s.tcpsockets[SOCKET_UNMARK(client_fd)]; + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_fd, NULL, NULL), -WOLFIP_EAGAIN); + client = parked_child(&s, listen_fd); + ck_assert_ptr_nonnull(client); ck_assert_uint_eq(client->local_ip, secondary_ip); ck_assert_uint_eq(client->bound_local_ip, secondary_ip); /* After accept(), socket stays in SYN_RCVD until final ACK. */ @@ -5600,7 +5599,6 @@ START_TEST(test_tcp_listen_accepts_any_interface) const ip4 secondary_ip = 0xC0A80105U; const uint16_t listen_port = 34567; int listen_fd; - int client_fd; struct wolfIP_sockaddr_in addr; struct tsocket *listener; struct tsocket *client; @@ -5624,9 +5622,9 @@ START_TEST(test_tcp_listen_accepts_any_interface) ck_assert_uint_eq(listener->local_ip, secondary_ip); ck_assert_uint_eq(listener->if_idx, TEST_SECOND_IF); - client_fd = wolfIP_sock_accept(&s, listen_fd, NULL, NULL); - ck_assert_int_ge(client_fd, 0); - client = &s.tcpsockets[SOCKET_UNMARK(client_fd)]; + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_fd, NULL, NULL), -WOLFIP_EAGAIN); + client = parked_child(&s, listen_fd); + ck_assert_ptr_nonnull(client); ck_assert_uint_eq(client->local_ip, secondary_ip); /* After accept(), socket stays in SYN_RCVD until final ACK. */ ck_assert_int_eq(client->sock.tcp.state, TCP_SYN_RCVD); diff --git a/src/test/unit/unit_tests_socket_api_arms.c b/src/test/unit/unit_tests_socket_api_arms.c index be1f441f..b6b9e4a4 100644 --- a/src/test/unit/unit_tests_socket_api_arms.c +++ b/src/test/unit/unit_tests_socket_api_arms.c @@ -2026,3 +2026,167 @@ START_TEST(test_notify_loopback_null_stack_no_crash) wolfIP_notify_loopback_space_available(NULL); } END_TEST + +START_TEST(test_sock_fd_reissued_slot_rejects_old_descriptor) +{ + struct wolfIP s; + struct wolfIP_sockaddr_in sin; + int old_sd, new_sd; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + + old_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_eq(old_sd, MARK_TCP_SOCKET | 0); + ck_assert_int_eq(wolfIP_sock_close(&s, old_sd), 0); + new_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_eq(SOCKET_UNMARK(new_sd), SOCKET_UNMARK(old_sd)); + ck_assert_int_ne(new_sd, old_sd); + + wolfIP_register_callback(&s, new_sd, test_socket_cb, (void *)0x42); + wolfIP_register_callback(&s, old_sd, NULL, NULL); + ck_assert_ptr_eq(s.tcpsockets[0].callback, test_socket_cb); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = ee16(8080); + ck_assert_int_eq(wolfIP_sock_bind(&s, old_sd, (struct wolfIP_sockaddr *)&sin, + sizeof(sin)), -WOLFIP_EBADF); + ck_assert_int_eq(wolfIP_sock_listen(&s, old_sd, 1), -WOLFIP_EBADF); + ck_assert_int_eq(wolfIP_sock_abort(&s, old_sd), -WOLFIP_EBADF); + ck_assert_int_eq(wolfIP_sock_close(&s, old_sd), -WOLFIP_EBADF); + ck_assert_int_eq(s.tcpsockets[0].proto, WI_IPPROTO_TCP); + + ck_assert_int_eq(wolfIP_sock_bind(&s, new_sd, (struct wolfIP_sockaddr *)&sin, + sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, new_sd, 1), 0); + ck_assert_int_eq(wolfIP_sock_close(&s, new_sd), 0); +} +END_TEST + +START_TEST(test_sock_fd_slot_freed_by_stack_gets_new_generation) +{ + struct wolfIP s; + int old_sd, new_sd; + + wolfIP_init(&s); + mock_link_init(&s); + + /* The stack frees the slot while the application still holds old_sd. */ + old_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_ge(old_sd, 0); + close_socket(&s.tcpsockets[SOCKET_UNMARK(old_sd)]); + take_tcp_slots_except(&s, SOCKET_UNMARK(old_sd)); + new_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_eq(SOCKET_UNMARK(new_sd), SOCKET_UNMARK(old_sd)); + ck_assert_int_ne(new_sd, old_sd); + ck_assert_int_eq(wolfIP_sock_close(&s, old_sd), -WOLFIP_EBADF); + ck_assert_int_eq(s.tcpsockets[SOCKET_UNMARK(new_sd)].proto, WI_IPPROTO_TCP); + + /* Closing a descriptor whose slot was freed but not reissued still works. */ + close_socket(&s.tcpsockets[SOCKET_UNMARK(new_sd)]); + ck_assert_int_eq(wolfIP_sock_close(&s, new_sd), 0); + ck_assert_int_eq(wolfIP_sock_close(&s, new_sd), -WOLFIP_EBADF); +} +END_TEST + +START_TEST(test_sock_fd_callback_carries_generation) +{ + struct wolfIP s; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_DGRAM, WI_IPPROTO_UDP); + ck_assert_int_ne(sd, MARK_UDP_SOCKET | 0); + wolfIP_register_callback(&s, sd, test_socket_cb, NULL); + s.udpsockets[SOCKET_UNMARK(sd)].events = CB_EVENT_READABLE; + socket_cb_calls = 0; + socket_cb_last_fd = -1; + (void)wolfIP_poll(&s, 100); + ck_assert_int_eq(socket_cb_calls, 1); + ck_assert_int_eq(socket_cb_last_fd, sd); +} +END_TEST + +START_TEST(test_sock_fd_internal_reuse_retires_open_descriptor) +{ + struct wolfIP s; + int sd; + struct tsocket *t; + + wolfIP_init(&s); + mock_link_init(&s); + + /* The stack frees the slot and reuses it for a socket of its own. */ + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_ge(sd, 0); + close_socket(&s.tcpsockets[SOCKET_UNMARK(sd)]); + take_tcp_slots_except(&s, SOCKET_UNMARK(sd)); + t = tcp_new_socket(&s); + ck_assert_ptr_eq(t, &s.tcpsockets[SOCKET_UNMARK(sd)]); + t->sock.tcp.state = TCP_ESTABLISHED; + ck_assert_int_eq(wolfIP_sock_abort(&s, sd), -WOLFIP_EBADF); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EBADF); + ck_assert_int_eq(t->proto, WI_IPPROTO_TCP); +} +END_TEST + +START_TEST(test_sock_fd_held_slot_is_reused_last) +{ + struct wolfIP s; + int held, other; + + wolfIP_init(&s); + mock_link_init(&s); + + /* The stack frees a slot the application still holds a descriptor to. */ + held = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + close_socket(&s.tcpsockets[SOCKET_UNMARK(held)]); + other = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_ne(SOCKET_UNMARK(other), SOCKET_UNMARK(held)); + ck_assert_int_eq(wolfIP_sock_close(&s, held), 0); + ck_assert_int_eq(wolfIP_sock_close(&s, other), 0); +} +END_TEST + +START_TEST(test_sock_fd_reused_held_slot_drops_old_callback) +{ + struct wolfIP s; + int held, sd; + + wolfIP_init(&s); + mock_link_init(&s); + + held = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + close_socket(&s.tcpsockets[SOCKET_UNMARK(held)]); + /* The descriptor is not stale yet, so this lands in the free slot. */ + wolfIP_register_callback(&s, held, test_socket_cb, (void *)0x42); + take_tcp_slots_except(&s, SOCKET_UNMARK(held)); + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_eq(SOCKET_UNMARK(sd), SOCKET_UNMARK(held)); + ck_assert_ptr_null(s.tcpsockets[SOCKET_UNMARK(sd)].callback); + ck_assert_ptr_null(s.tcpsockets[SOCKET_UNMARK(sd)].callback_arg); +} +END_TEST + +START_TEST(test_sock_fd_generation_wraps_positive) +{ + struct wolfIP s; + int sd; + + wolfIP_init(&s); + mock_link_init(&s); + s.tcp_gen[0].gen = SOCKET_GEN_MASK; + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, 0); + ck_assert_int_gt(sd, 0); + ck_assert_uint_eq(SOCKET_GEN(sd), SOCKET_GEN_MASK); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_uint_eq(s.tcp_gen[0].gen, 0); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EBADF); +} +END_TEST diff --git a/src/test/unit/unit_tests_tcp_flow.c b/src/test/unit/unit_tests_tcp_flow.c index a6119d27..42607938 100644 --- a/src/test/unit/unit_tests_tcp_flow.c +++ b/src/test/unit/unit_tests_tcp_flow.c @@ -4872,7 +4872,6 @@ START_TEST(test_tcp_input_listen_accept_final_ack_does_not_send_rst) { struct wolfIP s; int listen_sd; - int client_sd; struct tsocket *listen_ts; struct tsocket *client_ts; struct wolfIP_sockaddr_in sin; @@ -4897,10 +4896,9 @@ START_TEST(test_tcp_input_listen_accept_final_ack_does_not_send_rst) listen_ts = &s.tcpsockets[SOCKET_UNMARK(listen_sd)]; ck_assert_int_eq(listen_ts->sock.tcp.state, TCP_SYN_RCVD); - client_sd = wolfIP_sock_accept(&s, listen_sd, NULL, NULL); - ck_assert_int_gt(client_sd, 0); - - client_ts = &s.tcpsockets[SOCKET_UNMARK(client_sd)]; + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, NULL), -WOLFIP_EAGAIN); + client_ts = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(client_ts); ck_assert_int_eq(listen_ts->sock.tcp.state, TCP_LISTEN); ck_assert_int_eq(client_ts->sock.tcp.state, TCP_SYN_RCVD); @@ -5911,7 +5909,7 @@ START_TEST(test_tcp_listener_closed_while_pending_is_not_readable) ck_assert_int_eq(lsn->sock.tcp.state, TCP_ESTABLISHED); ck_assert_int_eq(wolfIP_sock_can_read(&s, fd), 1); - ck_assert_int_eq(wolfIP_sock_close(&s, fd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, fd), 0); ck_assert_int_eq(lsn->sock.tcp.state, TCP_FIN_WAIT_1); ck_assert_int_eq(lsn->sock.tcp.is_listener, 1); @@ -6017,14 +6015,15 @@ END_TEST /* With no socket free, accept() cannot hand the connection off and drops it; * the peer must be reset rather than left talking to a reverted listener. */ -START_TEST(test_tcp_listener_preaccept_accept_no_socket_resets_peer) +START_TEST(test_tcp_listener_preaccept_accept_no_socket_keeps_connection) { struct wolfIP s; int fd; + int sd; + int last = -1; struct tsocket *lsn; struct wolfIP_sockaddr_in peer; socklen_t peer_len = sizeof(peer); - const struct wolfIP_tcp_seg *out; uint32_t frames_before; wolfIP_init(&s); @@ -6038,19 +6037,23 @@ START_TEST(test_tcp_listener_preaccept_accept_no_socket_resets_peer) llk_complete_handshake(&s, lsn, LLK_ATT_IP, 41000, 1); ck_assert_int_eq(lsn->sock.tcp.state, TCP_ESTABLISHED); - while (wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, - WI_IPPROTO_TCP) > 0) - ; + while ((sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, + WI_IPPROTO_TCP)) > 0) + last = sd; + ck_assert_int_gt(last, 0); + /* No slot for the child: the connection waits on the listener. */ memset(&peer, 0, sizeof(peer)); frames_before = last_frame_sent_count; ck_assert_int_eq(wolfIP_sock_accept(&s, fd, - (struct wolfIP_sockaddr *)&peer, &peer_len), -1); - ck_assert_uint_gt(last_frame_sent_count, frames_before); - out = llk_last_tcp(); - ck_assert_ptr_nonnull(out); - ck_assert_uint_eq(out->flags, TCP_FLAG_RST | TCP_FLAG_ACK); - ck_assert_uint_eq(ee16(out->dst_port), 41000); + (struct wolfIP_sockaddr *)&peer, &peer_len), -WOLFIP_EAGAIN); + ck_assert_uint_eq(last_frame_sent_count, frames_before); + ck_assert_int_eq(lsn->sock.tcp.state, TCP_ESTABLISHED); + + ck_assert_int_eq(wolfIP_sock_close(&s, last), 0); + ck_assert_int_ge(wolfIP_sock_accept(&s, fd, + (struct wolfIP_sockaddr *)&peer, &peer_len), 0); + ck_assert_uint_eq(ee16(peer.sin_port), 41000); ck_assert_int_eq(lsn->sock.tcp.state, TCP_LISTEN); } END_TEST @@ -6120,7 +6123,7 @@ static void llk_closed_listener_rst_frees(int peer_fin) ck_assert_int_eq(lsn->sock.tcp.state, TCP_CLOSE_WAIT); rst_seq = 3; } - ck_assert_int_eq(wolfIP_sock_close(&s, fd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, fd), 0); ck_assert_int_eq(lsn->sock.tcp.state, peer_fin ? TCP_LAST_ACK : TCP_FIN_WAIT_1); (void)wolfIP_poll(&s, 3); @@ -6229,7 +6232,7 @@ START_TEST(test_sock_abort_established_resets_and_frees) END_TEST /* The case abort exists for: a graceful close that cannot finish. */ -START_TEST(test_sock_abort_after_close_eagain_frees) +START_TEST(test_sock_abort_after_close_frees) { struct wolfIP s; int sd; @@ -6240,7 +6243,7 @@ START_TEST(test_sock_abort_after_close_eagain_frees) sd = llk_accepted_child(&s); ck_assert_int_ge(sd, 0); ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); last = ts->sock.tcp.last; @@ -6291,7 +6294,7 @@ static int llk_child_fin_sent(struct wolfIP *s, uint32_t *last) sd = llk_accepted_child(s); ck_assert_int_ge(sd, 0); ts = &s->tcpsockets[SOCKET_UNMARK(sd)]; - ck_assert_int_eq(wolfIP_sock_close(s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(s, sd), 0); (void)wolfIP_poll(s, 3); ck_assert_ptr_null(fifo_peek(&ts->sock.tcp.txbuf)); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); @@ -6340,12 +6343,12 @@ START_TEST(test_sock_abort_fin_wait_2_rst_covers_acked_fin) } END_TEST -START_TEST(test_sock_abort_syn_rcvd_rst_covers_syn) +START_TEST(test_listener_close_resets_parked_syn_rcvd_child) { struct wolfIP s; int fd; - int sd; struct tsocket *lsn; + struct tsocket *child; uint32_t isn; const struct wolfIP_tcp_seg *out; @@ -6359,12 +6362,13 @@ START_TEST(test_sock_abort_syn_rcvd_rst_covers_syn) ck_assert_int_eq(lsn->sock.tcp.state, TCP_SYN_RCVD); isn = lsn->sock.tcp.seq; - sd = wolfIP_sock_accept(&s, fd, NULL, NULL); - ck_assert_int_ge(sd, 0); - ck_assert_int_eq(s.tcpsockets[SOCKET_UNMARK(sd)].sock.tcp.state, - TCP_SYN_RCVD); + ck_assert_int_eq(wolfIP_sock_accept(&s, fd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, fd); + ck_assert_ptr_nonnull(child); + ck_assert_int_eq(child->sock.tcp.state, TCP_SYN_RCVD); - ck_assert_int_eq(wolfIP_sock_abort(&s, sd), 0); + ck_assert_int_eq(wolfIP_sock_close(&s, fd), 0); + ck_assert_int_eq(child->proto, 0); out = llk_last_tcp(); ck_assert_ptr_nonnull(out); ck_assert_uint_eq(out->flags, TCP_FLAG_RST | TCP_FLAG_ACK); @@ -6482,7 +6486,7 @@ START_TEST(test_sock_abort_fin_behind_unacked_data_covers_fin) ck_assert_int_eq(wolfIP_sock_send(&s, sd, payload, sizeof(payload), 0), (int)sizeof(payload)); (void)wolfIP_poll(&s, 2); - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); last = ts->sock.tcp.last; ck_assert_uint_eq(last, ts->sock.tcp.snd_una + sizeof(payload)); (void)wolfIP_poll(&s, 3); @@ -6564,7 +6568,7 @@ START_TEST(test_sock_abort_last_ack_sends_nothing) uint32_t frames_before; sd = llk_child_close_wait(&s); - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(s.tcpsockets[SOCKET_UNMARK(sd)].sock.tcp.state, TCP_LAST_ACK); frames_before = last_frame_sent_count; @@ -6578,6 +6582,529 @@ START_TEST(test_sock_abort_last_ack_sends_nothing) } END_TEST +START_TEST(test_sock_close_repeated_in_last_ack_keeps_socket) +{ + struct wolfIP s; + int sd; + struct tsocket *ts; + uint32_t frames_before; + + sd = llk_child_close_wait(&s); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_int_eq(ts->sock.tcp.state, TCP_LAST_ACK); + + frames_before = last_frame_sent_count; + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_int_eq(ts->sock.tcp.state, TCP_LAST_ACK); + ck_assert_int_eq(ts->proto, WI_IPPROTO_TCP); + ck_assert_uint_ne(ts->sock.tcp.tmr_rto, NO_TIMER); + ck_assert_uint_eq(last_frame_sent_count, frames_before); +} +END_TEST + +START_TEST(test_sock_close_repeated_in_time_wait_keeps_socket) +{ + struct wolfIP s; + int sd; + struct tsocket *ts; + uint16_t gen; + + sd = llk_accepted_child(&s); + ck_assert_int_ge(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ts->sock.tcp.state = TCP_TIME_WAIT; + gen = s.tcp_gen[SOCKET_UNMARK(sd)].gen; + + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_int_eq(ts->sock.tcp.state, TCP_TIME_WAIT); + ck_assert_int_eq(ts->proto, WI_IPPROTO_TCP); + ck_assert_uint_eq(s.tcp_gen[SOCKET_UNMARK(sd)].gen, gen); + ck_assert_int_eq(wolfIP_sock_abort(&s, sd), 0); + ck_assert_int_eq(ts->proto, 0); +} +END_TEST + +START_TEST(test_sock_abort_after_close_stale_once_slot_reused) +{ + struct wolfIP s; + int sd, new_sd; + struct tsocket *ts; + + sd = llk_accepted_child(&s); + ck_assert_int_ge(sd, 0); + ts = &s.tcpsockets[SOCKET_UNMARK(sd)]; + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); + + /* The stack finishes the close on its own and the slot is reused. */ + close_socket(ts); + take_tcp_slots_except(&s, SOCKET_UNMARK(sd)); + new_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_eq(SOCKET_UNMARK(new_sd), SOCKET_UNMARK(sd)); + ts->sock.tcp.state = TCP_ESTABLISHED; + ck_assert_int_eq(wolfIP_sock_abort(&s, sd), -WOLFIP_EBADF); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EBADF); + ck_assert_int_eq(ts->sock.tcp.state, TCP_ESTABLISHED); + ck_assert_int_eq(ts->proto, WI_IPPROTO_TCP); +} +END_TEST + +START_TEST(test_tcp_full_table_reclaims_time_wait_first) +{ + struct wolfIP s; + int sd[MAX_TCPSOCKETS]; + int new_sd; + int i; + uint32_t frames_before; + + wolfIP_init(&s); + mock_link_init(&s); + for (i = 0; i < MAX_TCPSOCKETS; i++) { + sd[i] = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_ge(sd[i], 0); + s.tcpsockets[i].sock.tcp.state = TCP_ESTABLISHED; + } + s.tcpsockets[1].sock.tcp.state = TCP_FIN_WAIT_2; + s.tcpsockets[2].sock.tcp.state = TCP_TIME_WAIT; + + frames_before = last_frame_sent_count; + new_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_eq(SOCKET_UNMARK(new_sd), 2); + ck_assert_int_eq(s.tcpsockets[2].sock.tcp.state, TCP_CLOSED); + ck_assert_int_eq(s.tcpsockets[1].sock.tcp.state, TCP_FIN_WAIT_2); + ck_assert_uint_eq(last_frame_sent_count, frames_before); + ck_assert_int_eq(wolfIP_sock_close(&s, sd[2]), -WOLFIP_EBADF); +} +END_TEST + +START_TEST(test_tcp_full_table_reclaims_fin_wait_2_with_rst) +{ + struct wolfIP s; + int sd, new_sd; + int i; + const struct wolfIP_tcp_seg *out; + + /* The listener and the accepted child take two slots. */ + sd = llk_accepted_child(&s); + ck_assert_int_ge(sd, 0); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + s.tcpsockets[SOCKET_UNMARK(sd)].sock.tcp.state = TCP_FIN_WAIT_2; + for (i = 2; i < MAX_TCPSOCKETS; i++) + ck_assert_int_ge(wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, + WI_IPPROTO_TCP), 0); + + new_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_eq(SOCKET_UNMARK(new_sd), SOCKET_UNMARK(sd)); + out = llk_last_tcp(); + ck_assert_ptr_nonnull(out); + ck_assert_uint_eq(out->flags, TCP_FLAG_RST | TCP_FLAG_ACK); + ck_assert_uint_eq(ee16(out->dst_port), 41000); + ck_assert_int_eq(wolfIP_sock_abort(&s, sd), -WOLFIP_EBADF); + new_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_eq(new_sd, -1); +} +END_TEST + +START_TEST(test_tcp_full_table_keeps_open_sockets) +{ + struct wolfIP s; + int i; + + wolfIP_init(&s); + mock_link_init(&s); + for (i = 0; i < MAX_TCPSOCKETS; i++) { + ck_assert_int_ge(wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, + WI_IPPROTO_TCP), 0); + s.tcpsockets[i].sock.tcp.state = (i & 1) ? TCP_CLOSE_WAIT : TCP_ESTABLISHED; + } + ck_assert_int_eq(wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, + WI_IPPROTO_TCP), -1); + for (i = 0; i < MAX_TCPSOCKETS; i++) + ck_assert_int_eq(s.tcpsockets[i].proto, WI_IPPROTO_TCP); +} +END_TEST + +/* A socket still sending its FIN goes only when nothing cheaper is left. */ +static void full_table_reclaims_closing(int peer_fin, uint8_t expect_flags) +{ + struct wolfIP s; + int sd, new_sd; + int i; + uint32_t frames_before; + const struct wolfIP_tcp_seg *out; + + sd = peer_fin ? llk_child_close_wait(&s) : llk_accepted_child(&s); + ck_assert_int_ge(sd, 0); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); + ck_assert_int_eq(s.tcpsockets[SOCKET_UNMARK(sd)].sock.tcp.state, + peer_fin ? TCP_LAST_ACK : TCP_FIN_WAIT_1); + for (i = 2; i < MAX_TCPSOCKETS; i++) + ck_assert_int_ge(wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, + WI_IPPROTO_TCP), 0); + + frames_before = last_frame_sent_count; + new_sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_eq(SOCKET_UNMARK(new_sd), SOCKET_UNMARK(sd)); + if (expect_flags != 0) { + out = llk_last_tcp(); + ck_assert_ptr_nonnull(out); + ck_assert_uint_eq(out->flags, expect_flags); + ck_assert_uint_eq(ee16(out->dst_port), 41000); + } + else { + ck_assert_uint_eq(last_frame_sent_count, frames_before); + } +} + +START_TEST(test_tcp_full_table_reclaims_fin_wait_1_with_rst) +{ + full_table_reclaims_closing(0, TCP_FLAG_RST | TCP_FLAG_ACK); +} +END_TEST + +START_TEST(test_tcp_full_table_reclaims_last_ack_silently) +{ + full_table_reclaims_closing(1, 0); +} +END_TEST + +static int park_listener(struct wolfIP *s) +{ + struct wolfIP_sockaddr_in sin; + int sd; + + wolfIP_init(s); + mock_link_init(s); + wolfIP_ipconfig_set(s, 0x0A000001U, 0xFFFFFF00U, 0); + sd = wolfIP_sock_socket(s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_ge(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, sd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_listen(s, sd, 1), 0); + wolfIP_register_callback(s, sd, test_socket_cb, (void *)0x42); + llk_keep_arp_fresh(s, 0x0A0000A1U); + return sd; +} + +static void park_final_ack(struct wolfIP *s, struct tsocket *t, uint8_t flags) +{ + inject_tcp_segment(s, t->if_idx, t->remote_ip, t->local_ip, t->dst_port, + t->src_port, t->sock.tcp.ack, t->sock.tcp.seq + 1, flags); +} + +START_TEST(test_accept_parks_child_until_established) +{ + struct wolfIP s; + int lsd, sd; + struct tsocket *lsn, *child; + + lsd = park_listener(&s); + lsn = &s.tcpsockets[SOCKET_UNMARK(lsd)]; + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_can_read(&s, lsd), 1); + + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + ck_assert_ptr_null(child->callback); + ck_assert_int_eq(lsn->sock.tcp.state, TCP_LISTEN); + ck_assert_int_eq(wolfIP_sock_can_read(&s, lsd), 0); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + + lsn->events = 0; + park_final_ack(&s, child, TCP_FLAG_ACK); + ck_assert_int_eq(child->sock.tcp.state, TCP_ESTABLISHED); + ck_assert_uint_ne(lsn->events & CB_EVENT_READABLE, 0); + ck_assert_int_eq(wolfIP_sock_can_read(&s, lsd), 1); + + sd = wolfIP_sock_accept(&s, lsd, NULL, NULL); + ck_assert_int_ge(sd, 0); + ck_assert_ptr_eq(&s.tcpsockets[SOCKET_UNMARK(sd)], child); + ck_assert_uint_eq(child->sock.tcp.parked, 0); + ck_assert_ptr_eq(child->callback, test_socket_cb); + ck_assert_ptr_eq(child->callback_arg, (void *)0x42); + ck_assert_uint_eq(child->events, CB_EVENT_WRITABLE); + ck_assert_int_eq(wolfIP_sock_can_read(&s, lsd), 0); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + + /* Accepted in time: the pre-accept timeout no longer applies. */ + ck_assert_uint_eq(child->sock.tcp.preaccept_timeout_active, 0); + (void)wolfIP_poll(&s, s.last_tick + TCP_PREACCEPT_TIMEOUT_MS + 10); + ck_assert_int_eq(child->proto, WI_IPPROTO_TCP); + ck_assert_int_eq(child->sock.tcp.state, TCP_ESTABLISHED); +} +END_TEST + +START_TEST(test_accept_reannounces_second_ready_child) +{ + struct wolfIP s; + int lsd, sd; + struct tsocket *lsn, *a, *b; + + lsd = park_listener(&s); + lsn = &s.tcpsockets[SOCKET_UNMARK(lsd)]; + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + a = parked_child(&s, lsd); + ck_assert_ptr_nonnull(a); + inject_tcp_segment(&s, TEST_PRIMARY_IF, 0x0A0000A1U, 0x0A000001U, 40001, 1234, + 7, 0, TCP_FLAG_SYN); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + b = (a == &s.tcpsockets[2]) ? &s.tcpsockets[3] : &s.tcpsockets[2]; + ck_assert_uint_eq(b->sock.tcp.parked, 1); + + /* Both complete before the application runs: one READABLE for two. */ + park_final_ack(&s, a, TCP_FLAG_ACK); + park_final_ack(&s, b, TCP_FLAG_ACK); + lsn->events = 0; + sd = wolfIP_sock_accept(&s, lsd, NULL, NULL); + ck_assert_int_ge(sd, 0); + ck_assert_uint_ne(lsn->events & CB_EVENT_READABLE, 0); + ck_assert_uint_eq(s.tcpsockets[SOCKET_UNMARK(sd)].events, CB_EVENT_WRITABLE); + + lsn->events = 0; + sd = wolfIP_sock_accept(&s, lsd, NULL, NULL); + ck_assert_int_ge(sd, 0); + ck_assert_uint_eq(lsn->events & CB_EVENT_READABLE, 0); +} +END_TEST + +START_TEST(test_parked_child_handed_out_with_wide_listener_generation) +{ + struct wolfIP s; + struct wolfIP_sockaddr_in sin; + int lsd, sd; + struct tsocket *child; + + wolfIP_init(&s); + mock_link_init(&s); + wolfIP_ipconfig_set(&s, 0x0A000001U, 0xFFFFFF00U, 0); + llk_keep_arp_fresh(&s, 0x0A0000A1U); + /* The listener's slot has been reused well past 8 bits of generation. */ + s.tcp_gen[0].gen = 0x1234; + lsd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_uint_eq(SOCKET_GEN(lsd), 0x1234); + 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, lsd, (struct wolfIP_sockaddr *)&sin, sizeof(sin)), 0); + ck_assert_int_eq(wolfIP_sock_listen(&s, lsd, 1), 0); + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + park_final_ack(&s, child, TCP_FLAG_ACK); + sd = wolfIP_sock_accept(&s, lsd, NULL, NULL); + ck_assert_ptr_eq(&s.tcpsockets[SOCKET_UNMARK(sd)], child); +} +END_TEST + +START_TEST(test_parked_child_not_accepted_is_reset_after_timeout) +{ + struct wolfIP s; + int lsd; + struct tsocket *child; + const struct wolfIP_tcp_seg *out; + + lsd = park_listener(&s); + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + park_final_ack(&s, child, TCP_FLAG_ACK); + ck_assert_int_eq(child->sock.tcp.state, TCP_ESTABLISHED); + ck_assert_uint_eq(child->sock.tcp.preaccept_timeout_active, 1); + + (void)wolfIP_poll(&s, s.last_tick + TCP_PREACCEPT_TIMEOUT_MS + 10); + ck_assert_int_eq(child->proto, 0); + out = llk_last_tcp(); + ck_assert_ptr_nonnull(out); + ck_assert_uint_eq(out->flags, TCP_FLAG_RST | TCP_FLAG_ACK); + ck_assert_uint_eq(ee16(out->dst_port), 40000); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); +} +END_TEST + +static int stale_cb_calls; + +static void stale_cb(int fd, uint16_t events, void *arg) +{ + (void)fd; (void)events; (void)arg; + stale_cb_calls++; +} + +START_TEST(test_parked_child_in_held_slot_stays_silent) +{ + struct wolfIP s; + int lsd, sd; + struct tsocket *child; + + lsd = park_listener(&s); + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + close_socket(&s.tcpsockets[SOCKET_UNMARK(sd)]); + wolfIP_register_callback(&s, sd, stale_cb, NULL); + take_tcp_slots_except(&s, SOCKET_UNMARK(sd)); + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_eq(child, &s.tcpsockets[SOCKET_UNMARK(sd)]); + ck_assert_ptr_null(child->callback); + + stale_cb_calls = 0; + park_final_ack(&s, child, TCP_FLAG_RST | TCP_FLAG_ACK); + (void)wolfIP_poll(&s, 100); + ck_assert_int_eq(child->proto, 0); + ck_assert_int_eq(stale_cb_calls, 0); +} +END_TEST + +START_TEST(test_parked_child_in_freed_slot_retires_old_descriptor) +{ + struct wolfIP s; + int lsd, sd; + struct tsocket *child; + + lsd = park_listener(&s); + /* The stack frees a slot whose descriptor the application still holds. */ + sd = wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, WI_IPPROTO_TCP); + ck_assert_int_ge(sd, 0); + close_socket(&s.tcpsockets[SOCKET_UNMARK(sd)]); + take_tcp_slots_except(&s, SOCKET_UNMARK(sd)); + + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_eq(child, &s.tcpsockets[SOCKET_UNMARK(sd)]); + ck_assert_int_eq(wolfIP_sock_abort(&s, sd), -WOLFIP_EBADF); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EBADF); + ck_assert_int_eq(child->sock.tcp.state, TCP_SYN_RCVD); +} +END_TEST + +START_TEST(test_accept_hands_out_parked_close_wait) +{ + struct wolfIP s; + int lsd, sd; + struct tsocket *child; + + lsd = park_listener(&s); + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + park_final_ack(&s, child, TCP_FLAG_ACK | TCP_FLAG_FIN); + ck_assert_int_eq(child->sock.tcp.state, TCP_CLOSE_WAIT); + + sd = wolfIP_sock_accept(&s, lsd, NULL, NULL); + ck_assert_ptr_eq(&s.tcpsockets[SOCKET_UNMARK(sd)], child); + ck_assert_uint_ne(child->events & CB_EVENT_READABLE, 0); +} +END_TEST + +START_TEST(test_accept_drops_reset_parked_child_silently) +{ + struct wolfIP s; + int lsd; + struct tsocket *child; + + lsd = park_listener(&s); + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + + park_final_ack(&s, child, TCP_FLAG_RST | TCP_FLAG_ACK); + ck_assert_int_eq(child->proto, 0); + socket_cb_calls = 0; + (void)wolfIP_poll(&s, 100); + ck_assert_int_eq(socket_cb_calls, 0); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); +} +END_TEST + +START_TEST(test_accept_parked_child_gives_up_after_synack_cap) +{ + struct wolfIP s; + int lsd; + unsigned int i; + struct tsocket *child; + + lsd = park_listener(&s); + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + + for (i = 0; i < TCP_SYNACK_MAXRTX; i++) { + fifo_init(&child->sock.tcp.txbuf, child->txmem, TXBUF_SIZE); + tcp_rto_cb(child); + ck_assert_int_eq(child->proto, WI_IPPROTO_TCP); + } + ck_assert_uint_eq(child->sock.tcp.ctrl_rto_retries, TCP_SYNACK_MAXRTX); + tcp_rto_cb(child); + ck_assert_int_eq(child->proto, 0); +} +END_TEST + +START_TEST(test_full_table_reclaims_parked_syn_rcvd_child) +{ + struct wolfIP s; + int lsd; + int i; + struct tsocket *child; + const struct wolfIP_tcp_seg *out; + + lsd = park_listener(&s); + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + for (i = 2; i < MAX_TCPSOCKETS; i++) + ck_assert_int_ge(wolfIP_sock_socket(&s, AF_INET, IPSTACK_SOCK_STREAM, + WI_IPPROTO_TCP), 0); + + /* A second peer: its connection takes the slot of the one still handshaking. */ + inject_tcp_segment(&s, TEST_PRIMARY_IF, 0x0A0000A1U, 0x0A000001U, 40001, 1234, + 7, 0, TCP_FLAG_SYN); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + out = llk_last_tcp(); + ck_assert_ptr_nonnull(out); + ck_assert_uint_eq(out->flags, TCP_FLAG_RST | TCP_FLAG_ACK); + ck_assert_uint_eq(ee16(out->dst_port), 40000); + ck_assert_ptr_eq(parked_child(&s, lsd), child); + ck_assert_uint_eq(child->dst_port, 40001); +} +END_TEST + +START_TEST(test_parked_child_of_replaced_listener_not_handed_out) +{ + struct wolfIP s; + int lsd; + struct tsocket *child; + + lsd = park_listener(&s); + inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + child = parked_child(&s, lsd); + ck_assert_ptr_nonnull(child); + /* The listener's slot now belongs to a later generation. */ + s.tcp_gen[SOCKET_UNMARK(lsd)].gen++; + lsd = sock_fd_make(&s, MARK_TCP_SOCKET, SOCKET_UNMARK(lsd)); + park_final_ack(&s, child, TCP_FLAG_ACK); + ck_assert_int_eq(child->sock.tcp.state, TCP_ESTABLISHED); + ck_assert_int_eq(wolfIP_sock_accept(&s, lsd, NULL, NULL), -WOLFIP_EAGAIN); + ck_assert_int_eq(tcp_reclaim_rank(child), 2); +} +END_TEST + START_TEST(test_sock_abort_syn_rcvd_listener_resets_and_stops_listening) { struct wolfIP s; @@ -6758,7 +7285,7 @@ START_TEST(test_tcp_listener_preaccept_close_rto_retransmits_finack) /* Close the established-but-unaccepted connection: FIN_WAIT_1, the * control RTO owns the timer slot, and the pre-accept flag must be * gone with the timer it armed. */ - ck_assert_int_eq(wolfIP_sock_close(&s, fd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, fd), 0); ck_assert_int_eq(lsn->sock.tcp.state, TCP_FIN_WAIT_1); ck_assert_int_eq(lsn->sock.tcp.preaccept_timeout_active, 0); ck_assert_int_eq(lsn->sock.tcp.ctrl_rto_active, 1); diff --git a/src/test/unit/unit_tests_tcp_state.c b/src/test/unit/unit_tests_tcp_state.c index 21187949..f0890981 100644 --- a/src/test/unit/unit_tests_tcp_state.c +++ b/src/test/unit/unit_tests_tcp_state.c @@ -2724,7 +2724,7 @@ START_TEST(test_sock_close_established_disarms_callback) wolfIP_register_callback(&s, sd, test_socket_cb, &callback_arg); /* Active close: FIN sent, FIN_WAIT_1, EAGAIN, callback disarmed. */ - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); ck_assert_ptr_null(ts->callback); ck_assert_ptr_null(ts->callback_arg); @@ -2803,7 +2803,7 @@ START_TEST(test_sock_close_close_wait_disarms_callback) /* Active close from CLOSE_WAIT: FIN sent (seq 120), LAST_ACK, EAGAIN, * callback disarmed. */ - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_LAST_ACK); ck_assert_ptr_null(ts->callback); ck_assert_ptr_null(ts->callback_arg); @@ -2844,7 +2844,7 @@ START_TEST(test_rst_in_fin_wait_1_delivers_close_event) wolfIP_register_callback(&s, sd, test_socket_cb, &callback_arg); /* Active close: FIN sent, FIN_WAIT_1, EAGAIN, native callback disarmed. */ - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_FIN_WAIT_1); /* The wrapper re-arms its own callback to wait for CB_EVENT_CLOSED. */ @@ -2913,7 +2913,7 @@ START_TEST(test_last_ack_final_ack_delivers_close_event) /* Active close from CLOSE_WAIT: FIN sent (seq 120), LAST_ACK, EAGAIN, * native callback disarmed. */ - ck_assert_int_eq(wolfIP_sock_close(&s, sd), -WOLFIP_EAGAIN); + ck_assert_int_eq(wolfIP_sock_close(&s, sd), 0); ck_assert_int_eq(ts->sock.tcp.state, TCP_LAST_ACK); /* The wrapper re-arms its own callback to wait for CB_EVENT_CLOSED. */ @@ -2946,7 +2946,6 @@ START_TEST(test_accept_synack_retransmit_repeats_isn) struct tsocket *accepted; struct wolfIP_sockaddr_in sin; int syn_rcvd_sd; - int acc_sd; int i; uint32_t isn; @@ -3010,9 +3009,9 @@ START_TEST(test_accept_synack_retransmit_repeats_isn) /* accept() sends a duplicate SYN-ACK from the new socket, same ISN. */ syn_rcvd_sd = (int)(syn_rcvd - s.tcpsockets) | MARK_TCP_SOCKET; - acc_sd = wolfIP_sock_accept(&s, syn_rcvd_sd, NULL, 0); - ck_assert_int_gt(acc_sd, 0); - accepted = &s.tcpsockets[SOCKET_UNMARK(acc_sd)]; + ck_assert_int_eq(wolfIP_sock_accept(&s, syn_rcvd_sd, NULL, 0), -WOLFIP_EAGAIN); + accepted = parked_child(&s, syn_rcvd_sd); + ck_assert_ptr_nonnull(accepted); ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); /* Poll before the accept-armed control RTO (last_tick 1000 + 1000ms * default) expires, so this flushes accept's own SYN-ACK. */ @@ -3047,7 +3046,6 @@ START_TEST(test_accept_clears_listener_tx_fifo) struct tsocket *listener; struct tsocket *accepted; struct wolfIP_sockaddr_in sin; - int acc_sd; wolfIP_init(&s); mock_link_init(&s); @@ -3072,9 +3070,9 @@ START_TEST(test_accept_clears_listener_tx_fifo) /* accept() hands the handshake to the clone; the listener must revert * to LISTEN with an empty TX FIFO. */ - acc_sd = wolfIP_sock_accept(&s, listen_sd, NULL, 0); - ck_assert_int_gt(acc_sd, 0); - accepted = &s.tcpsockets[SOCKET_UNMARK(acc_sd)]; + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, 0), -WOLFIP_EAGAIN); + accepted = parked_child(&s, listen_sd); + ck_assert_ptr_nonnull(accepted); ck_assert_int_eq(accepted->sock.tcp.state, TCP_SYN_RCVD); /* The clone carries the connection's SYN-ACK. */ ck_assert_int_eq(fifo_is_empty(&accepted->sock.tcp.txbuf), 0); @@ -3128,7 +3126,6 @@ START_TEST(test_no_stale_synack_after_accept_new_conn) int listen_sd; struct tsocket *listener; struct wolfIP_sockaddr_in sin; - int acc_sd; uint32_t count_before; wolfIP_init(&s); @@ -3149,8 +3146,8 @@ START_TEST(test_no_stale_synack_after_accept_new_conn) inject_tcp_syn(&s, TEST_PRIMARY_IF, 0x0A000001U, 1234); wolfIP_poll(&s, 1000); ck_assert_int_eq(listener->sock.tcp.state, TCP_SYN_RCVD); - acc_sd = wolfIP_sock_accept(&s, listen_sd, NULL, 0); - ck_assert_int_gt(acc_sd, 0); + ck_assert_int_eq(wolfIP_sock_accept(&s, listen_sd, NULL, 0), -WOLFIP_EAGAIN); + ck_assert_ptr_nonnull(parked_child(&s, listen_sd)); /* Connection B: a second client port from the same peer while A's * SYN-ACK is still parked. */ diff --git a/src/wolfip.c b/src/wolfip.c index ffb74d9a..952d93ca 100644 --- a/src/wolfip.c +++ b/src/wolfip.c @@ -171,6 +171,10 @@ struct wolfIP_icmp_packet; * counter; a peer that keeps answering with a zero window still consumes * the budget - a deliberately finite patience, not infinite. */ #define TCP_PERSIST_MAXRTX 8U +/* SYN-ACK retries for a connection accept() holds back until it completes. */ +#ifndef TCP_SYNACK_MAXRTX +#define TCP_SYNACK_MAXRTX 3U +#endif #define TCP_RTO_MAX_BACKOFF 15U /* Max retries before closing; also clamps shift */ #ifdef IP_MULTICAST @@ -1266,6 +1270,10 @@ struct tcpsocket { uint8_t preaccept_timeout_active; uint8_t is_listener; uint8_t ack_retry_pending; + /* Cloned by accept() but not handed out until its handshake completes. */ + uint8_t parked; + uint8_t parent; + uint16_t parent_gen; ip4 local_ip, remote_ip; uint32_t peer_rwnd; uint16_t peer_mss; @@ -1304,6 +1312,16 @@ struct udpsocket { #endif }; +/* Generation of a socket slot, carried in bits 16-30 of its descriptors. */ +struct sock_gen { + uint16_t gen; + uint8_t open; +}; +#define SOCKET_GEN_SHIFT 16 +#define SOCKET_GEN_MASK 0x7FFFU +#define SOCKET_GEN(fd) \ + ((uint16_t)(((unsigned int)(fd) >> SOCKET_GEN_SHIFT) & SOCKET_GEN_MASK)) + struct tsocket { union tsocket_sock { struct tcpsocket tcp; @@ -1406,6 +1424,8 @@ static void tcp_fin_wait_2_timeout_stop(struct tsocket *t); static int tcp_preaccept_timeout_start(struct tsocket *t, uint64_t now); static void tcp_preaccept_timeout_stop(struct tsocket *t); static void tcp_listener_revert_to_listen(struct tsocket *t); +static void tcp_reclaim_if_full(struct wolfIP *s); +static void tcp_abort(struct tsocket *ts); static int tcp_ctrl_state_needs_rto(const struct tsocket *t); static int tcp_has_pending_unsent_payload(struct tsocket *t); static uint32_t tcp_snd_nxt(struct tsocket *t); @@ -1548,6 +1568,15 @@ struct wolfIP { #if WOLFIP_PACKET_SOCKETS struct packetsocket packetsockets[WOLFIP_MAX_PACKETSOCKETS]; #endif +#endif + struct sock_gen tcp_gen[MAX_TCPSOCKETS]; + struct sock_gen udp_gen[MAX_UDPSOCKETS]; + struct sock_gen icmp_gen[MAX_ICMPSOCKETS]; +#if WOLFIP_RAWSOCKETS + struct sock_gen raw_gen[WOLFIP_MAX_RAWSOCKETS]; +#if WOLFIP_PACKET_SOCKETS + struct sock_gen packet_gen[WOLFIP_MAX_PACKETSOCKETS]; +#endif #endif #ifdef IP_MULTICAST struct wolfIP_mcast_membership mcast[WOLFIP_MCAST_MEMBERSHIPS]; @@ -1812,10 +1841,11 @@ static void wolfIP_wake(struct wolfIP *s) s->wake_cb(s->wake_arg); } -/* Lowers the deadline the current wolfIP_poll() returns. */ +/* Lowers the deadline the current wolfIP_poll() returns, comparing in the + * 32-bit domain tick_expired() uses, since rebased timers are 32-bit values. */ static void wolfIP_poll_by(struct wolfIP *s, uint64_t when) { - if (when < s->poll_next_at) + if ((int32_t)((uint32_t)when - (uint32_t)s->poll_next_at) < 0) s->poll_next_at = when; } @@ -2856,6 +2886,120 @@ static void wolfIP_send_port_unreachable(struct wolfIP *s, unsigned int if_idx, } #endif +static struct sock_gen *sock_gen_slot(struct wolfIP *s, int mark, int idx) +{ + if (idx < 0) + return NULL; + if (mark == MARK_TCP_SOCKET) + return (idx < MAX_TCPSOCKETS) ? &s->tcp_gen[idx] : NULL; + if (mark == MARK_UDP_SOCKET) + return (idx < MAX_UDPSOCKETS) ? &s->udp_gen[idx] : NULL; + if (mark == MARK_ICMP_SOCKET) + return (idx < MAX_ICMPSOCKETS) ? &s->icmp_gen[idx] : NULL; +#if WOLFIP_RAWSOCKETS + if (mark == MARK_RAW_SOCKET) + return (idx < WOLFIP_MAX_RAWSOCKETS) ? &s->raw_gen[idx] : NULL; +#if WOLFIP_PACKET_SOCKETS + if (mark == MARK_PACKET_SOCKET) + return (idx < WOLFIP_MAX_PACKETSOCKETS) ? &s->packet_gen[idx] : NULL; +#endif +#endif + return NULL; +} + +static struct sock_gen *sock_gen_of_fd(struct wolfIP *s, int fd) +{ + int mark; + + if (!s || fd < 0) + return NULL; + if (IS_SOCKET_TCP(fd)) + mark = MARK_TCP_SOCKET; + else if (IS_SOCKET_UDP(fd)) + mark = MARK_UDP_SOCKET; + else if (IS_SOCKET_ICMP(fd)) + mark = MARK_ICMP_SOCKET; + else if (IS_SOCKET_RAW(fd)) + mark = MARK_RAW_SOCKET; + else if (IS_SOCKET_PACKET(fd)) + mark = MARK_PACKET_SOCKET; + else + return NULL; + return sock_gen_slot(s, mark, SOCKET_UNMARK(fd)); +} + +static void sock_gen_next(struct sock_gen *g) +{ + g->gen = (uint16_t)((g->gen + 1U) & SOCKET_GEN_MASK); + g->open = 0; +} + +static int sock_fd_make(struct wolfIP *s, int mark, int idx) +{ + struct sock_gen *g = sock_gen_slot(s, mark, idx); + int gen = g ? g->gen : 0; + + return idx | mark | (gen << SOCKET_GEN_SHIFT); +} + +/* A slot freed by the stack while its descriptor was still open gets a new + * generation when it is handed out again. */ +static int sock_fd_open(struct wolfIP *s, int mark, int idx) +{ + struct sock_gen *g = sock_gen_slot(s, mark, idx); + + if (g) { + if (g->open) + sock_gen_next(g); + g->open = 1; + } + return sock_fd_make(s, mark, idx); +} + +static int sock_fd_stale(struct wolfIP *s, int fd) +{ + struct sock_gen *g = sock_gen_of_fd(s, fd); + + return g && (g->gen != SOCKET_GEN(fd)); +} + +static void sock_fd_retire(struct wolfIP *s, int fd) +{ + struct sock_gen *g = sock_gen_of_fd(s, fd); + + if (g) + sock_gen_next(g); +} + +static struct tsocket *tcp_parked_parent(struct tsocket *t) +{ + struct wolfIP *s = t->S; + struct tsocket *lsn; + + if (!t->sock.tcp.parked || t->sock.tcp.parent >= MAX_TCPSOCKETS) + return NULL; + lsn = &s->tcpsockets[t->sock.tcp.parent]; + if ((s->tcp_gen[t->sock.tcp.parent].gen != t->sock.tcp.parent_gen) || + (lsn->proto != WI_IPPROTO_TCP) || !lsn->sock.tcp.is_listener) + return NULL; + return lsn; +} + +static struct tsocket *tcp_parked_ready(struct wolfIP *s, struct tsocket *lsn) +{ + int i; + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + struct tsocket *t = &s->tcpsockets[i]; + + if ((t->proto == WI_IPPROTO_TCP) && (tcp_parked_parent(t) == lsn) && + (t->sock.tcp.state == TCP_ESTABLISHED || + t->sock.tcp.state == TCP_CLOSE_WAIT)) + return t; + } + return NULL; +} + /* User Callbacks */ void wolfIP_register_callback(struct wolfIP *s, int sock_fd, tsocket_cb cb, void *arg) @@ -2864,7 +3008,7 @@ void wolfIP_register_callback(struct wolfIP *s, int sock_fd, tsocket_cb cb, uint16_t pending = 0; if (!s) return; - if (sock_fd < 0) + if (sock_fd < 0 || sock_fd_stale(s, sock_fd)) return; if (IS_SOCKET_TCP(sock_fd)) { if (SOCKET_UNMARK(sock_fd) >= MAX_TCPSOCKETS) @@ -3243,7 +3387,7 @@ static void udp_try_recv(struct wolfIP *s, unsigned int if_idx, * for any other (e.g. connected) socket that still has no local * address while DHCP is running. */ int is_dhcp = (s->dhcp_udp_sd > 0) && - ((uint32_t)(MARK_UDP_SOCKET | i) == (uint32_t)s->dhcp_udp_sd); + (sock_fd_make(s, MARK_UDP_SOCKET, i) == s->dhcp_udp_sd); /* Ingress matching uses the bound address, not the egress address * selected into local_ip at bind time: a wildcard (INADDR_ANY) * bind must receive datagrams addressed to any local address @@ -3619,15 +3763,42 @@ static uint32_t tcp_initial_ssthresh(uint32_t peer_rwnd) return (peer_rwnd < TXBUF_SIZE) ? peer_rwnd : TXBUF_SIZE; } +/* A free slot whose descriptor the application still holds is taken last. */ +static int tcp_free_slot(struct wolfIP *s) +{ + int i; + int held = -1; + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + if (s->tcpsockets[i].proto != 0) + continue; + if (!s->tcp_gen[i].open) + return i; + if (held < 0) + held = i; + } + return held; +} + static struct tsocket *tcp_new_socket(struct wolfIP *s) { struct tsocket *t; int i; + int pick; + tcp_reclaim_if_full(s); + pick = tcp_free_slot(s); for (i = 0; i < MAX_TCPSOCKETS; i++) { t = &s->tcpsockets[i]; - if (t->proto == 0) { + if (i == pick) { + /* A descriptor still open on a slot the stack freed is stale now. */ + if (s->tcp_gen[i].open) + sock_gen_next(&s->tcp_gen[i]); t->proto = WI_IPPROTO_TCP; t->S = s; + /* A callback registered through a stale descriptor may sit in a free slot. */ + t->callback = NULL; + t->callback_arg = NULL; + t->events = 0; t->if_idx = 0; t->sock.tcp.state = TCP_CLOSED; t->sock.tcp.rto = TCP_RTO_MIN_MS; @@ -6625,6 +6796,16 @@ static void tcp_input(struct wolfIP *S, unsigned int if_idx, } t->sock.tcp.state = TCP_ESTABLISHED; tcp_ctrl_rto_stop(t); + if (t->sock.tcp.parked) { + struct tsocket *lsn = tcp_parked_parent(t); + + if (tcp_preaccept_timeout_start(t, t->S->last_tick) < 0) { + tcp_abort(t); + continue; + } + if (lsn != NULL) + lsn->events |= CB_EVENT_READABLE; + } if (t->sock.tcp.is_listener && tcp_preaccept_timeout_start(t, t->S->last_tick) < 0) { /* Take the timeout's exit now rather than pin the port. */ @@ -6845,12 +7026,16 @@ static void tcp_rto_cb(void *arg) if (ts->sock.tcp.preaccept_timeout_active) { if ((ts->sock.tcp.state != TCP_ESTABLISHED && ts->sock.tcp.state != TCP_CLOSE_WAIT) || - !ts->sock.tcp.is_listener) { + (!ts->sock.tcp.is_listener && !ts->sock.tcp.parked)) { /* The socket left the pinned condition (accepted away, reset, * or closed): disarm quietly. */ tcp_preaccept_timeout_stop(ts); return; } + if (ts->sock.tcp.parked) { + tcp_abort(ts); + return; + } /* Never accepted: reclaim the port. The peer may have nothing left to * send, so it would never draw the LISTEN RST; tell it now. */ (void)tcp_send_reset_now(ts); @@ -6867,7 +7052,9 @@ static void tcp_rto_cb(void *arg) * delay the retransmit by a second RTO interval. */ tcp_ctrl_rto_stop(ts); } else { - if (ts->sock.tcp.ctrl_rto_retries >= TCP_CTRL_RTO_MAXRTX) { + if ((ts->sock.tcp.ctrl_rto_retries >= TCP_CTRL_RTO_MAXRTX) || + (ts->sock.tcp.parked && + ts->sock.tcp.ctrl_rto_retries >= TCP_SYNACK_MAXRTX)) { tcp_ctrl_rto_stop(ts); if (ts->sock.tcp.is_listener && ts->sock.tcp.state == TCP_SYN_RCVD) { @@ -7286,18 +7473,18 @@ int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol) ts = tcp_new_socket(s); if (!ts) return -1; - return (ts - s->tcpsockets) | MARK_TCP_SOCKET; + return sock_fd_open(s, MARK_TCP_SOCKET, (int)(ts - s->tcpsockets)); } else if (type == IPSTACK_SOCK_DGRAM) { if (protocol == 0 || protocol == WI_IPPROTO_UDP) { ts = udp_new_socket(s); if (!ts) return -1; - return (ts - s->udpsockets) | MARK_UDP_SOCKET; + return sock_fd_open(s, MARK_UDP_SOCKET, (int)(ts - s->udpsockets)); } else if (protocol == WI_IPPROTO_ICMP) { ts = icmp_new_socket(s); if (!ts) return -1; - return (ts - s->icmpsockets) | MARK_ICMP_SOCKET; + return sock_fd_open(s, MARK_ICMP_SOCKET, (int)(ts - s->icmpsockets)); } else { return -1; } @@ -7313,7 +7500,7 @@ int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol) rs = raw_new_socket(s, protocol, hdrincl); if (!rs) return -1; - return (int)((rs - s->rawsockets) | MARK_RAW_SOCKET); + return sock_fd_open(s, MARK_RAW_SOCKET, (int)(rs - s->rawsockets)); } #endif return -1; @@ -7325,7 +7512,7 @@ int wolfIP_sock_socket(struct wolfIP *s, int domain, int type, int protocol) ps = packet_new_socket(s, protocol); if (!ps) return -1; - return (int)((ps - s->packetsockets) | MARK_PACKET_SOCKET); + return sock_fd_open(s, MARK_PACKET_SOCKET, (int)(ps - s->packetsockets)); } #endif return -1; @@ -7372,6 +7559,8 @@ int wolfIP_sock_connect(struct wolfIP *s, int sockfd, const struct wolfIP_sockad struct tsocket *ts; const struct wolfIP_sockaddr_in *sin; unsigned int if_idx; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; if ((!addr)|| (sockfd < 0)) return -WOLFIP_EINVAL; if (!s) @@ -7603,6 +7792,9 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add if (!s) return -WOLFIP_EINVAL; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; + if (addrlen) *addrlen = sizeof(struct wolfIP_sockaddr_in); @@ -7610,6 +7802,35 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add if (SOCKET_UNMARK(sockfd) >= MAX_TCPSOCKETS) return -WOLFIP_EINVAL; ts = &s->tcpsockets[SOCKET_UNMARK(sockfd)]; + newts = ts->sock.tcp.is_listener ? tcp_parked_ready(s, ts) : NULL; + if (newts) { + newts->sock.tcp.parked = 0; + if (newts->sock.tcp.preaccept_timeout_active) + tcp_preaccept_timeout_stop(newts); + newts->callback = ts->callback; + newts->callback_arg = ts->callback_arg; + newts->events = 0; + if ((queue_len(&newts->sock.tcp.rxbuf) > 0) || + (newts->sock.tcp.state == TCP_CLOSE_WAIT)) + newts->events |= CB_EVENT_READABLE; + if (tx_has_writable_space(newts)) + newts->events |= CB_EVENT_WRITABLE; + /* The listener's READABLE is an edge: raise it again for what still waits. */ + if ((tcp_parked_ready(s, ts) != NULL) || + (ts->sock.tcp.state == TCP_SYN_RCVD) || + (ts->sock.tcp.state == TCP_ESTABLISHED) || + (ts->sock.tcp.state == TCP_CLOSE_WAIT)) { + ts->events |= CB_EVENT_READABLE; + if (ts->callback) + wolfIP_wake(s); + } + if (sin) { + sin->sin_family = AF_INET; + sin->sin_port = ee16(newts->dst_port); + sin->sin_addr.s_addr = ee32(newts->remote_ip); + } + return sock_fd_open(s, MARK_TCP_SOCKET, (int)(newts - s->tcpsockets)); + } if (ts->sock.tcp.is_listener && (ts->sock.tcp.state == TCP_ESTABLISHED || ts->sock.tcp.state == TCP_CLOSE_WAIT)) { @@ -7624,11 +7845,9 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add * then EOF, instead of accept() failing on a readable listener * and spinning the poll loop until the pre-accept timeout. */ newts = tcp_new_socket(s); - if (!newts) { - (void)tcp_send_reset_now(ts); - tcp_listener_revert_to_listen(ts); - return -1; - } + /* No slot yet: it waits on the listener, bounded by the pre-accept timeout. */ + if (!newts) + return -WOLFIP_EAGAIN; tcp_preaccept_timeout_stop(ts); *newts = *ts; newts->sock.tcp.is_listener = 0; @@ -7679,19 +7898,19 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add abort_accept_clone(newts); return -1; } - return (newts - s->tcpsockets) | MARK_TCP_SOCKET; + return sock_fd_open(s, MARK_TCP_SOCKET, (int)(newts - s->tcpsockets)); } if ((ts->sock.tcp.state != TCP_SYN_RCVD) && (ts->sock.tcp.state != TCP_LISTEN)) return -1; if (ts->sock.tcp.state == TCP_SYN_RCVD) { + /* With no free slot the listener completes the handshake itself. */ newts = tcp_new_socket(s); if (!newts) - return -1; - /* Don't signal writable until connection fully established */ - newts->events &= ~CB_EVENT_WRITABLE; - newts->callback = ts->callback; - newts->callback_arg = ts->callback_arg; + return -WOLFIP_EAGAIN; + newts->sock.tcp.parked = 1; + newts->sock.tcp.parent = (uint8_t)SOCKET_UNMARK(sockfd); + newts->sock.tcp.parent_gen = SOCKET_GEN(sockfd); newts->local_ip = ts->local_ip; newts->bound_local_ip = (ts->bound_local_ip != IPADDR_ANY) ? ts->bound_local_ip : ts->local_ip; newts->if_idx = ts->if_idx; @@ -7733,16 +7952,9 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add * advances seq to ISN+1 when the connection is established. */ newts->sock.tcp.ctrl_rto_retries = 0; if (tcp_ctrl_rto_start(newts, s->last_tick) < 0) { - newts->callback = NULL; - newts->callback_arg = NULL; close_socket(newts); return -WOLFIP_EAGAIN; } - if (sin) { - sin->sin_family = AF_INET; - sin->sin_port = ee16(ts->dst_port); - sin->sin_addr.s_addr = ee32(ts->remote_ip); - } /* The accepted connection owns the handshake now (its SYN-ACK * lives in the clone's TX FIFO). Revert the listener to the * fresh-socket baseline: the old partial reset (state, RTO, @@ -7759,7 +7971,8 @@ int wolfIP_sock_accept(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *add abort_accept_clone(newts); return -1; } - return (newts - s->tcpsockets) | MARK_TCP_SOCKET; + /* Handed out by a later accept() once the handshake completes. */ + return -WOLFIP_EAGAIN; } else if (ts->sock.tcp.state == TCP_LISTEN) { return -WOLFIP_EAGAIN; } @@ -7778,6 +7991,8 @@ int wolfIP_sock_sendto(struct wolfIP *s, int sockfd, const void *buf, size_t len #if WOLFIP_RAWSOCKETS struct wolfIP_ip_packet *rip; #endif + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; tcp = (struct wolfIP_tcp_seg *)frame; udp = (struct wolfIP_udp_datagram *)frame; icmp = (struct wolfIP_icmp_packet *)frame; @@ -8236,6 +8451,8 @@ int wolfIP_sock_recvfrom(struct wolfIP *s, int sockfd, void *buf, size_t len, in struct wolfIP_udp_datagram *udp; struct wolfIP_icmp_packet *icmp; struct tsocket *ts; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; (void)flags; if (sockfd < 0) @@ -8614,6 +8831,8 @@ int wolfIP_sock_setsockopt(struct wolfIP *s, int sockfd, int level, int optname, const void *optval, socklen_t optlen) { struct tsocket *ts; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; #if WOLFIP_RAWSOCKETS if (IS_SOCKET_RAW(sockfd)) { struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); @@ -8758,6 +8977,8 @@ int wolfIP_sock_setsockopt(struct wolfIP *s, int sockfd, int level, int optname, int wolfIP_sock_get_recv_ttl(struct wolfIP *s, int sockfd, int *ttl) { struct tsocket *ts; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; #if WOLFIP_RAWSOCKETS if (IS_SOCKET_RAW(sockfd)) { struct rawsocket *rs = wolfIP_rawsocket_from_fd(s, sockfd); @@ -8791,6 +9012,8 @@ int wolfIP_sock_getsockopt(struct wolfIP *s, int sockfd, int level, int optname, struct packetsocket *ps = NULL; #endif + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; if (sockfd < 0) return -WOLFIP_EINVAL; #if WOLFIP_RAWSOCKETS @@ -8898,7 +9121,27 @@ int wolfIP_sock_getsockopt(struct wolfIP *s, int sockfd, int level, int optname, #endif return 0; } -int wolfIP_sock_close(struct wolfIP *s, int sockfd) +/* Connections accept() never handed out die with their listener. */ +static void tcp_drop_parked(struct wolfIP *s, int sockfd) +{ + struct tsocket *lsn; + int i; + + if (!s || sockfd < 0 || !IS_SOCKET_TCP(sockfd) || + SOCKET_UNMARK(sockfd) >= MAX_TCPSOCKETS) + return; + lsn = &s->tcpsockets[SOCKET_UNMARK(sockfd)]; + if (!lsn->sock.tcp.is_listener) + return; + for (i = 0; i < MAX_TCPSOCKETS; i++) { + struct tsocket *t = &s->tcpsockets[i]; + + if ((t->proto == WI_IPPROTO_TCP) && (tcp_parked_parent(t) == lsn)) + tcp_abort(t); + } +} + +static int sock_close(struct wolfIP *s, int sockfd) { if (sockfd < 0) return -WOLFIP_EINVAL; @@ -8923,9 +9166,8 @@ int wolfIP_sock_close(struct wolfIP *s, int sockfd) /* No FIN retransmit is possible: release the socket. */ ts->sock.tcp.state = TCP_CLOSED; close_socket(ts); - return 0; } - return -WOLFIP_EAGAIN; + return 0; } else if (ts->sock.tcp.state == TCP_LISTEN) { ts->sock.tcp.state = TCP_CLOSED; (void)wolfIP_filter_notify_socket_event( @@ -8948,14 +9190,15 @@ int wolfIP_sock_close(struct wolfIP *s, int sockfd) if (tcp_ctrl_rto_start(ts, s->last_tick) < 0) { ts->sock.tcp.state = TCP_CLOSED; close_socket(ts); - return 0; } - return -WOLFIP_EAGAIN; - } else if (ts->sock.tcp.state == TCP_CLOSING) { - return -WOLFIP_EAGAIN; + return 0; } else if (ts->sock.tcp.state == TCP_FIN_WAIT_1 || - ts->sock.tcp.state == TCP_FIN_WAIT_2) { - return -WOLFIP_EAGAIN; + ts->sock.tcp.state == TCP_FIN_WAIT_2 || + ts->sock.tcp.state == TCP_CLOSING || + ts->sock.tcp.state == TCP_LAST_ACK || + ts->sock.tcp.state == TCP_TIME_WAIT) { + /* Our FIN is out: the stack finishes the close on its own. */ + return 0; } else if (ts->sock.tcp.state != TCP_CLOSED) { ts->sock.tcp.state = TCP_CLOSED; (void)wolfIP_filter_notify_socket_event( @@ -9026,17 +9269,24 @@ int wolfIP_sock_close(struct wolfIP *s, int sockfd) return 0; } -int wolfIP_sock_abort(struct wolfIP *s, int sockfd) +int wolfIP_sock_close(struct wolfIP *s, int sockfd) { - struct tsocket *ts; + int ret; - if (!s || sockfd < 0 || !IS_SOCKET_TCP(sockfd)) - return -WOLFIP_EINVAL; - if (SOCKET_UNMARK(sockfd) >= MAX_TCPSOCKETS) - return -WOLFIP_EINVAL; - ts = &s->tcpsockets[SOCKET_UNMARK(sockfd)]; - if (ts->sock.tcp.state == TCP_LISTEN || ts->sock.tcp.state == TCP_CLOSED) - return wolfIP_sock_close(s, sockfd); + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; + tcp_drop_parked(s, sockfd); + ret = sock_close(s, sockfd); + /* A socket still finishing its FIN exchange keeps its descriptor for + * wolfIP_sock_abort(); the stack retires it when it frees the slot. */ + if ((ret == 0) && !(IS_SOCKET_TCP(sockfd) && + (s->tcpsockets[SOCKET_UNMARK(sockfd)].proto != 0))) + sock_fd_retire(s, sockfd); + return ret; +} + +static void tcp_abort(struct tsocket *ts) +{ switch (ts->sock.tcp.state) { case TCP_SYN_RCVD: case TCP_ESTABLISHED: @@ -9050,25 +9300,88 @@ int wolfIP_sock_abort(struct wolfIP *s, int sockfd) } ts->sock.tcp.state = TCP_CLOSED; (void)wolfIP_filter_notify_socket_event( - WOLFIP_FILT_CLOSED, s, ts, + WOLFIP_FILT_CLOSED, ts->S, ts, ts->local_ip, ts->src_port, ts->remote_ip, ts->dst_port); if (ts->sock.tcp.is_listener) { (void)wolfIP_filter_notify_socket_event( - WOLFIP_FILT_STOP_LISTENING, s, ts, + WOLFIP_FILT_STOP_LISTENING, ts->S, ts, ts->local_ip, ts->src_port, IPADDR_ANY, 0); } ts->callback = NULL; ts->callback_arg = NULL; close_socket(ts); +} + +int wolfIP_sock_abort(struct wolfIP *s, int sockfd) +{ + struct tsocket *ts; + + if (!s || sockfd < 0 || !IS_SOCKET_TCP(sockfd)) + return -WOLFIP_EINVAL; + if (SOCKET_UNMARK(sockfd) >= MAX_TCPSOCKETS) + return -WOLFIP_EINVAL; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; + ts = &s->tcpsockets[SOCKET_UNMARK(sockfd)]; + if (ts->sock.tcp.state == TCP_LISTEN || ts->sock.tcp.state == TCP_CLOSED) + return wolfIP_sock_close(s, sockfd); + tcp_drop_parked(s, sockfd); + tcp_abort(ts); + sock_fd_retire(s, sockfd); return 0; } +/* Order in which sockets the application has already closed, or never got, + * give up their slot when the table is full; 0 never does. */ +static int tcp_reclaim_rank(struct tsocket *t) +{ + if (t->sock.tcp.parked) + return ((t->sock.tcp.state == TCP_SYN_RCVD) || + (tcp_parked_parent(t) == NULL)) ? 2 : 0; + switch (t->sock.tcp.state) { + case TCP_TIME_WAIT: + return 4; + case TCP_FIN_WAIT_2: + return 3; + case TCP_FIN_WAIT_1: + case TCP_CLOSING: + case TCP_LAST_ACK: + return 1; + default: + return 0; + } +} + +static void tcp_reclaim_if_full(struct wolfIP *s) +{ + struct tsocket *victim = NULL; + int best = 0; + int i; + + for (i = 0; i < MAX_TCPSOCKETS; i++) { + struct tsocket *t = &s->tcpsockets[i]; + int rank; + + if (t->proto == 0) + return; + rank = tcp_reclaim_rank(t); + if (rank > best) { + best = rank; + victim = t; + } + } + if (victim) + tcp_abort(victim); +} + int wolfIP_sock_getsockname(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr *addr, const socklen_t *addrlen) { struct tsocket *ts; struct wolfIP_sockaddr_in *sin; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; if ((!addr) || (sockfd < 0)) return -WOLFIP_EINVAL; @@ -9132,6 +9445,8 @@ int wolfIP_sock_can_read(struct wolfIP *s, int sockfd) { struct tsocket *ts = wolfIP_socket_from_fd(s, sockfd); + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; if (IS_SOCKET_TCP(sockfd)) { if (!ts) return -WOLFIP_EINVAL; @@ -9145,7 +9460,8 @@ int wolfIP_sock_can_read(struct wolfIP *s, int sockfd) if (ts->sock.tcp.is_listener && (ts->sock.tcp.state == TCP_SYN_RCVD || ts->sock.tcp.state == TCP_ESTABLISHED || - ts->sock.tcp.state == TCP_CLOSE_WAIT)) + ts->sock.tcp.state == TCP_CLOSE_WAIT || + tcp_parked_ready(s, ts))) return 1; if (queue_len(&ts->sock.tcp.rxbuf) > 0) return 1; @@ -9181,6 +9497,8 @@ int wolfIP_sock_can_write(struct wolfIP *s, int sockfd) { struct tsocket *ts = wolfIP_socket_from_fd(s, sockfd); + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; if (IS_SOCKET_TCP(sockfd)) { if (!ts) return -WOLFIP_EINVAL; @@ -9269,6 +9587,8 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr return -WOLFIP_EINVAL; #endif + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; if (sockfd < 0) return -WOLFIP_EINVAL; @@ -9474,6 +9794,8 @@ int wolfIP_sock_bind(struct wolfIP *s, int sockfd, const struct wolfIP_sockaddr int wolfIP_sock_listen(struct wolfIP *s, int sockfd, int backlog) { struct tsocket *ts; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; (void)backlog; if (sockfd < 0) return -WOLFIP_EINVAL; @@ -9505,6 +9827,8 @@ int wolfIP_sock_getpeername(struct wolfIP *s, int sockfd, struct wolfIP_sockaddr { struct tsocket *ts; struct wolfIP_sockaddr_in *sin = (struct wolfIP_sockaddr_in *)addr; + if (sock_fd_stale(s, sockfd)) + return -WOLFIP_EBADF; if (sockfd < 0) return -WOLFIP_EINVAL; if (!s) @@ -12841,7 +13165,8 @@ static void handle_timers(struct wolfIP *s, uint64_t now) } } -static void dispatch_events(struct tsocket *socks, int count, uint32_t mark) +static void dispatch_events(struct wolfIP *s, struct tsocket *socks, int count, + int mark) { int i; for (i = 0; i < count; i++) { @@ -12849,7 +13174,8 @@ static void dispatch_events(struct tsocket *socks, int count, uint32_t mark) if (ts->callback && ts->events) { uint16_t events = ts->events; ts->events = 0; - ts->callback(i | mark, events, ts->callback_arg); + ts->callback(sock_fd_make(s, mark, i), events, + ts->callback_arg); } } } @@ -12871,7 +13197,7 @@ static void handle_socket_callbacks(struct wolfIP *s) uint16_t events = ts->events; memset(ts, 0, sizeof(struct tsocket)); if (cb) - cb(i | MARK_TCP_SOCKET, events, cb_arg); + cb(sock_fd_make(s, MARK_TCP_SOCKET, i), events, cb_arg); continue; } @@ -12890,7 +13216,7 @@ static void handle_socket_callbacks(struct wolfIP *s) void *cb_arg = ts->callback_arg; uint16_t events = ts->events; ts->events = 0; - cb(i | MARK_TCP_SOCKET, events, cb_arg); + cb(sock_fd_make(s, MARK_TCP_SOCKET, i), events, cb_arg); /* Now that CB_EVENT_CLOSED has been delivered, reap the * deferred-close socket - but only if the slot still holds the @@ -12913,8 +13239,8 @@ static void handle_socket_callbacks(struct wolfIP *s) } } - dispatch_events(s->udpsockets, MAX_UDPSOCKETS, MARK_UDP_SOCKET); - dispatch_events(s->icmpsockets, MAX_ICMPSOCKETS, MARK_ICMP_SOCKET); + dispatch_events(s, s->udpsockets, MAX_UDPSOCKETS, MARK_UDP_SOCKET); + dispatch_events(s, s->icmpsockets, MAX_ICMPSOCKETS, MARK_ICMP_SOCKET); #if WOLFIP_RAWSOCKETS for (i = 0; i < WOLFIP_MAX_RAWSOCKETS; i++) { @@ -12926,7 +13252,8 @@ static void handle_socket_callbacks(struct wolfIP *s) * post-callback clear would wipe them. */ uint16_t events = r->events; r->events = 0; - r->callback(i | MARK_RAW_SOCKET, events, r->callback_arg); + r->callback(sock_fd_make(s, MARK_RAW_SOCKET, i), events, + r->callback_arg); } } #endif @@ -12940,7 +13267,8 @@ static void handle_socket_callbacks(struct wolfIP *s) * post-callback clear would wipe them. */ uint16_t events = p->events; p->events = 0; - p->callback(i | MARK_PACKET_SOCKET, events, p->callback_arg); + p->callback(sock_fd_make(s, MARK_PACKET_SOCKET, i), + events, p->callback_arg); } } #endif diff --git a/wolfip.h b/wolfip.h index a1078cb2..6445d416 100644 --- a/wolfip.h +++ b/wolfip.h @@ -72,6 +72,22 @@ typedef unsigned long size_t; #endif #endif +#ifndef WOLFIP_SO_RCVTIMEO +#ifdef SO_RCVTIMEO +#define WOLFIP_SO_RCVTIMEO SO_RCVTIMEO +#else +#define WOLFIP_SO_RCVTIMEO 20 +#endif +#endif + +#ifndef WOLFIP_SO_SNDTIMEO +#ifdef SO_SNDTIMEO +#define WOLFIP_SO_SNDTIMEO SO_SNDTIMEO +#else +#define WOLFIP_SO_SNDTIMEO 21 +#endif +#endif + #ifndef WOLFIP_SO_DONTROUTE #ifdef SO_DONTROUTE #define WOLFIP_SO_DONTROUTE SO_DONTROUTE @@ -179,6 +195,30 @@ typedef uint32_t ip4; #endif #endif +#ifndef WOLFIP_EINPROGRESS +#ifdef EINPROGRESS +#define WOLFIP_EINPROGRESS EINPROGRESS +#else +#define WOLFIP_EINPROGRESS (115) +#endif +#endif + +#ifndef WOLFIP_EDOM +#ifdef EDOM +#define WOLFIP_EDOM EDOM +#else +#define WOLFIP_EDOM (33) +#endif +#endif + +#ifndef WOLFIP_EBADF +#ifdef EBADF +#define WOLFIP_EBADF EBADF +#else +#define WOLFIP_EBADF (9) +#endif +#endif + #ifndef WOLFIP_EBUSY #ifdef EBUSY #define WOLFIP_EBUSY EBUSY @@ -360,6 +400,12 @@ struct wolfIP_sockaddr_in { struct wolfIP_sockaddr { uint16_t sa_family; }; typedef uint32_t socklen_t; +/* struct timeval for SO_RCVTIMEO / SO_SNDTIMEO, without needing sys/time.h. */ +struct wolfIP_timeval { + long tv_sec; + long tv_usec; +}; + /* Pull in the system socket types when available, but only declare * WOLFIP_HAVE_POSIX_TYPES once BOTH AND are * confirmed present. Zephyr is a special case: its POSIX socket layer