Skip to content

[BUG] Bug State Transition From FIN_WAIT_1. #1303

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 );

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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