Skip to content

[BUG] Bug State Transition From FIN_WAIT_1. #1303

@Fr3ya

Description

@Fr3ya

Describe the bug
The current implementation of the TCP state machine in FreeRTOS_TCP_State_Handling.c does not fully follow RFC793 behavior for connection termination when handling simultaneous close ( both sides send FIN). The stack transitions to CLOSE_WAIT rather than properly handling the CLOSING/TIME_WAIT states as specified by the RFC793.

Specifically, as specified in RFC793 [Page 74], when the state is in FIN-WAIT-1 STATE, it specifies:

If our FIN has been ACKed (perhaps in this segment), then enter TIME-WAIT, start the time-wait timer, turn off the other timers; otherwise, enter the CLOSING state.

When is FIN-WAIT2

Enter the TIME-WAIT state.  Start the time-wait timer, turn off the other timers.

However, In FreeRTOS_TCP_State_Handling.c, when the state is eFIN_WAIT_1 or eFIN_WAIT_2, it goes into the function prvTCPHandleFin. However, the implementation, when FIN is ACKed, it enters to CLOSE_WAIT (Line 302) instead of TIME_WAIT. When not ACKed, the implementation enters into LAST_ACK (Line 273) instead of CLOSING specified.

static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t * pxSocket,
const NetworkBufferDescriptor_t * pxNetworkBuffer )
{
/* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * )
&( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizePacket( pxNetworkBuffer ) ] ) );
TCPHeader_t * pxTCPHeader = &( pxProtocolHeaders->xTCPHeader );
uint8_t ucIntermediateResult = 0, ucTCPFlags = pxTCPHeader->ucTCPFlags;
TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;
BaseType_t xSendLength = 0;
uint32_t ulAckNr = FreeRTOS_ntohl( pxTCPHeader->ulAckNr );
if( ( ucTCPFlags & tcpTCP_FLAG_FIN ) != 0U )
{
pxTCPWindow->rx.ulCurrentSequenceNumber = pxTCPWindow->rx.ulFINSequenceNumber + 1U;
}
if( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED )
{
/* We haven't yet replied with a FIN, do so now. */
pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber;
pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED;
}
else
{
/* We did send a FIN already, see if it's ACK'd. */
if( ulAckNr == ( pxTCPWindow->tx.ulFINSequenceNumber + 1U ) )
{
pxSocket->u.xTCP.bits.bFinAcked = pdTRUE_UNSIGNED;
}
}
if( pxSocket->u.xTCP.bits.bFinAcked == pdFALSE_UNSIGNED )
{
pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFINSequenceNumber;
pxTCPHeader->ucTCPFlags = ( uint8_t ) tcpTCP_FLAG_ACK | ( uint8_t ) tcpTCP_FLAG_FIN;
/* And wait for the final ACK. */
vTCPStateChange( pxSocket, eLAST_ACK );
}
else
{
/* Our FIN has been ACK'd, the outgoing sequence number is now fixed. */
pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFINSequenceNumber + 1U;
if( pxSocket->u.xTCP.bits.bFinRecv == pdFALSE_UNSIGNED )
{
/* We have sent out a FIN but the peer hasn't replied with a FIN
* yet. Do nothing for the moment. */
pxTCPHeader->ucTCPFlags = 0U;
}
else
{
if( pxSocket->u.xTCP.bits.bFinLast == pdFALSE_UNSIGNED )
{
/* This is the third of the three-way hand shake: the last
* ACK. */
pxTCPHeader->ucTCPFlags = tcpTCP_FLAG_ACK;
}
else
{
/* The other party started the closure, so we just wait for the
* last ACK. */
pxTCPHeader->ucTCPFlags = 0U;
}
/* And wait for the user to close this socket. */
vTCPStateChange( pxSocket, eCLOSE_WAIT );

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions