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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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] $<"
Expand Down
11 changes: 6 additions & 5 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/port/amd/common/wolfip_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions src/port/freeRTOS/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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`.
Loading
Loading