mirror of
https://github.com/FreeRTOS/FreeRTOS-Plus-TCP
synced 2025-10-21 06:40:08 +08:00
1072 lines
48 KiB
C
1072 lines
48 KiB
C
/*
|
|
* FreeRTOS+TCP <DEVELOPMENT BRANCH>
|
|
* Copyright (C) 2022 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 );
|
|
|
|
|
|
/**
|
|
* @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( eIPTCPState_t eStatus )
|
|
{
|
|
BaseType_t xResult;
|
|
|
|
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 = pxSocket->u.xTCP.eTCPState;
|
|
|
|
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.xRemoteIP.ulIP_IPv4,
|
|
pxSocket->u.xTCP.usRemotePort,
|
|
FreeRTOS_GetTCPStateName( ( UBaseType_t ) pxSocket->u.xTCP.eTCPState ) ) );
|
|
}
|
|
#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. */
|
|
|
|
/* 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 );
|
|
}
|
|
}
|
|
|
|
pxTCPWindow->ulOurSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber;
|
|
|
|
if( pxTCPHeader->ucTCPFlags != 0U )
|
|
{
|
|
ucIntermediateResult = ( uint8_t ) ( 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. */
|
|
|
|
/* 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 + 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.eTCPState == 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.eTCPState == ( 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.eTCPState == eCONNECT_SYN )
|
|
{
|
|
/* Map the Last packet 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 * 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 )
|
|
{
|
|
char pcBuffer[ 40 ]; /* Space to print an IP-address. */
|
|
FreeRTOS_inet_ntop( ( pxSocket->bits.bIsIPv6 != 0 ) ? FREERTOS_AF_INET6 : FREERTOS_AF_INET,
|
|
( void * ) pxSocket->u.xTCP.xRemoteIP.xIP_IPv6.ucBytes,
|
|
pcBuffer,
|
|
sizeof( pcBuffer ) );
|
|
FreeRTOS_debug_printf( ( "TCP: %s %u => %s port %u set ESTAB (scaling %u)\n",
|
|
( pxSocket->u.xTCP.eTCPState == ( uint8_t ) eCONNECT_SYN ) ? "active" : "passive",
|
|
pxSocket->usLocalPort,
|
|
pcBuffer,
|
|
pxSocket->u.xTCP.usRemotePort,
|
|
( unsigned ) pxSocket->u.xTCP.bits.bWinScaling ) );
|
|
}
|
|
#endif /* ipconfigUSE_TCP_WIN */
|
|
|
|
if( ( pxSocket->u.xTCP.eTCPState == 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. */
|
|
|
|
/* 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 * )
|
|
&( ( *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. */
|
|
|
|
/* 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 * )
|
|
&( ( *ppxNetworkBuffer )->pucEthernetBuffer[ ipSIZE_OF_ETH_HEADER + uxIPHeaderSizePacket( *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.eTCPState >= 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.eTCPState == 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.eTCPState;
|
|
|
|
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 )
|
|
{
|
|
FreeRTOS_Socket_t * pxNewSocket = NULL;
|
|
|
|
if( uxIPHeaderSizePacket( pxNetworkBuffer ) == ipSIZE_OF_IPv6_HEADER )
|
|
{
|
|
pxNewSocket = prvHandleListen_IPV6( pxSocket, pxNetworkBuffer );
|
|
}
|
|
else
|
|
{
|
|
pxNewSocket = prvHandleListen_IPV4( pxSocket, pxNetworkBuffer );
|
|
}
|
|
|
|
return pxNewSocket;
|
|
}
|
|
/*-----------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
* @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.
|
|
*/
|
|
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. */
|
|
/*TODO 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 */
|