From ac7ea719d2ada7b09f81347a83ef629c6536817e Mon Sep 17 00:00:00 2001 From: Fr3ya Date: Fri, 21 Nov 2025 11:58:32 -0800 Subject: [PATCH 1/2] Fix incorrect TCP state transition on connection timeout When a TCP connection in eCONNECT_SYN state times out after exceeding the maximum retry count, the socket was incorrectly transitioning to eCLOSE_WAIT state instead of eCLOSED. According to the standard TCP state machine (RFC 793): - eCONNECT_SYN is the state where a client has sent a SYN and is waiting for a SYN-ACK - When connection establishment fails (timeout or RST), the socket should transition to eCLOSED state This fix: 1. Changes the state transition in prvTCPSendPacket() from eCLOSE_WAIT to eCLOSED when SYN retries are exhausted 2. Updates vTCPStateChange() to handle the eCONNECT_SYN -> eCLOSED transition, ensuring connection state change callbacks are properly triggered. This also aligns the timeout behavior with the existing RST handling code, which correctly uses eCLOSED state (see FreeRTOS_TCP_IP.c:859). Files changed: - source/FreeRTOS_TCP_Transmission.c: Fix state transition - source/FreeRTOS_TCP_IP.c: Update vTCPStateChange to handle eCLOSED transition from connecting states --- source/FreeRTOS_TCP_IP.c | 2 +- source/FreeRTOS_TCP_Transmission.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/FreeRTOS_TCP_IP.c b/source/FreeRTOS_TCP_IP.c index 9d9156882e..73526f7b4a 100644 --- a/source/FreeRTOS_TCP_IP.c +++ b/source/FreeRTOS_TCP_IP.c @@ -324,7 +324,7 @@ if( ( ( xPreviousState == eCONNECT_SYN ) || ( xPreviousState == eSYN_FIRST ) || ( xPreviousState == eSYN_RECEIVED ) ) && - ( eTCPState == eCLOSE_WAIT ) ) + ( ( eTCPState == eCLOSE_WAIT ) || ( eTCPState == eCLOSED ) ) ) { /* A socket was in the connecting phase but something * went wrong and it should be closed. */ diff --git a/source/FreeRTOS_TCP_Transmission.c b/source/FreeRTOS_TCP_Transmission.c index 36eb39c5f0..67adc8bcf3 100644 --- a/source/FreeRTOS_TCP_Transmission.c +++ b/source/FreeRTOS_TCP_Transmission.c @@ -132,11 +132,11 @@ { /* The connection is in the SYN status. The packet will be repeated * to most 3 times. When there is no response, the socket get the - * status 'eCLOSE_WAIT'. */ + * status 'eCLOSED'. */ FreeRTOS_debug_printf( ( "Connect: giving up %xip:%u\n", ( unsigned ) pxSocket->u.xTCP.xRemoteIP.ulIP_IPv4, /* IP address of remote machine. */ pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */ - vTCPStateChange( pxSocket, eCLOSE_WAIT ); + vTCPStateChange( pxSocket, eCLOSED ); } else if( prvTCPMakeSurePrepared( pxSocket ) == pdTRUE ) { From ef44afea7ea735f1735619c2a82effd7b010fcd0 Mon Sep 17 00:00:00 2001 From: Fr3ya Date: Fri, 5 Dec 2025 14:55:08 -0800 Subject: [PATCH 2/2] Update unit tests and coverage based on maintainer feedback --- source/FreeRTOS_TCP_IP.c | 3 +- .../FreeRTOS_TCP_IP/FreeRTOS_TCP_IP_utest.c | 78 +++++++++++++------ 2 files changed, 57 insertions(+), 24 deletions(-) diff --git a/source/FreeRTOS_TCP_IP.c b/source/FreeRTOS_TCP_IP.c index 73526f7b4a..e892cc569a 100644 --- a/source/FreeRTOS_TCP_IP.c +++ b/source/FreeRTOS_TCP_IP.c @@ -324,7 +324,8 @@ if( ( ( xPreviousState == eCONNECT_SYN ) || ( xPreviousState == eSYN_FIRST ) || ( xPreviousState == eSYN_RECEIVED ) ) && - ( ( eTCPState == eCLOSE_WAIT ) || ( eTCPState == eCLOSED ) ) ) + ( ( eTCPState == eCLOSE_WAIT ) || + ( eTCPState == eCLOSED ) ) ) { /* A socket was in the connecting phase but something * went wrong and it should be closed. */ diff --git a/test/unit-test/FreeRTOS_TCP_IP/FreeRTOS_TCP_IP_utest.c b/test/unit-test/FreeRTOS_TCP_IP/FreeRTOS_TCP_IP_utest.c index 7eee3348c6..69f19f94d6 100644 --- a/test/unit-test/FreeRTOS_TCP_IP/FreeRTOS_TCP_IP_utest.c +++ b/test/unit-test/FreeRTOS_TCP_IP/FreeRTOS_TCP_IP_utest.c @@ -802,29 +802,60 @@ void test_vTCPStateChange_ClosedWaitState_PrvStateSyn( void ) enum eTCP_STATE eTCPState; BaseType_t xTickCountAck = 0xAABBEEDD; BaseType_t xTickCountAlive = 0xAABBEFDD; - - memset( &xSocket, 0, sizeof( xSocket ) ); - eTCPState = eCLOSE_WAIT; - - xSocket.u.xTCP.eTCPState = eCONNECT_SYN; - - prvTCPSocketIsActive_ExpectAndReturn( xSocket.u.xTCP.eTCPState, pdTRUE ); - vTaskSuspendAll_Expect(); - xTaskResumeAll_ExpectAndReturn( 0 ); - xTaskGetTickCount_ExpectAndReturn( xTickCountAck ); - xTaskGetTickCount_ExpectAndReturn( xTickCountAlive ); - FreeRTOS_inet_ntop_ExpectAnyArgsAndReturn( NULL ); - - vSocketWakeUpUser_Expect( &xSocket ); - - vTCPStateChange( &xSocket, eTCPState ); - - TEST_ASSERT_EQUAL( eCLOSE_WAIT, xSocket.u.xTCP.eTCPState ); - TEST_ASSERT_EQUAL( xTickCountAck, xSocket.u.xTCP.xLastActTime ); - TEST_ASSERT_EQUAL( pdFALSE_UNSIGNED, xSocket.u.xTCP.bits.bWaitKeepAlive ); - TEST_ASSERT_EQUAL( pdFALSE_UNSIGNED, xSocket.u.xTCP.bits.bSendKeepAlive ); - TEST_ASSERT_EQUAL( 0, xSocket.u.xTCP.ucKeepRepCount ); - TEST_ASSERT_EQUAL( xTickCountAlive, xSocket.u.xTCP.xLastAliveTime ); + BaseType_t xRound; + + /* See Fix incorrect state transition #1302, see RFC 793. + * A changes in state transition: + * Before: eSYN_FIRST -> eCLOSE_WAIT + * After : eSYN_FIRST -> eCLOSED + * For example, a TCP client trying to connect to a remote + * device, reaching a time-out. + */ + for( xRound = 0; xRound < 3; xRound++ ) + { + memset( &xSocket, 0, sizeof( xSocket ) ); + /* The socket was in a SYN state, e.g. eSYN_FIRST. */ + xSocket.u.xTCP.eTCPState = eSYN_FIRST; + + switch( xRound ) + { + /* Check eSYN_FIRST to eCLOSED: */ + case 0: + eTCPState = eCLOSED; + break; + /* Check eSYN_FIRST to eCLOSE_WAIT: */ + case 1: + eTCPState = eCLOSE_WAIT; + break; + /* Check eSYN_FIRST to eSYN_RECEIVED: */ + case 2: + eTCPState = eSYN_RECEIVED; + break; + } + + if( xRound < 2 ) + { + prvTCPSocketIsActive_ExpectAndReturn( xSocket.u.xTCP.eTCPState, pdTRUE ); + vTaskSuspendAll_Expect(); + xTaskResumeAll_ExpectAndReturn( 0 ); + } + + xTaskGetTickCount_ExpectAndReturn( xTickCountAck ); + xTaskGetTickCount_ExpectAndReturn( xTickCountAlive ); + + FreeRTOS_inet_ntop_ExpectAnyArgsAndReturn( NULL ); + + vSocketWakeUpUser_Expect( &xSocket ); + + vTCPStateChange( &xSocket, eTCPState ); + + TEST_ASSERT_EQUAL( eTCPState, xSocket.u.xTCP.eTCPState ); + TEST_ASSERT_EQUAL( xTickCountAck, xSocket.u.xTCP.xLastActTime ); + TEST_ASSERT_EQUAL( pdFALSE_UNSIGNED, xSocket.u.xTCP.bits.bWaitKeepAlive ); + TEST_ASSERT_EQUAL( pdFALSE_UNSIGNED, xSocket.u.xTCP.bits.bSendKeepAlive ); + TEST_ASSERT_EQUAL( 0, xSocket.u.xTCP.ucKeepRepCount ); + TEST_ASSERT_EQUAL( xTickCountAlive, xSocket.u.xTCP.xLastAliveTime ); + } } /** @@ -2210,6 +2241,7 @@ void test_xProcessReceivedTCPPacket_ConnectSyn_State_Rst_Change_State( void ) uxIPHeaderSizePacket_ExpectAnyArgsAndReturn( ipSIZE_OF_IPv4_HEADER ); pxTCPSocketLookup_ExpectAnyArgsAndReturn( pxSocket ); prvTCPSocketIsActive_ExpectAnyArgsAndReturn( pdTRUE ); + prvTCPSocketIsActive_ExpectAnyArgsAndReturn( pdTRUE ); vTaskSuspendAll_Expect(); xTaskResumeAll_ExpectAndReturn( 0 ); xTaskGetTickCount_ExpectAndReturn( 1000 );