CHERIoT-Platform / CHERIoT-Platform/network-stack

Active TCP termination cannot send out final ACK.

Open
#110 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
9
Forks
14
Avg merge
6d 17h
Merged PRs (30d)
1

Description

### Summary
When the server initiates termination, it sends a FIN first, and the client responds FINACK. The server should then send the final ACK. But in current code path, the firewall endpoint is removed before ACK is sent out, so the egress firewall drops it. The client never receives the final ACK and ultimately closes through a timeout rather than a graceful termination.

### Root cause
The root cause is the ordering in FreeRTOS. After receiving the client’s FINACK, FreeRTOS prepares the final ACK and then calls:

vTCPStateChange(..., eCLOSE_WAIT);

This synchronously invokes on_tcp_connect(..., false), which removes the firewall endpoint. And after vTCPStateChange() returns, FreeRTOS attempts to send the final ACK, but the egress firewall rejects it because the connection tuple in the firewall no longer exists.

Here is the relevant code:https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/27a96812e84413763db60c08ec41311b75897e8c/source/FreeRTOS_TCP_State_Handling.c#L288

Also, I believe the same issue also happens failed handshakes path. If the server receives an invalid packet while in state SYN_RECEIVED, FreeRTOS will abort the handshake by calling vTCPStateChange() first. This invokes on_tcp_connect(..., false) and removes the firewall endpoint. FreeRTOS then constructs and attempts to send an RST msg, but the egress firewall will drop it because the tuple has already been removed.

Relevant code:https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/27a96812e84413763db60c08ec41311b75897e8c/source/FreeRTOS_TCP_State_Handling.c#L382
### Affected component
`lib/tcpip/network_wrapper.cc::network_socket_close()` and `lib/tcpip/network_wrapper.cc::on_tcp_connect()`, the later one is the FreeRTOS callback on receiving a packet, which will inherently remove the endpoint tuple in the firewall.

### Impact
The connection will be forced to terminated after 30 seconds without following the full 4-way handshake termination rule. (Forced by `ipconfigTCP_HANG_PROTECTION_TIME` in FreeRTOS)

### PoC
Here is the output when running `example 05.HTTP server` with extra firewall instrumentation.
Image
Because the client never received that ACK, it remained in LAST_ACK and repeatedly retransmitted FIN,ACK. Those are the repeated ingress drops:
`FW-OBS: DROP ingress proto 6 ... remotePort 0xd304`

### Suggested fix
When the first egress FIN is observed, set the firewall hole to a special state: only 1 incoming FINACK and 1 egress ACK is allowed. Once the budget is consumed, the hole will be dropped. That will ensure the firewall hole dropping happens after the egress of final ACK.

Contributor guide

No contributing guide indexed for this repository

Research direction

Read source/FreeRTOS_TCP_State_Handling.c at the two linked locations, then inspect lib/tcpip/network_wrapper.cc::network_socket_close() and on_tcp_connect(). Reproduce with example 05.HTTP server and firewall instrumentation; done means the final ACK and failed-handshake RST are no longer dropped after endpoint removal, and graceful close avoids the 30-second timeout.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
networking, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.