1
0
mirror of https://github.com/FreeRTOS/FreeRTOS-Plus-TCP synced 2025-10-21 15:10:39 +08:00
Files
FreeRTOS-Plus-TCP/source/FreeRTOS_TCP_State_Handling.c
Aniruddha Kanhere a4124602cc Merge changes to main.
This commit brings in the refactoring and restructuring changes
from IntegrationTesting1 branch to the main branch.
It also includes additional unit tests for 100% coverage.
The rationale behind not creating a PR is that the conflicts were too
huge to be resolved correctly. Thus, a force push to the main branch is
being done.
2022-05-26 12:42:45 -07:00

1141 lines
52 KiB
C

/*
* FreeRTOS+TCP V2.3.4
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file FreeRTOS_TCP_State_Handling.c
* @brief Module which handles the TCP protocol state transition for FreeRTOS+TCP.
*
* Endianness: in this module all ports and IP addresses are stored in
* host byte-order, except fields in the IP-packets
*/
/* Standard includes. */
#include <stdint.h>
#include <stdio.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_DHCP.h"
#include "NetworkInterface.h"
#include "NetworkBufferManagement.h"
#include "FreeRTOS_ARP.h"
#include "FreeRTOS_TCP_Reception.h"
#include "FreeRTOS_TCP_Transmission.h"
#include "FreeRTOS_TCP_State_Handling.h"
#include "FreeRTOS_TCP_Utils.h"
/* Just make sure the contents doesn't get compiled if TCP is not enabled. */
#if ipconfigUSE_TCP == 1
/*
* Called to handle the closure of a TCP connection.
*/
static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t * pxSocket,
const NetworkBufferDescriptor_t * pxNetworkBuffer );
/*
* Called from prvTCPHandleState() as long as the TCP status is eSYN_RECEIVED to
* eCONNECT_SYN.
*/
static BaseType_t prvHandleSynReceived( FreeRTOS_Socket_t * pxSocket,
const NetworkBufferDescriptor_t * pxNetworkBuffer,
uint32_t ulReceiveLength,
UBaseType_t uxOptionsLength );
/*
* Called from prvTCPHandleState() as long as the TCP status is eESTABLISHED.
*/
static BaseType_t prvHandleEstablished( FreeRTOS_Socket_t * pxSocket,
NetworkBufferDescriptor_t ** ppxNetworkBuffer,
uint32_t ulReceiveLength,
UBaseType_t uxOptionsLength );
/*
* After a listening socket receives a new connection, it may duplicate itself.
* The copying takes place in prvTCPSocketCopy.
*/
static BaseType_t prvTCPSocketCopy( FreeRTOS_Socket_t * pxNewSocket,
FreeRTOS_Socket_t * pxSocket );
/**
* @brief Check whether the socket is active or not.
*
* @param[in] ucStatus: The status of the socket.
*
* @return pdTRUE if the socket must be checked. Non-active sockets
* are waiting for user action, either connect() or close().
*/
BaseType_t prvTCPSocketIsActive( uint8_t ucStatus )
{
BaseType_t xResult;
eIPTCPState_t eStatus = ( eIPTCPState_t ) ucStatus;
switch( eStatus )
{
case eCLOSED:
case eCLOSE_WAIT:
case eFIN_WAIT_2:
case eCLOSING:
case eTIME_WAIT:
xResult = pdFALSE;
break;
case eTCP_LISTEN:
case eCONNECT_SYN:
case eSYN_FIRST:
case eSYN_RECEIVED:
case eESTABLISHED:
case eFIN_WAIT_1:
case eLAST_ACK:
default:
xResult = pdTRUE;
break;
}
return xResult;
}
/*-----------------------------------------------------------*/
#if ( ipconfigTCP_HANG_PROTECTION == 1 )
/**
* @brief Some of the TCP states may only last a certain amount of time.
* This function checks if the socket is 'hanging', i.e. staying
* too long in the same state.
*
* @param[in] The socket to be checked.
*
* @return pdFALSE if no checks are needed, pdTRUE if checks were done, or negative
* in case the socket has reached a critical time-out. The socket will go to
* the eCLOSE_WAIT state.
*/
BaseType_t prvTCPStatusAgeCheck( FreeRTOS_Socket_t * pxSocket )
{
BaseType_t xResult;
eIPTCPState_t eState = ( eIPTCPState_t ) pxSocket->u.xTCP.ucTCPState;
switch( eState )
{
case eESTABLISHED:
/* If the 'ipconfigTCP_KEEP_ALIVE' option is enabled, sockets in
* state ESTABLISHED can be protected using keep-alive messages. */
xResult = pdFALSE;
break;
case eCLOSED:
case eTCP_LISTEN:
case eCLOSE_WAIT:
/* These 3 states may last for ever, up to the owner. */
xResult = pdFALSE;
break;
case eCONNECT_SYN:
case eSYN_FIRST:
case eSYN_RECEIVED:
case eFIN_WAIT_1:
case eFIN_WAIT_2:
case eCLOSING:
case eLAST_ACK:
case eTIME_WAIT:
default:
/* All other (non-connected) states will get anti-hanging
* protection. */
xResult = pdTRUE;
break;
}
if( xResult != pdFALSE )
{
/* How much time has past since the last active moment which is
* defined as A) a state change or B) a packet has arrived. */
TickType_t xAge = xTaskGetTickCount() - pxSocket->u.xTCP.xLastActTime;
/* ipconfigTCP_HANG_PROTECTION_TIME is in units of seconds. */
if( xAge > ( ( TickType_t ) ipconfigTCP_HANG_PROTECTION_TIME * ( TickType_t ) configTICK_RATE_HZ ) )
{
#if ( ipconfigHAS_DEBUG_PRINTF == 1 )
{
FreeRTOS_debug_printf( ( "Inactive socket closed: port %u rem %xip:%u status %s\n",
pxSocket->usLocalPort,
( unsigned ) pxSocket->u.xTCP.ulRemoteIP,
pxSocket->u.xTCP.usRemotePort,
FreeRTOS_GetTCPStateName( ( UBaseType_t ) pxSocket->u.xTCP.ucTCPState ) ) );
}
#endif /* ipconfigHAS_DEBUG_PRINTF */
/* Move to eCLOSE_WAIT, user may close the socket. */
vTCPStateChange( pxSocket, eCLOSE_WAIT );
/* When 'bPassQueued' true, this socket is an orphan until it
* gets connected. */
if( pxSocket->u.xTCP.bits.bPassQueued != pdFALSE_UNSIGNED )
{
/* vTCPStateChange() has called vSocketCloseNextTime()
* in case the socket is not yet owned by the application.
* Return a negative value to inform the caller that
* the socket will be closed in the next cycle. */
xResult = -1;
}
}
}
return xResult;
}
/*-----------------------------------------------------------*/
#endif /* if ( ipconfigTCP_HANG_PROTECTION == 1 ) */
/**
* @brief prvTCPHandleFin() will be called to handle connection closure. The
* closure starts when either a FIN has been received and accepted,
* or when the socket has sent a FIN flag to the peer. Before being
* called, it has been checked that both reception and transmission
* are complete.
*
* @param[in] pxSocket: Socket owning the the connection.
* @param[in] pxNetworkBuffer: The network buffer carrying the TCP packet.
*
* @return Length of the packet to be sent.
*/
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. */
ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * )
&( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( 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 );
}
}
pxTCPWindow->ulOurSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber;
if( pxTCPHeader->ucTCPFlags != 0U )
{
ucIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength;
xSendLength = ( BaseType_t ) ucIntermediateResult;
}
pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength ) << 2 );
if( xTCPWindowLoggingLevel != 0 )
{
FreeRTOS_debug_printf( ( "TCP: send FIN+ACK (ack %u, cur/nxt %u/%u) ourSeqNr %u | Rx %u\n",
( unsigned ) ( ulAckNr - pxTCPWindow->tx.ulFirstSequenceNumber ),
( unsigned ) ( pxTCPWindow->tx.ulCurrentSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ),
( unsigned ) ( pxTCPWindow->ulNextTxSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ),
( unsigned ) ( pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ),
( unsigned ) ( pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ) );
}
return xSendLength;
}
/*-----------------------------------------------------------*/
/**
* @brief prvHandleSynReceived(): called from prvTCPHandleState(). Called
* from the states: eSYN_RECEIVED and eCONNECT_SYN. If the flags
* received are correct, the socket will move to eESTABLISHED.
*
* @param[in] pxSocket: The socket handling the connection.
* @param[in] pxNetworkBuffer: The pointer to the network buffer carrying
* the packet.
* @param[in] ulReceiveLength: Length in bytes of the data received.
* @param[in] uxOptionsLength: Length of the TCP options in bytes.
*
* @return Length of the data to be sent.
*/
static BaseType_t prvHandleSynReceived( FreeRTOS_Socket_t * pxSocket,
const NetworkBufferDescriptor_t * pxNetworkBuffer,
uint32_t ulReceiveLength,
UBaseType_t uxOptionsLength )
{
/* Map the ethernet buffer onto the ProtocolHeader_t struct for easy access to the fields. */
ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * )
&( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) );
TCPHeader_t * pxTCPHeader = &pxProtocolHeaders->xTCPHeader;
TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;
uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags;
uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber );
BaseType_t xSendLength = 0;
UBaseType_t uxIntermediateResult = 0U;
/* Either expect a ACK or a SYN+ACK. */
uint8_t ucExpect = tcpTCP_FLAG_ACK;
const uint8_t ucFlagsMask = tcpTCP_FLAG_ACK | tcpTCP_FLAG_RST | tcpTCP_FLAG_SYN | tcpTCP_FLAG_FIN;
if( pxSocket->u.xTCP.ucTCPState == ( uint8_t ) eCONNECT_SYN )
{
ucExpect |= tcpTCP_FLAG_SYN;
}
if( ( ucTCPFlags & ucFlagsMask ) != ucExpect )
{
/* eSYN_RECEIVED: flags 0010 expected, not 0002. */
/* eSYN_RECEIVED: flags ACK expected, not SYN. */
FreeRTOS_debug_printf( ( "%s: flags %04X expected, not %04X\n",
( pxSocket->u.xTCP.ucTCPState == ( uint8_t ) eSYN_RECEIVED ) ? "eSYN_RECEIVED" : "eCONNECT_SYN",
ucExpect, ucTCPFlags ) );
/* In case pxSocket is not yet owned by the application, a closure
* of the socket will be scheduled for the next cycle. */
vTCPStateChange( pxSocket, eCLOSE_WAIT );
/* Send RST with the expected sequence and ACK numbers,
* otherwise the packet will be ignored. */
pxTCPWindow->ulOurSequenceNumber = FreeRTOS_htonl( pxTCPHeader->ulAckNr );
pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber;
pxTCPHeader->ucTCPFlags |= tcpTCP_FLAG_RST;
uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength;
xSendLength = ( BaseType_t ) uxIntermediateResult;
pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 );
}
else
{
pxTCPWindow->usPeerPortNumber = pxSocket->u.xTCP.usRemotePort;
pxTCPWindow->usOurPortNumber = pxSocket->usLocalPort;
if( pxSocket->u.xTCP.ucTCPState == ( uint8_t ) eCONNECT_SYN )
{
/* Map the Last packet onto the ProtocolHeader_t struct for easy access to the fields. */
ProtocolHeaders_t * pxLastHeaders = ( ( ProtocolHeaders_t * )
&( pxSocket->u.xTCP.xPacket.u.ucLastPacket[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) );
/* Clear the SYN flag in lastPacket. */
pxLastHeaders->xTCPHeader.ucTCPFlags = tcpTCP_FLAG_ACK;
pxProtocolHeaders->xTCPHeader.ucTCPFlags = tcpTCP_FLAG_ACK;
/* This socket was the one connecting actively so now perform the
* synchronisation. */
vTCPWindowInit( &pxSocket->u.xTCP.xTCPWindow,
ulSequenceNumber, pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber, ( uint32_t ) pxSocket->u.xTCP.usMSS );
pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1U;
pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber + 1U;
pxTCPWindow->tx.ulCurrentSequenceNumber++; /* because we send a TCP_SYN [ | TCP_ACK ]; */
pxTCPWindow->ulNextTxSequenceNumber++;
}
else if( ulReceiveLength == 0U )
{
pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber;
}
else
{
/* Nothing. */
}
/* The SYN+ACK has been confirmed, increase the next sequence number by
* 1. */
pxTCPWindow->ulOurSequenceNumber = pxTCPWindow->tx.ulFirstSequenceNumber + 1U;
#if ( ipconfigUSE_TCP_WIN == 1 )
{
FreeRTOS_debug_printf( ( "TCP: %s %u => %xip:%u set ESTAB (scaling %u)\n",
( pxSocket->u.xTCP.ucTCPState == ( uint8_t ) eCONNECT_SYN ) ? "active" : "passive",
pxSocket->usLocalPort,
( unsigned ) pxSocket->u.xTCP.ulRemoteIP,
pxSocket->u.xTCP.usRemotePort,
( unsigned ) pxSocket->u.xTCP.bits.bWinScaling ) );
}
#endif /* ipconfigUSE_TCP_WIN */
if( ( pxSocket->u.xTCP.ucTCPState == ( EventBits_t ) eCONNECT_SYN ) || ( ulReceiveLength != 0U ) )
{
pxTCPHeader->ucTCPFlags = tcpTCP_FLAG_ACK;
uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ( size_t ) ipSIZE_OF_TCP_HEADER + uxOptionsLength;
xSendLength = ( BaseType_t ) uxIntermediateResult;
pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 );
}
#if ( ipconfigUSE_TCP_WIN != 0 )
{
if( pxSocket->u.xTCP.bits.bWinScaling == pdFALSE_UNSIGNED )
{
/* The other party did not send a scaling factor.
* A shifting factor in this side must be canceled. */
pxSocket->u.xTCP.ucMyWinScaleFactor = 0;
pxSocket->u.xTCP.ucPeerWinScaleFactor = 0;
}
}
#endif /* ipconfigUSE_TCP_WIN */
/* This was the third step of connecting: SYN, SYN+ACK, ACK so now the
* connection is established. */
vTCPStateChange( pxSocket, eESTABLISHED );
}
return xSendLength;
}
/*-----------------------------------------------------------*/
/**
* @brief prvHandleEstablished(): called from prvTCPHandleState()
* Called if the status is eESTABLISHED. Data reception has been handled
* earlier. Here the ACK's from peer will be checked, and if a FIN is received,
* the code will check if it may be accepted, i.e. if all expected data has been
* completely received.
*
* @param[in] pxSocket: The socket owning the connection.
* @param[in,out] ppxNetworkBuffer: Pointer to pointer to the network buffer.
* @param[in] ulReceiveLength: The length of the received packet.
* @param[in] uxOptionsLength: Length of TCP options.
*
* @return The send length of the packet to be sent.
*/
static BaseType_t prvHandleEstablished( FreeRTOS_Socket_t * pxSocket,
NetworkBufferDescriptor_t ** ppxNetworkBuffer,
uint32_t ulReceiveLength,
UBaseType_t uxOptionsLength )
{
/* Map the buffer onto the ProtocolHeader_t struct for easy access to the fields. */
ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * )
&( ( *ppxNetworkBuffer )->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizeSocket( pxSocket ) ] ) );
TCPHeader_t * pxTCPHeader = &pxProtocolHeaders->xTCPHeader;
TCPWindow_t * pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;
uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags;
uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ), ulCount, ulIntermediateResult = 0;
BaseType_t xSendLength = 0, xMayClose = pdFALSE, bRxComplete, bTxDone;
int32_t lDistance, lSendResult;
uint16_t usWindow;
UBaseType_t uxIntermediateResult = 0;
/* Remember the window size the peer is advertising. */
usWindow = FreeRTOS_ntohs( pxTCPHeader->usWindow );
pxSocket->u.xTCP.ulWindowSize = ( uint32_t ) usWindow;
#if ( ipconfigUSE_TCP_WIN != 0 )
{
pxSocket->u.xTCP.ulWindowSize =
( pxSocket->u.xTCP.ulWindowSize << pxSocket->u.xTCP.ucPeerWinScaleFactor );
}
#endif /* ipconfigUSE_TCP_WIN */
if( ( ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_ACK ) == 0U )
{
/* RFC793: If ACK bit is not set at this state, the segment should
* be dropped
*/
}
else
{
ulCount = ulTCPWindowTxAck( pxTCPWindow, FreeRTOS_ntohl( pxTCPHeader->ulAckNr ) );
/* ulTCPWindowTxAck() returns the number of bytes which have been acked,
* starting at 'tx.ulCurrentSequenceNumber'. Advance the tail pointer in
* txStream. */
if( ( pxSocket->u.xTCP.txStream != NULL ) && ( ulCount > 0U ) )
{
/* Just advancing the tail index, 'ulCount' bytes have been
* confirmed, and because there is new space in the txStream, the
* user/owner should be woken up. */
/* _HT_ : only in case the socket's waiting? */
if( uxStreamBufferGet( pxSocket->u.xTCP.txStream, 0U, NULL, ( size_t ) ulCount, pdFALSE ) != 0U )
{
pxSocket->xEventBits |= ( EventBits_t ) eSOCKET_SEND;
#if ipconfigSUPPORT_SELECT_FUNCTION == 1
{
if( ( pxSocket->xSelectBits & ( ( EventBits_t ) eSELECT_WRITE ) ) != 0U )
{
pxSocket->xEventBits |= ( ( EventBits_t ) eSELECT_WRITE ) << SOCKET_EVENT_BIT_COUNT;
}
}
#endif
/* In case the socket owner has installed an OnSent handler,
* call it now. */
#if ( ipconfigUSE_CALLBACKS == 1 )
{
if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleSent ) )
{
pxSocket->u.xTCP.pxHandleSent( ( Socket_t ) pxSocket, ulCount );
}
}
#endif /* ipconfigUSE_CALLBACKS == 1 */
}
}
/* If this socket has a stream for transmission, add the data to the
* outgoing segment(s). */
if( pxSocket->u.xTCP.txStream != NULL )
{
prvTCPAddTxData( pxSocket );
}
pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber;
if( ( pxSocket->u.xTCP.bits.bFinAccepted != pdFALSE_UNSIGNED ) || ( ( ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_FIN ) != 0U ) )
{
/* Peer is requesting to stop, see if we're really finished. */
xMayClose = pdTRUE;
/* Checks are only necessary if we haven't sent a FIN yet. */
if( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED )
{
/* xTCPWindowTxDone returns true when all Tx queues are empty. */
bRxComplete = xTCPWindowRxEmpty( pxTCPWindow );
bTxDone = xTCPWindowTxDone( pxTCPWindow );
if( ( bRxComplete == 0 ) || ( bTxDone == 0 ) )
{
/* Refusing FIN: Rx incomplete 1 optlen 4 tx done 1. */
FreeRTOS_debug_printf( ( "Refusing FIN[%u,%u]: RxCompl %d tx done %d\n",
pxSocket->usLocalPort,
pxSocket->u.xTCP.usRemotePort,
( int ) bRxComplete,
( int ) bTxDone ) );
xMayClose = pdFALSE;
}
else
{
ulIntermediateResult = ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulCurrentSequenceNumber;
lDistance = ( int32_t ) ulIntermediateResult;
if( lDistance > 1 )
{
FreeRTOS_debug_printf( ( "Refusing FIN: Rx not complete %d (cur %u high %u)\n",
( int ) lDistance,
( unsigned ) ( pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ),
( unsigned ) ( pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ) );
xMayClose = pdFALSE;
}
}
}
if( xTCPWindowLoggingLevel > 0 )
{
FreeRTOS_debug_printf( ( "TCP: FIN received, mayClose = %d (Rx %u Len %d, Tx %u)\n",
( int ) xMayClose,
( unsigned ) ( ulSequenceNumber - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber ),
( unsigned ) ulReceiveLength,
( unsigned ) ( pxTCPWindow->tx.ulCurrentSequenceNumber - pxSocket->u.xTCP.xTCPWindow.tx.ulFirstSequenceNumber ) ) );
}
if( xMayClose != pdFALSE )
{
pxSocket->u.xTCP.bits.bFinAccepted = pdTRUE_UNSIGNED;
xSendLength = prvTCPHandleFin( pxSocket, *ppxNetworkBuffer );
}
}
if( xMayClose == pdFALSE )
{
pxTCPHeader->ucTCPFlags = tcpTCP_FLAG_ACK;
if( ulReceiveLength != 0U )
{
uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength;
xSendLength = ( BaseType_t ) uxIntermediateResult;
/* TCP-offset equals '( ( length / 4 ) << 4 )', resulting in a shift-left 2 */
pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 );
if( pxSocket->u.xTCP.bits.bFinSent != pdFALSE_UNSIGNED )
{
pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFINSequenceNumber;
}
}
/* Now get data to be transmitted. */
/* _HT_ patch: since the MTU has be fixed at 1500 in stead of 1526, TCP
* can not send-out both TCP options and also a full packet. Sending
* options (SACK) is always more urgent than sending data, which can be
* sent later. */
if( uxOptionsLength == 0U )
{
/* prvTCPPrepareSend might allocate a bigger network buffer, if
* necessary. */
lSendResult = prvTCPPrepareSend( pxSocket, ppxNetworkBuffer, uxOptionsLength );
if( lSendResult > 0 )
{
xSendLength = ( BaseType_t ) lSendResult;
}
}
}
}
return xSendLength;
}
/*-----------------------------------------------------------*/
/**
* @brief Check incoming packets for valid data and handle the state of the
* TCP connection and respond according to the situation.
*
* @param[in] pxSocket: The socket whose connection state is being handled.
* @param[in] ppxNetworkBuffer: The network buffer descriptor holding the
* packet received from the peer.
*
* @return If the data is correct and some packet was sent to the peer, then
* the number of bytes sent is returned, or else a negative value is
* returned indicating an error.
*
* @note prvTCPHandleState() is the most important function of this TCP stack
* We've tried to keep it (relatively short) by putting a lot of code in
* the static functions above:
*
* prvCheckRxData()
* prvStoreRxData()
* prvSetOptions()
* prvHandleSynReceived()
* prvHandleEstablished()
* prvSendData()
*
* As these functions are declared static, and they're called from one location
* only, most compilers will inline them, thus avoiding a call and return.
*/
BaseType_t prvTCPHandleState( FreeRTOS_Socket_t * pxSocket,
NetworkBufferDescriptor_t ** ppxNetworkBuffer )
{
/* Map the buffer onto the ProtocolHeader_t struct for easy access to the fields. */
ProtocolHeaders_t * pxProtocolHeaders = ( ( ProtocolHeaders_t * )
&( ( *ppxNetworkBuffer )->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( *ppxNetworkBuffer ) ] ) );
TCPHeader_t * pxTCPHeader = &( pxProtocolHeaders->xTCPHeader );
BaseType_t xSendLength = 0;
uint32_t ulReceiveLength; /* Number of bytes contained in the TCP message. */
uint8_t * pucRecvData;
uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber );
/* uxOptionsLength: the size of the options to be sent (always a multiple of
* 4 bytes)
* 1. in the SYN phase, we shall communicate the MSS
* 2. in case of a SACK, Selective ACK, ack a segment which comes in
* out-of-order. */
UBaseType_t uxOptionsLength = 0U;
uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags;
TCPWindow_t * pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow );
UBaseType_t uxIntermediateResult = 0;
uint32_t ulSum;
/* First get the length and the position of the received data, if any.
* pucRecvData will point to the first byte of the TCP payload. */
ulReceiveLength = ( uint32_t ) prvCheckRxData( *ppxNetworkBuffer, &pucRecvData );
if( pxSocket->u.xTCP.ucTCPState >= ( uint8_t ) eESTABLISHED )
{
if( pxTCPWindow->rx.ulCurrentSequenceNumber == ( ulSequenceNumber + 1U ) )
{
/* This is most probably a keep-alive message from peer. Setting
* 'bWinChange' doesn't cause a window-size-change, the flag is used
* here to force sending an immediate ACK. */
pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED;
}
}
/* Keep track of the highest sequence number that might be expected within
* this connection. */
ulSum = ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulHighestSequenceNumber;
if( ( ( int32_t ) ulSum ) > 0 )
{
pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + ulReceiveLength;
}
/* Storing data may result in a fatal error if malloc() fails. */
if( prvStoreRxData( pxSocket, pucRecvData, *ppxNetworkBuffer, ulReceiveLength ) < 0 )
{
xSendLength = -1;
}
else
{
eIPTCPState_t eState;
uxOptionsLength = prvSetOptions( pxSocket, *ppxNetworkBuffer );
if( ( pxSocket->u.xTCP.ucTCPState == ( uint8_t ) eSYN_RECEIVED ) && ( ( ucTCPFlags & ( uint8_t ) tcpTCP_FLAG_CTRL ) == ( uint8_t ) tcpTCP_FLAG_SYN ) )
{
FreeRTOS_debug_printf( ( "eSYN_RECEIVED: ACK expected, not SYN: peer missed our SYN+ACK\n" ) );
/* In eSYN_RECEIVED a simple ACK is expected, but apparently the
* 'SYN+ACK' didn't arrive. Step back to the previous state in which
* a first incoming SYN is handled. The SYN was counted already so
* decrease it first. */
vTCPStateChange( pxSocket, eSYN_FIRST );
}
if( ( ( ucTCPFlags & tcpTCP_FLAG_FIN ) != 0U ) && ( pxSocket->u.xTCP.bits.bFinRecv == pdFALSE_UNSIGNED ) )
{
/* It's the first time a FIN has been received, remember its
* sequence number. */
pxTCPWindow->rx.ulFINSequenceNumber = ulSequenceNumber + ulReceiveLength;
pxSocket->u.xTCP.bits.bFinRecv = pdTRUE_UNSIGNED;
/* Was peer the first one to send a FIN? */
if( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED )
{
/* If so, don't send the-last-ACK. */
pxSocket->u.xTCP.bits.bFinLast = pdTRUE_UNSIGNED;
}
}
eState = ( eIPTCPState_t ) pxSocket->u.xTCP.ucTCPState;
switch( eState )
{
case eCLOSED: /* (server + client) no connection state at all. */
/* Nothing to do for a closed socket, except waiting for the
* owner. */
break;
case eTCP_LISTEN: /* (server) waiting for a connection request from
* any remote TCP and port. */
/* The listen state was handled in xProcessReceivedTCPPacket().
* Should not come here. */
break;
case eSYN_FIRST: /* (server) Just received a SYN request for a server
* socket. */
/* A new socket has been created, reply with a SYN+ACK.
* Acknowledge with seq+1 because the SYN is seen as pseudo data
* with len = 1. */
uxOptionsLength = prvSetSynAckOptions( pxSocket, pxTCPHeader );
pxTCPHeader->ucTCPFlags = ( uint8_t ) tcpTCP_FLAG_SYN | ( uint8_t ) tcpTCP_FLAG_ACK;
uxIntermediateResult = uxIPHeaderSizeSocket( pxSocket ) + ipSIZE_OF_TCP_HEADER + uxOptionsLength;
xSendLength = ( BaseType_t ) uxIntermediateResult;
/* Set the TCP offset field: ipSIZE_OF_TCP_HEADER equals 20 and
* uxOptionsLength is a multiple of 4. The complete expression is:
* ucTCPOffset = ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) / 4 ) << 4 */
pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 );
vTCPStateChange( pxSocket, eSYN_RECEIVED );
pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1U;
pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber + 1U;
pxTCPWindow->ulNextTxSequenceNumber = pxTCPWindow->tx.ulFirstSequenceNumber + 1U;
pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->tx.ulFirstSequenceNumber + 1U; /* because we send a TCP_SYN. */
break;
case eCONNECT_SYN: /* (client) also called SYN_SENT: we've just send a
* SYN, expect a SYN+ACK and send a ACK now. */
/* Fall through */
case eSYN_RECEIVED: /* (server) we've had a SYN, replied with SYN+SCK
* expect a ACK and do nothing. */
xSendLength = prvHandleSynReceived( pxSocket, *( ppxNetworkBuffer ), ulReceiveLength, uxOptionsLength );
break;
case eESTABLISHED: /* (server + client) an open connection, data
* received can be delivered to the user. The normal
* state for the data transfer phase of the connection
* The closing states are also handled here with the
* use of some flags. */
xSendLength = prvHandleEstablished( pxSocket, ppxNetworkBuffer, ulReceiveLength, uxOptionsLength );
break;
case eLAST_ACK: /* (server + client) waiting for an acknowledgement
* of the connection termination request previously
* sent to the remote TCP (which includes an
* acknowledgement of its connection termination
* request). */
/* Fall through */
case eFIN_WAIT_1: /* (server + client) waiting for a connection termination request from the remote TCP,
* or an acknowledgement of the connection termination request previously sent. */
/* Fall through */
case eFIN_WAIT_2: /* (server + client) waiting for a connection termination request from the remote TCP. */
xSendLength = prvTCPHandleFin( pxSocket, *ppxNetworkBuffer );
break;
case eCLOSE_WAIT: /* (server + client) waiting for a connection
* termination request from the local user. Nothing to
* do, connection is closed, wait for owner to close
* this socket. */
break;
case eCLOSING: /* (server + client) waiting for a connection
* termination request acknowledgement from the remote
* TCP. */
break;
case eTIME_WAIT: /* (either server or client) waiting for enough time
* to pass to be sure the remote TCP received the
* acknowledgement of its connection termination
* request. [According to RFC 793 a connection can stay
* in TIME-WAIT for a maximum of four minutes known as
* a MSL (maximum segment lifetime).] These states are
* implemented implicitly by settings flags like
* 'bFinSent', 'bFinRecv', and 'bFinAcked'. */
break;
default:
/* No more known states. */
break;
}
}
if( xSendLength > 0 )
{
xSendLength = prvSendData( pxSocket, ppxNetworkBuffer, ulReceiveLength, xSendLength );
}
return xSendLength;
}
/*-----------------------------------------------------------*/
/**
* @brief Handle 'listen' event on the given socket.
*
* @param[in] pxSocket: The socket on which the listen occurred.
* @param[in] pxNetworkBuffer: The network buffer carrying the packet.
*
* @return If a new socket/duplicate socket is created, then the pointer to
* that socket is returned or else, a NULL pointer is returned.
*/
FreeRTOS_Socket_t * prvHandleListen( FreeRTOS_Socket_t * pxSocket,
NetworkBufferDescriptor_t * pxNetworkBuffer )
{
/* Map the ethernet buffer onto a TCPPacket_t struct for easy access to the fields. */
const TCPPacket_t * pxTCPPacket = ( ( const TCPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer );
FreeRTOS_Socket_t * pxReturn = NULL;
uint32_t ulInitialSequenceNumber;
/* Silently discard a SYN packet which was not specifically sent for this node. */
if( pxTCPPacket->xIPHeader.ulDestinationIPAddress == *ipLOCAL_IP_ADDRESS_POINTER )
{
/* Assume that a new Initial Sequence Number will be required. Request
* it now in order to fail out if necessary. */
ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( *ipLOCAL_IP_ADDRESS_POINTER,
pxSocket->usLocalPort,
pxTCPPacket->xIPHeader.ulSourceIPAddress,
pxTCPPacket->xTCPHeader.usSourcePort );
}
else
{
/* Set the sequence number to 0 to avoid further processing. */
ulInitialSequenceNumber = 0U;
}
/* A pure SYN (without ACK) has come in, create a new socket to answer
* it. */
if( ulInitialSequenceNumber != 0U )
{
if( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED )
{
/* The flag bReuseSocket indicates that the same instance of the
* listening socket should be used for the connection. */
pxReturn = pxSocket;
pxSocket->u.xTCP.bits.bPassQueued = pdTRUE_UNSIGNED;
pxSocket->u.xTCP.pxPeerSocket = pxSocket;
}
else
{
/* The socket does not have the bReuseSocket flag set meaning create a
* new socket when a connection comes in. */
pxReturn = NULL;
if( pxSocket->u.xTCP.usChildCount >= pxSocket->u.xTCP.usBacklog )
{
FreeRTOS_printf( ( "Check: Socket %u already has %u / %u child%s\n",
pxSocket->usLocalPort,
pxSocket->u.xTCP.usChildCount,
pxSocket->u.xTCP.usBacklog,
( pxSocket->u.xTCP.usChildCount == 1U ) ? "" : "ren" ) );
( void ) prvTCPSendReset( pxNetworkBuffer );
}
else
{
FreeRTOS_Socket_t * pxNewSocket = ( FreeRTOS_Socket_t * )
FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
if( ( pxNewSocket == NULL ) || ( pxNewSocket == FREERTOS_INVALID_SOCKET ) )
{
FreeRTOS_debug_printf( ( "TCP: Listen: new socket failed\n" ) );
( void ) prvTCPSendReset( pxNetworkBuffer );
}
else if( prvTCPSocketCopy( pxNewSocket, pxSocket ) != pdFALSE )
{
/* The socket will be connected immediately, no time for the
* owner to setsockopt's, therefore copy properties of the server
* socket to the new socket. Only the binding might fail (due to
* lack of resources). */
pxReturn = pxNewSocket;
}
else
{
/* Copying failed somehow. */
}
}
}
}
if( ( ulInitialSequenceNumber != 0U ) && ( pxReturn != NULL ) )
{
/* Map the byte stream onto the ProtocolHeaders_t for easy access to the fields. */
const ProtocolHeaders_t * pxProtocolHeaders = ( ( const ProtocolHeaders_t * )
&( pxNetworkBuffer->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + xIPHeaderSize( pxNetworkBuffer ) ] ) );
pxReturn->u.xTCP.usRemotePort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usSourcePort );
pxReturn->u.xTCP.ulRemoteIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulSourceIPAddress );
pxReturn->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber;
/* Here is the SYN action. */
pxReturn->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = FreeRTOS_ntohl( pxProtocolHeaders->xTCPHeader.ulSequenceNumber );
prvSocketSetMSS( pxReturn );
prvTCPCreateWindow( pxReturn );
vTCPStateChange( pxReturn, eSYN_FIRST );
/* Make a copy of the header up to the TCP header. It is needed later
* on, whenever data must be sent to the peer. */
( void ) memcpy( ( void * ) pxReturn->u.xTCP.xPacket.u.ucLastPacket,
( const void * ) pxNetworkBuffer->pucEthernetBuffer,
sizeof( pxReturn->u.xTCP.xPacket.u.ucLastPacket ) );
}
return pxReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Duplicates a socket after a listening socket receives a connection and bind
* the new socket to the same port as the listening socket.
* Also, let the new socket inherit all properties from the listening socket.
*
* @param[in] pxNewSocket: Pointer to the new socket.
* @param[in] pxSocket: Pointer to the socket being duplicated.
*
* @return If all steps all successful, then pdTRUE is returned. Else, pdFALSE.
*/
static BaseType_t prvTCPSocketCopy( FreeRTOS_Socket_t * pxNewSocket,
FreeRTOS_Socket_t * pxSocket )
{
struct freertos_sockaddr xAddress;
BaseType_t xResult;
pxNewSocket->xReceiveBlockTime = pxSocket->xReceiveBlockTime;
pxNewSocket->xSendBlockTime = pxSocket->xSendBlockTime;
pxNewSocket->ucSocketOptions = pxSocket->ucSocketOptions;
pxNewSocket->u.xTCP.uxRxStreamSize = pxSocket->u.xTCP.uxRxStreamSize;
pxNewSocket->u.xTCP.uxTxStreamSize = pxSocket->u.xTCP.uxTxStreamSize;
pxNewSocket->u.xTCP.uxLittleSpace = pxSocket->u.xTCP.uxLittleSpace;
pxNewSocket->u.xTCP.uxEnoughSpace = pxSocket->u.xTCP.uxEnoughSpace;
pxNewSocket->u.xTCP.uxRxWinSize = pxSocket->u.xTCP.uxRxWinSize;
pxNewSocket->u.xTCP.uxTxWinSize = pxSocket->u.xTCP.uxTxWinSize;
#if ( ipconfigSOCKET_HAS_USER_SEMAPHORE == 1 )
{
pxNewSocket->pxUserSemaphore = pxSocket->pxUserSemaphore;
}
#endif /* ipconfigSOCKET_HAS_USER_SEMAPHORE */
#if ( ipconfigUSE_CALLBACKS == 1 )
{
/* In case call-backs are used, copy them from parent to child. */
pxNewSocket->u.xTCP.pxHandleConnected = pxSocket->u.xTCP.pxHandleConnected;
pxNewSocket->u.xTCP.pxHandleReceive = pxSocket->u.xTCP.pxHandleReceive;
pxNewSocket->u.xTCP.pxHandleSent = pxSocket->u.xTCP.pxHandleSent;
}
#endif /* ipconfigUSE_CALLBACKS */
#if ( ipconfigSUPPORT_SELECT_FUNCTION == 1 )
{
/* Child socket of listening sockets will inherit the Socket Set
* Otherwise the owner has no chance of including it into the set. */
if( pxSocket->pxSocketSet != NULL )
{
pxNewSocket->pxSocketSet = pxSocket->pxSocketSet;
pxNewSocket->xSelectBits = pxSocket->xSelectBits | ( ( EventBits_t ) eSELECT_READ ) | ( ( EventBits_t ) eSELECT_EXCEPT );
}
}
#endif /* ipconfigSUPPORT_SELECT_FUNCTION */
/* And bind it to the same local port as its parent. */
xAddress.sin_addr = *ipLOCAL_IP_ADDRESS_POINTER;
xAddress.sin_port = FreeRTOS_htons( pxSocket->usLocalPort );
#if ( ipconfigTCP_HANG_PROTECTION == 1 )
{
/* Only when there is anti-hanging protection, a socket may become an
* orphan temporarily. Once this socket is really connected, the owner of
* the server socket will be notified. */
/* When bPassQueued is true, the socket is an orphan until it gets
* connected. */
pxNewSocket->u.xTCP.bits.bPassQueued = pdTRUE_UNSIGNED;
pxNewSocket->u.xTCP.pxPeerSocket = pxSocket;
}
#else
{
/* A reference to the new socket may be stored and the socket is marked
* as 'passable'. */
/* When bPassAccept is true, this socket may be returned in a call to
* accept(). */
pxNewSocket->u.xTCP.bits.bPassAccept = pdTRUE_UNSIGNED;
if( pxSocket->u.xTCP.pxPeerSocket == NULL )
{
pxSocket->u.xTCP.pxPeerSocket = pxNewSocket;
}
}
#endif /* if ( ipconfigTCP_HANG_PROTECTION == 1 ) */
pxSocket->u.xTCP.usChildCount++;
FreeRTOS_debug_printf( ( "Gain: Socket %u now has %u / %u child%s\n",
pxSocket->usLocalPort,
pxSocket->u.xTCP.usChildCount,
pxSocket->u.xTCP.usBacklog,
( pxSocket->u.xTCP.usChildCount == 1U ) ? "" : "ren" ) );
/* Now bind the child socket to the same port as the listening socket. */
if( vSocketBind( pxNewSocket, &xAddress, sizeof( xAddress ), pdTRUE ) != 0 )
{
FreeRTOS_debug_printf( ( "TCP: Listen: new socket bind error\n" ) );
( void ) vSocketClose( pxNewSocket );
xResult = pdFALSE;
}
else
{
xResult = pdTRUE;
}
return xResult;
}
/*-----------------------------------------------------------*/
#if ( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) )
const char * FreeRTOS_GetTCPStateName( UBaseType_t ulState )
{
static const char * const pcStateNames[] =
{
"eCLOSED",
"eTCP_LISTEN",
"eCONNECT_SYN",
"eSYN_FIRST",
"eSYN_RECEIVED",
"eESTABLISHED",
"eFIN_WAIT_1",
"eFIN_WAIT_2",
"eCLOSE_WAIT",
"eCLOSING",
"eLAST_ACK",
"eTIME_WAIT",
"eUNKNOWN",
};
BaseType_t xIndex = ( BaseType_t ) ulState;
if( ( xIndex < 0 ) || ( xIndex >= ARRAY_SIZE( pcStateNames ) ) )
{
/* The last item is called 'eUNKNOWN' */
xIndex = ARRAY_SIZE( pcStateNames );
xIndex--;
}
return pcStateNames[ xIndex ];
}
#endif /* ( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) */
/*-----------------------------------------------------------*/
#endif /* ipconfigUSE_TCP == 1 */