Skip to content

Active TCP termination cannot send out final ACK. #110

Description

@nding0405

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions