Skip to content

Commit

Permalink
IPv4/single: connect() should return immediately after a protocol err…
Browse files Browse the repository at this point in the history
…or and other things (#559)

* IPv4/single: Let connect() return as soon as socket gets closed

* Let both connect() and accept() return after a 'eSOCKET_CLOSED' event

* Included hang protection of orphaned socket from PR #545

* Lexicon.txt change

* Remove a variable that was not used

* Update source/FreeRTOS_Sockets.c

Co-authored-by: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com>

* Update source/FreeRTOS_TCP_IP.c

Co-authored-by: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com>

* moved declaration to beginning of block

* Update source/FreeRTOS_TCP_IP.c

Co-authored-by: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com>

* Uncrustify: triggered by comment.

* Fix unit test expectations

* Avoid a recursive call to vTCPStateChange()

* Uncrustify: triggered by comment.

* Fix CBMC proof assumptions

* Get unit-test coverage up

* Fix timers unit-tests

* Socket unit-test for closed socket

* Fix a unit-test expectations

* Fix spell check

* Uncrustify: triggered by comment.

* Using debug_printf in stead of printf for logging.

* Use debug printf instead of printf in 2 locations

Co-authored-by: Hein Tibosch <hein@htibosch.net>
Co-authored-by: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com>
Co-authored-by: GitHub Action <action@github.com>
  • Loading branch information
4 people authored Oct 11, 2022
1 parent cb70927 commit 188a9d0
Show file tree
Hide file tree
Showing 10 changed files with 474 additions and 52 deletions.
2 changes: 2 additions & 0 deletions .github/lexicon.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,7 @@ vrxfaultinjection
vsocketbind
vsocketclose
vsocketclosenexttime
vsocketlistennexttime
vsocketselect
vsocketwakeupuser
vtasklist
Expand Down Expand Up @@ -1448,6 +1449,7 @@ xcallbacklist
xcheckloopback
xclearonexit
xclientsocket
xconnected
xcount
xdatalength
xdatalengthbytes
Expand Down
3 changes: 3 additions & 0 deletions source/FreeRTOS_IP_Timers.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ void vCheckNetworkTimers( void )

/* See if any socket was planned to be closed. */
vSocketCloseNextTime( NULL );

/* See if any reusable socket needs to go back to 'eTCP_LISTEN' state. */
vSocketListenNextTime( NULL );
#endif /* ipconfigUSE_TCP == 1 */
}
/*-----------------------------------------------------------*/
Expand Down
23 changes: 20 additions & 3 deletions source/FreeRTOS_Sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -3102,6 +3102,8 @@ void vSocketWakeUpUser( FreeRTOS_Socket_t * pxSocket )
/* And wait for the result */
for( ; ; )
{
EventBits_t uxEvents;

if( xTimed == pdFALSE )
{
/* Only in the first round, check for non-blocking */
Expand Down Expand Up @@ -3139,7 +3141,18 @@ void vSocketWakeUpUser( FreeRTOS_Socket_t * pxSocket )
}

/* Go sleeping until we get any down-stream event */
( void ) xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_CONNECT, pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime );
uxEvents = xEventGroupWaitBits( pxSocket->xEventGroup,
( EventBits_t ) eSOCKET_CONNECT | ( EventBits_t ) eSOCKET_CLOSED,
pdTRUE /*xClearOnExit*/,
pdFALSE /*xWaitAllBits*/,
xRemainingTime );

if( ( uxEvents & eSOCKET_CLOSED ) != 0U )
{
xResult = -pdFREERTOS_ERRNO_ENOTCONN;
FreeRTOS_debug_printf( ( "FreeRTOS_connect() stopped due to an error\n" ) );
break;
}
}
}

Expand Down Expand Up @@ -3286,8 +3299,12 @@ void vSocketWakeUpUser( FreeRTOS_Socket_t * pxSocket )
break;
}

/* Go sleeping until we get any down-stream event */
( void ) xEventGroupWaitBits( pxSocket->xEventGroup, ( EventBits_t ) eSOCKET_ACCEPT, pdTRUE /*xClearOnExit*/, pdFALSE /*xWaitAllBits*/, xRemainingTime );
/* Put the calling task to 'sleep' until a down-stream event is received. */
( void ) xEventGroupWaitBits( pxSocket->xEventGroup,
( EventBits_t ) eSOCKET_ACCEPT,
pdTRUE /*xClearOnExit*/,
pdFALSE /*xWaitAllBits*/,
xRemainingTime );
}
}

Expand Down
148 changes: 110 additions & 38 deletions source/FreeRTOS_TCP_IP.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,18 @@
/* MISRA Ref 8.9.1 [File scoped variables] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
/* coverity[misra_c_2012_rule_8_9_violation] */
static FreeRTOS_Socket_t * xPreviousSocket = NULL;
static FreeRTOS_Socket_t * xSocketToClose = NULL;

/** @brief When a connection is coming in on a reusable socket, and the
* SYN phase times out, the socket must be put back into eTCP_LISTEN
* mode, so it can accept a new connection again.
* This variable can be accessed by the IP task only. Thus, preventing any
* race condition.
*/
/* MISRA Ref 8.9.1 [File scoped variables] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
/* coverity[misra_c_2012_rule_8_9_violation] */
static FreeRTOS_Socket_t * xSocketToListen = NULL;

/*
* For anti-hang protection and TCP keep-alive messages. Called in two places:
Expand Down Expand Up @@ -110,12 +121,28 @@
/* coverity[single_use] */
void vSocketCloseNextTime( FreeRTOS_Socket_t * pxSocket )
{
if( ( xPreviousSocket != NULL ) && ( xPreviousSocket != pxSocket ) )
if( ( xSocketToClose != NULL ) && ( xSocketToClose != pxSocket ) )
{
( void ) vSocketClose( xPreviousSocket );
( void ) vSocketClose( xSocketToClose );
}

xPreviousSocket = pxSocket;
xSocketToClose = pxSocket;
}
/*-----------------------------------------------------------*/

/** @brief Postpone a call to FreeRTOS_listen() to avoid recursive calls.
*
* @param[in] pxSocket: The socket to be checked.
*/
/* coverity[single_use] */
void vSocketListenNextTime( FreeRTOS_Socket_t * pxSocket )
{
if( ( xSocketToListen != NULL ) && ( xSocketToListen != pxSocket ) )
{
( void ) FreeRTOS_listen( ( Socket_t ) xSocketToListen, xSocketToListen->u.xTCP.usBacklog );
}

xSocketToListen = pxSocket;
}
/*-----------------------------------------------------------*/

Expand Down Expand Up @@ -272,39 +299,60 @@
void vTCPStateChange( FreeRTOS_Socket_t * pxSocket,
enum eTCP_STATE eTCPState )
{
FreeRTOS_Socket_t * xParent = NULL;
FreeRTOS_Socket_t * xParent = pxSocket;
BaseType_t bBefore = tcpNOW_CONNECTED( ( BaseType_t ) pxSocket->u.xTCP.eTCPState ); /* Was it connected ? */
BaseType_t bAfter = tcpNOW_CONNECTED( ( BaseType_t ) eTCPState ); /* Is it connected now ? */

#if ( ipconfigHAS_DEBUG_PRINTF != 0 )
BaseType_t xPreviousState = ( BaseType_t ) pxSocket->u.xTCP.eTCPState;
#endif
BaseType_t xPreviousState = ( BaseType_t ) pxSocket->u.xTCP.eTCPState;

#if ( ipconfigUSE_CALLBACKS == 1 )
FreeRTOS_Socket_t * xConnected = NULL;
#endif

if( ( ( xPreviousState == eCONNECT_SYN ) ||
( xPreviousState == eSYN_FIRST ) ||
( xPreviousState == eSYN_RECEIVED ) ) &&
( eTCPState == eCLOSE_WAIT ) )
{
/* A socket was in the connecting phase but something
* went wrong and it should be closed. */
FreeRTOS_debug_printf( ( "Move from %s to %s\n",
FreeRTOS_GetTCPStateName( xPreviousState ),
FreeRTOS_GetTCPStateName( eTCPState ) ) );

/* Set the flag to show that it was connected before and that the
* status has changed now. This will cause the control flow to go
* in the below if condition.*/
bBefore = pdTRUE;
}

/* Has the connected status changed? */
if( bBefore != bAfter )
{
/* if bPassQueued is true, this socket is an orphan until it gets connected. */
if( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED )
{
/* Find it's parent if the reuse bit is not set. */
if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED )
{
xParent = pxSocket->u.xTCP.pxPeerSocket;
configASSERT( xParent != NULL );
}
}

/* Is the socket connected now ? */
if( bAfter != pdFALSE )
{
/* if bPassQueued is true, this socket is an orphan until it gets connected. */
if( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED )
{
/* Now that it is connected, find it's parent. */
if( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED )
{
xParent = pxSocket;
}
else
{
xParent = pxSocket->u.xTCP.pxPeerSocket;
configASSERT( xParent != NULL );
}

if( xParent != NULL )
{
/* The child socket has got connected. See if the parent
* ( the listening socket ) should be signalled, or if a
* call-back must be made, in which case 'xConnected' will
* be set to the parent socket. */

if( xParent->u.xTCP.pxPeerSocket == NULL )
{
xParent->u.xTCP.pxPeerSocket = pxSocket;
Expand Down Expand Up @@ -347,6 +395,9 @@
}
else
{
/* An active connect() has succeeded. In this case there is no
* ( listening ) parent socket. Signal the now connected socket. */

pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_CONNECT;

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
Expand All @@ -361,14 +412,14 @@
}
else /* bAfter == pdFALSE, connection is closed. */
{
/* Notify/wake-up the socket-owner by setting a semaphore. */
pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_CLOSED;
/* Notify/wake-up the socket-owner by setting the event bits. */
xParent->xEventBits |= ( EventBits_t ) eSOCKET_CLOSED;

#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
{
if( ( pxSocket->xSelectBits & ( EventBits_t ) eSELECT_EXCEPT ) != 0U )
if( ( xParent->xSelectBits & ( EventBits_t ) eSELECT_EXCEPT ) != 0U )
{
pxSocket->xEventBits |= ( ( EventBits_t ) eSELECT_EXCEPT ) << SOCKET_EVENT_BIT_COUNT;
xParent->xEventBits |= ( ( EventBits_t ) eSELECT_EXCEPT ) << SOCKET_EVENT_BIT_COUNT;
}
}
#endif
Expand All @@ -393,30 +444,51 @@
pxSocket->u.xTCP.usTimeout = 0U;
}
}
else

if( ( eTCPState == eCLOSED ) ||
( eTCPState == eCLOSE_WAIT ) )
{
if( ( eTCPState == eCLOSED ) ||
( eTCPState == eCLOSE_WAIT ) )
/* Socket goes to status eCLOSED because of a RST.
* When nobody owns the socket yet, delete it. */
if( ( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) ||
( pxSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) )
{
/* Socket goes to status eCLOSED because of a RST.
* When nobody owns the socket yet, delete it. */
if( ( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED ) ||
( pxSocket->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) )
{
FreeRTOS_debug_printf( ( "vTCPStateChange: Closing socket\n" ) );
FreeRTOS_debug_printf( ( "vTCPStateChange: Closing socket\n" ) );

if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED )
{
configASSERT( xIsCallingFromIPTask() != pdFALSE );
vSocketCloseNextTime( pxSocket );
}
if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED )
{
configASSERT( xIsCallingFromIPTask() != pdFALSE );
vSocketCloseNextTime( pxSocket );
}
}
}

/* Fill in the new state. */
pxSocket->u.xTCP.eTCPState = eTCPState;

if( ( eTCPState == eCLOSE_WAIT ) && ( pxSocket->u.xTCP.bits.bReuseSocket == pdTRUE_UNSIGNED ) )
{
switch( xPreviousState )
{
case eSYN_FIRST: /* 3 (server) Just created, must ACK the SYN request */
case eSYN_RECEIVED: /* 4 (server) waiting for a confirming connection request */
FreeRTOS_debug_printf( ( "Restoring a reuse socket port %u\n", pxSocket->usLocalPort ) );

/* Go back into listening mode. Set the TCP status to 'eCLOSED',
* otherwise FreeRTOS_listen() will refuse the action. */
pxSocket->u.xTCP.eTCPState = eCLOSED;

/* vSocketListenNextTime() makes sure that FreeRTOS_listen() will be called
* before the IP-task handles any new message. */
vSocketListenNextTime( pxSocket );
break;

default:
/* Nothing to do. */
break;
}
}

/* Touch the alive timers because moving to another state. */
prvTCPTouchSocket( pxSocket );

Expand Down Expand Up @@ -585,7 +657,7 @@
ulLocalIP = FreeRTOS_htonl( pxIPHeader->ulDestinationIPAddress );
ulRemoteIP = FreeRTOS_htonl( pxIPHeader->ulSourceIPAddress );

/* Find the destination socket, and if not found: return a socket listing to
/* Find the destination socket, and if not found: return a socket listening to
* the destination PORT. */
pxSocket = ( FreeRTOS_Socket_t * ) pxTCPSocketLookup( ulLocalIP, usLocalPort, ulRemoteIP, usRemotePort );

Expand Down
5 changes: 5 additions & 0 deletions source/include/FreeRTOS_IP_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,11 @@ typedef struct xSOCKET
*/
void vSocketCloseNextTime( FreeRTOS_Socket_t * pxSocket );

/*
* Postpone a call to listen() by the IP-task.
*/
void vSocketListenNextTime( FreeRTOS_Socket_t * pxSocket );

/*
* Lookup a TCP socket, using a multiple matching: both port numbers and
* return IP address.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ FreeRTOS_Socket_t * pxTCPSocketLookup( uint32_t ulLocalIP,
xRetSocket->u.xTCP.txStream = safeMalloc( sizeof( StreamBuffer_t ) );
xRetSocket->u.xTCP.pxPeerSocket = safeMalloc( sizeof( StreamBuffer_t ) );

/* This bit depicts whether the socket was supposed to be reused or not. */
if( xRetSocket->u.xTCP.pxPeerSocket == NULL )
{
xRetSocket->u.xTCP.bits.bReuseSocket = pdTRUE_UNSIGNED;
}
else
{
xRetSocket->u.xTCP.bits.bReuseSocket = pdFALSE_UNSIGNED;
}

if( xIsCallingFromIPTask() == pdFALSE )
{
xRetSocket->u.xTCP.bits.bPassQueued = pdFALSE_UNSIGNED;
Expand Down Expand Up @@ -94,13 +104,13 @@ void harness()
{
NetworkBufferDescriptor_t * pxNetworkBuffer = safeMalloc( sizeof( NetworkBufferDescriptor_t ) );

if( pxNetworkBuffer )
{
pxNetworkBuffer->pucEthernetBuffer = safeMalloc( sizeof( TCPPacket_t ) );
}
/* To avoid asserting on the network buffer being NULL. */
__CPROVER_assume( pxNetworkBuffer != NULL );

if( pxNetworkBuffer && pxNetworkBuffer->pucEthernetBuffer )
{
xProcessReceivedTCPPacket( pxNetworkBuffer );
}
pxNetworkBuffer->pucEthernetBuffer = safeMalloc( sizeof( TCPPacket_t ) );

/* To avoid asserting on the ethernet buffer being NULL. */
__CPROVER_assume( pxNetworkBuffer->pucEthernetBuffer != NULL );

xProcessReceivedTCPPacket( pxNetworkBuffer );
}
Loading

0 comments on commit 188a9d0

Please sign in to comment.