X-Git-Url: https://git.sur5r.net/?p=freertos;a=blobdiff_plain;f=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2FFreeRTOS_TCP_IP.c;fp=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2FFreeRTOS_TCP_IP.c;h=849796508a8e37ad34236c8d20ad6de8ea726441;hp=d26756589d64b65f6c459e028417063d6e02094a;hb=b15dfacb6026af3b0ba697e5753844923b468d2b;hpb=4334233a064299a09d167a497889d3860932a587 diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_TCP_IP.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_TCP_IP.c index d26756589..849796508 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_TCP_IP.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_TCP_IP.c @@ -1,3390 +1,3390 @@ -/* - * FreeRTOS+TCP V2.2.0 - * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 - */ - -/* - * FreeRTOS_TCP_IP.c - * Module which handles the TCP connections for FreeRTOS+TCP. - * It depends on FreeRTOS_TCP_WIN.c, which handles the TCP windowing - * schemes. - * - * Endianness: in this module all ports and IP addresses are stored in - * host byte-order, except fields in the IP-packets - */ - -/* Standard includes. */ -#include -#include - -/* 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_TCP_IP.h" -#include "FreeRTOS_DHCP.h" -#include "NetworkInterface.h" -#include "NetworkBufferManagement.h" -#include "FreeRTOS_ARP.h" -#include "FreeRTOS_TCP_WIN.h" - - -/* Just make sure the contents doesn't get compiled if TCP is not enabled. */ -#if ipconfigUSE_TCP == 1 - -/* This compile-time test was moved to here because some macro's -were unknown within 'FreeRTOSIPConfigDefaults.h'. It tests whether -the defined MTU size can contain at least a complete TCP packet. */ - -#if ( ( ipconfigTCP_MSS + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) > ipconfigNETWORK_MTU ) - #error The ipconfigTCP_MSS setting in FreeRTOSIPConfig.h is too large. -#endif - -/* - * The meaning of the TCP flags: - */ -#define ipTCP_FLAG_FIN 0x0001u /* No more data from sender */ -#define ipTCP_FLAG_SYN 0x0002u /* Synchronize sequence numbers */ -#define ipTCP_FLAG_RST 0x0004u /* Reset the connection */ -#define ipTCP_FLAG_PSH 0x0008u /* Push function: please push buffered data to the recv application */ -#define ipTCP_FLAG_ACK 0x0010u /* Acknowledgment field is significant */ -#define ipTCP_FLAG_URG 0x0020u /* Urgent pointer field is significant */ -#define ipTCP_FLAG_ECN 0x0040u /* ECN-Echo */ -#define ipTCP_FLAG_CWR 0x0080u /* Congestion Window Reduced */ -#define ipTCP_FLAG_NS 0x0100u /* ECN-nonce concealment protection */ -#define ipTCP_FLAG_RSV 0x0E00u /* Reserved, keep 0 */ - -/* A mask to filter all protocol flags. */ -#define ipTCP_FLAG_CTRL 0x001Fu - -/* - * A few values of the TCP options: - */ -#define TCP_OPT_END 0u /* End of TCP options list */ -#define TCP_OPT_NOOP 1u /* "No-operation" TCP option */ -#define TCP_OPT_MSS 2u /* Maximum segment size TCP option */ -#define TCP_OPT_WSOPT 3u /* TCP Window Scale Option (3-byte long) */ -#define TCP_OPT_SACK_P 4u /* Advertize that SACK is permitted */ -#define TCP_OPT_SACK_A 5u /* SACK option with first/last */ -#define TCP_OPT_TIMESTAMP 8u /* Time-stamp option */ - -#define TCP_OPT_MSS_LEN 4u /* Length of TCP MSS option. */ -#define TCP_OPT_WSOPT_LEN 3u /* Length of TCP WSOPT option. */ - -#define TCP_OPT_TIMESTAMP_LEN 10 /* fixed length of the time-stamp option */ - -#ifndef ipconfigTCP_ACK_EARLIER_PACKET - #define ipconfigTCP_ACK_EARLIER_PACKET 1 -#endif - -/* - * The macro NOW_CONNECTED() is use to determine if the connection makes a - * transition from connected to non-connected and vice versa. - * NOW_CONNECTED() returns true when the status has one of these values: - * eESTABLISHED, eFIN_WAIT_1, eFIN_WAIT_2, eCLOSING, eLAST_ACK, eTIME_WAIT - * Technically the connection status is closed earlier, but the library wants - * to prevent that the socket will be deleted before the last ACK has been - * and thus causing a 'RST' packet on either side. - */ -#define NOW_CONNECTED( status )\ - ( ( status >= eESTABLISHED ) && ( status != eCLOSE_WAIT ) ) - -/* - * The highest 4 bits in the TCP offset byte indicate the total length of the - * TCP header, divided by 4. - */ -#define VALID_BITS_IN_TCP_OFFSET_BYTE ( 0xF0u ) - -/* - * Acknowledgements to TCP data packets may be delayed as long as more is being expected. - * A normal delay would be 200ms. Here a much shorter delay of 20 ms is being used to - * gain performance. - */ -#define DELAYED_ACK_SHORT_DELAY_MS ( 2 ) -#define DELAYED_ACK_LONGER_DELAY_MS ( 20 ) - -/* - * The MSS (Maximum Segment Size) will be taken as large as possible. However, packets with - * an MSS of 1460 bytes won't be transported through the internet. The MSS will be reduced - * to 1400 bytes. - */ -#define REDUCED_MSS_THROUGH_INTERNET ( 1400 ) - -/* - * When there are no TCP options, the TCP offset equals 20 bytes, which is stored as - * the number 5 (words) in the higher niblle of the TCP-offset byte. - */ -#define TCP_OFFSET_LENGTH_BITS ( 0xf0u ) -#define TCP_OFFSET_STANDARD_LENGTH ( 0x50u ) - -/* - * Each TCP socket is checked regularly to see if it can send data packets. - * By default, the maximum number of packets sent during one check is limited to 8. - * This amount may be further limited by setting the socket's TX window size. - */ -#if( !defined( SEND_REPEATED_COUNT ) ) - #define SEND_REPEATED_COUNT ( 8 ) -#endif /* !defined( SEND_REPEATED_COUNT ) */ - -/* - * Define a maximum perdiod of time (ms) to leave a TCP-socket unattended. - * When a TCP timer expires, retries and keep-alive messages will be checked. - */ -#ifndef tcpMAXIMUM_TCP_WAKEUP_TIME_MS - #define tcpMAXIMUM_TCP_WAKEUP_TIME_MS 20000u -#endif - -/* - * The names of the different TCP states may be useful in logging. - */ -#if( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) - static const char *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", -}; -#endif /* ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) */ - -/* - * Returns true if the socket must be checked. Non-active sockets are waiting - * for user action, either connect() or close(). - */ -static BaseType_t prvTCPSocketIsActive( UBaseType_t uxStatus ); - -/* - * Either sends a SYN or calls prvTCPSendRepeated (for regular messages). - */ -static int32_t prvTCPSendPacket( FreeRTOS_Socket_t *pxSocket ); - -/* - * Try to send a series of messages. - */ -static int32_t prvTCPSendRepeated( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ); - -/* - * Return or send a packet to the other party. - */ -static void prvTCPReturnPacket( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, - uint32_t ulLen, BaseType_t xReleaseAfterSend ); - -/* - * Initialise the data structures which keep track of the TCP windowing system. - */ -static void prvTCPCreateWindow( FreeRTOS_Socket_t *pxSocket ); - -/* - * Let ARP look-up the MAC-address of the peer and initialise the first SYN - * packet. - */ -static BaseType_t prvTCPPrepareConnect( FreeRTOS_Socket_t *pxSocket ); - -#if( ipconfigHAS_DEBUG_PRINTF != 0 ) - /* - * For logging and debugging: make a string showing the TCP flags. - */ - static const char *prvTCPFlagMeaning( UBaseType_t xFlags); -#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ - -/* - * Parse the TCP option(s) received, if present. - */ -static void prvCheckOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ); - -/* - * Identify and deal with a single TCP header option, advancing the pointer to - * the header. This function returns pdTRUE or pdFALSE depending on whether the - * caller should continue to parse more header options or break the loop. - */ -static BaseType_t prvSingleStepTCPHeaderOptions( const unsigned char ** const ppucPtr, const unsigned char ** const ppucLast, FreeRTOS_Socket_t ** const ppxSocket, TCPWindow_t ** const ppxTCPWindow); - -/* - * Skip past TCP header options when doing Selective ACK, until there are no - * more options left. - */ -static void prvSkipPastRemainingOptions( const unsigned char ** const ppucPtr, FreeRTOS_Socket_t ** const ppxSocket, unsigned char * const ppucLen ); - -/* - * Set the initial properties in the options fields, like the preferred - * value of MSS and whether SACK allowed. Will be transmitted in the state - * 'eCONNECT_SYN'. - */ -static UBaseType_t prvSetSynAckOptions( FreeRTOS_Socket_t *pxSocket, TCPPacket_t * pxTCPPacket ); - -/* - * For anti-hang protection and TCP keep-alive messages. Called in two places: - * after receiving a packet and after a state change. The socket's alive timer - * may be reset. - */ -static void prvTCPTouchSocket( FreeRTOS_Socket_t *pxSocket ); - -/* - * Prepare an outgoing message, if anything has to be sent. - */ -static int32_t prvTCPPrepareSend( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, UBaseType_t uxOptionsLength ); - -/* - * Calculate when this socket needs to be checked to do (re-)transmissions. - */ -static TickType_t prvTCPNextTimeout( FreeRTOS_Socket_t *pxSocket ); - -/* - * The API FreeRTOS_send() adds data to the TX stream. Add - * this data to the windowing system to it can be transmitted. - */ -static void prvTCPAddTxData( FreeRTOS_Socket_t *pxSocket ); - -/* - * Called to handle the closure of a TCP connection. - */ -static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ); - -/* - * Called from prvTCPHandleState(). Find the TCP payload data and check and - * return its length. - */ -static BaseType_t prvCheckRxData( NetworkBufferDescriptor_t *pxNetworkBuffer, uint8_t **ppucRecvData ); - -/* - * Called from prvTCPHandleState(). Check if the payload data may be accepted. - * If so, it will be added to the socket's reception queue. - */ -static BaseType_t prvStoreRxData( FreeRTOS_Socket_t *pxSocket, uint8_t *pucRecvData, - NetworkBufferDescriptor_t *pxNetworkBuffer, uint32_t ulReceiveLength ); - -/* - * Set the TCP options (if any) for the outgoing packet. - */ -static UBaseType_t prvSetOptions( FreeRTOS_Socket_t *pxSocket, 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, NetworkBufferDescriptor_t **ppxNetworkBuffer, - 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 ); - -/* - * Called from prvTCPHandleState(). There is data to be sent. - * If ipconfigUSE_TCP_WIN is defined, and if only an ACK must be sent, it will - * be checked if it would better be postponed for efficiency. - */ -static BaseType_t prvSendData( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, - uint32_t ulReceiveLength, BaseType_t xSendLength ); - -/* - * The heart of all: check incoming packet for valid data and acks and do what - * is necessary in each state. - */ -static BaseType_t prvTCPHandleState( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ); - -/* - * Common code for sending a TCP protocol control packet (i.e. no options, no - * payload, just flags). - */ -static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t *pxNetworkBuffer, - uint8_t ucTCPFlags ); - -/* - * A "challenge ACK" is as per https://tools.ietf.org/html/rfc5961#section-3.2, - * case #3. In summary, an RST was received with a sequence number that is - * unexpected but still within the window. - */ -static BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t *pxNetworkBuffer ); - -/* - * Reply to a peer with the RST flag on, in case a packet can not be handled. - */ -static BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t *pxNetworkBuffer ); - -/* - * Set the initial value for MSS (Maximum Segment Size) to be used. - */ -static void prvSocketSetMSS( FreeRTOS_Socket_t *pxSocket ); - -/* - * Return either a newly created socket, or the current socket in a connected - * state (depends on the 'bReuseSocket' flag). - */ -static FreeRTOS_Socket_t *prvHandleListen( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ); - -/* - * 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 ); - -/* - * prvTCPStatusAgeCheck() will see if the socket has been in a non-connected - * state for too long. If so, the socket will be closed, and -1 will be - * returned. - */ -#if( ipconfigTCP_HANG_PROTECTION == 1 ) - static BaseType_t prvTCPStatusAgeCheck( FreeRTOS_Socket_t *pxSocket ); -#endif - -static NetworkBufferDescriptor_t *prvTCPBufferResize( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, - int32_t lDataLen, UBaseType_t uxOptionsLength ); - -#if( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) - const char *FreeRTOS_GetTCPStateName( UBaseType_t ulState ); -#endif - -#if( ipconfigUSE_TCP_WIN != 0 ) - static uint8_t prvWinScaleFactor( FreeRTOS_Socket_t *pxSocket ); -#endif - -/* - * Generate a randomized TCP Initial Sequence Number per RFC. - */ -extern uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress, - uint16_t usSourcePort, - uint32_t ulDestinationAddress, - uint16_t usDestinationPort ); - -/*-----------------------------------------------------------*/ - -/* prvTCPSocketIsActive() returns true if the socket must be checked. - * Non-active sockets are waiting for user action, either connect() - * or close(). */ -static BaseType_t prvTCPSocketIsActive( UBaseType_t uxStatus ) -{ - switch( uxStatus ) - { - case eCLOSED: - case eCLOSE_WAIT: - case eFIN_WAIT_2: - case eCLOSING: - case eTIME_WAIT: - return pdFALSE; - default: - return pdTRUE; - } -} -/*-----------------------------------------------------------*/ - -#if( ipconfigTCP_HANG_PROTECTION == 1 ) - - static BaseType_t prvTCPStatusAgeCheck( FreeRTOS_Socket_t *pxSocket ) - { - BaseType_t xResult; - switch( pxSocket->u.xTCP.ucTCPState ) - { - 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; - 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 > ( ipconfigTCP_HANG_PROTECTION_TIME * configTICK_RATE_HZ ) ) - { - #if( ipconfigHAS_DEBUG_PRINTF == 1 ) - { - FreeRTOS_debug_printf( ( "Inactive socket closed: port %u rem %lxip:%u status %s\n", - pxSocket->usLocalPort, - 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 ) - { - if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) - { - /* As it did not get connected, and the user can never - accept() it anymore, it will be deleted now. Called from - the IP-task, so it's safe to call the internal Close - function: vSocketClose(). */ - vSocketClose( pxSocket ); - } - /* Return a negative value to tell to inform the caller - xTCPTimerCheck() - that the socket got closed and may not be accessed anymore. */ - xResult = -1; - } - } - } - return xResult; - } - /*-----------------------------------------------------------*/ - -#endif - -/* - * As soon as a TCP socket timer expires, this function xTCPSocketCheck - * will be called (from xTCPTimerCheck) - * It can send a delayed ACK or new data - * Sequence of calling (normally) : - * IP-Task: - * xTCPTimerCheck() // Check all sockets ( declared in FreeRTOS_Sockets.c ) - * xTCPSocketCheck() // Either send a delayed ACK or call prvTCPSendPacket() - * prvTCPSendPacket() // Either send a SYN or call prvTCPSendRepeated ( regular messages ) - * prvTCPSendRepeated() // Send at most 8 messages on a row - * prvTCPReturnPacket() // Prepare for returning - * xNetworkInterfaceOutput() // Sends data to the NIC ( declared in portable/NetworkInterface/xxx ) - */ -BaseType_t xTCPSocketCheck( FreeRTOS_Socket_t *pxSocket ) -{ -BaseType_t xResult = 0; -BaseType_t xReady = pdFALSE; - - if( ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) && ( pxSocket->u.xTCP.txStream != NULL ) ) - { - /* The API FreeRTOS_send() might have added data to the TX stream. Add - this data to the windowing system to it can be transmitted. */ - prvTCPAddTxData( pxSocket ); - } - - #if ipconfigUSE_TCP_WIN == 1 - { - if( pxSocket->u.xTCP.pxAckMessage != NULL ) - { - /* The first task of this regular socket check is to send-out delayed - ACK's. */ - if( pxSocket->u.xTCP.bits.bUserShutdown == pdFALSE_UNSIGNED ) - { - /* Earlier data was received but not yet acknowledged. This - function is called when the TCP timer for the socket expires, the - ACK may be sent now. */ - if( pxSocket->u.xTCP.ucTCPState != eCLOSED ) - { - if( xTCPWindowLoggingLevel > 1 && ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) ) - { - FreeRTOS_debug_printf( ( "Send[%u->%u] del ACK %lu SEQ %lu (len %u)\n", - pxSocket->usLocalPort, - pxSocket->u.xTCP.usRemotePort, - pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber, - pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - pxSocket->u.xTCP.xTCPWindow.tx.ulFirstSequenceNumber, - ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ); - } - - prvTCPReturnPacket( pxSocket, pxSocket->u.xTCP.pxAckMessage, ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER, ipconfigZERO_COPY_TX_DRIVER ); - - #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - { - /* The ownership has been passed to the SEND routine, - clear the pointer to it. */ - pxSocket->u.xTCP.pxAckMessage = NULL; - } - #endif /* ipconfigZERO_COPY_TX_DRIVER */ - } - if( prvTCPNextTimeout( pxSocket ) > 1 ) - { - /* Tell the code below that this function is ready. */ - xReady = pdTRUE; - } - } - else - { - /* The user wants to perform an active shutdown(), skip sending - the delayed ACK. The function prvTCPSendPacket() will send the - FIN along with the ACK's. */ - } - - if( pxSocket->u.xTCP.pxAckMessage != NULL ) - { - vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); - pxSocket->u.xTCP.pxAckMessage = NULL; - } - } - } - #endif /* ipconfigUSE_TCP_WIN */ - - if( xReady == pdFALSE ) - { - /* The second task of this regular socket check is sending out data. */ - if( ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) || - ( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) ) - { - prvTCPSendPacket( pxSocket ); - } - - /* Set the time-out for the next wakeup for this socket. */ - prvTCPNextTimeout( pxSocket ); - - #if( ipconfigTCP_HANG_PROTECTION == 1 ) - { - /* In all (non-connected) states in which keep-alive messages can not be sent - the anti-hang protocol will close sockets that are 'hanging'. */ - xResult = prvTCPStatusAgeCheck( pxSocket ); - } - #endif - } - - return xResult; -} -/*-----------------------------------------------------------*/ - -/* - * prvTCPSendPacket() will be called when the socket time-out has been reached. - * It is only called by xTCPSocketCheck(). - */ -static int32_t prvTCPSendPacket( FreeRTOS_Socket_t *pxSocket ) -{ -int32_t lResult = 0; -UBaseType_t uxOptionsLength; -TCPPacket_t *pxTCPPacket; -NetworkBufferDescriptor_t *pxNetworkBuffer; - - if( pxSocket->u.xTCP.ucTCPState != eCONNECT_SYN ) - { - /* The connection is in s state other than SYN. */ - pxNetworkBuffer = NULL; - - /* prvTCPSendRepeated() will only create a network buffer if necessary, - i.e. when data must be sent to the peer. */ - lResult = prvTCPSendRepeated( pxSocket, &pxNetworkBuffer ); - - if( pxNetworkBuffer != NULL ) - { - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); - } - } - else - { - if( pxSocket->u.xTCP.ucRepCount >= 3u ) - { - /* The connection is in the SYN status. The packet will be repeated - to most 3 times. When there is no response, the socket get the - status 'eCLOSE_WAIT'. */ - FreeRTOS_debug_printf( ( "Connect: giving up %lxip:%u\n", - pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine. */ - pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */ - vTCPStateChange( pxSocket, eCLOSE_WAIT ); - } - else if( ( pxSocket->u.xTCP.bits.bConnPrepared != pdFALSE_UNSIGNED ) || ( prvTCPPrepareConnect( pxSocket ) == pdTRUE ) ) - { - /* Or else, if the connection has been prepared, or can be prepared - now, proceed to send the packet with the SYN flag. - prvTCPPrepareConnect() prepares 'xPacket' and returns pdTRUE if - the Ethernet address of the peer or the gateway is found. */ - pxTCPPacket = ( TCPPacket_t * )pxSocket->u.xTCP.xPacket.u.ucLastPacket; - - /* About to send a SYN packet. Call prvSetSynAckOptions() to set - the proper options: The size of MSS and whether SACK's are - allowed. */ - uxOptionsLength = prvSetSynAckOptions( pxSocket, pxTCPPacket ); - - /* Return the number of bytes to be sent. */ - lResult = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); - - /* Set the TCP offset field: ipSIZE_OF_TCP_HEADER equals 20 and - uxOptionsLength is always a multiple of 4. The complete expression - would be: - ucTCPOffset = ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) / 4 ) << 4 */ - pxTCPPacket->xTCPHeader.ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); - - /* Repeat Count is used for a connecting socket, to limit the number - of tries. */ - pxSocket->u.xTCP.ucRepCount++; - - /* Send the SYN message to make a connection. The messages is - stored in the socket field 'xPacket'. It will be wrapped in a - pseudo network buffer descriptor before it will be sent. */ - prvTCPReturnPacket( pxSocket, NULL, ( uint32_t ) lResult, pdFALSE ); - } - } - - /* Return the total number of bytes sent. */ - return lResult; -} -/*-----------------------------------------------------------*/ - -/* - * prvTCPSendRepeated will try to send a series of messages, as long as there is - * data to be sent and as long as the transmit window isn't full. - */ -static int32_t prvTCPSendRepeated( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ) -{ -UBaseType_t uxIndex; -int32_t lResult = 0; -UBaseType_t uxOptionsLength = 0u; -int32_t xSendLength; - - for( uxIndex = 0u; uxIndex < ( UBaseType_t ) SEND_REPEATED_COUNT; uxIndex++ ) - { - /* prvTCPPrepareSend() might allocate a network buffer if there is data - to be sent. */ - xSendLength = prvTCPPrepareSend( pxSocket, ppxNetworkBuffer, uxOptionsLength ); - if( xSendLength <= 0 ) - { - break; - } - - /* And return the packet to the peer. */ - prvTCPReturnPacket( pxSocket, *ppxNetworkBuffer, ( uint32_t ) xSendLength, ipconfigZERO_COPY_TX_DRIVER ); - - #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - { - *ppxNetworkBuffer = NULL; - } - #endif /* ipconfigZERO_COPY_TX_DRIVER */ - - lResult += xSendLength; - } - - /* Return the total number of bytes sent. */ - return lResult; -} -/*-----------------------------------------------------------*/ - -/* - * Return (or send) a packet the the peer. The data is stored in pxBuffer, - * which may either point to a real network buffer or to a TCP socket field - * called 'xTCP.xPacket'. A temporary xNetworkBuffer will be used to pass - * the data to the NIC. - */ -static void prvTCPReturnPacket( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, uint32_t ulLen, BaseType_t xReleaseAfterSend ) -{ -TCPPacket_t * pxTCPPacket; -IPHeader_t *pxIPHeader; -EthernetHeader_t *pxEthernetHeader; -uint32_t ulFrontSpace, ulSpace, ulSourceAddress, ulWinSize; -TCPWindow_t *pxTCPWindow; -NetworkBufferDescriptor_t xTempBuffer; -/* For sending, a pseudo network buffer will be used, as explained above. */ - - if( pxNetworkBuffer == NULL ) - { - pxNetworkBuffer = &xTempBuffer; - - #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) - { - xTempBuffer.pxNextBuffer = NULL; - } - #endif - xTempBuffer.pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket; - xTempBuffer.xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ); - xReleaseAfterSend = pdFALSE; - } - - #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - { - if( xReleaseAfterSend == pdFALSE ) - { - pxNetworkBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, ( BaseType_t ) pxNetworkBuffer->xDataLength ); - if( pxNetworkBuffer == NULL ) - { - FreeRTOS_debug_printf( ( "prvTCPReturnPacket: duplicate failed\n" ) ); - } - xReleaseAfterSend = pdTRUE; - } - } - #endif /* ipconfigZERO_COPY_TX_DRIVER */ - - if( pxNetworkBuffer != NULL ) - { - pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); - pxIPHeader = &pxTCPPacket->xIPHeader; - pxEthernetHeader = &pxTCPPacket->xEthernetHeader; - - /* Fill the packet, using hton translations. */ - if( pxSocket != NULL ) - { - /* Calculate the space in the RX buffer in order to advertise the - size of this socket's reception window. */ - pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow ); - - if( pxSocket->u.xTCP.rxStream != NULL ) - { - /* An RX stream was created already, see how much space is - available. */ - ulFrontSpace = ( uint32_t ) uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ); - } - else - { - /* No RX stream has been created, the full stream size is - available. */ - ulFrontSpace = ( uint32_t ) pxSocket->u.xTCP.uxRxStreamSize; - } - - /* Take the minimum of the RX buffer space and the RX window size. */ - ulSpace = FreeRTOS_min_uint32( pxTCPWindow->xSize.ulRxWindowLength, ulFrontSpace ); - - if( ( pxSocket->u.xTCP.bits.bLowWater != pdFALSE_UNSIGNED ) || ( pxSocket->u.xTCP.bits.bRxStopped != pdFALSE_UNSIGNED ) ) - { - /* The low-water mark was reached, meaning there was little - space left. The socket will wait until the application has read - or flushed the incoming data, and 'zero-window' will be - advertised. */ - ulSpace = 0u; - } - - /* If possible, advertise an RX window size of at least 1 MSS, otherwise - the peer might start 'zero window probing', i.e. sending small packets - (1, 2, 4, 8... bytes). */ - if( ( ulSpace < pxSocket->u.xTCP.usCurMSS ) && ( ulFrontSpace >= pxSocket->u.xTCP.usCurMSS ) ) - { - ulSpace = pxSocket->u.xTCP.usCurMSS; - } - - /* Avoid overflow of the 16-bit win field. */ - #if( ipconfigUSE_TCP_WIN != 0 ) - { - ulWinSize = ( ulSpace >> pxSocket->u.xTCP.ucMyWinScaleFactor ); - } - #else - { - ulWinSize = ulSpace; - } - #endif - if( ulWinSize > 0xfffcUL ) - { - ulWinSize = 0xfffcUL; - } - - pxTCPPacket->xTCPHeader.usWindow = FreeRTOS_htons( ( uint16_t ) ulWinSize ); - - #if( ipconfigHAS_DEBUG_PRINTF != 0 ) - { - if( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) - { - if( ( xTCPWindowLoggingLevel != 0 ) && ( pxSocket->u.xTCP.bits.bWinChange != pdFALSE_UNSIGNED ) ) - { - size_t uxFrontSpace; - - if(pxSocket->u.xTCP.rxStream != NULL) - { - uxFrontSpace = uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ) ; - } - else - { - uxFrontSpace = 0u; - } - - FreeRTOS_debug_printf( ( "%s: %lxip:%u: [%lu < %lu] winSize %ld\n", - pxSocket->u.xTCP.bits.bLowWater ? "STOP" : "GO ", - pxSocket->u.xTCP.ulRemoteIP, - pxSocket->u.xTCP.usRemotePort, - pxSocket->u.xTCP.bits.bLowWater ? pxSocket->u.xTCP.uxLittleSpace : uxFrontSpace, pxSocket->u.xTCP.uxEnoughSpace, - (int32_t) ( pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulCurrentSequenceNumber ) ) ); - } - } - } - #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ - - /* The new window size has been advertised, switch off the flag. */ - pxSocket->u.xTCP.bits.bWinChange = pdFALSE_UNSIGNED; - - /* Later on, when deciding to delay an ACK, a precise estimate is needed - of the free RX space. At this moment, 'ulHighestRxAllowed' would be the - highest sequence number minus 1 that the socket will accept. */ - pxSocket->u.xTCP.ulHighestRxAllowed = pxTCPWindow->rx.ulCurrentSequenceNumber + ulSpace; - - #if( ipconfigTCP_KEEP_ALIVE == 1 ) - if( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED ) - { - /* Sending a keep-alive packet, send the current sequence number - minus 1, which will be recognised as a keep-alive packet an - responded to by acknowledging the last byte. */ - pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE_UNSIGNED; - pxSocket->u.xTCP.bits.bWaitKeepAlive = pdTRUE_UNSIGNED; - - pxTCPPacket->xTCPHeader.ulSequenceNumber = pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - 1UL; - pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxTCPPacket->xTCPHeader.ulSequenceNumber ); - } - else - #endif - { - pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber ); - - if( ( pxTCPPacket->xTCPHeader.ucTCPFlags & ( uint8_t ) ipTCP_FLAG_FIN ) != 0u ) - { - /* Suppress FIN in case this packet carries earlier data to be - retransmitted. */ - uint32_t ulDataLen = ( uint32_t ) ( ulLen - ( ipSIZE_OF_TCP_HEADER + ipSIZE_OF_IPv4_HEADER ) ); - if( ( pxTCPWindow->ulOurSequenceNumber + ulDataLen ) != pxTCPWindow->tx.ulFINSequenceNumber ) - { - pxTCPPacket->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~ipTCP_FLAG_FIN ); - FreeRTOS_debug_printf( ( "Suppress FIN for %lu + %lu < %lu\n", - pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, - ulDataLen, - pxTCPWindow->tx.ulFINSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ) ); - } - } - } - - /* Tell which sequence number is expected next time */ - pxTCPPacket->xTCPHeader.ulAckNr = FreeRTOS_htonl( pxTCPWindow->rx.ulCurrentSequenceNumber ); - } - else - { - /* Sending data without a socket, probably replying with a RST flag - Just swap the two sequence numbers. */ - vFlip_32( pxTCPPacket->xTCPHeader.ulSequenceNumber, pxTCPPacket->xTCPHeader.ulAckNr ); - } - - pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE; - pxIPHeader->usLength = FreeRTOS_htons( ulLen ); - if( ( pxSocket == NULL ) || ( *ipLOCAL_IP_ADDRESS_POINTER == 0ul ) ) - { - /* When pxSocket is NULL, this function is called by prvTCPSendReset() - and the IP-addresses must be swapped. - Also swap the IP-addresses in case the IP-tack doesn't have an - IP-address yet, i.e. when ( *ipLOCAL_IP_ADDRESS_POINTER == 0ul ). */ - ulSourceAddress = pxIPHeader->ulDestinationIPAddress; - } - else - { - ulSourceAddress = *ipLOCAL_IP_ADDRESS_POINTER; - } - pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; - pxIPHeader->ulSourceIPAddress = ulSourceAddress; - vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort ); - - /* Just an increasing number. */ - pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier ); - usPacketIdentifier++; - pxIPHeader->usFragmentOffset = 0u; - - #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) - { - /* calculate the IP header checksum, in case the driver won't do that. */ - pxIPHeader->usHeaderChecksum = 0x00u; - pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); - pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); - - /* calculate the TCP checksum for an outgoing packet. */ - usGenerateProtocolChecksum( (uint8_t*)pxTCPPacket, pxNetworkBuffer->xDataLength, pdTRUE ); - - /* A calculated checksum of 0 must be inverted as 0 means the checksum - is disabled. */ - if( pxTCPPacket->xTCPHeader.usChecksum == 0x00u ) - { - pxTCPPacket->xTCPHeader.usChecksum = 0xffffU; - } - } - #endif - - #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) - pxNetworkBuffer->pxNextBuffer = NULL; - #endif - - /* Important: tell NIC driver how many bytes must be sent. */ - pxNetworkBuffer->xDataLength = ulLen + ipSIZE_OF_ETH_HEADER; - - /* Fill in the destination MAC addresses. */ - memcpy( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) &( pxEthernetHeader->xSourceAddress ), - sizeof( pxEthernetHeader->xDestinationAddress ) ); - - /* The source MAC addresses is fixed to 'ipLOCAL_MAC_ADDRESS'. */ - memcpy( ( void * ) &( pxEthernetHeader->xSourceAddress) , ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); - - #if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES ) - { - if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES ) - { - BaseType_t xIndex; - - for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ ) - { - pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0u; - } - pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; - } - } - #endif - - /* Send! */ - xNetworkInterfaceOutput( pxNetworkBuffer, xReleaseAfterSend ); - - if( xReleaseAfterSend == pdFALSE ) - { - /* Swap-back some fields, as pxBuffer probably points to a socket field - containing the packet header. */ - vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort); - pxTCPPacket->xIPHeader.ulSourceIPAddress = pxTCPPacket->xIPHeader.ulDestinationIPAddress; - memcpy( pxEthernetHeader->xSourceAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); - } - else - { - /* Nothing to do: the buffer has been passed to DMA and will be released after use */ - } - } /* if( pxNetworkBuffer != NULL ) */ -} -/*-----------------------------------------------------------*/ - -/* - * The SYN event is very important: the sequence numbers, which have a kind of - * random starting value, are being synchronised. The sliding window manager - * (in FreeRTOS_TCP_WIN.c) needs to know them, along with the Maximum Segment - * Size (MSS) in use. - */ -static void prvTCPCreateWindow( FreeRTOS_Socket_t *pxSocket ) -{ - if( xTCPWindowLoggingLevel ) - FreeRTOS_debug_printf( ( "Limits (using): TCP Win size %lu Water %lu <= %lu <= %lu\n", - pxSocket->u.xTCP.uxRxWinSize * ipconfigTCP_MSS, - pxSocket->u.xTCP.uxLittleSpace , - pxSocket->u.xTCP.uxEnoughSpace, - pxSocket->u.xTCP.uxRxStreamSize ) ); - vTCPWindowCreate( - &pxSocket->u.xTCP.xTCPWindow, - ipconfigTCP_MSS * pxSocket->u.xTCP.uxRxWinSize, - ipconfigTCP_MSS * pxSocket->u.xTCP.uxTxWinSize, - pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber, - pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber, - ( uint32_t ) pxSocket->u.xTCP.usInitMSS ); -} -/*-----------------------------------------------------------*/ - -/* - * Connecting sockets have a special state: eCONNECT_SYN. In this phase, - * the Ethernet address of the target will be found using ARP. In case the - * target IP address is not within the netmask, the hardware address of the - * gateway will be used. - */ -static BaseType_t prvTCPPrepareConnect( FreeRTOS_Socket_t *pxSocket ) -{ -TCPPacket_t *pxTCPPacket; -IPHeader_t *pxIPHeader; -eARPLookupResult_t eReturned; -uint32_t ulRemoteIP; -MACAddress_t xEthAddress; -BaseType_t xReturn = pdTRUE; -uint32_t ulInitialSequenceNumber = 0; - - #if( ipconfigHAS_PRINTF != 0 ) - { - /* Only necessary for nicer logging. */ - memset( xEthAddress.ucBytes, '\0', sizeof( xEthAddress.ucBytes ) ); - } - #endif /* ipconfigHAS_PRINTF != 0 */ - - ulRemoteIP = FreeRTOS_htonl( pxSocket->u.xTCP.ulRemoteIP ); - - /* Determine the ARP cache status for the requested IP address. */ - eReturned = eARPGetCacheEntry( &( ulRemoteIP ), &( xEthAddress ) ); - - switch( eReturned ) - { - case eARPCacheHit: /* An ARP table lookup found a valid entry. */ - break; /* We can now prepare the SYN packet. */ - case eARPCacheMiss: /* An ARP table lookup did not find a valid entry. */ - case eCantSendPacket: /* There is no IP address, or an ARP is still in progress. */ - default: - /* Count the number of times it couldn't find the ARP address. */ - pxSocket->u.xTCP.ucRepCount++; - - FreeRTOS_debug_printf( ( "ARP for %lxip (using %lxip): rc=%d %02X:%02X:%02X %02X:%02X:%02X\n", - pxSocket->u.xTCP.ulRemoteIP, - FreeRTOS_htonl( ulRemoteIP ), - eReturned, - xEthAddress.ucBytes[ 0 ], - xEthAddress.ucBytes[ 1 ], - xEthAddress.ucBytes[ 2 ], - xEthAddress.ucBytes[ 3 ], - xEthAddress.ucBytes[ 4 ], - xEthAddress.ucBytes[ 5 ] ) ); - - /* And issue a (new) ARP request */ - FreeRTOS_OutputARPRequest( ulRemoteIP ); - - xReturn = pdFALSE; - } - - if( xReturn != pdFALSE ) - { - /* Get a difficult-to-predict initial sequence number for this 4-tuple. */ - ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( *ipLOCAL_IP_ADDRESS_POINTER, - pxSocket->usLocalPort, - pxSocket->u.xTCP.ulRemoteIP, - pxSocket->u.xTCP.usRemotePort ); - - /* Check for a random number generation error. */ - if( 0 == ulInitialSequenceNumber ) - { - xReturn = pdFALSE; - } - } - - if( xReturn != pdFALSE ) - { - /* The MAC-address of the peer (or gateway) has been found, - now prepare the initial TCP packet and some fields in the socket. */ - pxTCPPacket = ( TCPPacket_t * )pxSocket->u.xTCP.xPacket.u.ucLastPacket; - pxIPHeader = &pxTCPPacket->xIPHeader; - - /* reset the retry counter to zero. */ - pxSocket->u.xTCP.ucRepCount = 0u; - - /* And remember that the connect/SYN data are prepared. */ - pxSocket->u.xTCP.bits.bConnPrepared = pdTRUE_UNSIGNED; - - /* Now that the Ethernet address is known, the initial packet can be - prepared. */ - memset( pxSocket->u.xTCP.xPacket.u.ucLastPacket, '\0', sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); - - /* Write the Ethernet address in Source, because it will be swapped by - prvTCPReturnPacket(). */ - memcpy( &pxTCPPacket->xEthernetHeader.xSourceAddress, &xEthAddress, sizeof( xEthAddress ) ); - - /* 'ipIPv4_FRAME_TYPE' is already in network-byte-order. */ - pxTCPPacket->xEthernetHeader.usFrameType = ipIPv4_FRAME_TYPE; - - pxIPHeader->ucVersionHeaderLength = 0x45u; - pxIPHeader->usLength = FreeRTOS_htons( sizeof( TCPPacket_t ) - sizeof( pxTCPPacket->xEthernetHeader ) ); - pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE; - - pxIPHeader->ucProtocol = ( uint8_t ) ipPROTOCOL_TCP; - - /* Addresses and ports will be stored swapped because prvTCPReturnPacket - will swap them back while replying. */ - pxIPHeader->ulDestinationIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; - pxIPHeader->ulSourceIPAddress = FreeRTOS_htonl( pxSocket->u.xTCP.ulRemoteIP ); - - pxTCPPacket->xTCPHeader.usSourcePort = FreeRTOS_htons( pxSocket->u.xTCP.usRemotePort ); - pxTCPPacket->xTCPHeader.usDestinationPort = FreeRTOS_htons( pxSocket->usLocalPort ); - - /* We are actively connecting, so the peer's Initial Sequence Number (ISN) - isn't known yet. */ - pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = 0ul; - - /* Start with ISN (Initial Sequence Number). */ - pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber; - - /* The TCP header size is 20 bytes, divided by 4 equals 5, which is put in - the high nibble of the TCP offset field. */ - pxTCPPacket->xTCPHeader.ucTCPOffset = 0x50u; - - /* Only set the SYN flag. */ - pxTCPPacket->xTCPHeader.ucTCPFlags = ipTCP_FLAG_SYN; - - /* Set the values of usInitMSS / usCurMSS for this socket. */ - prvSocketSetMSS( pxSocket ); - - /* The initial sequence numbers at our side are known. Later - vTCPWindowInit() will be called to fill in the peer's sequence numbers, but - first wait for a SYN+ACK reply. */ - prvTCPCreateWindow( pxSocket ); - } - - return xReturn; -} -/*-----------------------------------------------------------*/ - -/* For logging and debugging: make a string showing the TCP flags -*/ -#if( ipconfigHAS_DEBUG_PRINTF != 0 ) - - static const char *prvTCPFlagMeaning( UBaseType_t xFlags) - { - static char retString[10]; - snprintf(retString, sizeof( retString ), "%c%c%c%c%c%c%c%c%c", - ( xFlags & ipTCP_FLAG_FIN ) ? 'F' : '.', /* 0x0001: No more data from sender */ - ( xFlags & ipTCP_FLAG_SYN ) ? 'S' : '.', /* 0x0002: Synchronize sequence numbers */ - ( xFlags & ipTCP_FLAG_RST ) ? 'R' : '.', /* 0x0004: Reset the connection */ - ( xFlags & ipTCP_FLAG_PSH ) ? 'P' : '.', /* 0x0008: Push function: please push buffered data to the recv application */ - ( xFlags & ipTCP_FLAG_ACK ) ? 'A' : '.', /* 0x0010: Acknowledgment field is significant */ - ( xFlags & ipTCP_FLAG_URG ) ? 'U' : '.', /* 0x0020: Urgent pointer field is significant */ - ( xFlags & ipTCP_FLAG_ECN ) ? 'E' : '.', /* 0x0040: ECN-Echo */ - ( xFlags & ipTCP_FLAG_CWR ) ? 'C' : '.', /* 0x0080: Congestion Window Reduced */ - ( xFlags & ipTCP_FLAG_NS ) ? 'N' : '.'); /* 0x0100: ECN-nonce concealment protection */ - return retString; - } - /*-----------------------------------------------------------*/ - -#endif /* ipconfigHAS_DEBUG_PRINTF */ - -/* - * Parse the TCP option(s) received, if present. It has already been verified - * that: ((pxTCPHeader->ucTCPOffset & 0xf0) > 0x50), meaning that the TP header - * is longer than the usual 20 (5 x 4) bytes. - */ -static void prvCheckOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) -{ -TCPPacket_t * pxTCPPacket; -TCPHeader_t * pxTCPHeader; -const unsigned char *pucPtr; -const unsigned char *pucLast; -TCPWindow_t *pxTCPWindow; -BaseType_t xShouldContinueLoop; - - pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); - pxTCPHeader = &pxTCPPacket->xTCPHeader; - - /* A character pointer to iterate through the option data */ - pucPtr = pxTCPHeader->ucOptdata; - pucLast = pucPtr + (((pxTCPHeader->ucTCPOffset >> 4) - 5) << 2); - pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; - - /* Validate options size calculation. */ - if( pucLast > ( pxNetworkBuffer->pucEthernetBuffer + pxNetworkBuffer->xDataLength ) ) - { - return; - } - - /* The comparison with pucLast is only necessary in case the option data are - corrupted, we don't like to run into invalid memory and crash. */ - xShouldContinueLoop = pdTRUE; - while( ( pucPtr < pucLast ) && ( xShouldContinueLoop == pdTRUE ) ) - { - xShouldContinueLoop = prvSingleStepTCPHeaderOptions( &pucPtr, &pucLast, &pxSocket, &pxTCPWindow ); - } -} - -/*-----------------------------------------------------------*/ - -static BaseType_t prvSingleStepTCPHeaderOptions( const unsigned char ** const ppucPtr, const unsigned char ** const ppucLast, FreeRTOS_Socket_t ** const ppxSocket, TCPWindow_t ** const ppxTCPWindow) -{ - UBaseType_t uxNewMSS; - UBaseType_t xRemainingOptionsBytes = ( *ppucLast ) - ( *ppucPtr ); - unsigned char ucLen; - - if( ( *ppucPtr )[ 0 ] == TCP_OPT_END ) - { - /* End of options. */ - return pdFALSE; - } - if( ( *ppucPtr )[ 0 ] == TCP_OPT_NOOP) - { - /* NOP option, inserted to make the length a multiple of 4. */ - ( *ppucPtr )++; - return pdTRUE; - } - - /* Any other well-formed option must be at least two bytes: the option - type byte followed by a length byte. */ - if( xRemainingOptionsBytes < 2 ) - { - return pdFALSE; - } -#if( ipconfigUSE_TCP_WIN != 0 ) - else if( ( *ppucPtr )[ 0 ] == TCP_OPT_WSOPT ) - { - /* Confirm that the option fits in the remaining buffer space. */ - if( ( xRemainingOptionsBytes < TCP_OPT_WSOPT_LEN ) || ( ( *ppucPtr )[ 1 ] != TCP_OPT_WSOPT_LEN ) ) - { - return pdFALSE; - } - - ( *ppxSocket )->u.xTCP.ucPeerWinScaleFactor = ( *ppucPtr )[ 2 ]; - ( *ppxSocket )->u.xTCP.bits.bWinScaling = pdTRUE_UNSIGNED; - ( *ppucPtr ) += TCP_OPT_WSOPT_LEN; - } -#endif /* ipconfigUSE_TCP_WIN */ - else if( ( *ppucPtr )[ 0 ] == TCP_OPT_MSS ) - { - /* Confirm that the option fits in the remaining buffer space. */ - if( ( xRemainingOptionsBytes < TCP_OPT_MSS_LEN )|| ( ( *ppucPtr )[ 1 ] != TCP_OPT_MSS_LEN ) ) - { - return pdFALSE; - } - - /* An MSS option with the correct option length. FreeRTOS_htons() - is not needed here because usChar2u16() already returns a host - endian number. */ - uxNewMSS = usChar2u16( ( *ppucPtr ) + 2 ); - - if( ( *ppxSocket )->u.xTCP.usInitMSS != uxNewMSS ) - { - /* Perform a basic check on the the new MSS. */ - if( uxNewMSS == 0 ) - { - return pdFALSE; - } - - FreeRTOS_debug_printf( ( "MSS change %u -> %lu\n", ( *ppxSocket )->u.xTCP.usInitMSS, uxNewMSS ) ); - } - - if( ( *ppxSocket )->u.xTCP.usInitMSS > uxNewMSS ) - { - /* our MSS was bigger than the MSS of the other party: adapt it. */ - ( *ppxSocket )->u.xTCP.bits.bMssChange = pdTRUE_UNSIGNED; - if( ( ( *ppxTCPWindow ) != NULL ) && ( ( *ppxSocket )->u.xTCP.usCurMSS > uxNewMSS ) ) - { - /* The peer advertises a smaller MSS than this socket was - using. Use that as well. */ - FreeRTOS_debug_printf( ( "Change mss %d => %lu\n", ( *ppxSocket )->u.xTCP.usCurMSS, uxNewMSS ) ); - ( *ppxSocket )->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS; - } - ( *ppxTCPWindow )->xSize.ulRxWindowLength = ( ( uint32_t ) uxNewMSS ) * ( ( *ppxTCPWindow )->xSize.ulRxWindowLength / ( ( uint32_t ) uxNewMSS ) ); - ( *ppxTCPWindow )->usMSSInit = ( uint16_t ) uxNewMSS; - ( *ppxTCPWindow )->usMSS = ( uint16_t ) uxNewMSS; - ( *ppxSocket )->u.xTCP.usInitMSS = ( uint16_t ) uxNewMSS; - ( *ppxSocket )->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS; - } - - #if( ipconfigUSE_TCP_WIN != 1 ) - /* Without scaled windows, MSS is the only interesting option. */ - return pdFALSE; - #else - /* Or else we continue to check another option: selective ACK. */ - ( *ppucPtr ) += TCP_OPT_MSS_LEN; - #endif /* ipconfigUSE_TCP_WIN != 1 */ - } - else - { - /* All other options have a length field, so that we easily - can skip past them. */ - ucLen = ( *ppucPtr )[ 1 ]; - if( ( ucLen < 2 ) || ( ucLen > xRemainingOptionsBytes ) ) - { - /* If the length field is too small or too big, the options are - * malformed, don't process them further. - */ - return pdFALSE; - } - - #if( ipconfigUSE_TCP_WIN == 1 ) - { - /* Selective ACK: the peer has received a packet but it is missing - * earlier packets. At least this packet does not need retransmission - * anymore. ulTCPWindowTxSack( ) takes care of this administration. - */ - if( ( *ppucPtr )[0] == TCP_OPT_SACK_A ) - { - ucLen -= 2; - ( *ppucPtr ) += 2; - - while( ucLen >= 8 ) - { - prvSkipPastRemainingOptions( ppucPtr, ppxSocket, &ucLen ); - } - /* ucLen should be 0 by now. */ - } - } - #endif /* ipconfigUSE_TCP_WIN == 1 */ - - ( *ppucPtr ) += ucLen; - } - return pdTRUE; -} - -/*-----------------------------------------------------------*/ - -static void prvSkipPastRemainingOptions( const unsigned char ** const ppucPtr, FreeRTOS_Socket_t ** const ppxSocket, unsigned char * const pucLen ) -{ -uint32_t ulFirst = ulChar2u32( ( *ppucPtr ) ); -uint32_t ulLast = ulChar2u32( ( *ppucPtr ) + 4 ); -uint32_t ulCount = ulTCPWindowTxSack( &( *ppxSocket )->u.xTCP.xTCPWindow, ulFirst, ulLast ); - /* ulTCPWindowTxSack( ) returns the number of bytes which have been acked - * starting from the head position. Advance the tail pointer in txStream. - */ - if( ( ( *ppxSocket )->u.xTCP.txStream != NULL ) && ( ulCount > 0 ) ) - { - /* Just advancing the tail index, 'ulCount' bytes have been confirmed. */ - uxStreamBufferGet( ( *ppxSocket )->u.xTCP.txStream, 0, NULL, ( size_t ) ulCount, pdFALSE ); - ( *ppxSocket )->xEventBits |= eSOCKET_SEND; - - #if ipconfigSUPPORT_SELECT_FUNCTION == 1 - { - if( ( *ppxSocket )->xSelectBits & eSELECT_WRITE ) - { - /* The field 'xEventBits' is used to store regular socket events - * (at most 8), as well as 'select events', which will be left-shifted. - */ - ( *ppxSocket )->xEventBits |= ( 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( ( *ppxSocket )->u.xTCP.pxHandleSent ) ) - { - ( *ppxSocket )->u.xTCP.pxHandleSent( (Socket_t )( *ppxSocket ), ulCount ); - } - } - #endif /* ipconfigUSE_CALLBACKS == 1 */ - } - ( *ppucPtr ) += 8; - ( *pucLen ) -= 8; -} - -/*-----------------------------------------------------------*/ - -#if( ipconfigUSE_TCP_WIN != 0 ) - - static uint8_t prvWinScaleFactor( FreeRTOS_Socket_t *pxSocket ) - { - size_t uxWinSize; - uint8_t ucFactor; - - /* 'xTCP.uxRxWinSize' is the size of the reception window in units of MSS. */ - uxWinSize = pxSocket->u.xTCP.uxRxWinSize * ( size_t ) pxSocket->u.xTCP.usInitMSS; - ucFactor = 0u; - while( uxWinSize > 0xfffful ) - { - /* Divide by two and increase the binary factor by 1. */ - uxWinSize >>= 1; - ucFactor++; - } - - FreeRTOS_debug_printf( ( "prvWinScaleFactor: uxRxWinSize %lu MSS %lu Factor %u\n", - pxSocket->u.xTCP.uxRxWinSize, - pxSocket->u.xTCP.usInitMSS, - ucFactor ) ); - - return ucFactor; - } - -#endif -/*-----------------------------------------------------------*/ - -/* - * When opening a TCP connection, while SYN's are being sent, the parties may - * communicate what MSS (Maximum Segment Size) they intend to use. MSS is the - * nett size of the payload, always smaller than MTU. -*/ -static UBaseType_t prvSetSynAckOptions( FreeRTOS_Socket_t *pxSocket, TCPPacket_t * pxTCPPacket ) -{ -TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; -uint16_t usMSS = pxSocket->u.xTCP.usInitMSS; -UBaseType_t uxOptionsLength; - - /* We send out the TCP Maximum Segment Size option with our SYN[+ACK]. */ - - pxTCPHeader->ucOptdata[ 0 ] = ( uint8_t ) TCP_OPT_MSS; - pxTCPHeader->ucOptdata[ 1 ] = ( uint8_t ) TCP_OPT_MSS_LEN; - pxTCPHeader->ucOptdata[ 2 ] = ( uint8_t ) ( usMSS >> 8 ); - pxTCPHeader->ucOptdata[ 3 ] = ( uint8_t ) ( usMSS & 0xffu ); - - #if( ipconfigUSE_TCP_WIN != 0 ) - { - pxSocket->u.xTCP.ucMyWinScaleFactor = prvWinScaleFactor( pxSocket ); - - pxTCPHeader->ucOptdata[ 4 ] = TCP_OPT_NOOP; - pxTCPHeader->ucOptdata[ 5 ] = ( uint8_t ) ( TCP_OPT_WSOPT ); - pxTCPHeader->ucOptdata[ 6 ] = ( uint8_t ) ( TCP_OPT_WSOPT_LEN ); - pxTCPHeader->ucOptdata[ 7 ] = ( uint8_t ) pxSocket->u.xTCP.ucMyWinScaleFactor; - uxOptionsLength = 8u; - } - #else - { - uxOptionsLength = 4u; - } - #endif - - #if( ipconfigUSE_TCP_WIN == 0 ) - { - return uxOptionsLength; - } - #else - { - pxTCPHeader->ucOptdata[ uxOptionsLength + 0 ] = TCP_OPT_NOOP; - pxTCPHeader->ucOptdata[ uxOptionsLength + 1 ] = TCP_OPT_NOOP; - pxTCPHeader->ucOptdata[ uxOptionsLength + 2 ] = TCP_OPT_SACK_P; /* 4: Sack-Permitted Option. */ - pxTCPHeader->ucOptdata[ uxOptionsLength + 3 ] = 2; /* 2: length of this option. */ - uxOptionsLength += 4u; - - return uxOptionsLength; /* bytes, not words. */ - } - #endif /* ipconfigUSE_TCP_WIN == 0 */ -} - -/* - * For anti-hanging protection and TCP keep-alive messages. Called in two - * places: after receiving a packet and after a state change. The socket's - * alive timer may be reset. - */ -static void prvTCPTouchSocket( FreeRTOS_Socket_t *pxSocket ) -{ - #if( ipconfigTCP_HANG_PROTECTION == 1 ) - { - pxSocket->u.xTCP.xLastActTime = xTaskGetTickCount( ); - } - #endif - - #if( ipconfigTCP_KEEP_ALIVE == 1 ) - { - pxSocket->u.xTCP.bits.bWaitKeepAlive = pdFALSE_UNSIGNED; - pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE_UNSIGNED; - pxSocket->u.xTCP.ucKeepRepCount = 0u; - pxSocket->u.xTCP.xLastAliveTime = xTaskGetTickCount(); - } - #endif - - ( void ) pxSocket; -} -/*-----------------------------------------------------------*/ - -/* - * Changing to a new state. Centralised here to do specific actions such as - * resetting the alive timer, calling the user's OnConnect handler to notify - * that a socket has got (dis)connected, and setting bit to unblock a call to - * FreeRTOS_select() - */ -void vTCPStateChange( FreeRTOS_Socket_t *pxSocket, enum eTCP_STATE eTCPState ) -{ -FreeRTOS_Socket_t *xParent = NULL; -BaseType_t bBefore = ( BaseType_t ) NOW_CONNECTED( pxSocket->u.xTCP.ucTCPState ); /* Was it connected ? */ -BaseType_t bAfter = ( BaseType_t ) NOW_CONNECTED( eTCPState ); /* Is it connected now ? */ -#if( ipconfigHAS_DEBUG_PRINTF != 0 ) - BaseType_t xPreviousState = ( BaseType_t ) pxSocket->u.xTCP.ucTCPState; -#endif -#if( ipconfigUSE_CALLBACKS == 1 ) - FreeRTOS_Socket_t *xConnected = NULL; -#endif - - /* Has the connected status changed? */ - if( bBefore != bAfter ) - { - /* 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 ) - { - if( xParent->u.xTCP.pxPeerSocket == NULL ) - { - xParent->u.xTCP.pxPeerSocket = pxSocket; - } - - xParent->xEventBits |= eSOCKET_ACCEPT; - - #if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) - { - /* Library support FreeRTOS_select(). Receiving a new - connection is being translated as a READ event. */ - if( ( xParent->xSelectBits & eSELECT_READ ) != 0 ) - { - xParent->xEventBits |= ( eSELECT_READ << SOCKET_EVENT_BIT_COUNT ); - } - } - #endif - - #if( ipconfigUSE_CALLBACKS == 1 ) - { - if( ( ipconfigIS_VALID_PROG_ADDRESS( xParent->u.xTCP.pxHandleConnected ) != pdFALSE ) && - ( xParent->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) ) - { - /* The listening socket does not become connected itself, in stead - a child socket is created. - Postpone a call the OnConnect event until the end of this function. */ - xConnected = xParent; - } - } - #endif - } - - /* Don't need to access the parent socket anymore, so the - reference 'pxPeerSocket' may be cleared. */ - pxSocket->u.xTCP.pxPeerSocket = NULL; - pxSocket->u.xTCP.bits.bPassQueued = pdFALSE_UNSIGNED; - - /* When true, this socket may be returned in a call to accept(). */ - pxSocket->u.xTCP.bits.bPassAccept = pdTRUE_UNSIGNED; - } - else - { - pxSocket->xEventBits |= eSOCKET_CONNECT; - - #if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) - { - if( pxSocket->xSelectBits & eSELECT_WRITE ) - { - pxSocket->xEventBits |= ( eSELECT_WRITE << SOCKET_EVENT_BIT_COUNT ); - } - } - #endif - } - } - else /* bAfter == pdFALSE, connection is closed. */ - { - /* Notify/wake-up the socket-owner by setting a semaphore. */ - pxSocket->xEventBits |= eSOCKET_CLOSED; - - #if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) - { - if( ( pxSocket->xSelectBits & eSELECT_EXCEPT ) != 0 ) - { - pxSocket->xEventBits |= ( eSELECT_EXCEPT << SOCKET_EVENT_BIT_COUNT ); - } - } - #endif - } - #if( ipconfigUSE_CALLBACKS == 1 ) - { - if( ( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleConnected ) != pdFALSE ) && ( xConnected == NULL ) ) - { - /* The 'connected' state has changed, call the user handler. */ - xConnected = pxSocket; - } - } - #endif /* ipconfigUSE_CALLBACKS */ - - if( prvTCPSocketIsActive( ( UBaseType_t ) pxSocket->u.xTCP.ucTCPState ) == pdFALSE ) - { - /* Now the socket isn't in an active state anymore so it - won't need further attention of the IP-task. - Setting time-out to zero means that the socket won't get checked during - timer events. */ - pxSocket->u.xTCP.usTimeout = 0u; - } - } - else - { - if( eTCPState == eCLOSED ) - { - /* 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" ) ); - if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) - { - FreeRTOS_closesocket( pxSocket ); - } - } - } - } - - /* Fill in the new state. */ - pxSocket->u.xTCP.ucTCPState = ( uint8_t ) eTCPState; - - /* touch the alive timers because moving to another state. */ - prvTCPTouchSocket( pxSocket ); - - #if( ipconfigHAS_DEBUG_PRINTF == 1 ) - { - if( ( xTCPWindowLoggingLevel >= 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) ) - FreeRTOS_debug_printf( ( "Socket %d -> %lxip:%u State %s->%s\n", - pxSocket->usLocalPort, - pxSocket->u.xTCP.ulRemoteIP, - pxSocket->u.xTCP.usRemotePort, - FreeRTOS_GetTCPStateName( ( UBaseType_t ) xPreviousState ), - FreeRTOS_GetTCPStateName( ( UBaseType_t ) eTCPState ) ) ); - } - #endif /* ipconfigHAS_DEBUG_PRINTF */ - - #if( ipconfigUSE_CALLBACKS == 1 ) - { - if( xConnected != NULL ) - { - /* The 'connected' state has changed, call the OnConnect handler of the parent. */ - xConnected->u.xTCP.pxHandleConnected( ( Socket_t ) xConnected, bAfter ); - } - } - #endif - if( xParent != NULL ) - { - vSocketWakeUpUser( xParent ); - } -} -/*-----------------------------------------------------------*/ - -static NetworkBufferDescriptor_t *prvTCPBufferResize( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, - int32_t lDataLen, UBaseType_t uxOptionsLength ) -{ -NetworkBufferDescriptor_t *pxReturn; -int32_t lNeeded; -BaseType_t xResize; - - if( xBufferAllocFixedSize != pdFALSE ) - { - /* Network buffers are created with a fixed size and can hold the largest - MTU. */ - lNeeded = ( int32_t ) ipTOTAL_ETHERNET_FRAME_SIZE; - /* and therefore, the buffer won't be too small. - Only ask for a new network buffer in case none was supplied. */ - xResize = ( pxNetworkBuffer == NULL ); - } - else - { - /* Network buffers are created with a variable size. See if it must - grow. */ - lNeeded = FreeRTOS_max_int32( ( int32_t ) sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ), - ( int32_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ) + lDataLen ); - /* In case we were called from a TCP timer event, a buffer must be - created. Otherwise, test 'xDataLength' of the provided buffer. */ - xResize = ( pxNetworkBuffer == NULL ) || ( pxNetworkBuffer->xDataLength < (size_t)lNeeded ); - } - - if( xResize != pdFALSE ) - { - /* The caller didn't provide a network buffer or the provided buffer is - too small. As we must send-out a data packet, a buffer will be created - here. */ - pxReturn = pxGetNetworkBufferWithDescriptor( ( uint32_t ) lNeeded, 0u ); - - if( pxReturn != NULL ) - { - /* Set the actual packet size, in case the returned buffer is larger. */ - pxReturn->xDataLength = lNeeded; - - /* Copy the existing data to the new created buffer. */ - if( pxNetworkBuffer ) - { - /* Either from the previous buffer... */ - memcpy( pxReturn->pucEthernetBuffer, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); - - /* ...and release it. */ - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); - } - else - { - /* Or from the socket field 'xTCP.xPacket'. */ - memcpy( pxReturn->pucEthernetBuffer, pxSocket->u.xTCP.xPacket.u.ucLastPacket, sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); - } - } - } - else - { - /* xResize is false, the network buffer provided was big enough. */ - pxReturn = pxNetworkBuffer; - - /* Thanks to Andrey Ivanov from swissEmbedded for reporting that the - xDataLength member must get the correct length too! */ - pxNetworkBuffer->xDataLength = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ) + ( size_t ) lDataLen; - } - - return pxReturn; -} -/*-----------------------------------------------------------*/ - -/* - * Prepare an outgoing message, in case anything has to be sent. - */ -static int32_t prvTCPPrepareSend( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, UBaseType_t uxOptionsLength ) -{ -int32_t lDataLen; -uint8_t *pucEthernetBuffer, *pucSendData; -TCPPacket_t *pxTCPPacket; -size_t uxOffset; -uint32_t ulDataGot, ulDistance; -TCPWindow_t *pxTCPWindow; -NetworkBufferDescriptor_t *pxNewBuffer; -int32_t lStreamPos; - - if( ( *ppxNetworkBuffer ) != NULL ) - { - /* A network buffer descriptor was already supplied */ - pucEthernetBuffer = ( *ppxNetworkBuffer )->pucEthernetBuffer; - } - else - { - /* For now let it point to the last packet header */ - pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket; - } - - pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer ); - pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; - lDataLen = 0; - lStreamPos = 0; - pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_ACK; - - if( pxSocket->u.xTCP.txStream != NULL ) - { - /* ulTCPWindowTxGet will return the amount of data which may be sent - along with the position in the txStream. - Why check for MSS > 1 ? - Because some TCP-stacks (like uIP) use it for flow-control. */ - if( pxSocket->u.xTCP.usCurMSS > 1u ) - { - lDataLen = ( int32_t ) ulTCPWindowTxGet( pxTCPWindow, pxSocket->u.xTCP.ulWindowSize, &lStreamPos ); - } - - if( lDataLen > 0 ) - { - /* Check if the current network buffer is big enough, if not, - resize it. */ - pxNewBuffer = prvTCPBufferResize( pxSocket, *ppxNetworkBuffer, lDataLen, uxOptionsLength ); - - if( pxNewBuffer != NULL ) - { - *ppxNetworkBuffer = pxNewBuffer; - pucEthernetBuffer = pxNewBuffer->pucEthernetBuffer; - pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer ); - - pucSendData = pucEthernetBuffer + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength; - - /* Translate the position in txStream to an offset from the tail - marker. */ - uxOffset = uxStreamBufferDistance( pxSocket->u.xTCP.txStream, pxSocket->u.xTCP.txStream->uxTail, ( size_t ) lStreamPos ); - - /* Here data is copied from the txStream in 'peek' mode. Only - when the packets are acked, the tail marker will be updated. */ - ulDataGot = ( uint32_t ) uxStreamBufferGet( pxSocket->u.xTCP.txStream, uxOffset, pucSendData, ( size_t ) lDataLen, pdTRUE ); - - #if( ipconfigHAS_DEBUG_PRINTF != 0 ) - { - if( ulDataGot != ( uint32_t ) lDataLen ) - { - FreeRTOS_debug_printf( ( "uxStreamBufferGet: pos %lu offs %lu only %lu != %lu\n", - lStreamPos, uxOffset, ulDataGot, lDataLen ) ); - } - } - #endif - - /* If the owner of the socket requests a closure, add the FIN - flag to the last packet. */ - if( ( pxSocket->u.xTCP.bits.bCloseRequested != pdFALSE_UNSIGNED ) && ( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) ) - { - ulDistance = ( uint32_t ) uxStreamBufferDistance( pxSocket->u.xTCP.txStream, ( size_t ) lStreamPos, pxSocket->u.xTCP.txStream->uxHead ); - - if( ulDistance == ulDataGot ) - { - #if (ipconfigHAS_DEBUG_PRINTF == 1) - { - /* the order of volatile accesses is undefined - so such workaround */ - size_t uxHead = pxSocket->u.xTCP.txStream->uxHead; - size_t uxMid = pxSocket->u.xTCP.txStream->uxMid; - size_t uxTail = pxSocket->u.xTCP.txStream->uxTail; - - FreeRTOS_debug_printf( ( "CheckClose %lu <= %lu (%lu <= %lu <= %lu)\n", ulDataGot, ulDistance, - uxTail, uxMid, uxHead ) ); - } - #endif - /* Although the socket sends a FIN, it will stay in - ESTABLISHED until all current data has been received or - delivered. */ - pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_FIN; - pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->ulOurSequenceNumber + ( uint32_t ) lDataLen; - pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED; - } - } - } - else - { - lDataLen = -1; - } - } - } - - if( ( lDataLen >= 0 ) && ( pxSocket->u.xTCP.ucTCPState == eESTABLISHED ) ) - { - /* See if the socket owner wants to shutdown this connection. */ - if( ( pxSocket->u.xTCP.bits.bUserShutdown != pdFALSE_UNSIGNED ) && - ( xTCPWindowTxDone( pxTCPWindow ) != pdFALSE ) ) - { - pxSocket->u.xTCP.bits.bUserShutdown = pdFALSE_UNSIGNED; - pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_FIN; - pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED; - pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED; - pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber; - vTCPStateChange( pxSocket, eFIN_WAIT_1 ); - } - - #if( ipconfigTCP_KEEP_ALIVE != 0 ) - { - if( pxSocket->u.xTCP.ucKeepRepCount > 3u ) - { - FreeRTOS_debug_printf( ( "keep-alive: giving up %lxip:%u\n", - pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine. */ - pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */ - vTCPStateChange( pxSocket, eCLOSE_WAIT ); - lDataLen = -1; - } - if( ( lDataLen == 0 ) && ( pxSocket->u.xTCP.bits.bWinChange == pdFALSE_UNSIGNED ) ) - { - /* If there is no data to be sent, and no window-update message, - we might want to send a keep-alive message. */ - TickType_t xAge = xTaskGetTickCount( ) - pxSocket->u.xTCP.xLastAliveTime; - TickType_t xMax; - xMax = ( ( TickType_t ) ipconfigTCP_KEEP_ALIVE_INTERVAL * configTICK_RATE_HZ ); - if( pxSocket->u.xTCP.ucKeepRepCount ) - { - xMax = ( 3u * configTICK_RATE_HZ ); - } - if( xAge > xMax ) - { - pxSocket->u.xTCP.xLastAliveTime = xTaskGetTickCount( ); - if( xTCPWindowLoggingLevel ) - FreeRTOS_debug_printf( ( "keep-alive: %lxip:%u count %u\n", - pxSocket->u.xTCP.ulRemoteIP, - pxSocket->u.xTCP.usRemotePort, - pxSocket->u.xTCP.ucKeepRepCount ) ); - pxSocket->u.xTCP.bits.bSendKeepAlive = pdTRUE_UNSIGNED; - pxSocket->u.xTCP.usTimeout = ( ( uint16_t ) pdMS_TO_TICKS( 2500 ) ); - pxSocket->u.xTCP.ucKeepRepCount++; - } - } - } - #endif /* ipconfigTCP_KEEP_ALIVE */ - } - - /* Anything to send, a change of the advertised window size, or maybe send a - keep-alive message? */ - if( ( lDataLen > 0 ) || - ( pxSocket->u.xTCP.bits.bWinChange != pdFALSE_UNSIGNED ) || - ( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED ) ) - { - pxTCPPacket->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~ipTCP_FLAG_PSH ); - pxTCPPacket->xTCPHeader.ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); - - pxTCPPacket->xTCPHeader.ucTCPFlags |= ( uint8_t ) ipTCP_FLAG_ACK; - - if( lDataLen != 0l ) - { - pxTCPPacket->xTCPHeader.ucTCPFlags |= ( uint8_t ) ipTCP_FLAG_PSH; - } - - lDataLen += ( int32_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); - } - - return lDataLen; -} -/*-----------------------------------------------------------*/ - -/* - * Calculate after how much time this socket needs to be checked again. - */ -static TickType_t prvTCPNextTimeout ( FreeRTOS_Socket_t *pxSocket ) -{ -TickType_t ulDelayMs = ( TickType_t ) tcpMAXIMUM_TCP_WAKEUP_TIME_MS; - - if( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) - { - /* The socket is actively connecting to a peer. */ - if( pxSocket->u.xTCP.bits.bConnPrepared ) - { - /* Ethernet address has been found, use progressive timeout for - active connect(). */ - if( pxSocket->u.xTCP.ucRepCount < 3u ) - { - ulDelayMs = ( 3000UL << ( pxSocket->u.xTCP.ucRepCount - 1u ) ); - } - else - { - ulDelayMs = 11000UL; - } - } - else - { - /* Still in the ARP phase: check every half second. */ - ulDelayMs = 500UL; - } - - FreeRTOS_debug_printf( ( "Connect[%lxip:%u]: next timeout %u: %lu ms\n", - pxSocket->u.xTCP.ulRemoteIP, pxSocket->u.xTCP.usRemotePort, - pxSocket->u.xTCP.ucRepCount, ulDelayMs ) ); - pxSocket->u.xTCP.usTimeout = ( uint16_t )pdMS_TO_MIN_TICKS( ulDelayMs ); - } - else if( pxSocket->u.xTCP.usTimeout == 0u ) - { - /* Let the sliding window mechanism decide what time-out is appropriate. */ - BaseType_t xResult = xTCPWindowTxHasData( &pxSocket->u.xTCP.xTCPWindow, pxSocket->u.xTCP.ulWindowSize, &ulDelayMs ); - if( ulDelayMs == 0u ) - { - if( xResult != ( BaseType_t )0 ) - { - ulDelayMs = 1UL; - } - else - { - ulDelayMs = tcpMAXIMUM_TCP_WAKEUP_TIME_MS; - } - } - else - { - /* ulDelayMs contains the time to wait before a re-transmission. */ - } - pxSocket->u.xTCP.usTimeout = ( uint16_t )pdMS_TO_MIN_TICKS( ulDelayMs ); - } - else - { - /* field '.usTimeout' has already been set (by the - keep-alive/delayed-ACK mechanism). */ - } - - /* Return the number of clock ticks before the timer expires. */ - return ( TickType_t ) pxSocket->u.xTCP.usTimeout; -} -/*-----------------------------------------------------------*/ - -static void prvTCPAddTxData( FreeRTOS_Socket_t *pxSocket ) -{ -int32_t lCount, lLength; - - /* A txStream has been created already, see if the socket has new data for - the sliding window. - - uxStreamBufferMidSpace() returns the distance between rxHead and rxMid. It contains new - Tx data which has not been passed to the sliding window yet. The oldest - data not-yet-confirmed can be found at rxTail. */ - lLength = ( int32_t ) uxStreamBufferMidSpace( pxSocket->u.xTCP.txStream ); - - if( lLength > 0 ) - { - /* All data between txMid and rxHead will now be passed to the sliding - window manager, so it can start transmitting them. - - Hand over the new data to the sliding window handler. It will be - split-up in chunks of 1460 bytes each (or less, depending on - ipconfigTCP_MSS). */ - lCount = lTCPWindowTxAdd( &pxSocket->u.xTCP.xTCPWindow, - ( uint32_t ) lLength, - ( int32_t ) pxSocket->u.xTCP.txStream->uxMid, - ( int32_t ) pxSocket->u.xTCP.txStream->LENGTH ); - - /* Move the rxMid pointer forward up to rxHead. */ - if( lCount > 0 ) - { - vStreamBufferMoveMid( pxSocket->u.xTCP.txStream, ( size_t ) lCount ); - } - } -} -/*-----------------------------------------------------------*/ - -/* - * prvTCPHandleFin() will be called to handle socket 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. - */ -static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; -uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; -TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; -BaseType_t xSendLength = 0; -uint32_t ulAckNr = FreeRTOS_ntohl( pxTCPHeader->ulAckNr ); - - if( ( ucTCPFlags & ipTCP_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 = ipTCP_FLAG_ACK | ipTCP_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 = ipTCP_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 ) - { - xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength ); - } - - pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength ) << 2 ); - - if( xTCPWindowLoggingLevel != 0 ) - { - FreeRTOS_debug_printf( ( "TCP: send FIN+ACK (ack %lu, cur/nxt %lu/%lu) ourSeqNr %lu | Rx %lu\n", - ulAckNr - pxTCPWindow->tx.ulFirstSequenceNumber, - pxTCPWindow->tx.ulCurrentSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, - pxTCPWindow->ulNextTxSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, - pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, - pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ); - } - - return xSendLength; -} -/*-----------------------------------------------------------*/ - -/* - * prvCheckRxData(): called from prvTCPHandleState() - * - * The first thing that will be done is find the TCP payload data - * and check the length of this data. - */ -static BaseType_t prvCheckRxData( NetworkBufferDescriptor_t *pxNetworkBuffer, uint8_t **ppucRecvData ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &( pxTCPPacket->xTCPHeader ); -int32_t lLength, lTCPHeaderLength, lReceiveLength, lUrgentLength; - - /* Determine the length and the offset of the user-data sent to this - node. - - The size of the TCP header is given in a multiple of 4-byte words (single - byte, needs no ntoh() translation). A shift-right 2: is the same as - (offset >> 4) * 4. */ - lTCPHeaderLength = ( BaseType_t ) ( ( pxTCPHeader->ucTCPOffset & VALID_BITS_IN_TCP_OFFSET_BYTE ) >> 2 ); - - /* Let pucRecvData point to the first byte received. */ - *ppucRecvData = pxNetworkBuffer->pucEthernetBuffer + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + lTCPHeaderLength; - - /* Calculate lReceiveLength - the length of the TCP data received. This is - equal to the total packet length minus: - ( LinkLayer length (14) + IP header length (20) + size of TCP header(20 +) ).*/ - lReceiveLength = ( ( int32_t ) pxNetworkBuffer->xDataLength ) - ( int32_t ) ipSIZE_OF_ETH_HEADER; - lLength = ( int32_t )FreeRTOS_htons( pxTCPPacket->xIPHeader.usLength ); - - if( lReceiveLength > lLength ) - { - /* More bytes were received than the reported length, often because of - padding bytes at the end. */ - lReceiveLength = lLength; - } - - /* Subtract the size of the TCP and IP headers and the actual data size is - known. */ - if( lReceiveLength > ( lTCPHeaderLength + ( int32_t ) ipSIZE_OF_IPv4_HEADER ) ) - { - lReceiveLength -= ( lTCPHeaderLength + ( int32_t ) ipSIZE_OF_IPv4_HEADER ); - } - else - { - lReceiveLength = 0; - } - - /* Urgent Pointer: - This field communicates the current value of the urgent pointer as a - positive offset from the sequence number in this segment. The urgent - pointer points to the sequence number of the octet following the urgent - data. This field is only be interpreted in segments with the URG control - bit set. */ - if( ( pxTCPHeader->ucTCPFlags & ipTCP_FLAG_URG ) != 0u ) - { - /* Although we ignore the urgent data, we have to skip it. */ - lUrgentLength = ( int32_t ) FreeRTOS_htons( pxTCPHeader->usUrgent ); - *ppucRecvData += lUrgentLength; - lReceiveLength -= FreeRTOS_min_int32( lReceiveLength, lUrgentLength ); - } - - return ( BaseType_t ) lReceiveLength; -} -/*-----------------------------------------------------------*/ - -/* - * prvStoreRxData(): called from prvTCPHandleState() - * - * The second thing is to do is check if the payload data may be accepted - * If so, they will be added to the reception queue. - */ -static BaseType_t prvStoreRxData( FreeRTOS_Socket_t *pxSocket, uint8_t *pucRecvData, - NetworkBufferDescriptor_t *pxNetworkBuffer, uint32_t ulReceiveLength ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; -TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; -uint32_t ulSequenceNumber, ulSpace; -int32_t lOffset, lStored; -BaseType_t xResult = 0; - - ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ); - - if( ( ulReceiveLength > 0u ) && ( pxSocket->u.xTCP.ucTCPState >= eSYN_RECEIVED ) ) - { - /* See if way may accept the data contents and forward it to the socket - owner. - - If it can't be "accept"ed it may have to be stored and send a selective - ack (SACK) option to confirm it. In that case, xTCPWindowRxStore() will be - called later to store an out-of-order packet (in case lOffset is - negative). */ - if ( pxSocket->u.xTCP.rxStream ) - { - ulSpace = ( uint32_t )uxStreamBufferGetSpace ( pxSocket->u.xTCP.rxStream ); - } - else - { - ulSpace = ( uint32_t )pxSocket->u.xTCP.uxRxStreamSize; - } - - lOffset = lTCPWindowRxCheck( pxTCPWindow, ulSequenceNumber, ulReceiveLength, ulSpace ); - - if( lOffset >= 0 ) - { - /* New data has arrived and may be made available to the user. See - if the head marker in rxStream may be advanced, only if lOffset == 0. - In case the low-water mark is reached, bLowWater will be set - "low-water" here stands for "little space". */ - lStored = lTCPAddRxdata( pxSocket, ( uint32_t ) lOffset, pucRecvData, ulReceiveLength ); - - if( lStored != ( int32_t ) ulReceiveLength ) - { - FreeRTOS_debug_printf( ( "lTCPAddRxdata: stored %ld / %lu bytes??\n", lStored, ulReceiveLength ) ); - - /* Received data could not be stored. The socket's flag - bMallocError has been set. The socket now has the status - eCLOSE_WAIT and a RST packet will be sent back. */ - prvTCPSendReset( pxNetworkBuffer ); - xResult = -1; - } - } - - /* After a missing packet has come in, higher packets may be passed to - the user. */ - #if( ipconfigUSE_TCP_WIN == 1 ) - { - /* Now lTCPAddRxdata() will move the rxHead pointer forward - so data becomes available to the user immediately - In case the low-water mark is reached, bLowWater will be set. */ - if( ( xResult == 0 ) && ( pxTCPWindow->ulUserDataLength > 0 ) ) - { - lTCPAddRxdata( pxSocket, 0ul, NULL, pxTCPWindow->ulUserDataLength ); - pxTCPWindow->ulUserDataLength = 0; - } - } - #endif /* ipconfigUSE_TCP_WIN */ - } - else - { - pxTCPWindow->ucOptionLength = 0u; - } - - return xResult; -} -/*-----------------------------------------------------------*/ - -/* Set the TCP options (if any) for the outgoing packet. */ -static UBaseType_t prvSetOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; -TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; -UBaseType_t uxOptionsLength = pxTCPWindow->ucOptionLength; - - #if( ipconfigUSE_TCP_WIN == 1 ) - if( uxOptionsLength != 0u ) - { - /* TCP options must be sent because a packet which is out-of-order - was received. */ - if( xTCPWindowLoggingLevel >= 0 ) - FreeRTOS_debug_printf( ( "SACK[%d,%d]: optlen %lu sending %lu - %lu\n", - pxSocket->usLocalPort, - pxSocket->u.xTCP.usRemotePort, - uxOptionsLength, - FreeRTOS_ntohl( pxTCPWindow->ulOptionsData[ 1 ] ) - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber, - FreeRTOS_ntohl( pxTCPWindow->ulOptionsData[ 2 ] ) - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber ) ); - memcpy( pxTCPHeader->ucOptdata, pxTCPWindow->ulOptionsData, ( size_t ) uxOptionsLength ); - - /* The header length divided by 4, goes into the higher nibble, - effectively a shift-left 2. */ - pxTCPHeader->ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); - } - else - #endif /* ipconfigUSE_TCP_WIN */ - if( ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) && ( pxSocket->u.xTCP.bits.bMssChange != pdFALSE_UNSIGNED ) ) - { - /* TCP options must be sent because the MSS has changed. */ - pxSocket->u.xTCP.bits.bMssChange = pdFALSE_UNSIGNED; - if( xTCPWindowLoggingLevel >= 0 ) - { - FreeRTOS_debug_printf( ( "MSS: sending %d\n", pxSocket->u.xTCP.usCurMSS ) ); - } - - pxTCPHeader->ucOptdata[ 0 ] = TCP_OPT_MSS; - pxTCPHeader->ucOptdata[ 1 ] = TCP_OPT_MSS_LEN; - pxTCPHeader->ucOptdata[ 2 ] = ( uint8_t ) ( ( pxSocket->u.xTCP.usCurMSS ) >> 8 ); - pxTCPHeader->ucOptdata[ 3 ] = ( uint8_t ) ( ( pxSocket->u.xTCP.usCurMSS ) & 0xffu ); - uxOptionsLength = 4u; - pxTCPHeader->ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); - } - - return uxOptionsLength; -} -/*-----------------------------------------------------------*/ - -/* - * 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. - */ -static BaseType_t prvHandleSynReceived( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, - uint32_t ulReceiveLength, UBaseType_t uxOptionsLength ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; -TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; -uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; -uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ); -BaseType_t xSendLength = 0; - - /* Either expect a ACK or a SYN+ACK. */ - uint16_t usExpect = ( uint16_t ) ipTCP_FLAG_ACK; - if( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) - { - usExpect |= ( uint16_t ) ipTCP_FLAG_SYN; - } - - if( ( ucTCPFlags & 0x17u ) != usExpect ) - { - /* 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 == eSYN_RECEIVED ? "eSYN_RECEIVED" : "eCONNECT_SYN", - usExpect, ucTCPFlags ) ); - vTCPStateChange( pxSocket, eCLOSE_WAIT ); - pxTCPHeader->ucTCPFlags |= ipTCP_FLAG_RST; - xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); - 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 == eCONNECT_SYN ) - { - TCPPacket_t *pxLastTCPPacket = ( TCPPacket_t * ) ( pxSocket->u.xTCP.xPacket.u.ucLastPacket ); - - /* Clear the SYN flag in lastPacket. */ - pxLastTCPPacket->xTCPHeader.ucTCPFlags = ipTCP_FLAG_ACK; - - /* This socket was the one connecting actively so now perofmr the - synchronisation. */ - vTCPWindowInit( &pxSocket->u.xTCP.xTCPWindow, - ulSequenceNumber, pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber, ( uint32_t ) pxSocket->u.xTCP.usCurMSS ); - pxTCPWindow->rx.ulCurrentSequenceNumber = pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1u; - pxTCPWindow->tx.ulCurrentSequenceNumber++; /* because we send a TCP_SYN [ | TCP_ACK ]; */ - pxTCPWindow->ulNextTxSequenceNumber++; - } - else if( ulReceiveLength == 0u ) - { - pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber; - } - - /* 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 %d => %lxip:%d set ESTAB (scaling %u)\n", - pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ? "active" : "passive", - pxSocket->usLocalPort, - pxSocket->u.xTCP.ulRemoteIP, - pxSocket->u.xTCP.usRemotePort, - ( unsigned ) pxSocket->u.xTCP.bits.bWinScaling ) ); - } - #endif /* ipconfigUSE_TCP_WIN */ - - if( ( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) || ( ulReceiveLength != 0u ) ) - { - pxTCPHeader->ucTCPFlags = ipTCP_FLAG_ACK; - xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); - 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; -} -/*-----------------------------------------------------------*/ - -/* - * 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. - */ -static BaseType_t prvHandleEstablished( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, - uint32_t ulReceiveLength, UBaseType_t uxOptionsLength ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; -TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; -uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; -uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ), ulCount; -BaseType_t xSendLength = 0, xMayClose = pdFALSE, bRxComplete, bTxDone; -int32_t lDistance, lSendResult; - - /* Remember the window size the peer is advertising. */ - pxSocket->u.xTCP.ulWindowSize = FreeRTOS_ntohs( pxTCPHeader->usWindow ); - #if( ipconfigUSE_TCP_WIN != 0 ) - { - pxSocket->u.xTCP.ulWindowSize = - ( pxSocket->u.xTCP.ulWindowSize << pxSocket->u.xTCP.ucPeerWinScaleFactor ); - } - #endif - - if( ( ucTCPFlags & ( uint8_t ) ipTCP_FLAG_ACK ) != 0u ) - { - ulCount = ulTCPWindowTxAck( pxTCPWindow, FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.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 |= eSOCKET_SEND; - - #if ipconfigSUPPORT_SELECT_FUNCTION == 1 - { - if( ( pxSocket->xSelectBits & eSELECT_WRITE ) != 0 ) - { - pxSocket->xEventBits |= ( 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 ) ipTCP_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 incomp 1 optlen 4 tx done 1. */ - FreeRTOS_debug_printf( ( "Refusing FIN[%u,%u]: RxCompl %lu tx done %ld\n", - pxSocket->usLocalPort, - pxSocket->u.xTCP.usRemotePort, - bRxComplete, bTxDone ) ); - xMayClose = pdFALSE; - } - else - { - lDistance = ( int32_t ) ( ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulCurrentSequenceNumber ); - - if( lDistance > 1 ) - { - FreeRTOS_debug_printf( ( "Refusing FIN: Rx not complete %ld (cur %lu high %lu)\n", - lDistance, pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber, - pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ); - - xMayClose = pdFALSE; - } - } - } - - if( xTCPWindowLoggingLevel > 0 ) - { - FreeRTOS_debug_printf( ( "TCP: FIN received, mayClose = %ld (Rx %lu Len %ld, Tx %lu)\n", - xMayClose, ulSequenceNumber - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber, ulReceiveLength, - 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 = ipTCP_FLAG_ACK; - - if( ulReceiveLength != 0u ) - { - xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); - /* TCP-offsett 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; -} -/*-----------------------------------------------------------*/ - -/* - * Called from prvTCPHandleState(). There is data to be sent. If - * ipconfigUSE_TCP_WIN is defined, and if only an ACK must be sent, it will be - * checked if it would better be postponed for efficiency. - */ -static BaseType_t prvSendData( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, - uint32_t ulReceiveLength, BaseType_t xSendLength ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; -TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; -/* Find out what window size we may advertised. */ -int32_t lRxSpace; -#if( ipconfigUSE_TCP_WIN == 1 ) - #if( ipconfigTCP_ACK_EARLIER_PACKET == 0 ) - const int32_t lMinLength = 0; - #else - int32_t lMinLength; - #endif -#endif - - /* Set the time-out field, so that we'll be called by the IP-task in case no - next message will be received. */ - lRxSpace = (int32_t)( pxSocket->u.xTCP.ulHighestRxAllowed - pxTCPWindow->rx.ulCurrentSequenceNumber ); - #if ipconfigUSE_TCP_WIN == 1 - { - - #if( ipconfigTCP_ACK_EARLIER_PACKET != 0 ) - { - lMinLength = ( ( int32_t ) 2 ) * ( ( int32_t ) pxSocket->u.xTCP.usCurMSS ); - } - #endif /* ipconfigTCP_ACK_EARLIER_PACKET */ - - /* In case we're receiving data continuously, we might postpone sending - an ACK to gain performance. */ - if( ( ulReceiveLength > 0 ) && /* Data was sent to this socket. */ - ( lRxSpace >= lMinLength ) && /* There is Rx space for more data. */ - ( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) && /* Not in a closure phase. */ - ( xSendLength == ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ) && /* No Tx data or options to be sent. */ - ( pxSocket->u.xTCP.ucTCPState == eESTABLISHED ) && /* Connection established. */ - ( pxTCPHeader->ucTCPFlags == ipTCP_FLAG_ACK ) ) /* There are no other flags than an ACK. */ - { - if( pxSocket->u.xTCP.pxAckMessage != *ppxNetworkBuffer ) - { - /* There was still a delayed in queue, delete it. */ - if( pxSocket->u.xTCP.pxAckMessage != 0 ) - { - vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); - } - - pxSocket->u.xTCP.pxAckMessage = *ppxNetworkBuffer; - } - if( ( ulReceiveLength < ( uint32_t ) pxSocket->u.xTCP.usCurMSS ) || /* Received a small message. */ - ( lRxSpace < ( int32_t ) ( 2U * pxSocket->u.xTCP.usCurMSS ) ) ) /* There are less than 2 x MSS space in the Rx buffer. */ - { - pxSocket->u.xTCP.usTimeout = ( uint16_t ) pdMS_TO_MIN_TICKS( DELAYED_ACK_SHORT_DELAY_MS ); - } - else - { - /* Normally a delayed ACK should wait 200 ms for a next incoming - packet. Only wait 20 ms here to gain performance. A slow ACK - for full-size message. */ - pxSocket->u.xTCP.usTimeout = ( uint16_t ) pdMS_TO_MIN_TICKS( DELAYED_ACK_LONGER_DELAY_MS ); - } - - if( ( xTCPWindowLoggingLevel > 1 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) ) - { - FreeRTOS_debug_printf( ( "Send[%u->%u] del ACK %lu SEQ %lu (len %lu) tmout %u d %lu\n", - pxSocket->usLocalPort, - pxSocket->u.xTCP.usRemotePort, - pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber, - pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, - xSendLength, - pxSocket->u.xTCP.usTimeout, lRxSpace ) ); - } - - *ppxNetworkBuffer = NULL; - xSendLength = 0; - } - else if( pxSocket->u.xTCP.pxAckMessage != NULL ) - { - /* As an ACK is not being delayed, remove any earlier delayed ACK - message. */ - if( pxSocket->u.xTCP.pxAckMessage != *ppxNetworkBuffer ) - { - vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); - } - - pxSocket->u.xTCP.pxAckMessage = NULL; - } - } - #else - { - /* Remove compiler warnings. */ - ( void ) ulReceiveLength; - ( void ) pxTCPHeader; - ( void ) lRxSpace; - } - #endif /* ipconfigUSE_TCP_WIN */ - - if( xSendLength != 0 ) - { - if( ( xTCPWindowLoggingLevel > 1 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) ) - { - FreeRTOS_debug_printf( ( "Send[%u->%u] imm ACK %lu SEQ %lu (len %lu)\n", - pxSocket->usLocalPort, - pxSocket->u.xTCP.usRemotePort, - pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber, - pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, - xSendLength ) ); - } - - /* Set the parameter 'xReleaseAfterSend' to the value of - ipconfigZERO_COPY_TX_DRIVER. */ - prvTCPReturnPacket( pxSocket, *ppxNetworkBuffer, ( uint32_t ) xSendLength, ipconfigZERO_COPY_TX_DRIVER ); - #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) - { - /* The driver has taken ownership of the Network Buffer. */ - *ppxNetworkBuffer = NULL; - } - #endif - } - - return xSendLength; -} -/*-----------------------------------------------------------*/ - -/* - * 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. - */ -static BaseType_t prvTCPHandleState( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ) -{ -TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); -TCPHeader_t *pxTCPHeader = &( pxTCPPacket->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 ); - - /* 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 >= 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. */ - if( ( ( int32_t ) ( ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulHighestSequenceNumber ) ) > 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 - { - uxOptionsLength = prvSetOptions( pxSocket, *ppxNetworkBuffer ); - - if( ( pxSocket->u.xTCP.ucTCPState == eSYN_RECEIVED ) && ( ( ucTCPFlags & ipTCP_FLAG_CTRL ) == ipTCP_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 & ipTCP_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; - } - } - - switch (pxSocket->u.xTCP.ucTCPState) - { - 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, pxTCPPacket ); - pxTCPHeader->ucTCPFlags = ipTCP_FLAG_SYN | ipTCP_FLAG_ACK; - - xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); - - /* 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.ulCurrentSequenceNumber = pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1u; - pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->ulNextTxSequenceNumber = 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: - break; - } - } - - if( xSendLength > 0 ) - { - xSendLength = prvSendData( pxSocket, ppxNetworkBuffer, ulReceiveLength, xSendLength ); - } - - return xSendLength; -} -/*-----------------------------------------------------------*/ - -static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t *pxNetworkBuffer, - uint8_t ucTCPFlags ) -{ -#if( ipconfigIGNORE_UNKNOWN_PACKETS == 0 ) - { - TCPPacket_t *pxTCPPacket = ( TCPPacket_t * )( pxNetworkBuffer->pucEthernetBuffer ); - const BaseType_t xSendLength = ( BaseType_t ) - ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + 0u ); /* Plus 0 options. */ - - pxTCPPacket->xTCPHeader.ucTCPFlags = ucTCPFlags; - pxTCPPacket->xTCPHeader.ucTCPOffset = ( ipSIZE_OF_TCP_HEADER + 0u ) << 2; - - prvTCPReturnPacket( NULL, pxNetworkBuffer, ( uint32_t )xSendLength, pdFALSE ); - } -#endif /* !ipconfigIGNORE_UNKNOWN_PACKETS */ - - /* Remove compiler warnings if ipconfigIGNORE_UNKNOWN_PACKETS == 1. */ - ( void )pxNetworkBuffer; - ( void )ucTCPFlags; - - /* The packet was not consumed. */ - return pdFAIL; -} -/*-----------------------------------------------------------*/ - -static BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t *pxNetworkBuffer ) -{ - return prvTCPSendSpecialPacketHelper( pxNetworkBuffer, ipTCP_FLAG_ACK ); -} -/*-----------------------------------------------------------*/ - -static BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t *pxNetworkBuffer ) -{ - return prvTCPSendSpecialPacketHelper( pxNetworkBuffer, - ipTCP_FLAG_ACK | ipTCP_FLAG_RST ); -} -/*-----------------------------------------------------------*/ - -static void prvSocketSetMSS( FreeRTOS_Socket_t *pxSocket ) -{ -uint32_t ulMSS = ipconfigTCP_MSS; - - if( ( ( FreeRTOS_ntohl( pxSocket->u.xTCP.ulRemoteIP ) ^ *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) != 0ul ) - { - /* Data for this peer will pass through a router, and maybe through - the internet. Limit the MSS to 1400 bytes or less. */ - ulMSS = FreeRTOS_min_uint32( ( uint32_t ) REDUCED_MSS_THROUGH_INTERNET, ulMSS ); - } - - FreeRTOS_debug_printf( ( "prvSocketSetMSS: %lu bytes for %lxip:%u\n", ulMSS, pxSocket->u.xTCP.ulRemoteIP, pxSocket->u.xTCP.usRemotePort ) ); - - pxSocket->u.xTCP.usInitMSS = pxSocket->u.xTCP.usCurMSS = ( uint16_t ) ulMSS; -} -/*-----------------------------------------------------------*/ - -/* - * FreeRTOS_TCP_IP has only 2 public functions, this is the second one: - * xProcessReceivedTCPPacket() - * prvTCPHandleState() - * prvTCPPrepareSend() - * prvTCPReturnPacket() - * xNetworkInterfaceOutput() // Sends data to the NIC - * prvTCPSendRepeated() - * prvTCPReturnPacket() // Prepare for returning - * xNetworkInterfaceOutput() // Sends data to the NIC -*/ -BaseType_t xProcessReceivedTCPPacket( NetworkBufferDescriptor_t *pxNetworkBuffer ) -{ -FreeRTOS_Socket_t *pxSocket; -TCPPacket_t * pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); -uint16_t ucTCPFlags; -uint32_t ulLocalIP; -uint16_t xLocalPort; -uint32_t ulRemoteIP; -uint16_t xRemotePort; -uint32_t ulSequenceNumber; -uint32_t ulAckNumber; -BaseType_t xResult = pdPASS; -configASSERT(pxNetworkBuffer); -configASSERT(pxNetworkBuffer->pucEthernetBuffer); - - /* Check for a minimum packet size. */ - if( pxNetworkBuffer->xDataLength >= ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ) - { - ucTCPFlags = pxTCPPacket->xTCPHeader.ucTCPFlags; - ulLocalIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulDestinationIPAddress ); - xLocalPort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usDestinationPort ); - ulRemoteIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulSourceIPAddress ); - xRemotePort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usSourcePort ); - ulSequenceNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulSequenceNumber ); - ulAckNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulAckNr ); - - /* Find the destination socket, and if not found: return a socket listing to - the destination PORT. */ - pxSocket = ( FreeRTOS_Socket_t * )pxTCPSocketLookup( ulLocalIP, xLocalPort, ulRemoteIP, xRemotePort ); - } - else - { - return pdFAIL; - } - - if( ( pxSocket == NULL ) || ( prvTCPSocketIsActive( ( UBaseType_t ) pxSocket->u.xTCP.ucTCPState ) == pdFALSE ) ) - { - /* A TCP messages is received but either there is no socket with the - given port number or the there is a socket, but it is in one of these - non-active states: eCLOSED, eCLOSE_WAIT, eFIN_WAIT_2, eCLOSING, or - eTIME_WAIT. */ - - FreeRTOS_debug_printf( ( "TCP: No active socket on port %d (%lxip:%d)\n", xLocalPort, ulRemoteIP, xRemotePort ) ); - - /* Send a RST to all packets that can not be handled. As a result - the other party will get a ECONN error. There are two exceptions: - 1) A packet that already has the RST flag set. - 2) A packet that only has the ACK flag set. - A packet with only the ACK flag set might be the last ACK in - a three-way hand-shake that closes a connection. */ - if( ( ( ucTCPFlags & ipTCP_FLAG_CTRL ) != ipTCP_FLAG_ACK ) && - ( ( ucTCPFlags & ipTCP_FLAG_RST ) == 0u ) ) - { - prvTCPSendReset( pxNetworkBuffer ); - } - - /* The packet can't be handled. */ - xResult = pdFAIL; - } - else - { - pxSocket->u.xTCP.ucRepCount = 0u; - - if( pxSocket->u.xTCP.ucTCPState == eTCP_LISTEN ) - { - /* The matching socket is in a listening state. Test if the peer - has set the SYN flag. */ - if( ( ucTCPFlags & ipTCP_FLAG_CTRL ) != ipTCP_FLAG_SYN ) - { - /* What happens: maybe after a reboot, a client doesn't know the - connection had gone. Send a RST in order to get a new connect - request. */ - #if( ipconfigHAS_DEBUG_PRINTF == 1 ) - { - FreeRTOS_debug_printf( ( "TCP: Server can't handle flags: %s from %lxip:%u to port %u\n", - prvTCPFlagMeaning( ( UBaseType_t ) ucTCPFlags ), ulRemoteIP, xRemotePort, xLocalPort ) ); - } - #endif /* ipconfigHAS_DEBUG_PRINTF */ - - if( ( ucTCPFlags & ipTCP_FLAG_RST ) == 0u ) - { - prvTCPSendReset( pxNetworkBuffer ); - } - xResult = pdFAIL; - } - else - { - /* prvHandleListen() will either return a newly created socket - (if bReuseSocket is false), otherwise it returns the current - socket which will later get connected. */ - pxSocket = prvHandleListen( pxSocket, pxNetworkBuffer ); - - if( pxSocket == NULL ) - { - xResult = pdFAIL; - } - } - } /* if( pxSocket->u.xTCP.ucTCPState == eTCP_LISTEN ). */ - else - { - /* This is not a socket in listening mode. Check for the RST - flag. */ - if( ( ucTCPFlags & ipTCP_FLAG_RST ) != 0u ) - { - FreeRTOS_debug_printf( ( "TCP: RST received from %lxip:%u for %u\n", ulRemoteIP, xRemotePort, xLocalPort ) ); - - /* Implement https://tools.ietf.org/html/rfc5961#section-3.2. */ - if( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) - { - /* Per the above RFC, "In the SYN-SENT state ... the RST is - acceptable if the ACK field acknowledges the SYN." */ - if( ulAckNumber == pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber + 1 ) - { - vTCPStateChange( pxSocket, eCLOSED ); - } - } - else - { - /* Check whether the packet matches the next expected sequence number. */ - if( ulSequenceNumber == pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber ) - { - vTCPStateChange( pxSocket, eCLOSED ); - } - /* Otherwise, check whether the packet is within the receive window. */ - else if( ulSequenceNumber > pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber && - ulSequenceNumber < ( pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber + - pxSocket->u.xTCP.xTCPWindow.xSize.ulRxWindowLength ) ) - { - /* Send a challenge ACK. */ - prvTCPSendChallengeAck( pxNetworkBuffer ); - } - } - - /* Otherwise, do nothing. In any case, the packet cannot be handled. */ - xResult = pdFAIL; - } - else if( ( ( ucTCPFlags & ipTCP_FLAG_CTRL ) == ipTCP_FLAG_SYN ) && ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) ) - { - /* SYN flag while this socket is already connected. */ - FreeRTOS_debug_printf( ( "TCP: SYN unexpected from %lxip:%u\n", ulRemoteIP, xRemotePort ) ); - - /* The packet cannot be handled. */ - xResult = pdFAIL; - } - else - { - /* Update the copy of the TCP header only (skipping eth and IP - headers). It might be used later on, whenever data must be sent - to the peer. */ - const BaseType_t lOffset = ( BaseType_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER ); - memcpy( pxSocket->u.xTCP.xPacket.u.ucLastPacket + lOffset, pxNetworkBuffer->pucEthernetBuffer + lOffset, ipSIZE_OF_TCP_HEADER ); - } - } - } - - if( xResult != pdFAIL ) - { - /* Touch the alive timers because we received a message for this - socket. */ - prvTCPTouchSocket( pxSocket ); - - /* Parse the TCP option(s), if present. */ - /* _HT_ : if we're in the SYN phase, and peer does not send a MSS option, - then we MUST assume an MSS size of 536 bytes for backward compatibility. */ - - /* When there are no TCP options, the TCP offset equals 20 bytes, which is stored as - the number 5 (words) in the higher niblle of the TCP-offset byte. */ - if( ( pxTCPPacket->xTCPHeader.ucTCPOffset & TCP_OFFSET_LENGTH_BITS ) > TCP_OFFSET_STANDARD_LENGTH ) - { - prvCheckOptions( pxSocket, pxNetworkBuffer ); - } - - - #if( ipconfigUSE_TCP_WIN == 1 ) - { - pxSocket->u.xTCP.ulWindowSize = FreeRTOS_ntohs( pxTCPPacket->xTCPHeader.usWindow ); - pxSocket->u.xTCP.ulWindowSize = - ( pxSocket->u.xTCP.ulWindowSize << pxSocket->u.xTCP.ucPeerWinScaleFactor ); - } - #endif - - /* In prvTCPHandleState() the incoming messages will be handled - depending on the current state of the connection. */ - if( prvTCPHandleState( pxSocket, &pxNetworkBuffer ) > 0 ) - { - /* prvTCPHandleState() has sent a message, see if there are more to - be transmitted. */ - #if( ipconfigUSE_TCP_WIN == 1 ) - { - prvTCPSendRepeated( pxSocket, &pxNetworkBuffer ); - } - #endif /* ipconfigUSE_TCP_WIN */ - } - - if( pxNetworkBuffer != NULL ) - { - /* We must check if the buffer is unequal to NULL, because the - socket might keep a reference to it in case a delayed ACK must be - sent. */ - vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); - pxNetworkBuffer = NULL; - } - - /* And finally, calculate when this socket wants to be woken up. */ - prvTCPNextTimeout ( pxSocket ); - /* Return pdPASS to tell that the network buffer is 'consumed'. */ - xResult = pdPASS; - } - - /* pdPASS being returned means the buffer has been consumed. */ - return xResult; -} -/*-----------------------------------------------------------*/ - -static FreeRTOS_Socket_t *prvHandleListen( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) -{ -TCPPacket_t * pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); -FreeRTOS_Socket_t *pxReturn = NULL; -uint32_t ulInitialSequenceNumber; - - /* 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 ); - - /* A pure SYN (without ACK) has come in, create a new socket to answer - it. */ - if( 0 != ulInitialSequenceNumber ) - { - 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 == 1 ? "" : "ren" ) ); - 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" ) ); - 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; - } - } - } - } - - if( ( 0 != ulInitialSequenceNumber ) && ( pxReturn != NULL ) ) - { - 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( pxTCPPacket->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. */ - memcpy( pxReturn->u.xTCP.xPacket.u.ucLastPacket, pxNetworkBuffer->pucEthernetBuffer, sizeof( pxReturn->u.xTCP.xPacket.u.ucLastPacket ) ); - } - return pxReturn; -} -/*-----------------------------------------------------------*/ - -/* - * Duplicates a socket after a listening socket receives a connection. - */ -static BaseType_t prvTCPSocketCopy( FreeRTOS_Socket_t *pxNewSocket, FreeRTOS_Socket_t *pxSocket ) -{ -struct freertos_sockaddr xAddress; - - 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 ) - { - pxNewSocket->pxSocketSet = pxSocket->pxSocketSet; - pxNewSocket->xSelectBits = pxSocket->xSelectBits | eSELECT_READ | 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 pdTRUE_UNSIGNED 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 - - 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" ) ); - vSocketClose( pxNewSocket ); - return pdFALSE; - } - - return pdTRUE; -} -/*-----------------------------------------------------------*/ - -#if( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) - - const char *FreeRTOS_GetTCPStateName( UBaseType_t ulState ) - { - if( ulState >= ( UBaseType_t ) ARRAY_SIZE( pcStateNames ) ) - { - ulState = ( UBaseType_t ) ARRAY_SIZE( pcStateNames ) - 1u; - } - return pcStateNames[ ulState ]; - } - -#endif /* ( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) */ -/*-----------------------------------------------------------*/ - -/* - * In the API accept(), the user asks is there is a new client? As API's can - * not walk through the xBoundTCPSocketsList the IP-task will do this. - */ -BaseType_t xTCPCheckNewClient( FreeRTOS_Socket_t *pxSocket ) -{ -TickType_t xLocalPort = FreeRTOS_htons( pxSocket->usLocalPort ); -ListItem_t *pxIterator; -FreeRTOS_Socket_t *pxFound; -BaseType_t xResult = pdFALSE; - - /* Here xBoundTCPSocketsList can be accessed safely IP-task is the only one - who has access. */ - for( pxIterator = ( ListItem_t * ) listGET_HEAD_ENTRY( &xBoundTCPSocketsList ); - pxIterator != ( ListItem_t * ) listGET_END_MARKER( &xBoundTCPSocketsList ); - pxIterator = ( ListItem_t * ) listGET_NEXT( pxIterator ) ) - { - if( listGET_LIST_ITEM_VALUE( pxIterator ) == xLocalPort ) - { - pxFound = ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); - if( ( pxFound->ucProtocol == FREERTOS_IPPROTO_TCP ) && ( pxFound->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) - { - pxSocket->u.xTCP.pxPeerSocket = pxFound; - FreeRTOS_debug_printf( ( "xTCPCheckNewClient[0]: client on port %u\n", pxSocket->usLocalPort ) ); - xResult = pdTRUE; - break; - } - } - } - return xResult; -} -/*-----------------------------------------------------------*/ - -#endif /* ipconfigUSE_TCP == 1 */ - -/* Provide access to private members for testing. */ -#ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS - #include "iot_freertos_tcp_test_access_tcp_define.h" -#endif - -/* Provide access to private members for verification. */ -#ifdef FREERTOS_TCP_ENABLE_VERIFICATION - #include "aws_freertos_tcp_verification_access_tcp_define.h" -#endif - +/* + * FreeRTOS+TCP V2.2.0 + * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 + */ + +/* + * FreeRTOS_TCP_IP.c + * Module which handles the TCP connections for FreeRTOS+TCP. + * It depends on FreeRTOS_TCP_WIN.c, which handles the TCP windowing + * schemes. + * + * Endianness: in this module all ports and IP addresses are stored in + * host byte-order, except fields in the IP-packets + */ + +/* Standard includes. */ +#include +#include + +/* 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_TCP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" +#include "FreeRTOS_ARP.h" +#include "FreeRTOS_TCP_WIN.h" + + +/* Just make sure the contents doesn't get compiled if TCP is not enabled. */ +#if ipconfigUSE_TCP == 1 + +/* This compile-time test was moved to here because some macro's +were unknown within 'FreeRTOSIPConfigDefaults.h'. It tests whether +the defined MTU size can contain at least a complete TCP packet. */ + +#if ( ( ipconfigTCP_MSS + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) > ipconfigNETWORK_MTU ) + #error The ipconfigTCP_MSS setting in FreeRTOSIPConfig.h is too large. +#endif + +/* + * The meaning of the TCP flags: + */ +#define ipTCP_FLAG_FIN 0x0001u /* No more data from sender */ +#define ipTCP_FLAG_SYN 0x0002u /* Synchronize sequence numbers */ +#define ipTCP_FLAG_RST 0x0004u /* Reset the connection */ +#define ipTCP_FLAG_PSH 0x0008u /* Push function: please push buffered data to the recv application */ +#define ipTCP_FLAG_ACK 0x0010u /* Acknowledgment field is significant */ +#define ipTCP_FLAG_URG 0x0020u /* Urgent pointer field is significant */ +#define ipTCP_FLAG_ECN 0x0040u /* ECN-Echo */ +#define ipTCP_FLAG_CWR 0x0080u /* Congestion Window Reduced */ +#define ipTCP_FLAG_NS 0x0100u /* ECN-nonce concealment protection */ +#define ipTCP_FLAG_RSV 0x0E00u /* Reserved, keep 0 */ + +/* A mask to filter all protocol flags. */ +#define ipTCP_FLAG_CTRL 0x001Fu + +/* + * A few values of the TCP options: + */ +#define TCP_OPT_END 0u /* End of TCP options list */ +#define TCP_OPT_NOOP 1u /* "No-operation" TCP option */ +#define TCP_OPT_MSS 2u /* Maximum segment size TCP option */ +#define TCP_OPT_WSOPT 3u /* TCP Window Scale Option (3-byte long) */ +#define TCP_OPT_SACK_P 4u /* Advertize that SACK is permitted */ +#define TCP_OPT_SACK_A 5u /* SACK option with first/last */ +#define TCP_OPT_TIMESTAMP 8u /* Time-stamp option */ + +#define TCP_OPT_MSS_LEN 4u /* Length of TCP MSS option. */ +#define TCP_OPT_WSOPT_LEN 3u /* Length of TCP WSOPT option. */ + +#define TCP_OPT_TIMESTAMP_LEN 10 /* fixed length of the time-stamp option */ + +#ifndef ipconfigTCP_ACK_EARLIER_PACKET + #define ipconfigTCP_ACK_EARLIER_PACKET 1 +#endif + +/* + * The macro NOW_CONNECTED() is use to determine if the connection makes a + * transition from connected to non-connected and vice versa. + * NOW_CONNECTED() returns true when the status has one of these values: + * eESTABLISHED, eFIN_WAIT_1, eFIN_WAIT_2, eCLOSING, eLAST_ACK, eTIME_WAIT + * Technically the connection status is closed earlier, but the library wants + * to prevent that the socket will be deleted before the last ACK has been + * and thus causing a 'RST' packet on either side. + */ +#define NOW_CONNECTED( status )\ + ( ( status >= eESTABLISHED ) && ( status != eCLOSE_WAIT ) ) + +/* + * The highest 4 bits in the TCP offset byte indicate the total length of the + * TCP header, divided by 4. + */ +#define VALID_BITS_IN_TCP_OFFSET_BYTE ( 0xF0u ) + +/* + * Acknowledgements to TCP data packets may be delayed as long as more is being expected. + * A normal delay would be 200ms. Here a much shorter delay of 20 ms is being used to + * gain performance. + */ +#define DELAYED_ACK_SHORT_DELAY_MS ( 2 ) +#define DELAYED_ACK_LONGER_DELAY_MS ( 20 ) + +/* + * The MSS (Maximum Segment Size) will be taken as large as possible. However, packets with + * an MSS of 1460 bytes won't be transported through the internet. The MSS will be reduced + * to 1400 bytes. + */ +#define REDUCED_MSS_THROUGH_INTERNET ( 1400 ) + +/* + * When there are no TCP options, the TCP offset equals 20 bytes, which is stored as + * the number 5 (words) in the higher niblle of the TCP-offset byte. + */ +#define TCP_OFFSET_LENGTH_BITS ( 0xf0u ) +#define TCP_OFFSET_STANDARD_LENGTH ( 0x50u ) + +/* + * Each TCP socket is checked regularly to see if it can send data packets. + * By default, the maximum number of packets sent during one check is limited to 8. + * This amount may be further limited by setting the socket's TX window size. + */ +#if( !defined( SEND_REPEATED_COUNT ) ) + #define SEND_REPEATED_COUNT ( 8 ) +#endif /* !defined( SEND_REPEATED_COUNT ) */ + +/* + * Define a maximum perdiod of time (ms) to leave a TCP-socket unattended. + * When a TCP timer expires, retries and keep-alive messages will be checked. + */ +#ifndef tcpMAXIMUM_TCP_WAKEUP_TIME_MS + #define tcpMAXIMUM_TCP_WAKEUP_TIME_MS 20000u +#endif + +/* + * The names of the different TCP states may be useful in logging. + */ +#if( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) + static const char *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", +}; +#endif /* ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) */ + +/* + * Returns true if the socket must be checked. Non-active sockets are waiting + * for user action, either connect() or close(). + */ +static BaseType_t prvTCPSocketIsActive( UBaseType_t uxStatus ); + +/* + * Either sends a SYN or calls prvTCPSendRepeated (for regular messages). + */ +static int32_t prvTCPSendPacket( FreeRTOS_Socket_t *pxSocket ); + +/* + * Try to send a series of messages. + */ +static int32_t prvTCPSendRepeated( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ); + +/* + * Return or send a packet to the other party. + */ +static void prvTCPReturnPacket( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, + uint32_t ulLen, BaseType_t xReleaseAfterSend ); + +/* + * Initialise the data structures which keep track of the TCP windowing system. + */ +static void prvTCPCreateWindow( FreeRTOS_Socket_t *pxSocket ); + +/* + * Let ARP look-up the MAC-address of the peer and initialise the first SYN + * packet. + */ +static BaseType_t prvTCPPrepareConnect( FreeRTOS_Socket_t *pxSocket ); + +#if( ipconfigHAS_DEBUG_PRINTF != 0 ) + /* + * For logging and debugging: make a string showing the TCP flags. + */ + static const char *prvTCPFlagMeaning( UBaseType_t xFlags); +#endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + +/* + * Parse the TCP option(s) received, if present. + */ +static void prvCheckOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ); + +/* + * Identify and deal with a single TCP header option, advancing the pointer to + * the header. This function returns pdTRUE or pdFALSE depending on whether the + * caller should continue to parse more header options or break the loop. + */ +static BaseType_t prvSingleStepTCPHeaderOptions( const unsigned char ** const ppucPtr, const unsigned char ** const ppucLast, FreeRTOS_Socket_t ** const ppxSocket, TCPWindow_t ** const ppxTCPWindow); + +/* + * Skip past TCP header options when doing Selective ACK, until there are no + * more options left. + */ +static void prvSkipPastRemainingOptions( const unsigned char ** const ppucPtr, FreeRTOS_Socket_t ** const ppxSocket, unsigned char * const ppucLen ); + +/* + * Set the initial properties in the options fields, like the preferred + * value of MSS and whether SACK allowed. Will be transmitted in the state + * 'eCONNECT_SYN'. + */ +static UBaseType_t prvSetSynAckOptions( FreeRTOS_Socket_t *pxSocket, TCPPacket_t * pxTCPPacket ); + +/* + * For anti-hang protection and TCP keep-alive messages. Called in two places: + * after receiving a packet and after a state change. The socket's alive timer + * may be reset. + */ +static void prvTCPTouchSocket( FreeRTOS_Socket_t *pxSocket ); + +/* + * Prepare an outgoing message, if anything has to be sent. + */ +static int32_t prvTCPPrepareSend( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, UBaseType_t uxOptionsLength ); + +/* + * Calculate when this socket needs to be checked to do (re-)transmissions. + */ +static TickType_t prvTCPNextTimeout( FreeRTOS_Socket_t *pxSocket ); + +/* + * The API FreeRTOS_send() adds data to the TX stream. Add + * this data to the windowing system to it can be transmitted. + */ +static void prvTCPAddTxData( FreeRTOS_Socket_t *pxSocket ); + +/* + * Called to handle the closure of a TCP connection. + */ +static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ); + +/* + * Called from prvTCPHandleState(). Find the TCP payload data and check and + * return its length. + */ +static BaseType_t prvCheckRxData( NetworkBufferDescriptor_t *pxNetworkBuffer, uint8_t **ppucRecvData ); + +/* + * Called from prvTCPHandleState(). Check if the payload data may be accepted. + * If so, it will be added to the socket's reception queue. + */ +static BaseType_t prvStoreRxData( FreeRTOS_Socket_t *pxSocket, uint8_t *pucRecvData, + NetworkBufferDescriptor_t *pxNetworkBuffer, uint32_t ulReceiveLength ); + +/* + * Set the TCP options (if any) for the outgoing packet. + */ +static UBaseType_t prvSetOptions( FreeRTOS_Socket_t *pxSocket, 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, NetworkBufferDescriptor_t **ppxNetworkBuffer, + 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 ); + +/* + * Called from prvTCPHandleState(). There is data to be sent. + * If ipconfigUSE_TCP_WIN is defined, and if only an ACK must be sent, it will + * be checked if it would better be postponed for efficiency. + */ +static BaseType_t prvSendData( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, + uint32_t ulReceiveLength, BaseType_t xSendLength ); + +/* + * The heart of all: check incoming packet for valid data and acks and do what + * is necessary in each state. + */ +static BaseType_t prvTCPHandleState( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ); + +/* + * Common code for sending a TCP protocol control packet (i.e. no options, no + * payload, just flags). + */ +static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t *pxNetworkBuffer, + uint8_t ucTCPFlags ); + +/* + * A "challenge ACK" is as per https://tools.ietf.org/html/rfc5961#section-3.2, + * case #3. In summary, an RST was received with a sequence number that is + * unexpected but still within the window. + */ +static BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t *pxNetworkBuffer ); + +/* + * Reply to a peer with the RST flag on, in case a packet can not be handled. + */ +static BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t *pxNetworkBuffer ); + +/* + * Set the initial value for MSS (Maximum Segment Size) to be used. + */ +static void prvSocketSetMSS( FreeRTOS_Socket_t *pxSocket ); + +/* + * Return either a newly created socket, or the current socket in a connected + * state (depends on the 'bReuseSocket' flag). + */ +static FreeRTOS_Socket_t *prvHandleListen( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ); + +/* + * 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 ); + +/* + * prvTCPStatusAgeCheck() will see if the socket has been in a non-connected + * state for too long. If so, the socket will be closed, and -1 will be + * returned. + */ +#if( ipconfigTCP_HANG_PROTECTION == 1 ) + static BaseType_t prvTCPStatusAgeCheck( FreeRTOS_Socket_t *pxSocket ); +#endif + +static NetworkBufferDescriptor_t *prvTCPBufferResize( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, + int32_t lDataLen, UBaseType_t uxOptionsLength ); + +#if( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) + const char *FreeRTOS_GetTCPStateName( UBaseType_t ulState ); +#endif + +#if( ipconfigUSE_TCP_WIN != 0 ) + static uint8_t prvWinScaleFactor( FreeRTOS_Socket_t *pxSocket ); +#endif + +/* + * Generate a randomized TCP Initial Sequence Number per RFC. + */ +extern uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress, + uint16_t usSourcePort, + uint32_t ulDestinationAddress, + uint16_t usDestinationPort ); + +/*-----------------------------------------------------------*/ + +/* prvTCPSocketIsActive() returns true if the socket must be checked. + * Non-active sockets are waiting for user action, either connect() + * or close(). */ +static BaseType_t prvTCPSocketIsActive( UBaseType_t uxStatus ) +{ + switch( uxStatus ) + { + case eCLOSED: + case eCLOSE_WAIT: + case eFIN_WAIT_2: + case eCLOSING: + case eTIME_WAIT: + return pdFALSE; + default: + return pdTRUE; + } +} +/*-----------------------------------------------------------*/ + +#if( ipconfigTCP_HANG_PROTECTION == 1 ) + + static BaseType_t prvTCPStatusAgeCheck( FreeRTOS_Socket_t *pxSocket ) + { + BaseType_t xResult; + switch( pxSocket->u.xTCP.ucTCPState ) + { + 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; + 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 > ( ipconfigTCP_HANG_PROTECTION_TIME * configTICK_RATE_HZ ) ) + { + #if( ipconfigHAS_DEBUG_PRINTF == 1 ) + { + FreeRTOS_debug_printf( ( "Inactive socket closed: port %u rem %lxip:%u status %s\n", + pxSocket->usLocalPort, + 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 ) + { + if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) + { + /* As it did not get connected, and the user can never + accept() it anymore, it will be deleted now. Called from + the IP-task, so it's safe to call the internal Close + function: vSocketClose(). */ + vSocketClose( pxSocket ); + } + /* Return a negative value to tell to inform the caller + xTCPTimerCheck() + that the socket got closed and may not be accessed anymore. */ + xResult = -1; + } + } + } + return xResult; + } + /*-----------------------------------------------------------*/ + +#endif + +/* + * As soon as a TCP socket timer expires, this function xTCPSocketCheck + * will be called (from xTCPTimerCheck) + * It can send a delayed ACK or new data + * Sequence of calling (normally) : + * IP-Task: + * xTCPTimerCheck() // Check all sockets ( declared in FreeRTOS_Sockets.c ) + * xTCPSocketCheck() // Either send a delayed ACK or call prvTCPSendPacket() + * prvTCPSendPacket() // Either send a SYN or call prvTCPSendRepeated ( regular messages ) + * prvTCPSendRepeated() // Send at most 8 messages on a row + * prvTCPReturnPacket() // Prepare for returning + * xNetworkInterfaceOutput() // Sends data to the NIC ( declared in portable/NetworkInterface/xxx ) + */ +BaseType_t xTCPSocketCheck( FreeRTOS_Socket_t *pxSocket ) +{ +BaseType_t xResult = 0; +BaseType_t xReady = pdFALSE; + + if( ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) && ( pxSocket->u.xTCP.txStream != NULL ) ) + { + /* The API FreeRTOS_send() might have added data to the TX stream. Add + this data to the windowing system to it can be transmitted. */ + prvTCPAddTxData( pxSocket ); + } + + #if ipconfigUSE_TCP_WIN == 1 + { + if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + /* The first task of this regular socket check is to send-out delayed + ACK's. */ + if( pxSocket->u.xTCP.bits.bUserShutdown == pdFALSE_UNSIGNED ) + { + /* Earlier data was received but not yet acknowledged. This + function is called when the TCP timer for the socket expires, the + ACK may be sent now. */ + if( pxSocket->u.xTCP.ucTCPState != eCLOSED ) + { + if( xTCPWindowLoggingLevel > 1 && ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) ) + { + FreeRTOS_debug_printf( ( "Send[%u->%u] del ACK %lu SEQ %lu (len %u)\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber, + pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - pxSocket->u.xTCP.xTCPWindow.tx.ulFirstSequenceNumber, + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ); + } + + prvTCPReturnPacket( pxSocket, pxSocket->u.xTCP.pxAckMessage, ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER, ipconfigZERO_COPY_TX_DRIVER ); + + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* The ownership has been passed to the SEND routine, + clear the pointer to it. */ + pxSocket->u.xTCP.pxAckMessage = NULL; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + } + if( prvTCPNextTimeout( pxSocket ) > 1 ) + { + /* Tell the code below that this function is ready. */ + xReady = pdTRUE; + } + } + else + { + /* The user wants to perform an active shutdown(), skip sending + the delayed ACK. The function prvTCPSendPacket() will send the + FIN along with the ACK's. */ + } + + if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); + pxSocket->u.xTCP.pxAckMessage = NULL; + } + } + } + #endif /* ipconfigUSE_TCP_WIN */ + + if( xReady == pdFALSE ) + { + /* The second task of this regular socket check is sending out data. */ + if( ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) || + ( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) ) + { + prvTCPSendPacket( pxSocket ); + } + + /* Set the time-out for the next wakeup for this socket. */ + prvTCPNextTimeout( pxSocket ); + + #if( ipconfigTCP_HANG_PROTECTION == 1 ) + { + /* In all (non-connected) states in which keep-alive messages can not be sent + the anti-hang protocol will close sockets that are 'hanging'. */ + xResult = prvTCPStatusAgeCheck( pxSocket ); + } + #endif + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/* + * prvTCPSendPacket() will be called when the socket time-out has been reached. + * It is only called by xTCPSocketCheck(). + */ +static int32_t prvTCPSendPacket( FreeRTOS_Socket_t *pxSocket ) +{ +int32_t lResult = 0; +UBaseType_t uxOptionsLength; +TCPPacket_t *pxTCPPacket; +NetworkBufferDescriptor_t *pxNetworkBuffer; + + if( pxSocket->u.xTCP.ucTCPState != eCONNECT_SYN ) + { + /* The connection is in s state other than SYN. */ + pxNetworkBuffer = NULL; + + /* prvTCPSendRepeated() will only create a network buffer if necessary, + i.e. when data must be sent to the peer. */ + lResult = prvTCPSendRepeated( pxSocket, &pxNetworkBuffer ); + + if( pxNetworkBuffer != NULL ) + { + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + } + else + { + if( pxSocket->u.xTCP.ucRepCount >= 3u ) + { + /* The connection is in the SYN status. The packet will be repeated + to most 3 times. When there is no response, the socket get the + status 'eCLOSE_WAIT'. */ + FreeRTOS_debug_printf( ( "Connect: giving up %lxip:%u\n", + pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine. */ + pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */ + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + } + else if( ( pxSocket->u.xTCP.bits.bConnPrepared != pdFALSE_UNSIGNED ) || ( prvTCPPrepareConnect( pxSocket ) == pdTRUE ) ) + { + /* Or else, if the connection has been prepared, or can be prepared + now, proceed to send the packet with the SYN flag. + prvTCPPrepareConnect() prepares 'xPacket' and returns pdTRUE if + the Ethernet address of the peer or the gateway is found. */ + pxTCPPacket = ( TCPPacket_t * )pxSocket->u.xTCP.xPacket.u.ucLastPacket; + + /* About to send a SYN packet. Call prvSetSynAckOptions() to set + the proper options: The size of MSS and whether SACK's are + allowed. */ + uxOptionsLength = prvSetSynAckOptions( pxSocket, pxTCPPacket ); + + /* Return the number of bytes to be sent. */ + lResult = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); + + /* Set the TCP offset field: ipSIZE_OF_TCP_HEADER equals 20 and + uxOptionsLength is always a multiple of 4. The complete expression + would be: + ucTCPOffset = ( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) / 4 ) << 4 */ + pxTCPPacket->xTCPHeader.ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + + /* Repeat Count is used for a connecting socket, to limit the number + of tries. */ + pxSocket->u.xTCP.ucRepCount++; + + /* Send the SYN message to make a connection. The messages is + stored in the socket field 'xPacket'. It will be wrapped in a + pseudo network buffer descriptor before it will be sent. */ + prvTCPReturnPacket( pxSocket, NULL, ( uint32_t ) lResult, pdFALSE ); + } + } + + /* Return the total number of bytes sent. */ + return lResult; +} +/*-----------------------------------------------------------*/ + +/* + * prvTCPSendRepeated will try to send a series of messages, as long as there is + * data to be sent and as long as the transmit window isn't full. + */ +static int32_t prvTCPSendRepeated( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ) +{ +UBaseType_t uxIndex; +int32_t lResult = 0; +UBaseType_t uxOptionsLength = 0u; +int32_t xSendLength; + + for( uxIndex = 0u; uxIndex < ( UBaseType_t ) SEND_REPEATED_COUNT; uxIndex++ ) + { + /* prvTCPPrepareSend() might allocate a network buffer if there is data + to be sent. */ + xSendLength = prvTCPPrepareSend( pxSocket, ppxNetworkBuffer, uxOptionsLength ); + if( xSendLength <= 0 ) + { + break; + } + + /* And return the packet to the peer. */ + prvTCPReturnPacket( pxSocket, *ppxNetworkBuffer, ( uint32_t ) xSendLength, ipconfigZERO_COPY_TX_DRIVER ); + + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + *ppxNetworkBuffer = NULL; + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + lResult += xSendLength; + } + + /* Return the total number of bytes sent. */ + return lResult; +} +/*-----------------------------------------------------------*/ + +/* + * Return (or send) a packet the the peer. The data is stored in pxBuffer, + * which may either point to a real network buffer or to a TCP socket field + * called 'xTCP.xPacket'. A temporary xNetworkBuffer will be used to pass + * the data to the NIC. + */ +static void prvTCPReturnPacket( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, uint32_t ulLen, BaseType_t xReleaseAfterSend ) +{ +TCPPacket_t * pxTCPPacket; +IPHeader_t *pxIPHeader; +EthernetHeader_t *pxEthernetHeader; +uint32_t ulFrontSpace, ulSpace, ulSourceAddress, ulWinSize; +TCPWindow_t *pxTCPWindow; +NetworkBufferDescriptor_t xTempBuffer; +/* For sending, a pseudo network buffer will be used, as explained above. */ + + if( pxNetworkBuffer == NULL ) + { + pxNetworkBuffer = &xTempBuffer; + + #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + { + xTempBuffer.pxNextBuffer = NULL; + } + #endif + xTempBuffer.pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket; + xTempBuffer.xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ); + xReleaseAfterSend = pdFALSE; + } + + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + if( xReleaseAfterSend == pdFALSE ) + { + pxNetworkBuffer = pxDuplicateNetworkBufferWithDescriptor( pxNetworkBuffer, ( BaseType_t ) pxNetworkBuffer->xDataLength ); + if( pxNetworkBuffer == NULL ) + { + FreeRTOS_debug_printf( ( "prvTCPReturnPacket: duplicate failed\n" ) ); + } + xReleaseAfterSend = pdTRUE; + } + } + #endif /* ipconfigZERO_COPY_TX_DRIVER */ + + if( pxNetworkBuffer != NULL ) + { + pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); + pxIPHeader = &pxTCPPacket->xIPHeader; + pxEthernetHeader = &pxTCPPacket->xEthernetHeader; + + /* Fill the packet, using hton translations. */ + if( pxSocket != NULL ) + { + /* Calculate the space in the RX buffer in order to advertise the + size of this socket's reception window. */ + pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow ); + + if( pxSocket->u.xTCP.rxStream != NULL ) + { + /* An RX stream was created already, see how much space is + available. */ + ulFrontSpace = ( uint32_t ) uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ); + } + else + { + /* No RX stream has been created, the full stream size is + available. */ + ulFrontSpace = ( uint32_t ) pxSocket->u.xTCP.uxRxStreamSize; + } + + /* Take the minimum of the RX buffer space and the RX window size. */ + ulSpace = FreeRTOS_min_uint32( pxTCPWindow->xSize.ulRxWindowLength, ulFrontSpace ); + + if( ( pxSocket->u.xTCP.bits.bLowWater != pdFALSE_UNSIGNED ) || ( pxSocket->u.xTCP.bits.bRxStopped != pdFALSE_UNSIGNED ) ) + { + /* The low-water mark was reached, meaning there was little + space left. The socket will wait until the application has read + or flushed the incoming data, and 'zero-window' will be + advertised. */ + ulSpace = 0u; + } + + /* If possible, advertise an RX window size of at least 1 MSS, otherwise + the peer might start 'zero window probing', i.e. sending small packets + (1, 2, 4, 8... bytes). */ + if( ( ulSpace < pxSocket->u.xTCP.usCurMSS ) && ( ulFrontSpace >= pxSocket->u.xTCP.usCurMSS ) ) + { + ulSpace = pxSocket->u.xTCP.usCurMSS; + } + + /* Avoid overflow of the 16-bit win field. */ + #if( ipconfigUSE_TCP_WIN != 0 ) + { + ulWinSize = ( ulSpace >> pxSocket->u.xTCP.ucMyWinScaleFactor ); + } + #else + { + ulWinSize = ulSpace; + } + #endif + if( ulWinSize > 0xfffcUL ) + { + ulWinSize = 0xfffcUL; + } + + pxTCPPacket->xTCPHeader.usWindow = FreeRTOS_htons( ( uint16_t ) ulWinSize ); + + #if( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + if( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) + { + if( ( xTCPWindowLoggingLevel != 0 ) && ( pxSocket->u.xTCP.bits.bWinChange != pdFALSE_UNSIGNED ) ) + { + size_t uxFrontSpace; + + if(pxSocket->u.xTCP.rxStream != NULL) + { + uxFrontSpace = uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream ) ; + } + else + { + uxFrontSpace = 0u; + } + + FreeRTOS_debug_printf( ( "%s: %lxip:%u: [%lu < %lu] winSize %ld\n", + pxSocket->u.xTCP.bits.bLowWater ? "STOP" : "GO ", + pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + pxSocket->u.xTCP.bits.bLowWater ? pxSocket->u.xTCP.uxLittleSpace : uxFrontSpace, pxSocket->u.xTCP.uxEnoughSpace, + (int32_t) ( pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulCurrentSequenceNumber ) ) ); + } + } + } + #endif /* ipconfigHAS_DEBUG_PRINTF != 0 */ + + /* The new window size has been advertised, switch off the flag. */ + pxSocket->u.xTCP.bits.bWinChange = pdFALSE_UNSIGNED; + + /* Later on, when deciding to delay an ACK, a precise estimate is needed + of the free RX space. At this moment, 'ulHighestRxAllowed' would be the + highest sequence number minus 1 that the socket will accept. */ + pxSocket->u.xTCP.ulHighestRxAllowed = pxTCPWindow->rx.ulCurrentSequenceNumber + ulSpace; + + #if( ipconfigTCP_KEEP_ALIVE == 1 ) + if( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED ) + { + /* Sending a keep-alive packet, send the current sequence number + minus 1, which will be recognised as a keep-alive packet an + responded to by acknowledging the last byte. */ + pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE_UNSIGNED; + pxSocket->u.xTCP.bits.bWaitKeepAlive = pdTRUE_UNSIGNED; + + pxTCPPacket->xTCPHeader.ulSequenceNumber = pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - 1UL; + pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxTCPPacket->xTCPHeader.ulSequenceNumber ); + } + else + #endif + { + pxTCPPacket->xTCPHeader.ulSequenceNumber = FreeRTOS_htonl( pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber ); + + if( ( pxTCPPacket->xTCPHeader.ucTCPFlags & ( uint8_t ) ipTCP_FLAG_FIN ) != 0u ) + { + /* Suppress FIN in case this packet carries earlier data to be + retransmitted. */ + uint32_t ulDataLen = ( uint32_t ) ( ulLen - ( ipSIZE_OF_TCP_HEADER + ipSIZE_OF_IPv4_HEADER ) ); + if( ( pxTCPWindow->ulOurSequenceNumber + ulDataLen ) != pxTCPWindow->tx.ulFINSequenceNumber ) + { + pxTCPPacket->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~ipTCP_FLAG_FIN ); + FreeRTOS_debug_printf( ( "Suppress FIN for %lu + %lu < %lu\n", + pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, + ulDataLen, + pxTCPWindow->tx.ulFINSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber ) ); + } + } + } + + /* Tell which sequence number is expected next time */ + pxTCPPacket->xTCPHeader.ulAckNr = FreeRTOS_htonl( pxTCPWindow->rx.ulCurrentSequenceNumber ); + } + else + { + /* Sending data without a socket, probably replying with a RST flag + Just swap the two sequence numbers. */ + vFlip_32( pxTCPPacket->xTCPHeader.ulSequenceNumber, pxTCPPacket->xTCPHeader.ulAckNr ); + } + + pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE; + pxIPHeader->usLength = FreeRTOS_htons( ulLen ); + if( ( pxSocket == NULL ) || ( *ipLOCAL_IP_ADDRESS_POINTER == 0ul ) ) + { + /* When pxSocket is NULL, this function is called by prvTCPSendReset() + and the IP-addresses must be swapped. + Also swap the IP-addresses in case the IP-tack doesn't have an + IP-address yet, i.e. when ( *ipLOCAL_IP_ADDRESS_POINTER == 0ul ). */ + ulSourceAddress = pxIPHeader->ulDestinationIPAddress; + } + else + { + ulSourceAddress = *ipLOCAL_IP_ADDRESS_POINTER; + } + pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress; + pxIPHeader->ulSourceIPAddress = ulSourceAddress; + vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort ); + + /* Just an increasing number. */ + pxIPHeader->usIdentification = FreeRTOS_htons( usPacketIdentifier ); + usPacketIdentifier++; + pxIPHeader->usFragmentOffset = 0u; + + #if( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) + { + /* calculate the IP header checksum, in case the driver won't do that. */ + pxIPHeader->usHeaderChecksum = 0x00u; + pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER ); + pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum ); + + /* calculate the TCP checksum for an outgoing packet. */ + usGenerateProtocolChecksum( (uint8_t*)pxTCPPacket, pxNetworkBuffer->xDataLength, pdTRUE ); + + /* A calculated checksum of 0 must be inverted as 0 means the checksum + is disabled. */ + if( pxTCPPacket->xTCPHeader.usChecksum == 0x00u ) + { + pxTCPPacket->xTCPHeader.usChecksum = 0xffffU; + } + } + #endif + + #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 ) + pxNetworkBuffer->pxNextBuffer = NULL; + #endif + + /* Important: tell NIC driver how many bytes must be sent. */ + pxNetworkBuffer->xDataLength = ulLen + ipSIZE_OF_ETH_HEADER; + + /* Fill in the destination MAC addresses. */ + memcpy( ( void * ) &( pxEthernetHeader->xDestinationAddress ), ( void * ) &( pxEthernetHeader->xSourceAddress ), + sizeof( pxEthernetHeader->xDestinationAddress ) ); + + /* The source MAC addresses is fixed to 'ipLOCAL_MAC_ADDRESS'. */ + memcpy( ( void * ) &( pxEthernetHeader->xSourceAddress) , ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); + + #if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + { + if( pxNetworkBuffer->xDataLength < ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES ) + { + BaseType_t xIndex; + + for( xIndex = ( BaseType_t ) pxNetworkBuffer->xDataLength; xIndex < ( BaseType_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; xIndex++ ) + { + pxNetworkBuffer->pucEthernetBuffer[ xIndex ] = 0u; + } + pxNetworkBuffer->xDataLength = ( size_t ) ipconfigETHERNET_MINIMUM_PACKET_BYTES; + } + } + #endif + + /* Send! */ + xNetworkInterfaceOutput( pxNetworkBuffer, xReleaseAfterSend ); + + if( xReleaseAfterSend == pdFALSE ) + { + /* Swap-back some fields, as pxBuffer probably points to a socket field + containing the packet header. */ + vFlip_16( pxTCPPacket->xTCPHeader.usSourcePort, pxTCPPacket->xTCPHeader.usDestinationPort); + pxTCPPacket->xIPHeader.ulSourceIPAddress = pxTCPPacket->xIPHeader.ulDestinationIPAddress; + memcpy( pxEthernetHeader->xSourceAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES ); + } + else + { + /* Nothing to do: the buffer has been passed to DMA and will be released after use */ + } + } /* if( pxNetworkBuffer != NULL ) */ +} +/*-----------------------------------------------------------*/ + +/* + * The SYN event is very important: the sequence numbers, which have a kind of + * random starting value, are being synchronised. The sliding window manager + * (in FreeRTOS_TCP_WIN.c) needs to know them, along with the Maximum Segment + * Size (MSS) in use. + */ +static void prvTCPCreateWindow( FreeRTOS_Socket_t *pxSocket ) +{ + if( xTCPWindowLoggingLevel ) + FreeRTOS_debug_printf( ( "Limits (using): TCP Win size %lu Water %lu <= %lu <= %lu\n", + pxSocket->u.xTCP.uxRxWinSize * ipconfigTCP_MSS, + pxSocket->u.xTCP.uxLittleSpace , + pxSocket->u.xTCP.uxEnoughSpace, + pxSocket->u.xTCP.uxRxStreamSize ) ); + vTCPWindowCreate( + &pxSocket->u.xTCP.xTCPWindow, + ipconfigTCP_MSS * pxSocket->u.xTCP.uxRxWinSize, + ipconfigTCP_MSS * pxSocket->u.xTCP.uxTxWinSize, + pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber, + pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber, + ( uint32_t ) pxSocket->u.xTCP.usInitMSS ); +} +/*-----------------------------------------------------------*/ + +/* + * Connecting sockets have a special state: eCONNECT_SYN. In this phase, + * the Ethernet address of the target will be found using ARP. In case the + * target IP address is not within the netmask, the hardware address of the + * gateway will be used. + */ +static BaseType_t prvTCPPrepareConnect( FreeRTOS_Socket_t *pxSocket ) +{ +TCPPacket_t *pxTCPPacket; +IPHeader_t *pxIPHeader; +eARPLookupResult_t eReturned; +uint32_t ulRemoteIP; +MACAddress_t xEthAddress; +BaseType_t xReturn = pdTRUE; +uint32_t ulInitialSequenceNumber = 0; + + #if( ipconfigHAS_PRINTF != 0 ) + { + /* Only necessary for nicer logging. */ + memset( xEthAddress.ucBytes, '\0', sizeof( xEthAddress.ucBytes ) ); + } + #endif /* ipconfigHAS_PRINTF != 0 */ + + ulRemoteIP = FreeRTOS_htonl( pxSocket->u.xTCP.ulRemoteIP ); + + /* Determine the ARP cache status for the requested IP address. */ + eReturned = eARPGetCacheEntry( &( ulRemoteIP ), &( xEthAddress ) ); + + switch( eReturned ) + { + case eARPCacheHit: /* An ARP table lookup found a valid entry. */ + break; /* We can now prepare the SYN packet. */ + case eARPCacheMiss: /* An ARP table lookup did not find a valid entry. */ + case eCantSendPacket: /* There is no IP address, or an ARP is still in progress. */ + default: + /* Count the number of times it couldn't find the ARP address. */ + pxSocket->u.xTCP.ucRepCount++; + + FreeRTOS_debug_printf( ( "ARP for %lxip (using %lxip): rc=%d %02X:%02X:%02X %02X:%02X:%02X\n", + pxSocket->u.xTCP.ulRemoteIP, + FreeRTOS_htonl( ulRemoteIP ), + eReturned, + xEthAddress.ucBytes[ 0 ], + xEthAddress.ucBytes[ 1 ], + xEthAddress.ucBytes[ 2 ], + xEthAddress.ucBytes[ 3 ], + xEthAddress.ucBytes[ 4 ], + xEthAddress.ucBytes[ 5 ] ) ); + + /* And issue a (new) ARP request */ + FreeRTOS_OutputARPRequest( ulRemoteIP ); + + xReturn = pdFALSE; + } + + if( xReturn != pdFALSE ) + { + /* Get a difficult-to-predict initial sequence number for this 4-tuple. */ + ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( *ipLOCAL_IP_ADDRESS_POINTER, + pxSocket->usLocalPort, + pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort ); + + /* Check for a random number generation error. */ + if( 0 == ulInitialSequenceNumber ) + { + xReturn = pdFALSE; + } + } + + if( xReturn != pdFALSE ) + { + /* The MAC-address of the peer (or gateway) has been found, + now prepare the initial TCP packet and some fields in the socket. */ + pxTCPPacket = ( TCPPacket_t * )pxSocket->u.xTCP.xPacket.u.ucLastPacket; + pxIPHeader = &pxTCPPacket->xIPHeader; + + /* reset the retry counter to zero. */ + pxSocket->u.xTCP.ucRepCount = 0u; + + /* And remember that the connect/SYN data are prepared. */ + pxSocket->u.xTCP.bits.bConnPrepared = pdTRUE_UNSIGNED; + + /* Now that the Ethernet address is known, the initial packet can be + prepared. */ + memset( pxSocket->u.xTCP.xPacket.u.ucLastPacket, '\0', sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); + + /* Write the Ethernet address in Source, because it will be swapped by + prvTCPReturnPacket(). */ + memcpy( &pxTCPPacket->xEthernetHeader.xSourceAddress, &xEthAddress, sizeof( xEthAddress ) ); + + /* 'ipIPv4_FRAME_TYPE' is already in network-byte-order. */ + pxTCPPacket->xEthernetHeader.usFrameType = ipIPv4_FRAME_TYPE; + + pxIPHeader->ucVersionHeaderLength = 0x45u; + pxIPHeader->usLength = FreeRTOS_htons( sizeof( TCPPacket_t ) - sizeof( pxTCPPacket->xEthernetHeader ) ); + pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE; + + pxIPHeader->ucProtocol = ( uint8_t ) ipPROTOCOL_TCP; + + /* Addresses and ports will be stored swapped because prvTCPReturnPacket + will swap them back while replying. */ + pxIPHeader->ulDestinationIPAddress = *ipLOCAL_IP_ADDRESS_POINTER; + pxIPHeader->ulSourceIPAddress = FreeRTOS_htonl( pxSocket->u.xTCP.ulRemoteIP ); + + pxTCPPacket->xTCPHeader.usSourcePort = FreeRTOS_htons( pxSocket->u.xTCP.usRemotePort ); + pxTCPPacket->xTCPHeader.usDestinationPort = FreeRTOS_htons( pxSocket->usLocalPort ); + + /* We are actively connecting, so the peer's Initial Sequence Number (ISN) + isn't known yet. */ + pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = 0ul; + + /* Start with ISN (Initial Sequence Number). */ + pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber; + + /* The TCP header size is 20 bytes, divided by 4 equals 5, which is put in + the high nibble of the TCP offset field. */ + pxTCPPacket->xTCPHeader.ucTCPOffset = 0x50u; + + /* Only set the SYN flag. */ + pxTCPPacket->xTCPHeader.ucTCPFlags = ipTCP_FLAG_SYN; + + /* Set the values of usInitMSS / usCurMSS for this socket. */ + prvSocketSetMSS( pxSocket ); + + /* The initial sequence numbers at our side are known. Later + vTCPWindowInit() will be called to fill in the peer's sequence numbers, but + first wait for a SYN+ACK reply. */ + prvTCPCreateWindow( pxSocket ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +/* For logging and debugging: make a string showing the TCP flags +*/ +#if( ipconfigHAS_DEBUG_PRINTF != 0 ) + + static const char *prvTCPFlagMeaning( UBaseType_t xFlags) + { + static char retString[10]; + snprintf(retString, sizeof( retString ), "%c%c%c%c%c%c%c%c%c", + ( xFlags & ipTCP_FLAG_FIN ) ? 'F' : '.', /* 0x0001: No more data from sender */ + ( xFlags & ipTCP_FLAG_SYN ) ? 'S' : '.', /* 0x0002: Synchronize sequence numbers */ + ( xFlags & ipTCP_FLAG_RST ) ? 'R' : '.', /* 0x0004: Reset the connection */ + ( xFlags & ipTCP_FLAG_PSH ) ? 'P' : '.', /* 0x0008: Push function: please push buffered data to the recv application */ + ( xFlags & ipTCP_FLAG_ACK ) ? 'A' : '.', /* 0x0010: Acknowledgment field is significant */ + ( xFlags & ipTCP_FLAG_URG ) ? 'U' : '.', /* 0x0020: Urgent pointer field is significant */ + ( xFlags & ipTCP_FLAG_ECN ) ? 'E' : '.', /* 0x0040: ECN-Echo */ + ( xFlags & ipTCP_FLAG_CWR ) ? 'C' : '.', /* 0x0080: Congestion Window Reduced */ + ( xFlags & ipTCP_FLAG_NS ) ? 'N' : '.'); /* 0x0100: ECN-nonce concealment protection */ + return retString; + } + /*-----------------------------------------------------------*/ + +#endif /* ipconfigHAS_DEBUG_PRINTF */ + +/* + * Parse the TCP option(s) received, if present. It has already been verified + * that: ((pxTCPHeader->ucTCPOffset & 0xf0) > 0x50), meaning that the TP header + * is longer than the usual 20 (5 x 4) bytes. + */ +static void prvCheckOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) +{ +TCPPacket_t * pxTCPPacket; +TCPHeader_t * pxTCPHeader; +const unsigned char *pucPtr; +const unsigned char *pucLast; +TCPWindow_t *pxTCPWindow; +BaseType_t xShouldContinueLoop; + + pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); + pxTCPHeader = &pxTCPPacket->xTCPHeader; + + /* A character pointer to iterate through the option data */ + pucPtr = pxTCPHeader->ucOptdata; + pucLast = pucPtr + (((pxTCPHeader->ucTCPOffset >> 4) - 5) << 2); + pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + + /* Validate options size calculation. */ + if( pucLast > ( pxNetworkBuffer->pucEthernetBuffer + pxNetworkBuffer->xDataLength ) ) + { + return; + } + + /* The comparison with pucLast is only necessary in case the option data are + corrupted, we don't like to run into invalid memory and crash. */ + xShouldContinueLoop = pdTRUE; + while( ( pucPtr < pucLast ) && ( xShouldContinueLoop == pdTRUE ) ) + { + xShouldContinueLoop = prvSingleStepTCPHeaderOptions( &pucPtr, &pucLast, &pxSocket, &pxTCPWindow ); + } +} + +/*-----------------------------------------------------------*/ + +static BaseType_t prvSingleStepTCPHeaderOptions( const unsigned char ** const ppucPtr, const unsigned char ** const ppucLast, FreeRTOS_Socket_t ** const ppxSocket, TCPWindow_t ** const ppxTCPWindow) +{ + UBaseType_t uxNewMSS; + UBaseType_t xRemainingOptionsBytes = ( *ppucLast ) - ( *ppucPtr ); + unsigned char ucLen; + + if( ( *ppucPtr )[ 0 ] == TCP_OPT_END ) + { + /* End of options. */ + return pdFALSE; + } + if( ( *ppucPtr )[ 0 ] == TCP_OPT_NOOP) + { + /* NOP option, inserted to make the length a multiple of 4. */ + ( *ppucPtr )++; + return pdTRUE; + } + + /* Any other well-formed option must be at least two bytes: the option + type byte followed by a length byte. */ + if( xRemainingOptionsBytes < 2 ) + { + return pdFALSE; + } +#if( ipconfigUSE_TCP_WIN != 0 ) + else if( ( *ppucPtr )[ 0 ] == TCP_OPT_WSOPT ) + { + /* Confirm that the option fits in the remaining buffer space. */ + if( ( xRemainingOptionsBytes < TCP_OPT_WSOPT_LEN ) || ( ( *ppucPtr )[ 1 ] != TCP_OPT_WSOPT_LEN ) ) + { + return pdFALSE; + } + + ( *ppxSocket )->u.xTCP.ucPeerWinScaleFactor = ( *ppucPtr )[ 2 ]; + ( *ppxSocket )->u.xTCP.bits.bWinScaling = pdTRUE_UNSIGNED; + ( *ppucPtr ) += TCP_OPT_WSOPT_LEN; + } +#endif /* ipconfigUSE_TCP_WIN */ + else if( ( *ppucPtr )[ 0 ] == TCP_OPT_MSS ) + { + /* Confirm that the option fits in the remaining buffer space. */ + if( ( xRemainingOptionsBytes < TCP_OPT_MSS_LEN )|| ( ( *ppucPtr )[ 1 ] != TCP_OPT_MSS_LEN ) ) + { + return pdFALSE; + } + + /* An MSS option with the correct option length. FreeRTOS_htons() + is not needed here because usChar2u16() already returns a host + endian number. */ + uxNewMSS = usChar2u16( ( *ppucPtr ) + 2 ); + + if( ( *ppxSocket )->u.xTCP.usInitMSS != uxNewMSS ) + { + /* Perform a basic check on the the new MSS. */ + if( uxNewMSS == 0 ) + { + return pdFALSE; + } + + FreeRTOS_debug_printf( ( "MSS change %u -> %lu\n", ( *ppxSocket )->u.xTCP.usInitMSS, uxNewMSS ) ); + } + + if( ( *ppxSocket )->u.xTCP.usInitMSS > uxNewMSS ) + { + /* our MSS was bigger than the MSS of the other party: adapt it. */ + ( *ppxSocket )->u.xTCP.bits.bMssChange = pdTRUE_UNSIGNED; + if( ( ( *ppxTCPWindow ) != NULL ) && ( ( *ppxSocket )->u.xTCP.usCurMSS > uxNewMSS ) ) + { + /* The peer advertises a smaller MSS than this socket was + using. Use that as well. */ + FreeRTOS_debug_printf( ( "Change mss %d => %lu\n", ( *ppxSocket )->u.xTCP.usCurMSS, uxNewMSS ) ); + ( *ppxSocket )->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS; + } + ( *ppxTCPWindow )->xSize.ulRxWindowLength = ( ( uint32_t ) uxNewMSS ) * ( ( *ppxTCPWindow )->xSize.ulRxWindowLength / ( ( uint32_t ) uxNewMSS ) ); + ( *ppxTCPWindow )->usMSSInit = ( uint16_t ) uxNewMSS; + ( *ppxTCPWindow )->usMSS = ( uint16_t ) uxNewMSS; + ( *ppxSocket )->u.xTCP.usInitMSS = ( uint16_t ) uxNewMSS; + ( *ppxSocket )->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS; + } + + #if( ipconfigUSE_TCP_WIN != 1 ) + /* Without scaled windows, MSS is the only interesting option. */ + return pdFALSE; + #else + /* Or else we continue to check another option: selective ACK. */ + ( *ppucPtr ) += TCP_OPT_MSS_LEN; + #endif /* ipconfigUSE_TCP_WIN != 1 */ + } + else + { + /* All other options have a length field, so that we easily + can skip past them. */ + ucLen = ( *ppucPtr )[ 1 ]; + if( ( ucLen < 2 ) || ( ucLen > xRemainingOptionsBytes ) ) + { + /* If the length field is too small or too big, the options are + * malformed, don't process them further. + */ + return pdFALSE; + } + + #if( ipconfigUSE_TCP_WIN == 1 ) + { + /* Selective ACK: the peer has received a packet but it is missing + * earlier packets. At least this packet does not need retransmission + * anymore. ulTCPWindowTxSack( ) takes care of this administration. + */ + if( ( *ppucPtr )[0] == TCP_OPT_SACK_A ) + { + ucLen -= 2; + ( *ppucPtr ) += 2; + + while( ucLen >= 8 ) + { + prvSkipPastRemainingOptions( ppucPtr, ppxSocket, &ucLen ); + } + /* ucLen should be 0 by now. */ + } + } + #endif /* ipconfigUSE_TCP_WIN == 1 */ + + ( *ppucPtr ) += ucLen; + } + return pdTRUE; +} + +/*-----------------------------------------------------------*/ + +static void prvSkipPastRemainingOptions( const unsigned char ** const ppucPtr, FreeRTOS_Socket_t ** const ppxSocket, unsigned char * const pucLen ) +{ +uint32_t ulFirst = ulChar2u32( ( *ppucPtr ) ); +uint32_t ulLast = ulChar2u32( ( *ppucPtr ) + 4 ); +uint32_t ulCount = ulTCPWindowTxSack( &( *ppxSocket )->u.xTCP.xTCPWindow, ulFirst, ulLast ); + /* ulTCPWindowTxSack( ) returns the number of bytes which have been acked + * starting from the head position. Advance the tail pointer in txStream. + */ + if( ( ( *ppxSocket )->u.xTCP.txStream != NULL ) && ( ulCount > 0 ) ) + { + /* Just advancing the tail index, 'ulCount' bytes have been confirmed. */ + uxStreamBufferGet( ( *ppxSocket )->u.xTCP.txStream, 0, NULL, ( size_t ) ulCount, pdFALSE ); + ( *ppxSocket )->xEventBits |= eSOCKET_SEND; + + #if ipconfigSUPPORT_SELECT_FUNCTION == 1 + { + if( ( *ppxSocket )->xSelectBits & eSELECT_WRITE ) + { + /* The field 'xEventBits' is used to store regular socket events + * (at most 8), as well as 'select events', which will be left-shifted. + */ + ( *ppxSocket )->xEventBits |= ( 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( ( *ppxSocket )->u.xTCP.pxHandleSent ) ) + { + ( *ppxSocket )->u.xTCP.pxHandleSent( (Socket_t )( *ppxSocket ), ulCount ); + } + } + #endif /* ipconfigUSE_CALLBACKS == 1 */ + } + ( *ppucPtr ) += 8; + ( *pucLen ) -= 8; +} + +/*-----------------------------------------------------------*/ + +#if( ipconfigUSE_TCP_WIN != 0 ) + + static uint8_t prvWinScaleFactor( FreeRTOS_Socket_t *pxSocket ) + { + size_t uxWinSize; + uint8_t ucFactor; + + /* 'xTCP.uxRxWinSize' is the size of the reception window in units of MSS. */ + uxWinSize = pxSocket->u.xTCP.uxRxWinSize * ( size_t ) pxSocket->u.xTCP.usInitMSS; + ucFactor = 0u; + while( uxWinSize > 0xfffful ) + { + /* Divide by two and increase the binary factor by 1. */ + uxWinSize >>= 1; + ucFactor++; + } + + FreeRTOS_debug_printf( ( "prvWinScaleFactor: uxRxWinSize %lu MSS %lu Factor %u\n", + pxSocket->u.xTCP.uxRxWinSize, + pxSocket->u.xTCP.usInitMSS, + ucFactor ) ); + + return ucFactor; + } + +#endif +/*-----------------------------------------------------------*/ + +/* + * When opening a TCP connection, while SYN's are being sent, the parties may + * communicate what MSS (Maximum Segment Size) they intend to use. MSS is the + * nett size of the payload, always smaller than MTU. +*/ +static UBaseType_t prvSetSynAckOptions( FreeRTOS_Socket_t *pxSocket, TCPPacket_t * pxTCPPacket ) +{ +TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; +uint16_t usMSS = pxSocket->u.xTCP.usInitMSS; +UBaseType_t uxOptionsLength; + + /* We send out the TCP Maximum Segment Size option with our SYN[+ACK]. */ + + pxTCPHeader->ucOptdata[ 0 ] = ( uint8_t ) TCP_OPT_MSS; + pxTCPHeader->ucOptdata[ 1 ] = ( uint8_t ) TCP_OPT_MSS_LEN; + pxTCPHeader->ucOptdata[ 2 ] = ( uint8_t ) ( usMSS >> 8 ); + pxTCPHeader->ucOptdata[ 3 ] = ( uint8_t ) ( usMSS & 0xffu ); + + #if( ipconfigUSE_TCP_WIN != 0 ) + { + pxSocket->u.xTCP.ucMyWinScaleFactor = prvWinScaleFactor( pxSocket ); + + pxTCPHeader->ucOptdata[ 4 ] = TCP_OPT_NOOP; + pxTCPHeader->ucOptdata[ 5 ] = ( uint8_t ) ( TCP_OPT_WSOPT ); + pxTCPHeader->ucOptdata[ 6 ] = ( uint8_t ) ( TCP_OPT_WSOPT_LEN ); + pxTCPHeader->ucOptdata[ 7 ] = ( uint8_t ) pxSocket->u.xTCP.ucMyWinScaleFactor; + uxOptionsLength = 8u; + } + #else + { + uxOptionsLength = 4u; + } + #endif + + #if( ipconfigUSE_TCP_WIN == 0 ) + { + return uxOptionsLength; + } + #else + { + pxTCPHeader->ucOptdata[ uxOptionsLength + 0 ] = TCP_OPT_NOOP; + pxTCPHeader->ucOptdata[ uxOptionsLength + 1 ] = TCP_OPT_NOOP; + pxTCPHeader->ucOptdata[ uxOptionsLength + 2 ] = TCP_OPT_SACK_P; /* 4: Sack-Permitted Option. */ + pxTCPHeader->ucOptdata[ uxOptionsLength + 3 ] = 2; /* 2: length of this option. */ + uxOptionsLength += 4u; + + return uxOptionsLength; /* bytes, not words. */ + } + #endif /* ipconfigUSE_TCP_WIN == 0 */ +} + +/* + * For anti-hanging protection and TCP keep-alive messages. Called in two + * places: after receiving a packet and after a state change. The socket's + * alive timer may be reset. + */ +static void prvTCPTouchSocket( FreeRTOS_Socket_t *pxSocket ) +{ + #if( ipconfigTCP_HANG_PROTECTION == 1 ) + { + pxSocket->u.xTCP.xLastActTime = xTaskGetTickCount( ); + } + #endif + + #if( ipconfigTCP_KEEP_ALIVE == 1 ) + { + pxSocket->u.xTCP.bits.bWaitKeepAlive = pdFALSE_UNSIGNED; + pxSocket->u.xTCP.bits.bSendKeepAlive = pdFALSE_UNSIGNED; + pxSocket->u.xTCP.ucKeepRepCount = 0u; + pxSocket->u.xTCP.xLastAliveTime = xTaskGetTickCount(); + } + #endif + + ( void ) pxSocket; +} +/*-----------------------------------------------------------*/ + +/* + * Changing to a new state. Centralised here to do specific actions such as + * resetting the alive timer, calling the user's OnConnect handler to notify + * that a socket has got (dis)connected, and setting bit to unblock a call to + * FreeRTOS_select() + */ +void vTCPStateChange( FreeRTOS_Socket_t *pxSocket, enum eTCP_STATE eTCPState ) +{ +FreeRTOS_Socket_t *xParent = NULL; +BaseType_t bBefore = ( BaseType_t ) NOW_CONNECTED( pxSocket->u.xTCP.ucTCPState ); /* Was it connected ? */ +BaseType_t bAfter = ( BaseType_t ) NOW_CONNECTED( eTCPState ); /* Is it connected now ? */ +#if( ipconfigHAS_DEBUG_PRINTF != 0 ) + BaseType_t xPreviousState = ( BaseType_t ) pxSocket->u.xTCP.ucTCPState; +#endif +#if( ipconfigUSE_CALLBACKS == 1 ) + FreeRTOS_Socket_t *xConnected = NULL; +#endif + + /* Has the connected status changed? */ + if( bBefore != bAfter ) + { + /* 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 ) + { + if( xParent->u.xTCP.pxPeerSocket == NULL ) + { + xParent->u.xTCP.pxPeerSocket = pxSocket; + } + + xParent->xEventBits |= eSOCKET_ACCEPT; + + #if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + /* Library support FreeRTOS_select(). Receiving a new + connection is being translated as a READ event. */ + if( ( xParent->xSelectBits & eSELECT_READ ) != 0 ) + { + xParent->xEventBits |= ( eSELECT_READ << SOCKET_EVENT_BIT_COUNT ); + } + } + #endif + + #if( ipconfigUSE_CALLBACKS == 1 ) + { + if( ( ipconfigIS_VALID_PROG_ADDRESS( xParent->u.xTCP.pxHandleConnected ) != pdFALSE ) && + ( xParent->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) ) + { + /* The listening socket does not become connected itself, in stead + a child socket is created. + Postpone a call the OnConnect event until the end of this function. */ + xConnected = xParent; + } + } + #endif + } + + /* Don't need to access the parent socket anymore, so the + reference 'pxPeerSocket' may be cleared. */ + pxSocket->u.xTCP.pxPeerSocket = NULL; + pxSocket->u.xTCP.bits.bPassQueued = pdFALSE_UNSIGNED; + + /* When true, this socket may be returned in a call to accept(). */ + pxSocket->u.xTCP.bits.bPassAccept = pdTRUE_UNSIGNED; + } + else + { + pxSocket->xEventBits |= eSOCKET_CONNECT; + + #if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + if( pxSocket->xSelectBits & eSELECT_WRITE ) + { + pxSocket->xEventBits |= ( eSELECT_WRITE << SOCKET_EVENT_BIT_COUNT ); + } + } + #endif + } + } + else /* bAfter == pdFALSE, connection is closed. */ + { + /* Notify/wake-up the socket-owner by setting a semaphore. */ + pxSocket->xEventBits |= eSOCKET_CLOSED; + + #if( ipconfigSUPPORT_SELECT_FUNCTION == 1 ) + { + if( ( pxSocket->xSelectBits & eSELECT_EXCEPT ) != 0 ) + { + pxSocket->xEventBits |= ( eSELECT_EXCEPT << SOCKET_EVENT_BIT_COUNT ); + } + } + #endif + } + #if( ipconfigUSE_CALLBACKS == 1 ) + { + if( ( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleConnected ) != pdFALSE ) && ( xConnected == NULL ) ) + { + /* The 'connected' state has changed, call the user handler. */ + xConnected = pxSocket; + } + } + #endif /* ipconfigUSE_CALLBACKS */ + + if( prvTCPSocketIsActive( ( UBaseType_t ) pxSocket->u.xTCP.ucTCPState ) == pdFALSE ) + { + /* Now the socket isn't in an active state anymore so it + won't need further attention of the IP-task. + Setting time-out to zero means that the socket won't get checked during + timer events. */ + pxSocket->u.xTCP.usTimeout = 0u; + } + } + else + { + if( eTCPState == eCLOSED ) + { + /* 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" ) ); + if( pxSocket->u.xTCP.bits.bReuseSocket == pdFALSE_UNSIGNED ) + { + FreeRTOS_closesocket( pxSocket ); + } + } + } + } + + /* Fill in the new state. */ + pxSocket->u.xTCP.ucTCPState = ( uint8_t ) eTCPState; + + /* touch the alive timers because moving to another state. */ + prvTCPTouchSocket( pxSocket ); + + #if( ipconfigHAS_DEBUG_PRINTF == 1 ) + { + if( ( xTCPWindowLoggingLevel >= 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) ) + FreeRTOS_debug_printf( ( "Socket %d -> %lxip:%u State %s->%s\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + FreeRTOS_GetTCPStateName( ( UBaseType_t ) xPreviousState ), + FreeRTOS_GetTCPStateName( ( UBaseType_t ) eTCPState ) ) ); + } + #endif /* ipconfigHAS_DEBUG_PRINTF */ + + #if( ipconfigUSE_CALLBACKS == 1 ) + { + if( xConnected != NULL ) + { + /* The 'connected' state has changed, call the OnConnect handler of the parent. */ + xConnected->u.xTCP.pxHandleConnected( ( Socket_t ) xConnected, bAfter ); + } + } + #endif + if( xParent != NULL ) + { + vSocketWakeUpUser( xParent ); + } +} +/*-----------------------------------------------------------*/ + +static NetworkBufferDescriptor_t *prvTCPBufferResize( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer, + int32_t lDataLen, UBaseType_t uxOptionsLength ) +{ +NetworkBufferDescriptor_t *pxReturn; +int32_t lNeeded; +BaseType_t xResize; + + if( xBufferAllocFixedSize != pdFALSE ) + { + /* Network buffers are created with a fixed size and can hold the largest + MTU. */ + lNeeded = ( int32_t ) ipTOTAL_ETHERNET_FRAME_SIZE; + /* and therefore, the buffer won't be too small. + Only ask for a new network buffer in case none was supplied. */ + xResize = ( pxNetworkBuffer == NULL ); + } + else + { + /* Network buffers are created with a variable size. See if it must + grow. */ + lNeeded = FreeRTOS_max_int32( ( int32_t ) sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ), + ( int32_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ) + lDataLen ); + /* In case we were called from a TCP timer event, a buffer must be + created. Otherwise, test 'xDataLength' of the provided buffer. */ + xResize = ( pxNetworkBuffer == NULL ) || ( pxNetworkBuffer->xDataLength < (size_t)lNeeded ); + } + + if( xResize != pdFALSE ) + { + /* The caller didn't provide a network buffer or the provided buffer is + too small. As we must send-out a data packet, a buffer will be created + here. */ + pxReturn = pxGetNetworkBufferWithDescriptor( ( uint32_t ) lNeeded, 0u ); + + if( pxReturn != NULL ) + { + /* Set the actual packet size, in case the returned buffer is larger. */ + pxReturn->xDataLength = lNeeded; + + /* Copy the existing data to the new created buffer. */ + if( pxNetworkBuffer ) + { + /* Either from the previous buffer... */ + memcpy( pxReturn->pucEthernetBuffer, pxNetworkBuffer->pucEthernetBuffer, pxNetworkBuffer->xDataLength ); + + /* ...and release it. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + } + else + { + /* Or from the socket field 'xTCP.xPacket'. */ + memcpy( pxReturn->pucEthernetBuffer, pxSocket->u.xTCP.xPacket.u.ucLastPacket, sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket ) ); + } + } + } + else + { + /* xResize is false, the network buffer provided was big enough. */ + pxReturn = pxNetworkBuffer; + + /* Thanks to Andrey Ivanov from swissEmbedded for reporting that the + xDataLength member must get the correct length too! */ + pxNetworkBuffer->xDataLength = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ) + ( size_t ) lDataLen; + } + + return pxReturn; +} +/*-----------------------------------------------------------*/ + +/* + * Prepare an outgoing message, in case anything has to be sent. + */ +static int32_t prvTCPPrepareSend( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, UBaseType_t uxOptionsLength ) +{ +int32_t lDataLen; +uint8_t *pucEthernetBuffer, *pucSendData; +TCPPacket_t *pxTCPPacket; +size_t uxOffset; +uint32_t ulDataGot, ulDistance; +TCPWindow_t *pxTCPWindow; +NetworkBufferDescriptor_t *pxNewBuffer; +int32_t lStreamPos; + + if( ( *ppxNetworkBuffer ) != NULL ) + { + /* A network buffer descriptor was already supplied */ + pucEthernetBuffer = ( *ppxNetworkBuffer )->pucEthernetBuffer; + } + else + { + /* For now let it point to the last packet header */ + pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket; + } + + pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer ); + pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; + lDataLen = 0; + lStreamPos = 0; + pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_ACK; + + if( pxSocket->u.xTCP.txStream != NULL ) + { + /* ulTCPWindowTxGet will return the amount of data which may be sent + along with the position in the txStream. + Why check for MSS > 1 ? + Because some TCP-stacks (like uIP) use it for flow-control. */ + if( pxSocket->u.xTCP.usCurMSS > 1u ) + { + lDataLen = ( int32_t ) ulTCPWindowTxGet( pxTCPWindow, pxSocket->u.xTCP.ulWindowSize, &lStreamPos ); + } + + if( lDataLen > 0 ) + { + /* Check if the current network buffer is big enough, if not, + resize it. */ + pxNewBuffer = prvTCPBufferResize( pxSocket, *ppxNetworkBuffer, lDataLen, uxOptionsLength ); + + if( pxNewBuffer != NULL ) + { + *ppxNetworkBuffer = pxNewBuffer; + pucEthernetBuffer = pxNewBuffer->pucEthernetBuffer; + pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer ); + + pucSendData = pucEthernetBuffer + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength; + + /* Translate the position in txStream to an offset from the tail + marker. */ + uxOffset = uxStreamBufferDistance( pxSocket->u.xTCP.txStream, pxSocket->u.xTCP.txStream->uxTail, ( size_t ) lStreamPos ); + + /* Here data is copied from the txStream in 'peek' mode. Only + when the packets are acked, the tail marker will be updated. */ + ulDataGot = ( uint32_t ) uxStreamBufferGet( pxSocket->u.xTCP.txStream, uxOffset, pucSendData, ( size_t ) lDataLen, pdTRUE ); + + #if( ipconfigHAS_DEBUG_PRINTF != 0 ) + { + if( ulDataGot != ( uint32_t ) lDataLen ) + { + FreeRTOS_debug_printf( ( "uxStreamBufferGet: pos %lu offs %lu only %lu != %lu\n", + lStreamPos, uxOffset, ulDataGot, lDataLen ) ); + } + } + #endif + + /* If the owner of the socket requests a closure, add the FIN + flag to the last packet. */ + if( ( pxSocket->u.xTCP.bits.bCloseRequested != pdFALSE_UNSIGNED ) && ( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) ) + { + ulDistance = ( uint32_t ) uxStreamBufferDistance( pxSocket->u.xTCP.txStream, ( size_t ) lStreamPos, pxSocket->u.xTCP.txStream->uxHead ); + + if( ulDistance == ulDataGot ) + { + #if (ipconfigHAS_DEBUG_PRINTF == 1) + { + /* the order of volatile accesses is undefined + so such workaround */ + size_t uxHead = pxSocket->u.xTCP.txStream->uxHead; + size_t uxMid = pxSocket->u.xTCP.txStream->uxMid; + size_t uxTail = pxSocket->u.xTCP.txStream->uxTail; + + FreeRTOS_debug_printf( ( "CheckClose %lu <= %lu (%lu <= %lu <= %lu)\n", ulDataGot, ulDistance, + uxTail, uxMid, uxHead ) ); + } + #endif + /* Although the socket sends a FIN, it will stay in + ESTABLISHED until all current data has been received or + delivered. */ + pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_FIN; + pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->ulOurSequenceNumber + ( uint32_t ) lDataLen; + pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED; + } + } + } + else + { + lDataLen = -1; + } + } + } + + if( ( lDataLen >= 0 ) && ( pxSocket->u.xTCP.ucTCPState == eESTABLISHED ) ) + { + /* See if the socket owner wants to shutdown this connection. */ + if( ( pxSocket->u.xTCP.bits.bUserShutdown != pdFALSE_UNSIGNED ) && + ( xTCPWindowTxDone( pxTCPWindow ) != pdFALSE ) ) + { + pxSocket->u.xTCP.bits.bUserShutdown = pdFALSE_UNSIGNED; + pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_FIN; + pxSocket->u.xTCP.bits.bFinSent = pdTRUE_UNSIGNED; + pxSocket->u.xTCP.bits.bWinChange = pdTRUE_UNSIGNED; + pxTCPWindow->tx.ulFINSequenceNumber = pxTCPWindow->tx.ulCurrentSequenceNumber; + vTCPStateChange( pxSocket, eFIN_WAIT_1 ); + } + + #if( ipconfigTCP_KEEP_ALIVE != 0 ) + { + if( pxSocket->u.xTCP.ucKeepRepCount > 3u ) + { + FreeRTOS_debug_printf( ( "keep-alive: giving up %lxip:%u\n", + pxSocket->u.xTCP.ulRemoteIP, /* IP address of remote machine. */ + pxSocket->u.xTCP.usRemotePort ) ); /* Port on remote machine. */ + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + lDataLen = -1; + } + if( ( lDataLen == 0 ) && ( pxSocket->u.xTCP.bits.bWinChange == pdFALSE_UNSIGNED ) ) + { + /* If there is no data to be sent, and no window-update message, + we might want to send a keep-alive message. */ + TickType_t xAge = xTaskGetTickCount( ) - pxSocket->u.xTCP.xLastAliveTime; + TickType_t xMax; + xMax = ( ( TickType_t ) ipconfigTCP_KEEP_ALIVE_INTERVAL * configTICK_RATE_HZ ); + if( pxSocket->u.xTCP.ucKeepRepCount ) + { + xMax = ( 3u * configTICK_RATE_HZ ); + } + if( xAge > xMax ) + { + pxSocket->u.xTCP.xLastAliveTime = xTaskGetTickCount( ); + if( xTCPWindowLoggingLevel ) + FreeRTOS_debug_printf( ( "keep-alive: %lxip:%u count %u\n", + pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + pxSocket->u.xTCP.ucKeepRepCount ) ); + pxSocket->u.xTCP.bits.bSendKeepAlive = pdTRUE_UNSIGNED; + pxSocket->u.xTCP.usTimeout = ( ( uint16_t ) pdMS_TO_TICKS( 2500 ) ); + pxSocket->u.xTCP.ucKeepRepCount++; + } + } + } + #endif /* ipconfigTCP_KEEP_ALIVE */ + } + + /* Anything to send, a change of the advertised window size, or maybe send a + keep-alive message? */ + if( ( lDataLen > 0 ) || + ( pxSocket->u.xTCP.bits.bWinChange != pdFALSE_UNSIGNED ) || + ( pxSocket->u.xTCP.bits.bSendKeepAlive != pdFALSE_UNSIGNED ) ) + { + pxTCPPacket->xTCPHeader.ucTCPFlags &= ( ( uint8_t ) ~ipTCP_FLAG_PSH ); + pxTCPPacket->xTCPHeader.ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + + pxTCPPacket->xTCPHeader.ucTCPFlags |= ( uint8_t ) ipTCP_FLAG_ACK; + + if( lDataLen != 0l ) + { + pxTCPPacket->xTCPHeader.ucTCPFlags |= ( uint8_t ) ipTCP_FLAG_PSH; + } + + lDataLen += ( int32_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); + } + + return lDataLen; +} +/*-----------------------------------------------------------*/ + +/* + * Calculate after how much time this socket needs to be checked again. + */ +static TickType_t prvTCPNextTimeout ( FreeRTOS_Socket_t *pxSocket ) +{ +TickType_t ulDelayMs = ( TickType_t ) tcpMAXIMUM_TCP_WAKEUP_TIME_MS; + + if( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) + { + /* The socket is actively connecting to a peer. */ + if( pxSocket->u.xTCP.bits.bConnPrepared ) + { + /* Ethernet address has been found, use progressive timeout for + active connect(). */ + if( pxSocket->u.xTCP.ucRepCount < 3u ) + { + ulDelayMs = ( 3000UL << ( pxSocket->u.xTCP.ucRepCount - 1u ) ); + } + else + { + ulDelayMs = 11000UL; + } + } + else + { + /* Still in the ARP phase: check every half second. */ + ulDelayMs = 500UL; + } + + FreeRTOS_debug_printf( ( "Connect[%lxip:%u]: next timeout %u: %lu ms\n", + pxSocket->u.xTCP.ulRemoteIP, pxSocket->u.xTCP.usRemotePort, + pxSocket->u.xTCP.ucRepCount, ulDelayMs ) ); + pxSocket->u.xTCP.usTimeout = ( uint16_t )pdMS_TO_MIN_TICKS( ulDelayMs ); + } + else if( pxSocket->u.xTCP.usTimeout == 0u ) + { + /* Let the sliding window mechanism decide what time-out is appropriate. */ + BaseType_t xResult = xTCPWindowTxHasData( &pxSocket->u.xTCP.xTCPWindow, pxSocket->u.xTCP.ulWindowSize, &ulDelayMs ); + if( ulDelayMs == 0u ) + { + if( xResult != ( BaseType_t )0 ) + { + ulDelayMs = 1UL; + } + else + { + ulDelayMs = tcpMAXIMUM_TCP_WAKEUP_TIME_MS; + } + } + else + { + /* ulDelayMs contains the time to wait before a re-transmission. */ + } + pxSocket->u.xTCP.usTimeout = ( uint16_t )pdMS_TO_MIN_TICKS( ulDelayMs ); + } + else + { + /* field '.usTimeout' has already been set (by the + keep-alive/delayed-ACK mechanism). */ + } + + /* Return the number of clock ticks before the timer expires. */ + return ( TickType_t ) pxSocket->u.xTCP.usTimeout; +} +/*-----------------------------------------------------------*/ + +static void prvTCPAddTxData( FreeRTOS_Socket_t *pxSocket ) +{ +int32_t lCount, lLength; + + /* A txStream has been created already, see if the socket has new data for + the sliding window. + + uxStreamBufferMidSpace() returns the distance between rxHead and rxMid. It contains new + Tx data which has not been passed to the sliding window yet. The oldest + data not-yet-confirmed can be found at rxTail. */ + lLength = ( int32_t ) uxStreamBufferMidSpace( pxSocket->u.xTCP.txStream ); + + if( lLength > 0 ) + { + /* All data between txMid and rxHead will now be passed to the sliding + window manager, so it can start transmitting them. + + Hand over the new data to the sliding window handler. It will be + split-up in chunks of 1460 bytes each (or less, depending on + ipconfigTCP_MSS). */ + lCount = lTCPWindowTxAdd( &pxSocket->u.xTCP.xTCPWindow, + ( uint32_t ) lLength, + ( int32_t ) pxSocket->u.xTCP.txStream->uxMid, + ( int32_t ) pxSocket->u.xTCP.txStream->LENGTH ); + + /* Move the rxMid pointer forward up to rxHead. */ + if( lCount > 0 ) + { + vStreamBufferMoveMid( pxSocket->u.xTCP.txStream, ( size_t ) lCount ); + } + } +} +/*-----------------------------------------------------------*/ + +/* + * prvTCPHandleFin() will be called to handle socket 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. + */ +static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; +uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; +TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; +BaseType_t xSendLength = 0; +uint32_t ulAckNr = FreeRTOS_ntohl( pxTCPHeader->ulAckNr ); + + if( ( ucTCPFlags & ipTCP_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 = ipTCP_FLAG_ACK | ipTCP_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 = ipTCP_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 ) + { + xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength ); + } + + pxTCPHeader->ucTCPOffset = ( uint8_t ) ( ( ipSIZE_OF_TCP_HEADER + pxTCPWindow->ucOptionLength ) << 2 ); + + if( xTCPWindowLoggingLevel != 0 ) + { + FreeRTOS_debug_printf( ( "TCP: send FIN+ACK (ack %lu, cur/nxt %lu/%lu) ourSeqNr %lu | Rx %lu\n", + ulAckNr - pxTCPWindow->tx.ulFirstSequenceNumber, + pxTCPWindow->tx.ulCurrentSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, + pxTCPWindow->ulNextTxSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, + pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, + pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ); + } + + return xSendLength; +} +/*-----------------------------------------------------------*/ + +/* + * prvCheckRxData(): called from prvTCPHandleState() + * + * The first thing that will be done is find the TCP payload data + * and check the length of this data. + */ +static BaseType_t prvCheckRxData( NetworkBufferDescriptor_t *pxNetworkBuffer, uint8_t **ppucRecvData ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &( pxTCPPacket->xTCPHeader ); +int32_t lLength, lTCPHeaderLength, lReceiveLength, lUrgentLength; + + /* Determine the length and the offset of the user-data sent to this + node. + + The size of the TCP header is given in a multiple of 4-byte words (single + byte, needs no ntoh() translation). A shift-right 2: is the same as + (offset >> 4) * 4. */ + lTCPHeaderLength = ( BaseType_t ) ( ( pxTCPHeader->ucTCPOffset & VALID_BITS_IN_TCP_OFFSET_BYTE ) >> 2 ); + + /* Let pucRecvData point to the first byte received. */ + *ppucRecvData = pxNetworkBuffer->pucEthernetBuffer + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + lTCPHeaderLength; + + /* Calculate lReceiveLength - the length of the TCP data received. This is + equal to the total packet length minus: + ( LinkLayer length (14) + IP header length (20) + size of TCP header(20 +) ).*/ + lReceiveLength = ( ( int32_t ) pxNetworkBuffer->xDataLength ) - ( int32_t ) ipSIZE_OF_ETH_HEADER; + lLength = ( int32_t )FreeRTOS_htons( pxTCPPacket->xIPHeader.usLength ); + + if( lReceiveLength > lLength ) + { + /* More bytes were received than the reported length, often because of + padding bytes at the end. */ + lReceiveLength = lLength; + } + + /* Subtract the size of the TCP and IP headers and the actual data size is + known. */ + if( lReceiveLength > ( lTCPHeaderLength + ( int32_t ) ipSIZE_OF_IPv4_HEADER ) ) + { + lReceiveLength -= ( lTCPHeaderLength + ( int32_t ) ipSIZE_OF_IPv4_HEADER ); + } + else + { + lReceiveLength = 0; + } + + /* Urgent Pointer: + This field communicates the current value of the urgent pointer as a + positive offset from the sequence number in this segment. The urgent + pointer points to the sequence number of the octet following the urgent + data. This field is only be interpreted in segments with the URG control + bit set. */ + if( ( pxTCPHeader->ucTCPFlags & ipTCP_FLAG_URG ) != 0u ) + { + /* Although we ignore the urgent data, we have to skip it. */ + lUrgentLength = ( int32_t ) FreeRTOS_htons( pxTCPHeader->usUrgent ); + *ppucRecvData += lUrgentLength; + lReceiveLength -= FreeRTOS_min_int32( lReceiveLength, lUrgentLength ); + } + + return ( BaseType_t ) lReceiveLength; +} +/*-----------------------------------------------------------*/ + +/* + * prvStoreRxData(): called from prvTCPHandleState() + * + * The second thing is to do is check if the payload data may be accepted + * If so, they will be added to the reception queue. + */ +static BaseType_t prvStoreRxData( FreeRTOS_Socket_t *pxSocket, uint8_t *pucRecvData, + NetworkBufferDescriptor_t *pxNetworkBuffer, uint32_t ulReceiveLength ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; +TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; +uint32_t ulSequenceNumber, ulSpace; +int32_t lOffset, lStored; +BaseType_t xResult = 0; + + ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ); + + if( ( ulReceiveLength > 0u ) && ( pxSocket->u.xTCP.ucTCPState >= eSYN_RECEIVED ) ) + { + /* See if way may accept the data contents and forward it to the socket + owner. + + If it can't be "accept"ed it may have to be stored and send a selective + ack (SACK) option to confirm it. In that case, xTCPWindowRxStore() will be + called later to store an out-of-order packet (in case lOffset is + negative). */ + if ( pxSocket->u.xTCP.rxStream ) + { + ulSpace = ( uint32_t )uxStreamBufferGetSpace ( pxSocket->u.xTCP.rxStream ); + } + else + { + ulSpace = ( uint32_t )pxSocket->u.xTCP.uxRxStreamSize; + } + + lOffset = lTCPWindowRxCheck( pxTCPWindow, ulSequenceNumber, ulReceiveLength, ulSpace ); + + if( lOffset >= 0 ) + { + /* New data has arrived and may be made available to the user. See + if the head marker in rxStream may be advanced, only if lOffset == 0. + In case the low-water mark is reached, bLowWater will be set + "low-water" here stands for "little space". */ + lStored = lTCPAddRxdata( pxSocket, ( uint32_t ) lOffset, pucRecvData, ulReceiveLength ); + + if( lStored != ( int32_t ) ulReceiveLength ) + { + FreeRTOS_debug_printf( ( "lTCPAddRxdata: stored %ld / %lu bytes??\n", lStored, ulReceiveLength ) ); + + /* Received data could not be stored. The socket's flag + bMallocError has been set. The socket now has the status + eCLOSE_WAIT and a RST packet will be sent back. */ + prvTCPSendReset( pxNetworkBuffer ); + xResult = -1; + } + } + + /* After a missing packet has come in, higher packets may be passed to + the user. */ + #if( ipconfigUSE_TCP_WIN == 1 ) + { + /* Now lTCPAddRxdata() will move the rxHead pointer forward + so data becomes available to the user immediately + In case the low-water mark is reached, bLowWater will be set. */ + if( ( xResult == 0 ) && ( pxTCPWindow->ulUserDataLength > 0 ) ) + { + lTCPAddRxdata( pxSocket, 0ul, NULL, pxTCPWindow->ulUserDataLength ); + pxTCPWindow->ulUserDataLength = 0; + } + } + #endif /* ipconfigUSE_TCP_WIN */ + } + else + { + pxTCPWindow->ucOptionLength = 0u; + } + + return xResult; +} +/*-----------------------------------------------------------*/ + +/* Set the TCP options (if any) for the outgoing packet. */ +static UBaseType_t prvSetOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; +TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; +UBaseType_t uxOptionsLength = pxTCPWindow->ucOptionLength; + + #if( ipconfigUSE_TCP_WIN == 1 ) + if( uxOptionsLength != 0u ) + { + /* TCP options must be sent because a packet which is out-of-order + was received. */ + if( xTCPWindowLoggingLevel >= 0 ) + FreeRTOS_debug_printf( ( "SACK[%d,%d]: optlen %lu sending %lu - %lu\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + uxOptionsLength, + FreeRTOS_ntohl( pxTCPWindow->ulOptionsData[ 1 ] ) - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber, + FreeRTOS_ntohl( pxTCPWindow->ulOptionsData[ 2 ] ) - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber ) ); + memcpy( pxTCPHeader->ucOptdata, pxTCPWindow->ulOptionsData, ( size_t ) uxOptionsLength ); + + /* The header length divided by 4, goes into the higher nibble, + effectively a shift-left 2. */ + pxTCPHeader->ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + } + else + #endif /* ipconfigUSE_TCP_WIN */ + if( ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) && ( pxSocket->u.xTCP.bits.bMssChange != pdFALSE_UNSIGNED ) ) + { + /* TCP options must be sent because the MSS has changed. */ + pxSocket->u.xTCP.bits.bMssChange = pdFALSE_UNSIGNED; + if( xTCPWindowLoggingLevel >= 0 ) + { + FreeRTOS_debug_printf( ( "MSS: sending %d\n", pxSocket->u.xTCP.usCurMSS ) ); + } + + pxTCPHeader->ucOptdata[ 0 ] = TCP_OPT_MSS; + pxTCPHeader->ucOptdata[ 1 ] = TCP_OPT_MSS_LEN; + pxTCPHeader->ucOptdata[ 2 ] = ( uint8_t ) ( ( pxSocket->u.xTCP.usCurMSS ) >> 8 ); + pxTCPHeader->ucOptdata[ 3 ] = ( uint8_t ) ( ( pxSocket->u.xTCP.usCurMSS ) & 0xffu ); + uxOptionsLength = 4u; + pxTCPHeader->ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 ); + } + + return uxOptionsLength; +} +/*-----------------------------------------------------------*/ + +/* + * 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. + */ +static BaseType_t prvHandleSynReceived( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, + uint32_t ulReceiveLength, UBaseType_t uxOptionsLength ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; +TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; +uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; +uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ); +BaseType_t xSendLength = 0; + + /* Either expect a ACK or a SYN+ACK. */ + uint16_t usExpect = ( uint16_t ) ipTCP_FLAG_ACK; + if( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) + { + usExpect |= ( uint16_t ) ipTCP_FLAG_SYN; + } + + if( ( ucTCPFlags & 0x17u ) != usExpect ) + { + /* 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 == eSYN_RECEIVED ? "eSYN_RECEIVED" : "eCONNECT_SYN", + usExpect, ucTCPFlags ) ); + vTCPStateChange( pxSocket, eCLOSE_WAIT ); + pxTCPHeader->ucTCPFlags |= ipTCP_FLAG_RST; + xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); + 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 == eCONNECT_SYN ) + { + TCPPacket_t *pxLastTCPPacket = ( TCPPacket_t * ) ( pxSocket->u.xTCP.xPacket.u.ucLastPacket ); + + /* Clear the SYN flag in lastPacket. */ + pxLastTCPPacket->xTCPHeader.ucTCPFlags = ipTCP_FLAG_ACK; + + /* This socket was the one connecting actively so now perofmr the + synchronisation. */ + vTCPWindowInit( &pxSocket->u.xTCP.xTCPWindow, + ulSequenceNumber, pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber, ( uint32_t ) pxSocket->u.xTCP.usCurMSS ); + pxTCPWindow->rx.ulCurrentSequenceNumber = pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1u; + pxTCPWindow->tx.ulCurrentSequenceNumber++; /* because we send a TCP_SYN [ | TCP_ACK ]; */ + pxTCPWindow->ulNextTxSequenceNumber++; + } + else if( ulReceiveLength == 0u ) + { + pxTCPWindow->rx.ulCurrentSequenceNumber = ulSequenceNumber; + } + + /* 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 %d => %lxip:%d set ESTAB (scaling %u)\n", + pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ? "active" : "passive", + pxSocket->usLocalPort, + pxSocket->u.xTCP.ulRemoteIP, + pxSocket->u.xTCP.usRemotePort, + ( unsigned ) pxSocket->u.xTCP.bits.bWinScaling ) ); + } + #endif /* ipconfigUSE_TCP_WIN */ + + if( ( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) || ( ulReceiveLength != 0u ) ) + { + pxTCPHeader->ucTCPFlags = ipTCP_FLAG_ACK; + xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); + 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; +} +/*-----------------------------------------------------------*/ + +/* + * 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. + */ +static BaseType_t prvHandleEstablished( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, + uint32_t ulReceiveLength, UBaseType_t uxOptionsLength ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; +TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; +uint8_t ucTCPFlags = pxTCPHeader->ucTCPFlags; +uint32_t ulSequenceNumber = FreeRTOS_ntohl( pxTCPHeader->ulSequenceNumber ), ulCount; +BaseType_t xSendLength = 0, xMayClose = pdFALSE, bRxComplete, bTxDone; +int32_t lDistance, lSendResult; + + /* Remember the window size the peer is advertising. */ + pxSocket->u.xTCP.ulWindowSize = FreeRTOS_ntohs( pxTCPHeader->usWindow ); + #if( ipconfigUSE_TCP_WIN != 0 ) + { + pxSocket->u.xTCP.ulWindowSize = + ( pxSocket->u.xTCP.ulWindowSize << pxSocket->u.xTCP.ucPeerWinScaleFactor ); + } + #endif + + if( ( ucTCPFlags & ( uint8_t ) ipTCP_FLAG_ACK ) != 0u ) + { + ulCount = ulTCPWindowTxAck( pxTCPWindow, FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.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 |= eSOCKET_SEND; + + #if ipconfigSUPPORT_SELECT_FUNCTION == 1 + { + if( ( pxSocket->xSelectBits & eSELECT_WRITE ) != 0 ) + { + pxSocket->xEventBits |= ( 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 ) ipTCP_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 incomp 1 optlen 4 tx done 1. */ + FreeRTOS_debug_printf( ( "Refusing FIN[%u,%u]: RxCompl %lu tx done %ld\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + bRxComplete, bTxDone ) ); + xMayClose = pdFALSE; + } + else + { + lDistance = ( int32_t ) ( ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulCurrentSequenceNumber ); + + if( lDistance > 1 ) + { + FreeRTOS_debug_printf( ( "Refusing FIN: Rx not complete %ld (cur %lu high %lu)\n", + lDistance, pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber, + pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber ) ); + + xMayClose = pdFALSE; + } + } + } + + if( xTCPWindowLoggingLevel > 0 ) + { + FreeRTOS_debug_printf( ( "TCP: FIN received, mayClose = %ld (Rx %lu Len %ld, Tx %lu)\n", + xMayClose, ulSequenceNumber - pxSocket->u.xTCP.xTCPWindow.rx.ulFirstSequenceNumber, ulReceiveLength, + 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 = ipTCP_FLAG_ACK; + + if( ulReceiveLength != 0u ) + { + xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); + /* TCP-offsett 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; +} +/*-----------------------------------------------------------*/ + +/* + * Called from prvTCPHandleState(). There is data to be sent. If + * ipconfigUSE_TCP_WIN is defined, and if only an ACK must be sent, it will be + * checked if it would better be postponed for efficiency. + */ +static BaseType_t prvSendData( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer, + uint32_t ulReceiveLength, BaseType_t xSendLength ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader; +TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow; +/* Find out what window size we may advertised. */ +int32_t lRxSpace; +#if( ipconfigUSE_TCP_WIN == 1 ) + #if( ipconfigTCP_ACK_EARLIER_PACKET == 0 ) + const int32_t lMinLength = 0; + #else + int32_t lMinLength; + #endif +#endif + + /* Set the time-out field, so that we'll be called by the IP-task in case no + next message will be received. */ + lRxSpace = (int32_t)( pxSocket->u.xTCP.ulHighestRxAllowed - pxTCPWindow->rx.ulCurrentSequenceNumber ); + #if ipconfigUSE_TCP_WIN == 1 + { + + #if( ipconfigTCP_ACK_EARLIER_PACKET != 0 ) + { + lMinLength = ( ( int32_t ) 2 ) * ( ( int32_t ) pxSocket->u.xTCP.usCurMSS ); + } + #endif /* ipconfigTCP_ACK_EARLIER_PACKET */ + + /* In case we're receiving data continuously, we might postpone sending + an ACK to gain performance. */ + if( ( ulReceiveLength > 0 ) && /* Data was sent to this socket. */ + ( lRxSpace >= lMinLength ) && /* There is Rx space for more data. */ + ( pxSocket->u.xTCP.bits.bFinSent == pdFALSE_UNSIGNED ) && /* Not in a closure phase. */ + ( xSendLength == ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ) && /* No Tx data or options to be sent. */ + ( pxSocket->u.xTCP.ucTCPState == eESTABLISHED ) && /* Connection established. */ + ( pxTCPHeader->ucTCPFlags == ipTCP_FLAG_ACK ) ) /* There are no other flags than an ACK. */ + { + if( pxSocket->u.xTCP.pxAckMessage != *ppxNetworkBuffer ) + { + /* There was still a delayed in queue, delete it. */ + if( pxSocket->u.xTCP.pxAckMessage != 0 ) + { + vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); + } + + pxSocket->u.xTCP.pxAckMessage = *ppxNetworkBuffer; + } + if( ( ulReceiveLength < ( uint32_t ) pxSocket->u.xTCP.usCurMSS ) || /* Received a small message. */ + ( lRxSpace < ( int32_t ) ( 2U * pxSocket->u.xTCP.usCurMSS ) ) ) /* There are less than 2 x MSS space in the Rx buffer. */ + { + pxSocket->u.xTCP.usTimeout = ( uint16_t ) pdMS_TO_MIN_TICKS( DELAYED_ACK_SHORT_DELAY_MS ); + } + else + { + /* Normally a delayed ACK should wait 200 ms for a next incoming + packet. Only wait 20 ms here to gain performance. A slow ACK + for full-size message. */ + pxSocket->u.xTCP.usTimeout = ( uint16_t ) pdMS_TO_MIN_TICKS( DELAYED_ACK_LONGER_DELAY_MS ); + } + + if( ( xTCPWindowLoggingLevel > 1 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) ) + { + FreeRTOS_debug_printf( ( "Send[%u->%u] del ACK %lu SEQ %lu (len %lu) tmout %u d %lu\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber, + pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, + xSendLength, + pxSocket->u.xTCP.usTimeout, lRxSpace ) ); + } + + *ppxNetworkBuffer = NULL; + xSendLength = 0; + } + else if( pxSocket->u.xTCP.pxAckMessage != NULL ) + { + /* As an ACK is not being delayed, remove any earlier delayed ACK + message. */ + if( pxSocket->u.xTCP.pxAckMessage != *ppxNetworkBuffer ) + { + vReleaseNetworkBufferAndDescriptor( pxSocket->u.xTCP.pxAckMessage ); + } + + pxSocket->u.xTCP.pxAckMessage = NULL; + } + } + #else + { + /* Remove compiler warnings. */ + ( void ) ulReceiveLength; + ( void ) pxTCPHeader; + ( void ) lRxSpace; + } + #endif /* ipconfigUSE_TCP_WIN */ + + if( xSendLength != 0 ) + { + if( ( xTCPWindowLoggingLevel > 1 ) && ( ipconfigTCP_MAY_LOG_PORT( pxSocket->usLocalPort ) != pdFALSE ) ) + { + FreeRTOS_debug_printf( ( "Send[%u->%u] imm ACK %lu SEQ %lu (len %lu)\n", + pxSocket->usLocalPort, + pxSocket->u.xTCP.usRemotePort, + pxTCPWindow->rx.ulCurrentSequenceNumber - pxTCPWindow->rx.ulFirstSequenceNumber, + pxTCPWindow->ulOurSequenceNumber - pxTCPWindow->tx.ulFirstSequenceNumber, + xSendLength ) ); + } + + /* Set the parameter 'xReleaseAfterSend' to the value of + ipconfigZERO_COPY_TX_DRIVER. */ + prvTCPReturnPacket( pxSocket, *ppxNetworkBuffer, ( uint32_t ) xSendLength, ipconfigZERO_COPY_TX_DRIVER ); + #if( ipconfigZERO_COPY_TX_DRIVER != 0 ) + { + /* The driver has taken ownership of the Network Buffer. */ + *ppxNetworkBuffer = NULL; + } + #endif + } + + return xSendLength; +} +/*-----------------------------------------------------------*/ + +/* + * 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. + */ +static BaseType_t prvTCPHandleState( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer ) +{ +TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( (*ppxNetworkBuffer)->pucEthernetBuffer ); +TCPHeader_t *pxTCPHeader = &( pxTCPPacket->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 ); + + /* 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 >= 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. */ + if( ( ( int32_t ) ( ulSequenceNumber + ulReceiveLength - pxTCPWindow->rx.ulHighestSequenceNumber ) ) > 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 + { + uxOptionsLength = prvSetOptions( pxSocket, *ppxNetworkBuffer ); + + if( ( pxSocket->u.xTCP.ucTCPState == eSYN_RECEIVED ) && ( ( ucTCPFlags & ipTCP_FLAG_CTRL ) == ipTCP_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 & ipTCP_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; + } + } + + switch (pxSocket->u.xTCP.ucTCPState) + { + 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, pxTCPPacket ); + pxTCPHeader->ucTCPFlags = ipTCP_FLAG_SYN | ipTCP_FLAG_ACK; + + xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ); + + /* 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.ulCurrentSequenceNumber = pxTCPWindow->rx.ulHighestSequenceNumber = ulSequenceNumber + 1u; + pxTCPWindow->tx.ulCurrentSequenceNumber = pxTCPWindow->ulNextTxSequenceNumber = 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: + break; + } + } + + if( xSendLength > 0 ) + { + xSendLength = prvSendData( pxSocket, ppxNetworkBuffer, ulReceiveLength, xSendLength ); + } + + return xSendLength; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t *pxNetworkBuffer, + uint8_t ucTCPFlags ) +{ +#if( ipconfigIGNORE_UNKNOWN_PACKETS == 0 ) + { + TCPPacket_t *pxTCPPacket = ( TCPPacket_t * )( pxNetworkBuffer->pucEthernetBuffer ); + const BaseType_t xSendLength = ( BaseType_t ) + ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + 0u ); /* Plus 0 options. */ + + pxTCPPacket->xTCPHeader.ucTCPFlags = ucTCPFlags; + pxTCPPacket->xTCPHeader.ucTCPOffset = ( ipSIZE_OF_TCP_HEADER + 0u ) << 2; + + prvTCPReturnPacket( NULL, pxNetworkBuffer, ( uint32_t )xSendLength, pdFALSE ); + } +#endif /* !ipconfigIGNORE_UNKNOWN_PACKETS */ + + /* Remove compiler warnings if ipconfigIGNORE_UNKNOWN_PACKETS == 1. */ + ( void )pxNetworkBuffer; + ( void )ucTCPFlags; + + /* The packet was not consumed. */ + return pdFAIL; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t *pxNetworkBuffer ) +{ + return prvTCPSendSpecialPacketHelper( pxNetworkBuffer, ipTCP_FLAG_ACK ); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t *pxNetworkBuffer ) +{ + return prvTCPSendSpecialPacketHelper( pxNetworkBuffer, + ipTCP_FLAG_ACK | ipTCP_FLAG_RST ); +} +/*-----------------------------------------------------------*/ + +static void prvSocketSetMSS( FreeRTOS_Socket_t *pxSocket ) +{ +uint32_t ulMSS = ipconfigTCP_MSS; + + if( ( ( FreeRTOS_ntohl( pxSocket->u.xTCP.ulRemoteIP ) ^ *ipLOCAL_IP_ADDRESS_POINTER ) & xNetworkAddressing.ulNetMask ) != 0ul ) + { + /* Data for this peer will pass through a router, and maybe through + the internet. Limit the MSS to 1400 bytes or less. */ + ulMSS = FreeRTOS_min_uint32( ( uint32_t ) REDUCED_MSS_THROUGH_INTERNET, ulMSS ); + } + + FreeRTOS_debug_printf( ( "prvSocketSetMSS: %lu bytes for %lxip:%u\n", ulMSS, pxSocket->u.xTCP.ulRemoteIP, pxSocket->u.xTCP.usRemotePort ) ); + + pxSocket->u.xTCP.usInitMSS = pxSocket->u.xTCP.usCurMSS = ( uint16_t ) ulMSS; +} +/*-----------------------------------------------------------*/ + +/* + * FreeRTOS_TCP_IP has only 2 public functions, this is the second one: + * xProcessReceivedTCPPacket() + * prvTCPHandleState() + * prvTCPPrepareSend() + * prvTCPReturnPacket() + * xNetworkInterfaceOutput() // Sends data to the NIC + * prvTCPSendRepeated() + * prvTCPReturnPacket() // Prepare for returning + * xNetworkInterfaceOutput() // Sends data to the NIC +*/ +BaseType_t xProcessReceivedTCPPacket( NetworkBufferDescriptor_t *pxNetworkBuffer ) +{ +FreeRTOS_Socket_t *pxSocket; +TCPPacket_t * pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); +uint16_t ucTCPFlags; +uint32_t ulLocalIP; +uint16_t xLocalPort; +uint32_t ulRemoteIP; +uint16_t xRemotePort; +uint32_t ulSequenceNumber; +uint32_t ulAckNumber; +BaseType_t xResult = pdPASS; +configASSERT(pxNetworkBuffer); +configASSERT(pxNetworkBuffer->pucEthernetBuffer); + + /* Check for a minimum packet size. */ + if( pxNetworkBuffer->xDataLength >= ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) ) + { + ucTCPFlags = pxTCPPacket->xTCPHeader.ucTCPFlags; + ulLocalIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulDestinationIPAddress ); + xLocalPort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usDestinationPort ); + ulRemoteIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulSourceIPAddress ); + xRemotePort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usSourcePort ); + ulSequenceNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulSequenceNumber ); + ulAckNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulAckNr ); + + /* Find the destination socket, and if not found: return a socket listing to + the destination PORT. */ + pxSocket = ( FreeRTOS_Socket_t * )pxTCPSocketLookup( ulLocalIP, xLocalPort, ulRemoteIP, xRemotePort ); + } + else + { + return pdFAIL; + } + + if( ( pxSocket == NULL ) || ( prvTCPSocketIsActive( ( UBaseType_t ) pxSocket->u.xTCP.ucTCPState ) == pdFALSE ) ) + { + /* A TCP messages is received but either there is no socket with the + given port number or the there is a socket, but it is in one of these + non-active states: eCLOSED, eCLOSE_WAIT, eFIN_WAIT_2, eCLOSING, or + eTIME_WAIT. */ + + FreeRTOS_debug_printf( ( "TCP: No active socket on port %d (%lxip:%d)\n", xLocalPort, ulRemoteIP, xRemotePort ) ); + + /* Send a RST to all packets that can not be handled. As a result + the other party will get a ECONN error. There are two exceptions: + 1) A packet that already has the RST flag set. + 2) A packet that only has the ACK flag set. + A packet with only the ACK flag set might be the last ACK in + a three-way hand-shake that closes a connection. */ + if( ( ( ucTCPFlags & ipTCP_FLAG_CTRL ) != ipTCP_FLAG_ACK ) && + ( ( ucTCPFlags & ipTCP_FLAG_RST ) == 0u ) ) + { + prvTCPSendReset( pxNetworkBuffer ); + } + + /* The packet can't be handled. */ + xResult = pdFAIL; + } + else + { + pxSocket->u.xTCP.ucRepCount = 0u; + + if( pxSocket->u.xTCP.ucTCPState == eTCP_LISTEN ) + { + /* The matching socket is in a listening state. Test if the peer + has set the SYN flag. */ + if( ( ucTCPFlags & ipTCP_FLAG_CTRL ) != ipTCP_FLAG_SYN ) + { + /* What happens: maybe after a reboot, a client doesn't know the + connection had gone. Send a RST in order to get a new connect + request. */ + #if( ipconfigHAS_DEBUG_PRINTF == 1 ) + { + FreeRTOS_debug_printf( ( "TCP: Server can't handle flags: %s from %lxip:%u to port %u\n", + prvTCPFlagMeaning( ( UBaseType_t ) ucTCPFlags ), ulRemoteIP, xRemotePort, xLocalPort ) ); + } + #endif /* ipconfigHAS_DEBUG_PRINTF */ + + if( ( ucTCPFlags & ipTCP_FLAG_RST ) == 0u ) + { + prvTCPSendReset( pxNetworkBuffer ); + } + xResult = pdFAIL; + } + else + { + /* prvHandleListen() will either return a newly created socket + (if bReuseSocket is false), otherwise it returns the current + socket which will later get connected. */ + pxSocket = prvHandleListen( pxSocket, pxNetworkBuffer ); + + if( pxSocket == NULL ) + { + xResult = pdFAIL; + } + } + } /* if( pxSocket->u.xTCP.ucTCPState == eTCP_LISTEN ). */ + else + { + /* This is not a socket in listening mode. Check for the RST + flag. */ + if( ( ucTCPFlags & ipTCP_FLAG_RST ) != 0u ) + { + FreeRTOS_debug_printf( ( "TCP: RST received from %lxip:%u for %u\n", ulRemoteIP, xRemotePort, xLocalPort ) ); + + /* Implement https://tools.ietf.org/html/rfc5961#section-3.2. */ + if( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN ) + { + /* Per the above RFC, "In the SYN-SENT state ... the RST is + acceptable if the ACK field acknowledges the SYN." */ + if( ulAckNumber == pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber + 1 ) + { + vTCPStateChange( pxSocket, eCLOSED ); + } + } + else + { + /* Check whether the packet matches the next expected sequence number. */ + if( ulSequenceNumber == pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber ) + { + vTCPStateChange( pxSocket, eCLOSED ); + } + /* Otherwise, check whether the packet is within the receive window. */ + else if( ulSequenceNumber > pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber && + ulSequenceNumber < ( pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber + + pxSocket->u.xTCP.xTCPWindow.xSize.ulRxWindowLength ) ) + { + /* Send a challenge ACK. */ + prvTCPSendChallengeAck( pxNetworkBuffer ); + } + } + + /* Otherwise, do nothing. In any case, the packet cannot be handled. */ + xResult = pdFAIL; + } + else if( ( ( ucTCPFlags & ipTCP_FLAG_CTRL ) == ipTCP_FLAG_SYN ) && ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) ) + { + /* SYN flag while this socket is already connected. */ + FreeRTOS_debug_printf( ( "TCP: SYN unexpected from %lxip:%u\n", ulRemoteIP, xRemotePort ) ); + + /* The packet cannot be handled. */ + xResult = pdFAIL; + } + else + { + /* Update the copy of the TCP header only (skipping eth and IP + headers). It might be used later on, whenever data must be sent + to the peer. */ + const BaseType_t lOffset = ( BaseType_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER ); + memcpy( pxSocket->u.xTCP.xPacket.u.ucLastPacket + lOffset, pxNetworkBuffer->pucEthernetBuffer + lOffset, ipSIZE_OF_TCP_HEADER ); + } + } + } + + if( xResult != pdFAIL ) + { + /* Touch the alive timers because we received a message for this + socket. */ + prvTCPTouchSocket( pxSocket ); + + /* Parse the TCP option(s), if present. */ + /* _HT_ : if we're in the SYN phase, and peer does not send a MSS option, + then we MUST assume an MSS size of 536 bytes for backward compatibility. */ + + /* When there are no TCP options, the TCP offset equals 20 bytes, which is stored as + the number 5 (words) in the higher niblle of the TCP-offset byte. */ + if( ( pxTCPPacket->xTCPHeader.ucTCPOffset & TCP_OFFSET_LENGTH_BITS ) > TCP_OFFSET_STANDARD_LENGTH ) + { + prvCheckOptions( pxSocket, pxNetworkBuffer ); + } + + + #if( ipconfigUSE_TCP_WIN == 1 ) + { + pxSocket->u.xTCP.ulWindowSize = FreeRTOS_ntohs( pxTCPPacket->xTCPHeader.usWindow ); + pxSocket->u.xTCP.ulWindowSize = + ( pxSocket->u.xTCP.ulWindowSize << pxSocket->u.xTCP.ucPeerWinScaleFactor ); + } + #endif + + /* In prvTCPHandleState() the incoming messages will be handled + depending on the current state of the connection. */ + if( prvTCPHandleState( pxSocket, &pxNetworkBuffer ) > 0 ) + { + /* prvTCPHandleState() has sent a message, see if there are more to + be transmitted. */ + #if( ipconfigUSE_TCP_WIN == 1 ) + { + prvTCPSendRepeated( pxSocket, &pxNetworkBuffer ); + } + #endif /* ipconfigUSE_TCP_WIN */ + } + + if( pxNetworkBuffer != NULL ) + { + /* We must check if the buffer is unequal to NULL, because the + socket might keep a reference to it in case a delayed ACK must be + sent. */ + vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer ); + pxNetworkBuffer = NULL; + } + + /* And finally, calculate when this socket wants to be woken up. */ + prvTCPNextTimeout ( pxSocket ); + /* Return pdPASS to tell that the network buffer is 'consumed'. */ + xResult = pdPASS; + } + + /* pdPASS being returned means the buffer has been consumed. */ + return xResult; +} +/*-----------------------------------------------------------*/ + +static FreeRTOS_Socket_t *prvHandleListen( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer ) +{ +TCPPacket_t * pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer ); +FreeRTOS_Socket_t *pxReturn = NULL; +uint32_t ulInitialSequenceNumber; + + /* 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 ); + + /* A pure SYN (without ACK) has come in, create a new socket to answer + it. */ + if( 0 != ulInitialSequenceNumber ) + { + 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 == 1 ? "" : "ren" ) ); + 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" ) ); + 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; + } + } + } + } + + if( ( 0 != ulInitialSequenceNumber ) && ( pxReturn != NULL ) ) + { + 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( pxTCPPacket->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. */ + memcpy( pxReturn->u.xTCP.xPacket.u.ucLastPacket, pxNetworkBuffer->pucEthernetBuffer, sizeof( pxReturn->u.xTCP.xPacket.u.ucLastPacket ) ); + } + return pxReturn; +} +/*-----------------------------------------------------------*/ + +/* + * Duplicates a socket after a listening socket receives a connection. + */ +static BaseType_t prvTCPSocketCopy( FreeRTOS_Socket_t *pxNewSocket, FreeRTOS_Socket_t *pxSocket ) +{ +struct freertos_sockaddr xAddress; + + 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 ) + { + pxNewSocket->pxSocketSet = pxSocket->pxSocketSet; + pxNewSocket->xSelectBits = pxSocket->xSelectBits | eSELECT_READ | 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 pdTRUE_UNSIGNED 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 + + 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" ) ); + vSocketClose( pxNewSocket ); + return pdFALSE; + } + + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +#if( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) + + const char *FreeRTOS_GetTCPStateName( UBaseType_t ulState ) + { + if( ulState >= ( UBaseType_t ) ARRAY_SIZE( pcStateNames ) ) + { + ulState = ( UBaseType_t ) ARRAY_SIZE( pcStateNames ) - 1u; + } + return pcStateNames[ ulState ]; + } + +#endif /* ( ( ipconfigHAS_DEBUG_PRINTF != 0 ) || ( ipconfigHAS_PRINTF != 0 ) ) */ +/*-----------------------------------------------------------*/ + +/* + * In the API accept(), the user asks is there is a new client? As API's can + * not walk through the xBoundTCPSocketsList the IP-task will do this. + */ +BaseType_t xTCPCheckNewClient( FreeRTOS_Socket_t *pxSocket ) +{ +TickType_t xLocalPort = FreeRTOS_htons( pxSocket->usLocalPort ); +ListItem_t *pxIterator; +FreeRTOS_Socket_t *pxFound; +BaseType_t xResult = pdFALSE; + + /* Here xBoundTCPSocketsList can be accessed safely IP-task is the only one + who has access. */ + for( pxIterator = ( ListItem_t * ) listGET_HEAD_ENTRY( &xBoundTCPSocketsList ); + pxIterator != ( ListItem_t * ) listGET_END_MARKER( &xBoundTCPSocketsList ); + pxIterator = ( ListItem_t * ) listGET_NEXT( pxIterator ) ) + { + if( listGET_LIST_ITEM_VALUE( pxIterator ) == xLocalPort ) + { + pxFound = ( FreeRTOS_Socket_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); + if( ( pxFound->ucProtocol == FREERTOS_IPPROTO_TCP ) && ( pxFound->u.xTCP.bits.bPassAccept != pdFALSE_UNSIGNED ) ) + { + pxSocket->u.xTCP.pxPeerSocket = pxFound; + FreeRTOS_debug_printf( ( "xTCPCheckNewClient[0]: client on port %u\n", pxSocket->usLocalPort ) ); + xResult = pdTRUE; + break; + } + } + } + return xResult; +} +/*-----------------------------------------------------------*/ + +#endif /* ipconfigUSE_TCP == 1 */ + +/* Provide access to private members for testing. */ +#ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS + #include "iot_freertos_tcp_test_access_tcp_define.h" +#endif + +/* Provide access to private members for verification. */ +#ifdef FREERTOS_TCP_ENABLE_VERIFICATION + #include "aws_freertos_tcp_verification_access_tcp_define.h" +#endif +