/*\r
- * FreeRTOS+TCP Labs Build 160919 (C) 2016 Real Time Engineers ltd.\r
- * Authors include Hein Tibosch and Richard Barry\r
+ * FreeRTOS+TCP V2.2.0\r
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
*\r
- *******************************************************************************\r
- ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
- *** ***\r
- *** ***\r
- *** FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP ***\r
- *** demos have a dependency on FreeRTOS+FAT, which is only in the Labs ***\r
- *** download): ***\r
- *** ***\r
- *** FreeRTOS+TCP is functional and has been used in commercial products ***\r
- *** for some time. Be aware however that we are still refining its ***\r
- *** design, the source code does not yet quite conform to the strict ***\r
- *** coding and style standards mandated by Real Time Engineers ltd., and ***\r
- *** the documentation and testing is not necessarily complete. ***\r
- *** ***\r
- *** PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE ***\r
- *** URL: http://www.FreeRTOS.org/contact Active early adopters may, at ***\r
- *** the sole discretion of Real Time Engineers Ltd., be offered versions ***\r
- *** under a license other than that described below. ***\r
- *** ***\r
- *** ***\r
- ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
- *******************************************************************************\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
*\r
- * FreeRTOS+TCP can be used under two different free open source licenses. The\r
- * license that applies is dependent on the processor on which FreeRTOS+TCP is\r
- * executed, as follows:\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
*\r
- * If FreeRTOS+TCP is executed on one of the processors listed under the Special\r
- * License Arrangements heading of the FreeRTOS+TCP license information web\r
- * page, then it can be used under the terms of the FreeRTOS Open Source\r
- * License. If FreeRTOS+TCP is used on any other processor, then it can be used\r
- * under the terms of the GNU General Public License V2. Links to the relevant\r
- * licenses follow:\r
- *\r
- * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license\r
- * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license\r
- * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt\r
- *\r
- * FreeRTOS+TCP is distributed in the hope that it will be useful. You cannot\r
- * use FreeRTOS+TCP unless you agree that you use the software 'as is'.\r
- * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied\r
- * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
- * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
- * implied, expressed, or statutory.\r
- *\r
- * 1 tab == 4 spaces!\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
*\r
+ * http://aws.amazon.com/freertos\r
* http://www.FreeRTOS.org\r
- * http://www.FreeRTOS.org/plus\r
- * http://www.FreeRTOS.org/labs\r
- *\r
*/\r
\r
/*\r
\r
/* This compile-time test was moved to here because some macro's\r
were unknown within 'FreeRTOSIPConfigDefaults.h'. It tests whether\r
-the defined MTU size can contain at ;east a complete TCP packet. */\r
+the defined MTU size can contain at least a complete TCP packet. */\r
\r
#if ( ( ipconfigTCP_MSS + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) > ipconfigNETWORK_MTU )\r
#error The ipconfigTCP_MSS setting in FreeRTOSIPConfig.h is too large.\r
*/\r
#define REDUCED_MSS_THROUGH_INTERNET ( 1400 )\r
\r
-/*\r
- * Each time a new TCP connection is being made, a new Initial Sequence Number shall be used.\r
- * The variable 'ulNextInitialSequenceNumber' will be incremented with a recommended value\r
- * of 0x102.\r
- */\r
-#define INITIAL_SEQUENCE_NUMBER_INCREMENT ( 0x102UL )\r
-\r
/*\r
* When there are no TCP options, the TCP offset equals 20 bytes, which is stored as\r
* the number 5 (words) in the higher niblle of the TCP-offset byte.\r
*/\r
static void prvCheckOptions( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer );\r
\r
+/*\r
+ * Identify and deal with a single TCP header option, advancing the pointer to\r
+ * the header. This function returns pdTRUE or pdFALSE depending on whether the\r
+ * caller should continue to parse more header options or break the loop.\r
+ */\r
+static BaseType_t prvSingleStepTCPHeaderOptions( const unsigned char ** const ppucPtr, const unsigned char ** const ppucLast, FreeRTOS_Socket_t ** const ppxSocket, TCPWindow_t ** const ppxTCPWindow);\r
+\r
+/*\r
+ * Skip past TCP header options when doing Selective ACK, until there are no\r
+ * more options left.\r
+ */\r
+static void prvSkipPastRemainingOptions( const unsigned char ** const ppucPtr, FreeRTOS_Socket_t ** const ppxSocket, unsigned char * const ppucLen );\r
+\r
/*\r
* Set the initial properties in the options fields, like the preferred\r
* value of MSS and whether SACK allowed. Will be transmitted in the state\r
*/\r
static BaseType_t prvTCPHandleFin( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer );\r
\r
-#if( ipconfigUSE_TCP_TIMESTAMPS == 1 )\r
- static UBaseType_t prvTCPSetTimeStamp( BaseType_t lOffset, FreeRTOS_Socket_t *pxSocket, TCPHeader_t *pxTCPHeader );\r
-#endif\r
-\r
/*\r
* Called from prvTCPHandleState(). Find the TCP payload data and check and\r
* return its length.\r
*/\r
static BaseType_t prvTCPHandleState( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t **ppxNetworkBuffer );\r
\r
+/*\r
+ * Common code for sending a TCP protocol control packet (i.e. no options, no\r
+ * payload, just flags).\r
+ */\r
+static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t *pxNetworkBuffer,\r
+ uint8_t ucTCPFlags );\r
+\r
+/*\r
+ * A "challenge ACK" is as per https://tools.ietf.org/html/rfc5961#section-3.2,\r
+ * case #3. In summary, an RST was received with a sequence number that is\r
+ * unexpected but still within the window.\r
+ */\r
+static BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t *pxNetworkBuffer );\r
+\r
/*\r
* Reply to a peer with the RST flag on, in case a packet can not be handled.\r
*/\r
static uint8_t prvWinScaleFactor( FreeRTOS_Socket_t *pxSocket );\r
#endif\r
\r
-/*-----------------------------------------------------------*/\r
-\r
-/* Initial Sequence Number, i.e. the next initial sequence number that will be\r
-used when a new connection is opened. The value should be randomized to prevent\r
-attacks from outside (spoofing). */\r
-uint32_t ulNextInitialSequenceNumber = 0ul;\r
+/*\r
+ * Generate a randomized TCP Initial Sequence Number per RFC.\r
+ */\r
+extern uint32_t ulApplicationGetNextSequenceNumber( uint32_t ulSourceAddress,\r
+ uint16_t usSourcePort,\r
+ uint32_t ulDestinationAddress,\r
+ uint16_t usDestinationPort );\r
\r
/*-----------------------------------------------------------*/\r
\r
if( ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) && ( pxSocket->u.xTCP.txStream != NULL ) )\r
{\r
/* The API FreeRTOS_send() might have added data to the TX stream. Add\r
- this data to the windowing system so it can be transmitted. */\r
+ this data to the windowing system to it can be transmitted. */\r
prvTCPAddTxData( pxSocket );\r
}\r
\r
- #if( ipconfigUSE_TCP_WIN == 1 )\r
+ #if ipconfigUSE_TCP_WIN == 1\r
{\r
if( pxSocket->u.xTCP.pxAckMessage != NULL )\r
{\r
\r
if( pxSocket->u.xTCP.ucTCPState != eCONNECT_SYN )\r
{\r
- /* The connection is in a state other than SYN. */\r
+ /* The connection is in s state other than SYN. */\r
pxNetworkBuffer = NULL;\r
\r
/* prvTCPSendRepeated() will only create a network buffer if necessary,\r
the Ethernet address of the peer or the gateway is found. */\r
pxTCPPacket = ( TCPPacket_t * )pxSocket->u.xTCP.xPacket.u.ucLastPacket;\r
\r
- #if( ipconfigUSE_TCP_TIMESTAMPS == 1 )\r
- {\r
- /* When TCP time stamps are enabled, but they will only be applied\r
- if the peer is outside the netmask, usually on the internet.\r
- Packages sent on a LAN are usually too big to carry time stamps. */\r
- if( ( ( pxSocket->u.xTCP.ulRemoteIP ^ FreeRTOS_ntohl( *ipLOCAL_IP_ADDRESS_POINTER ) ) & xNetworkAddressing.ulNetMask ) != 0ul )\r
- {\r
- pxSocket->u.xTCP.xTCPWindow.u.bits.bTimeStamps = pdTRUE_UNSIGNED;\r
- }\r
- }\r
- #endif\r
-\r
/* About to send a SYN packet. Call prvSetSynAckOptions() to set\r
the proper options: The size of MSS and whether SACK's are\r
allowed. */\r
\r
if( pxNetworkBuffer == NULL )\r
{\r
- memset( &xTempBuffer, '\0', sizeof( xTempBuffer ) );\r
pxNetworkBuffer = &xTempBuffer;\r
\r
+ #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )\r
+ {\r
+ xTempBuffer.pxNextBuffer = NULL;\r
+ }\r
+ #endif\r
xTempBuffer.pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket;\r
xTempBuffer.xDataLength = sizeof( pxSocket->u.xTCP.xPacket.u.ucLastPacket );\r
+ xReleaseAfterSend = pdFALSE;\r
}\r
\r
#if( ipconfigZERO_COPY_TX_DRIVER != 0 )\r
}\r
\r
/* Take the minimum of the RX buffer space and the RX window size. */\r
- ulSpace = FreeRTOS_min_uint32( pxSocket->u.xTCP.ulRxCurWinSize, pxTCPWindow->xSize.ulRxWindowLength );\r
+ ulSpace = FreeRTOS_min_uint32( pxTCPWindow->xSize.ulRxWindowLength, ulFrontSpace );\r
\r
if( ( pxSocket->u.xTCP.bits.bLowWater != pdFALSE_UNSIGNED ) || ( pxSocket->u.xTCP.bits.bRxStopped != pdFALSE_UNSIGNED ) )\r
{\r
vFlip_32( pxTCPPacket->xTCPHeader.ulSequenceNumber, pxTCPPacket->xTCPHeader.ulAckNr );\r
}\r
\r
- pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE;\r
- pxIPHeader->usLength = FreeRTOS_htons( ulLen );\r
+ pxIPHeader->ucTimeToLive = ( uint8_t ) ipconfigTCP_TIME_TO_LIVE;\r
+ pxIPHeader->usLength = FreeRTOS_htons( ulLen );\r
if( ( pxSocket == NULL ) || ( *ipLOCAL_IP_ADDRESS_POINTER == 0ul ) )\r
{\r
/* When pxSocket is NULL, this function is called by prvTCPSendReset()\r
{\r
/* calculate the IP header checksum, in case the driver won't do that. */\r
pxIPHeader->usHeaderChecksum = 0x00u;\r
- pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0u, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );\r
+ pxIPHeader->usHeaderChecksum = usGenerateChecksum( 0UL, ( uint8_t * ) &( pxIPHeader->ucVersionHeaderLength ), ipSIZE_OF_IPv4_HEADER );\r
pxIPHeader->usHeaderChecksum = ~FreeRTOS_htons( pxIPHeader->usHeaderChecksum );\r
\r
/* calculate the TCP checksum for an outgoing packet. */\r
- usGenerateProtocolChecksum( (uint8_t*)pxTCPPacket, pdTRUE );\r
+ usGenerateProtocolChecksum( (uint8_t*)pxTCPPacket, pxNetworkBuffer->xDataLength, pdTRUE );\r
\r
/* A calculated checksum of 0 must be inverted as 0 means the checksum\r
is disabled. */\r
}\r
#endif\r
\r
- #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )\r
- {\r
- pxNetworkBuffer->pxNextBuffer = NULL;\r
- }\r
- #endif\r
+ #if( ipconfigUSE_LINKED_RX_MESSAGES != 0 )\r
+ pxNetworkBuffer->pxNextBuffer = NULL;\r
+ #endif\r
\r
/* Important: tell NIC driver how many bytes must be sent. */\r
pxNetworkBuffer->xDataLength = ulLen + ipSIZE_OF_ETH_HEADER;\r
sizeof( pxEthernetHeader->xDestinationAddress ) );\r
\r
/* The source MAC addresses is fixed to 'ipLOCAL_MAC_ADDRESS'. */\r
- memcpy( ( void * ) &( pxEthernetHeader->xSourceAddress ), ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES );\r
+ memcpy( ( void * ) &( pxEthernetHeader->xSourceAddress) , ( void * ) ipLOCAL_MAC_ADDRESS, ( size_t ) ipMAC_ADDRESS_LENGTH_BYTES );\r
\r
#if defined( ipconfigETHERNET_MINIMUM_PACKET_BYTES )\r
{\r
uint32_t ulRemoteIP;\r
MACAddress_t xEthAddress;\r
BaseType_t xReturn = pdTRUE;\r
+uint32_t ulInitialSequenceNumber = 0;\r
\r
#if( ipconfigHAS_PRINTF != 0 )\r
{\r
\r
switch( eReturned )\r
{\r
- case eARPCacheHit: /* An ARP table lookup found a valid entry. */\r
- break; /* We can now prepare the SYN packet. */\r
- case eARPCacheMiss: /* An ARP table lookup did not find a valid entry. */\r
- case eCantSendPacket: /* There is no IP address, or an ARP is still in progress. */\r
- default:\r
- /* Count the number of times it couldn't find the ARP address. */\r
- pxSocket->u.xTCP.ucRepCount++;\r
- \r
- FreeRTOS_debug_printf( ( "ARP for %lxip (using %lxip): rc=%d %02X:%02X:%02X %02X:%02X:%02X\n",\r
- pxSocket->u.xTCP.ulRemoteIP,\r
- FreeRTOS_htonl( ulRemoteIP ),\r
- eReturned,\r
- xEthAddress.ucBytes[ 0 ],\r
- xEthAddress.ucBytes[ 1 ],\r
- xEthAddress.ucBytes[ 2 ],\r
- xEthAddress.ucBytes[ 3 ],\r
- xEthAddress.ucBytes[ 4 ],\r
- xEthAddress.ucBytes[ 5 ] ) );\r
- \r
- /* And issue a (new) ARP request */\r
- FreeRTOS_OutputARPRequest( ulRemoteIP );\r
- \r
+ case eARPCacheHit: /* An ARP table lookup found a valid entry. */\r
+ break; /* We can now prepare the SYN packet. */\r
+ case eARPCacheMiss: /* An ARP table lookup did not find a valid entry. */\r
+ case eCantSendPacket: /* There is no IP address, or an ARP is still in progress. */\r
+ default:\r
+ /* Count the number of times it couldn't find the ARP address. */\r
+ pxSocket->u.xTCP.ucRepCount++;\r
+\r
+ FreeRTOS_debug_printf( ( "ARP for %lxip (using %lxip): rc=%d %02X:%02X:%02X %02X:%02X:%02X\n",\r
+ pxSocket->u.xTCP.ulRemoteIP,\r
+ FreeRTOS_htonl( ulRemoteIP ),\r
+ eReturned,\r
+ xEthAddress.ucBytes[ 0 ],\r
+ xEthAddress.ucBytes[ 1 ],\r
+ xEthAddress.ucBytes[ 2 ],\r
+ xEthAddress.ucBytes[ 3 ],\r
+ xEthAddress.ucBytes[ 4 ],\r
+ xEthAddress.ucBytes[ 5 ] ) );\r
+\r
+ /* And issue a (new) ARP request */\r
+ FreeRTOS_OutputARPRequest( ulRemoteIP );\r
+\r
+ xReturn = pdFALSE;\r
+ }\r
+\r
+ if( xReturn != pdFALSE )\r
+ {\r
+ /* Get a difficult-to-predict initial sequence number for this 4-tuple. */\r
+ ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( *ipLOCAL_IP_ADDRESS_POINTER,\r
+ pxSocket->usLocalPort,\r
+ pxSocket->u.xTCP.ulRemoteIP,\r
+ pxSocket->u.xTCP.usRemotePort );\r
+\r
+ /* Check for a random number generation error. */\r
+ if( 0 == ulInitialSequenceNumber )\r
+ {\r
xReturn = pdFALSE;\r
- break;\r
+ }\r
}\r
\r
if( xReturn != pdFALSE )\r
pxTCPPacket = ( TCPPacket_t * )pxSocket->u.xTCP.xPacket.u.ucLastPacket;\r
pxIPHeader = &pxTCPPacket->xIPHeader;\r
\r
- /* Reset the retry counter to zero... */\r
+ /* reset the retry counter to zero. */\r
pxSocket->u.xTCP.ucRepCount = 0u;\r
\r
- /* ...and remember that the connect/SYN data are prepared. */\r
+ /* And remember that the connect/SYN data are prepared. */\r
pxSocket->u.xTCP.bits.bConnPrepared = pdTRUE_UNSIGNED;\r
\r
/* Now that the Ethernet address is known, the initial packet can be\r
pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = 0ul;\r
\r
/* Start with ISN (Initial Sequence Number). */\r
- pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulNextInitialSequenceNumber;\r
-\r
- /* And increment it with 268 for the next new connection, which is\r
- recommended value. */\r
- ulNextInitialSequenceNumber += 0x102UL;\r
+ pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber;\r
\r
/* The TCP header size is 20 bytes, divided by 4 equals 5, which is put in\r
the high nibble of the TCP offset field. */\r
/* Set the values of usInitMSS / usCurMSS for this socket. */\r
prvSocketSetMSS( pxSocket );\r
\r
- /* For now this is also the advertised window size. */\r
- pxSocket->u.xTCP.ulRxCurWinSize = pxSocket->u.xTCP.usInitMSS;\r
-\r
/* The initial sequence numbers at our side are known. Later\r
vTCPWindowInit() will be called to fill in the peer's sequence numbers, but\r
first wait for a SYN+ACK reply. */\r
const unsigned char *pucPtr;\r
const unsigned char *pucLast;\r
TCPWindow_t *pxTCPWindow;\r
-UBaseType_t uxNewMSS;\r
+BaseType_t xShouldContinueLoop;\r
\r
pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer );\r
pxTCPHeader = &pxTCPPacket->xTCPHeader;\r
pucLast = pucPtr + (((pxTCPHeader->ucTCPOffset >> 4) - 5) << 2);\r
pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;\r
\r
+ /* Validate options size calculation. */\r
+ if( pucLast > ( pxNetworkBuffer->pucEthernetBuffer + pxNetworkBuffer->xDataLength ) )\r
+ {\r
+ return;\r
+ }\r
+\r
/* The comparison with pucLast is only necessary in case the option data are\r
corrupted, we don't like to run into invalid memory and crash. */\r
- while( pucPtr < pucLast )\r
+ xShouldContinueLoop = pdTRUE;\r
+ while( ( pucPtr < pucLast ) && ( xShouldContinueLoop == pdTRUE ) )\r
{\r
- if( pucPtr[ 0 ] == TCP_OPT_END )\r
- {\r
- /* End of options. */\r
- return;\r
- }\r
- if( pucPtr[ 0 ] == TCP_OPT_NOOP)\r
- {\r
- pucPtr++;\r
+ xShouldContinueLoop = prvSingleStepTCPHeaderOptions( &pucPtr, &pucLast, &pxSocket, &pxTCPWindow );\r
+ }\r
+}\r
\r
- /* NOP option, inserted to make the length a multiple of 4. */\r
- }\r
+/*-----------------------------------------------------------*/\r
+\r
+static BaseType_t prvSingleStepTCPHeaderOptions( const unsigned char ** const ppucPtr, const unsigned char ** const ppucLast, FreeRTOS_Socket_t ** const ppxSocket, TCPWindow_t ** const ppxTCPWindow)\r
+{\r
+ UBaseType_t uxNewMSS;\r
+ UBaseType_t xRemainingOptionsBytes = ( *ppucLast ) - ( *ppucPtr );\r
+ unsigned char ucLen;\r
+\r
+ if( ( *ppucPtr )[ 0 ] == TCP_OPT_END )\r
+ {\r
+ /* End of options. */\r
+ return pdFALSE;\r
+ }\r
+ if( ( *ppucPtr )[ 0 ] == TCP_OPT_NOOP)\r
+ {\r
+ /* NOP option, inserted to make the length a multiple of 4. */\r
+ ( *ppucPtr )++;\r
+ return pdTRUE;\r
+ }\r
+\r
+ /* Any other well-formed option must be at least two bytes: the option\r
+ type byte followed by a length byte. */\r
+ if( xRemainingOptionsBytes < 2 )\r
+ {\r
+ return pdFALSE;\r
+ }\r
#if( ipconfigUSE_TCP_WIN != 0 )\r
- else if( ( pucPtr[ 0 ] == TCP_OPT_WSOPT ) && ( pucPtr[ 1 ] == TCP_OPT_WSOPT_LEN ) )\r
+ else if( ( *ppucPtr )[ 0 ] == TCP_OPT_WSOPT )\r
+ {\r
+ /* Confirm that the option fits in the remaining buffer space. */\r
+ if( ( xRemainingOptionsBytes < TCP_OPT_WSOPT_LEN ) || ( ( *ppucPtr )[ 1 ] != TCP_OPT_WSOPT_LEN ) )\r
{\r
- pxSocket->u.xTCP.ucPeerWinScaleFactor = pucPtr[ 2 ];\r
- pxSocket->u.xTCP.bits.bWinScaling = pdTRUE_UNSIGNED;\r
- pucPtr += TCP_OPT_WSOPT_LEN;\r
+ return pdFALSE;\r
}\r
+\r
+ ( *ppxSocket )->u.xTCP.ucPeerWinScaleFactor = ( *ppucPtr )[ 2 ];\r
+ ( *ppxSocket )->u.xTCP.bits.bWinScaling = pdTRUE_UNSIGNED;\r
+ ( *ppucPtr ) += TCP_OPT_WSOPT_LEN;\r
+ }\r
#endif /* ipconfigUSE_TCP_WIN */\r
- else if( ( pucPtr[ 0 ] == TCP_OPT_MSS ) && ( pucPtr[ 1 ] == TCP_OPT_MSS_LEN ) )\r
+ else if( ( *ppucPtr )[ 0 ] == TCP_OPT_MSS )\r
+ {\r
+ /* Confirm that the option fits in the remaining buffer space. */\r
+ if( ( xRemainingOptionsBytes < TCP_OPT_MSS_LEN )|| ( ( *ppucPtr )[ 1 ] != TCP_OPT_MSS_LEN ) )\r
{\r
- /* An MSS option with the correct option length. FreeRTOS_htons()\r
- is not needed here because usChar2u16() already returns a host\r
- endian number. */\r
- uxNewMSS = usChar2u16( pucPtr + 2 );\r
+ return pdFALSE;\r
+ }\r
\r
- if( pxSocket->u.xTCP.usInitMSS != uxNewMSS )\r
- {\r
- FreeRTOS_debug_printf( ( "MSS change %u -> %lu\n", pxSocket->u.xTCP.usInitMSS, uxNewMSS ) );\r
- }\r
+ /* An MSS option with the correct option length. FreeRTOS_htons()\r
+ is not needed here because usChar2u16() already returns a host\r
+ endian number. */\r
+ uxNewMSS = usChar2u16( ( *ppucPtr ) + 2 );\r
\r
- if( pxSocket->u.xTCP.usInitMSS > uxNewMSS )\r
+ if( ( *ppxSocket )->u.xTCP.usInitMSS != uxNewMSS )\r
+ {\r
+ /* Perform a basic check on the the new MSS. */\r
+ if( uxNewMSS == 0 )\r
{\r
- /* our MSS was bigger than the MSS of the other party: adapt it. */\r
- pxSocket->u.xTCP.bits.bMssChange = pdTRUE_UNSIGNED;\r
- if( ( pxTCPWindow != NULL ) && ( pxSocket->u.xTCP.usCurMSS > uxNewMSS ) )\r
- {\r
- /* The peer advertises a smaller MSS than this socket was\r
- using. Use that as well. */\r
- FreeRTOS_debug_printf( ( "Change mss %d => %lu\n", pxSocket->u.xTCP.usCurMSS, uxNewMSS ) );\r
- pxSocket->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS;\r
- }\r
- pxTCPWindow->xSize.ulRxWindowLength = ( ( uint32_t ) uxNewMSS ) * ( pxTCPWindow->xSize.ulRxWindowLength / ( ( uint32_t ) uxNewMSS ) );\r
- pxTCPWindow->usMSSInit = ( uint16_t ) uxNewMSS;\r
- pxTCPWindow->usMSS = ( uint16_t ) uxNewMSS;\r
- pxSocket->u.xTCP.usInitMSS = ( uint16_t ) uxNewMSS;\r
- pxSocket->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS;\r
+ return pdFALSE;\r
}\r
\r
- #if( ipconfigUSE_TCP_WIN != 1 )\r
- /* Without scaled windows, MSS is the only interesting option. */\r
- break;\r
- #else\r
- /* Or else we continue to check another option: selective ACK. */\r
- pucPtr += TCP_OPT_MSS_LEN;\r
- #endif /* ipconfigUSE_TCP_WIN != 1 */\r
+ FreeRTOS_debug_printf( ( "MSS change %u -> %lu\n", ( *ppxSocket )->u.xTCP.usInitMSS, uxNewMSS ) );\r
}\r
- else\r
+\r
+ if( ( *ppxSocket )->u.xTCP.usInitMSS > uxNewMSS )\r
{\r
- /* All other options have a length field, so that we easily\r
- can skip past them. */\r
- int len = ( int )pucPtr[ 1 ];\r
- if( len == 0 )\r
+ /* our MSS was bigger than the MSS of the other party: adapt it. */\r
+ ( *ppxSocket )->u.xTCP.bits.bMssChange = pdTRUE_UNSIGNED;\r
+ if( ( ( *ppxTCPWindow ) != NULL ) && ( ( *ppxSocket )->u.xTCP.usCurMSS > uxNewMSS ) )\r
{\r
- /* If the length field is zero, the options are malformed\r
- and we don't process them further. */\r
- break;\r
+ /* The peer advertises a smaller MSS than this socket was\r
+ using. Use that as well. */\r
+ FreeRTOS_debug_printf( ( "Change mss %d => %lu\n", ( *ppxSocket )->u.xTCP.usCurMSS, uxNewMSS ) );\r
+ ( *ppxSocket )->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS;\r
}\r
+ ( *ppxTCPWindow )->xSize.ulRxWindowLength = ( ( uint32_t ) uxNewMSS ) * ( ( *ppxTCPWindow )->xSize.ulRxWindowLength / ( ( uint32_t ) uxNewMSS ) );\r
+ ( *ppxTCPWindow )->usMSSInit = ( uint16_t ) uxNewMSS;\r
+ ( *ppxTCPWindow )->usMSS = ( uint16_t ) uxNewMSS;\r
+ ( *ppxSocket )->u.xTCP.usInitMSS = ( uint16_t ) uxNewMSS;\r
+ ( *ppxSocket )->u.xTCP.usCurMSS = ( uint16_t ) uxNewMSS;\r
+ }\r
\r
- #if( ipconfigUSE_TCP_WIN == 1 )\r
+ #if( ipconfigUSE_TCP_WIN != 1 )\r
+ /* Without scaled windows, MSS is the only interesting option. */\r
+ return pdFALSE;\r
+ #else\r
+ /* Or else we continue to check another option: selective ACK. */\r
+ ( *ppucPtr ) += TCP_OPT_MSS_LEN;\r
+ #endif /* ipconfigUSE_TCP_WIN != 1 */\r
+ }\r
+ else\r
+ {\r
+ /* All other options have a length field, so that we easily\r
+ can skip past them. */\r
+ ucLen = ( *ppucPtr )[ 1 ];\r
+ if( ( ucLen < 2 ) || ( ucLen > xRemainingOptionsBytes ) )\r
+ {\r
+ /* If the length field is too small or too big, the options are\r
+ * malformed, don't process them further.\r
+ */\r
+ return pdFALSE;\r
+ }\r
+\r
+ #if( ipconfigUSE_TCP_WIN == 1 )\r
+ {\r
+ /* Selective ACK: the peer has received a packet but it is missing\r
+ * earlier packets. At least this packet does not need retransmission\r
+ * anymore. ulTCPWindowTxSack( ) takes care of this administration.\r
+ */\r
+ if( ( *ppucPtr )[0] == TCP_OPT_SACK_A )\r
{\r
- /* Selective ACK: the peer has received a packet but it is missing earlier\r
- packets. At least this packet does not need retransmission anymore\r
- ulTCPWindowTxSack( ) takes care of this administration. */\r
- if( pucPtr[0] == TCP_OPT_SACK_A )\r
- {\r
- len -= 2;\r
- pucPtr += 2;\r
+ ucLen -= 2;\r
+ ( *ppucPtr ) += 2;\r
\r
- while( len >= 8 )\r
- {\r
- uint32_t ulFirst = ulChar2u32( pucPtr );\r
- uint32_t ulLast = ulChar2u32( pucPtr + 4 );\r
- uint32_t ulCount = ulTCPWindowTxSack( &pxSocket->u.xTCP.xTCPWindow, ulFirst, ulLast );\r
- /* ulTCPWindowTxSack( ) returns the number of bytes which have been acked\r
- starting from the head position.\r
- Advance the tail pointer in txStream. */\r
- if( ( pxSocket->u.xTCP.txStream != NULL ) && ( ulCount > 0 ) )\r
- {\r
- /* Just advancing the tail index, 'ulCount' bytes have been confirmed. */\r
- uxStreamBufferGet( pxSocket->u.xTCP.txStream, 0, NULL, ( size_t ) ulCount, pdFALSE );\r
- pxSocket->xEventBits |= eSOCKET_SEND;\r
-\r
- #if ipconfigSUPPORT_SELECT_FUNCTION == 1\r
- {\r
- if( pxSocket->xSelectBits & eSELECT_WRITE )\r
- {\r
- /* The field 'xEventBits' is used to store regular socket events (at most 8),\r
- as well as 'select events', which will be left-shifted */\r
- pxSocket->xEventBits |= ( eSELECT_WRITE << SOCKET_EVENT_BIT_COUNT );\r
- }\r
- }\r
- #endif\r
-\r
- /* In case the socket owner has installed an OnSent handler,\r
- call it now. */\r
- #if( ipconfigUSE_CALLBACKS == 1 )\r
- {\r
- if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleSent ) )\r
- {\r
- pxSocket->u.xTCP.pxHandleSent( (Socket_t *)pxSocket, ulCount );\r
- }\r
- }\r
- #endif /* ipconfigUSE_CALLBACKS == 1 */\r
- }\r
- pucPtr += 8;\r
- len -= 8;\r
- }\r
- /* len should be 0 by now. */\r
+ while( ucLen >= 8 )\r
+ {\r
+ prvSkipPastRemainingOptions( ppucPtr, ppxSocket, &ucLen );\r
}\r
- #if ipconfigUSE_TCP_TIMESTAMPS == 1\r
- else if( pucPtr[0] == TCP_OPT_TIMESTAMP )\r
- {\r
- len -= 2; /* Skip option and length byte. */\r
- pucPtr += 2;\r
- pxSocket->u.xTCP.xTCPWindow.u.bits.bTimeStamps = pdTRUE_UNSIGNED;\r
- pxSocket->u.xTCP.xTCPWindow.rx.ulTimeStamp = ulChar2u32( pucPtr );\r
- pxSocket->u.xTCP.xTCPWindow.tx.ulTimeStamp = ulChar2u32( pucPtr + 4 );\r
- }\r
- #endif /* ipconfigUSE_TCP_TIMESTAMPS == 1 */\r
+ /* ucLen should be 0 by now. */\r
+ }\r
+ }\r
+ #endif /* ipconfigUSE_TCP_WIN == 1 */\r
+\r
+ ( *ppucPtr ) += ucLen;\r
+ }\r
+ return pdTRUE;\r
+}\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvSkipPastRemainingOptions( const unsigned char ** const ppucPtr, FreeRTOS_Socket_t ** const ppxSocket, unsigned char * const pucLen )\r
+{\r
+uint32_t ulFirst = ulChar2u32( ( *ppucPtr ) );\r
+uint32_t ulLast = ulChar2u32( ( *ppucPtr ) + 4 );\r
+uint32_t ulCount = ulTCPWindowTxSack( &( *ppxSocket )->u.xTCP.xTCPWindow, ulFirst, ulLast );\r
+ /* ulTCPWindowTxSack( ) returns the number of bytes which have been acked\r
+ * starting from the head position. Advance the tail pointer in txStream.\r
+ */\r
+ if( ( ( *ppxSocket )->u.xTCP.txStream != NULL ) && ( ulCount > 0 ) )\r
+ {\r
+ /* Just advancing the tail index, 'ulCount' bytes have been confirmed. */\r
+ uxStreamBufferGet( ( *ppxSocket )->u.xTCP.txStream, 0, NULL, ( size_t ) ulCount, pdFALSE );\r
+ ( *ppxSocket )->xEventBits |= eSOCKET_SEND;\r
+\r
+ #if ipconfigSUPPORT_SELECT_FUNCTION == 1\r
+ {\r
+ if( ( *ppxSocket )->xSelectBits & eSELECT_WRITE )\r
+ {\r
+ /* The field 'xEventBits' is used to store regular socket events\r
+ * (at most 8), as well as 'select events', which will be left-shifted.\r
+ */\r
+ ( *ppxSocket )->xEventBits |= ( eSELECT_WRITE << SOCKET_EVENT_BIT_COUNT );\r
}\r
- #endif /* ipconfigUSE_TCP_WIN == 1 */\r
+ }\r
+ #endif\r
\r
- pucPtr += len;\r
+ /* In case the socket owner has installed an OnSent handler, call it now.\r
+ */\r
+ #if( ipconfigUSE_CALLBACKS == 1 )\r
+ {\r
+ if( ipconfigIS_VALID_PROG_ADDRESS( ( *ppxSocket )->u.xTCP.pxHandleSent ) )\r
+ {\r
+ ( *ppxSocket )->u.xTCP.pxHandleSent( (Socket_t )( *ppxSocket ), ulCount );\r
+ }\r
}\r
+ #endif /* ipconfigUSE_CALLBACKS == 1 */\r
}\r
+ ( *ppucPtr ) += 8;\r
+ ( *pucLen ) -= 8;\r
}\r
+\r
/*-----------------------------------------------------------*/\r
\r
#if( ipconfigUSE_TCP_WIN != 0 )\r
}\r
#else\r
{\r
- #if( ipconfigUSE_TCP_TIMESTAMPS == 1 )\r
- if( pxSocket->u.xTCP.xTCPWindow.u.bits.bTimeStamps )\r
- {\r
- uxOptionsLength += prvTCPSetTimeStamp( uxOptionsLength, pxSocket, &pxTCPPacket->xTCPHeader );\r
- pxTCPHeader->ucOptdata[ uxOptionsLength + 0 ] = TCP_OPT_SACK_P; /* 4: Sack-Permitted Option. */\r
- pxTCPHeader->ucOptdata[ uxOptionsLength + 1 ] = 2u;\r
- uxOptionsLength += 2u;\r
- }\r
- else\r
- #endif\r
- {\r
- pxTCPHeader->ucOptdata[ uxOptionsLength + 0 ] = TCP_OPT_NOOP;\r
- pxTCPHeader->ucOptdata[ uxOptionsLength + 1 ] = TCP_OPT_NOOP;\r
- pxTCPHeader->ucOptdata[ uxOptionsLength + 2 ] = TCP_OPT_SACK_P; /* 4: Sack-Permitted Option. */\r
- pxTCPHeader->ucOptdata[ uxOptionsLength + 3 ] = 2; /* 2: length of this option. */\r
- uxOptionsLength += 4u;\r
- }\r
+ pxTCPHeader->ucOptdata[ uxOptionsLength + 0 ] = TCP_OPT_NOOP;\r
+ pxTCPHeader->ucOptdata[ uxOptionsLength + 1 ] = TCP_OPT_NOOP;\r
+ pxTCPHeader->ucOptdata[ uxOptionsLength + 2 ] = TCP_OPT_SACK_P; /* 4: Sack-Permitted Option. */\r
+ pxTCPHeader->ucOptdata[ uxOptionsLength + 3 ] = 2; /* 2: length of this option. */\r
+ uxOptionsLength += 4u;\r
+\r
return uxOptionsLength; /* bytes, not words. */\r
}\r
#endif /* ipconfigUSE_TCP_WIN == 0 */\r
/* Fill in the new state. */\r
pxSocket->u.xTCP.ucTCPState = ( uint8_t ) eTCPState;\r
\r
- /* Touch the alive timers because moving to another state. */\r
+ /* touch the alive timers because moving to another state. */\r
prvTCPTouchSocket( pxSocket );\r
\r
#if( ipconfigHAS_DEBUG_PRINTF == 1 )\r
if( xConnected != NULL )\r
{\r
/* The 'connected' state has changed, call the OnConnect handler of the parent. */\r
- xConnected->u.xTCP.pxHandleConnected( ( Socket_t * ) xConnected, bAfter );\r
+ xConnected->u.xTCP.pxHandleConnected( ( Socket_t ) xConnected, bAfter );\r
}\r
}\r
#endif\r
( int32_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength ) + lDataLen );\r
/* In case we were called from a TCP timer event, a buffer must be\r
created. Otherwise, test 'xDataLength' of the provided buffer. */\r
- if( ( pxNetworkBuffer == NULL ) || ( pxNetworkBuffer->xDataLength < (size_t)lNeeded ) )\r
- {\r
- xResize = pdTRUE;\r
- }\r
- else\r
- {\r
- xResize = pdFALSE;\r
- }\r
+ xResize = ( pxNetworkBuffer == NULL ) || ( pxNetworkBuffer->xDataLength < (size_t)lNeeded );\r
}\r
\r
if( xResize != pdFALSE )\r
\r
if( pxReturn != NULL )\r
{\r
+ /* Set the actual packet size, in case the returned buffer is larger. */\r
+ pxReturn->xDataLength = lNeeded;\r
+\r
/* Copy the existing data to the new created buffer. */\r
if( pxNetworkBuffer )\r
{\r
pucEthernetBuffer = pxSocket->u.xTCP.xPacket.u.ucLastPacket;\r
}\r
\r
- pxTCPPacket = ( TCPPacket_t * ) pucEthernetBuffer;\r
- pxTCPWindow = &( pxSocket->u.xTCP.xTCPWindow );\r
+ pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer );\r
+ pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;\r
lDataLen = 0;\r
lStreamPos = 0;\r
pxTCPPacket->xTCPHeader.ucTCPFlags |= ipTCP_FLAG_ACK;\r
pxTCPPacket->xTCPHeader.ucTCPFlags |= ( uint8_t ) ipTCP_FLAG_PSH;\r
}\r
\r
- #if ipconfigUSE_TCP_TIMESTAMPS == 1\r
- {\r
- if( uxOptionsLength == 0u )\r
- {\r
- if( pxSocket->u.xTCP.xTCPWindow.u.bits.bTimeStamps )\r
- {\r
- TCPPacket_t * pxTCPPacket = ( TCPPacket_t * ) ( pucEthernetBuffer );\r
- uxOptionsLength = prvTCPSetTimeStamp( 0, pxSocket, &pxTCPPacket->xTCPHeader );\r
- }\r
- }\r
- }\r
- #endif\r
-\r
lDataLen += ( int32_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + uxOptionsLength );\r
}\r
\r
/* A txStream has been created already, see if the socket has new data for\r
the sliding window.\r
\r
- uxStreamBufferMidSpace() returns the distance between rxHead and rxMid. It\r
- contains new Tx data which has not been passed to the sliding window yet.\r
- The oldest data not-yet-confirmed can be found at rxTail. */\r
+ uxStreamBufferMidSpace() returns the distance between rxHead and rxMid. It contains new\r
+ Tx data which has not been passed to the sliding window yet. The oldest\r
+ data not-yet-confirmed can be found at rxTail. */\r
lLength = ( int32_t ) uxStreamBufferMidSpace( pxSocket->u.xTCP.txStream );\r
\r
if( lLength > 0 )\r
}\r
/*-----------------------------------------------------------*/\r
\r
-#if ipconfigUSE_TCP_TIMESTAMPS == 1\r
-\r
- static UBaseType_t prvTCPSetTimeStamp( BaseType_t lOffset, FreeRTOS_Socket_t *pxSocket, TCPHeader_t *pxTCPHeader )\r
- {\r
- uint32_t ulTimes[2];\r
- uint8_t *ucOptdata = &( pxTCPHeader->ucOptdata[ lOffset ] );\r
-\r
- ulTimes[0] = ( xTaskGetTickCount ( ) * 1000u ) / configTICK_RATE_HZ;\r
- ulTimes[0] = FreeRTOS_htonl( ulTimes[0] );\r
- ulTimes[1] = FreeRTOS_htonl( pxSocket->u.xTCP.xTCPWindow.rx.ulTimeStamp );\r
- ucOptdata[0] = ( uint8_t ) TCP_OPT_TIMESTAMP;\r
- ucOptdata[1] = ( uint8_t ) TCP_OPT_TIMESTAMP_LEN;\r
- memcpy( &(ucOptdata[2] ), ulTimes, 8u );\r
- ucOptdata[10] = ( uint8_t ) TCP_OPT_NOOP;\r
- ucOptdata[11] = ( uint8_t ) TCP_OPT_NOOP;\r
- /* Do not return the same timestamps 2 times. */\r
- pxSocket->u.xTCP.xTCPWindow.rx.ulTimeStamp = 0ul;\r
- return 12u;\r
- }\r
-\r
-#endif\r
-/*-----------------------------------------------------------*/\r
-\r
/*\r
* prvCheckRxData(): called from prvTCPHandleState()\r
*\r
The size of the TCP header is given in a multiple of 4-byte words (single\r
byte, needs no ntoh() translation). A shift-right 2: is the same as\r
(offset >> 4) * 4. */\r
- lTCPHeaderLength = ( BaseType_t ) ( ( pxTCPHeader->ucTCPOffset & VALID_BITS_IN_TCP_OFFSET_BYTE ) >> 2 );\r
+ lTCPHeaderLength = ( BaseType_t ) ( ( pxTCPHeader->ucTCPOffset & VALID_BITS_IN_TCP_OFFSET_BYTE ) >> 2 );\r
\r
/* Let pucRecvData point to the first byte received. */\r
*ppucRecvData = pxNetworkBuffer->pucEthernetBuffer + ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + lTCPHeaderLength;\r
pxTCPHeader->ucTCPOffset = ( uint8_t )( ( ipSIZE_OF_TCP_HEADER + uxOptionsLength ) << 2 );\r
}\r
\r
- #if( ipconfigUSE_TCP_TIMESTAMPS == 1 )\r
- {\r
- if( pxSocket->u.xTCP.xTCPWindow.u.bits.bTimeStamps )\r
- {\r
- uxOptionsLength += prvTCPSetTimeStamp( uxOptionsLength, pxSocket, pxTCPHeader );\r
- }\r
- }\r
- #endif /* ipconfigUSE_TCP_TIMESTAMPS == 1 */\r
-\r
return uxOptionsLength;\r
}\r
/*-----------------------------------------------------------*/\r
{\r
if( ipconfigIS_VALID_PROG_ADDRESS( pxSocket->u.xTCP.pxHandleSent ) )\r
{\r
- pxSocket->u.xTCP.pxHandleSent( (Socket_t *)pxSocket, ulCount );\r
+ pxSocket->u.xTCP.pxHandleSent( ( Socket_t )pxSocket, ulCount );\r
}\r
}\r
#endif /* ipconfigUSE_CALLBACKS == 1 */\r
{\r
/* xTCPWindowTxDone returns true when all Tx queues are empty. */\r
bRxComplete = xTCPWindowRxEmpty( pxTCPWindow );\r
- bTxDone = xTCPWindowTxDone( pxTCPWindow );\r
+ bTxDone = xTCPWindowTxDone( pxTCPWindow );\r
\r
if( ( bRxComplete == 0 ) || ( bTxDone == 0 ) )\r
{\r
TCPHeader_t *pxTCPHeader = &pxTCPPacket->xTCPHeader;\r
TCPWindow_t *pxTCPWindow = &pxSocket->u.xTCP.xTCPWindow;\r
/* Find out what window size we may advertised. */\r
-uint32_t ulFrontSpace;\r
int32_t lRxSpace;\r
#if( ipconfigUSE_TCP_WIN == 1 )\r
#if( ipconfigTCP_ACK_EARLIER_PACKET == 0 )\r
int32_t lMinLength;\r
#endif\r
#endif\r
- pxSocket->u.xTCP.ulRxCurWinSize = pxTCPWindow->xSize.ulRxWindowLength -\r
- ( pxTCPWindow->rx.ulHighestSequenceNumber - pxTCPWindow->rx.ulCurrentSequenceNumber );\r
-\r
- /* Free space in rxStream. */\r
- if( pxSocket->u.xTCP.rxStream != NULL )\r
- {\r
- ulFrontSpace = ( uint32_t ) uxStreamBufferFrontSpace( pxSocket->u.xTCP.rxStream );\r
- }\r
- else\r
- {\r
- ulFrontSpace = ( uint32_t ) pxSocket->u.xTCP.uxRxStreamSize;\r
- }\r
-\r
- pxSocket->u.xTCP.ulRxCurWinSize = FreeRTOS_min_uint32( ulFrontSpace, pxSocket->u.xTCP.ulRxCurWinSize );\r
\r
/* Set the time-out field, so that we'll be called by the IP-task in case no\r
next message will be received. */\r
}\r
/*-----------------------------------------------------------*/\r
\r
-static BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t *pxNetworkBuffer )\r
+static BaseType_t prvTCPSendSpecialPacketHelper( NetworkBufferDescriptor_t *pxNetworkBuffer,\r
+ uint8_t ucTCPFlags )\r
{\r
- #if( ipconfigIGNORE_UNKNOWN_PACKETS == 0 )\r
- {\r
- TCPPacket_t *pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer );\r
- const BaseType_t xSendLength = ( BaseType_t ) ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + 0u ); /* Plus 0 options. */\r
+#if( ipconfigIGNORE_UNKNOWN_PACKETS == 0 )\r
+ {\r
+ TCPPacket_t *pxTCPPacket = ( TCPPacket_t * )( pxNetworkBuffer->pucEthernetBuffer );\r
+ const BaseType_t xSendLength = ( BaseType_t )\r
+ ( ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER + 0u ); /* Plus 0 options. */\r
\r
- pxTCPPacket->xTCPHeader.ucTCPFlags = ipTCP_FLAG_ACK | ipTCP_FLAG_RST;\r
- pxTCPPacket->xTCPHeader.ucTCPOffset = ( ipSIZE_OF_TCP_HEADER + 0u ) << 2;\r
+ pxTCPPacket->xTCPHeader.ucTCPFlags = ucTCPFlags;\r
+ pxTCPPacket->xTCPHeader.ucTCPOffset = ( ipSIZE_OF_TCP_HEADER + 0u ) << 2;\r
\r
- prvTCPReturnPacket( NULL, pxNetworkBuffer, ( uint32_t ) xSendLength, pdFALSE );\r
- }\r
- #endif /* !ipconfigIGNORE_UNKNOWN_PACKETS */\r
+ prvTCPReturnPacket( NULL, pxNetworkBuffer, ( uint32_t )xSendLength, pdFALSE );\r
+ }\r
+#endif /* !ipconfigIGNORE_UNKNOWN_PACKETS */\r
+\r
+ /* Remove compiler warnings if ipconfigIGNORE_UNKNOWN_PACKETS == 1. */\r
+ ( void )pxNetworkBuffer;\r
+ ( void )ucTCPFlags;\r
+\r
+ /* The packet was not consumed. */\r
+ return pdFAIL;\r
+}\r
+/*-----------------------------------------------------------*/\r
\r
- /* Remove compiler warnings if ipconfigIGNORE_UNKNOWN_PACKETS == 1. */\r
- ( void ) pxNetworkBuffer;\r
+static BaseType_t prvTCPSendChallengeAck( NetworkBufferDescriptor_t *pxNetworkBuffer )\r
+{\r
+ return prvTCPSendSpecialPacketHelper( pxNetworkBuffer, ipTCP_FLAG_ACK );\r
+}\r
+/*-----------------------------------------------------------*/\r
\r
- /* The packet was not consumed. */\r
- return pdFAIL;\r
+static BaseType_t prvTCPSendReset( NetworkBufferDescriptor_t *pxNetworkBuffer )\r
+{\r
+ return prvTCPSendSpecialPacketHelper( pxNetworkBuffer,\r
+ ipTCP_FLAG_ACK | ipTCP_FLAG_RST );\r
}\r
/*-----------------------------------------------------------*/\r
\r
{\r
FreeRTOS_Socket_t *pxSocket;\r
TCPPacket_t * pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer );\r
-uint16_t ucTCPFlags = pxTCPPacket->xTCPHeader.ucTCPFlags;\r
-uint32_t ulLocalIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulDestinationIPAddress );\r
-uint16_t xLocalPort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usDestinationPort );\r
-uint32_t ulRemoteIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulSourceIPAddress );\r
-uint16_t xRemotePort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usSourcePort );\r
+uint16_t ucTCPFlags;\r
+uint32_t ulLocalIP;\r
+uint16_t xLocalPort;\r
+uint32_t ulRemoteIP;\r
+uint16_t xRemotePort;\r
+uint32_t ulSequenceNumber;\r
+uint32_t ulAckNumber;\r
BaseType_t xResult = pdPASS;\r
+configASSERT(pxNetworkBuffer);\r
+configASSERT(pxNetworkBuffer->pucEthernetBuffer);\r
\r
- /* Find the destination socket, and if not found: return a socket listing to\r
- the destination PORT. */\r
- pxSocket = ( FreeRTOS_Socket_t * ) pxTCPSocketLookup( ulLocalIP, xLocalPort, ulRemoteIP, xRemotePort );\r
+ /* Check for a minimum packet size. */\r
+ if( pxNetworkBuffer->xDataLength >= ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv4_HEADER + ipSIZE_OF_TCP_HEADER ) )\r
+ {\r
+ ucTCPFlags = pxTCPPacket->xTCPHeader.ucTCPFlags;\r
+ ulLocalIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulDestinationIPAddress );\r
+ xLocalPort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usDestinationPort );\r
+ ulRemoteIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulSourceIPAddress );\r
+ xRemotePort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usSourcePort );\r
+ ulSequenceNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulSequenceNumber );\r
+ ulAckNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulAckNr );\r
+\r
+ /* Find the destination socket, and if not found: return a socket listing to\r
+ the destination PORT. */\r
+ pxSocket = ( FreeRTOS_Socket_t * )pxTCPSocketLookup( ulLocalIP, xLocalPort, ulRemoteIP, xRemotePort );\r
+ }\r
+ else\r
+ {\r
+ return pdFAIL;\r
+ }\r
\r
if( ( pxSocket == NULL ) || ( prvTCPSocketIsActive( ( UBaseType_t ) pxSocket->u.xTCP.ucTCPState ) == pdFALSE ) )\r
{\r
flag. */\r
if( ( ucTCPFlags & ipTCP_FLAG_RST ) != 0u )\r
{\r
- /* The target socket is not in a listening state, any RST packet\r
- will cause the socket to be closed. */\r
- FreeRTOS_debug_printf( ( "TCP: RST received from %lxip:%u for %u\n", ulRemoteIP, xRemotePort, xLocalPort ) );\r
- /* _HT_: should indicate that 'ECONNRESET' must be returned to the used during next API. */\r
- vTCPStateChange( pxSocket, eCLOSED );\r
-\r
- /* The packet cannot be handled. */\r
+ FreeRTOS_debug_printf( ( "TCP: RST received from %lxip:%u for %u\n", ulRemoteIP, xRemotePort, xLocalPort ) );\r
+\r
+ /* Implement https://tools.ietf.org/html/rfc5961#section-3.2. */\r
+ if( pxSocket->u.xTCP.ucTCPState == eCONNECT_SYN )\r
+ {\r
+ /* Per the above RFC, "In the SYN-SENT state ... the RST is\r
+ acceptable if the ACK field acknowledges the SYN." */\r
+ if( ulAckNumber == pxSocket->u.xTCP.xTCPWindow.ulOurSequenceNumber + 1 )\r
+ {\r
+ vTCPStateChange( pxSocket, eCLOSED );\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /* Check whether the packet matches the next expected sequence number. */\r
+ if( ulSequenceNumber == pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber )\r
+ {\r
+ vTCPStateChange( pxSocket, eCLOSED );\r
+ }\r
+ /* Otherwise, check whether the packet is within the receive window. */\r
+ else if( ulSequenceNumber > pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber &&\r
+ ulSequenceNumber < ( pxSocket->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber +\r
+ pxSocket->u.xTCP.xTCPWindow.xSize.ulRxWindowLength ) )\r
+ {\r
+ /* Send a challenge ACK. */\r
+ prvTCPSendChallengeAck( pxNetworkBuffer );\r
+ }\r
+ }\r
+\r
+ /* Otherwise, do nothing. In any case, the packet cannot be handled. */\r
xResult = pdFAIL;\r
}\r
else if( ( ( ucTCPFlags & ipTCP_FLAG_CTRL ) == ipTCP_FLAG_SYN ) && ( pxSocket->u.xTCP.ucTCPState >= eESTABLISHED ) )\r
static FreeRTOS_Socket_t *prvHandleListen( FreeRTOS_Socket_t *pxSocket, NetworkBufferDescriptor_t *pxNetworkBuffer )\r
{\r
TCPPacket_t * pxTCPPacket = ( TCPPacket_t * ) ( pxNetworkBuffer->pucEthernetBuffer );\r
-FreeRTOS_Socket_t *pxReturn;\r
+FreeRTOS_Socket_t *pxReturn = NULL;\r
+uint32_t ulInitialSequenceNumber;\r
+\r
+ /* Assume that a new Initial Sequence Number will be required. Request\r
+ it now in order to fail out if necessary. */\r
+ ulInitialSequenceNumber = ulApplicationGetNextSequenceNumber( *ipLOCAL_IP_ADDRESS_POINTER,\r
+ pxSocket->usLocalPort,\r
+ pxTCPPacket->xIPHeader.ulSourceIPAddress,\r
+ pxTCPPacket->xTCPHeader.usSourcePort );\r
\r
/* A pure SYN (without ACK) has come in, create a new socket to answer\r
it. */\r
- if( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED )\r
- {\r
- /* The flag bReuseSocket indicates that the same instance of the\r
- listening socket should be used for the connection. */\r
- pxReturn = pxSocket;\r
- pxSocket->u.xTCP.bits.bPassQueued = pdTRUE_UNSIGNED;\r
- pxSocket->u.xTCP.pxPeerSocket = pxSocket;\r
- }\r
- else\r
+ if( 0 != ulInitialSequenceNumber )\r
{\r
- /* The socket does not have the bReuseSocket flag set meaning create a\r
- new socket when a connection comes in. */\r
- pxReturn = NULL;\r
-\r
- if( pxSocket->u.xTCP.usChildCount >= pxSocket->u.xTCP.usBacklog )\r
+ if( pxSocket->u.xTCP.bits.bReuseSocket != pdFALSE_UNSIGNED )\r
{\r
- FreeRTOS_printf( ( "Check: Socket %u already has %u / %u child%s\n",\r
- pxSocket->usLocalPort,\r
- pxSocket->u.xTCP.usChildCount,\r
- pxSocket->u.xTCP.usBacklog,\r
- pxSocket->u.xTCP.usChildCount == 1 ? "" : "ren" ) );\r
- prvTCPSendReset( pxNetworkBuffer );\r
+ /* The flag bReuseSocket indicates that the same instance of the\r
+ listening socket should be used for the connection. */\r
+ pxReturn = pxSocket;\r
+ pxSocket->u.xTCP.bits.bPassQueued = pdTRUE_UNSIGNED;\r
+ pxSocket->u.xTCP.pxPeerSocket = pxSocket;\r
}\r
else\r
{\r
- FreeRTOS_Socket_t *pxNewSocket = (FreeRTOS_Socket_t *)\r
- FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );\r
+ /* The socket does not have the bReuseSocket flag set meaning create a\r
+ new socket when a connection comes in. */\r
+ pxReturn = NULL;\r
\r
- if( ( pxNewSocket == NULL ) || ( pxNewSocket == FREERTOS_INVALID_SOCKET ) )\r
+ if( pxSocket->u.xTCP.usChildCount >= pxSocket->u.xTCP.usBacklog )\r
{\r
- FreeRTOS_debug_printf( ( "TCP: Listen: new socket failed\n" ) );\r
+ FreeRTOS_printf( ( "Check: Socket %u already has %u / %u child%s\n",\r
+ pxSocket->usLocalPort,\r
+ pxSocket->u.xTCP.usChildCount,\r
+ pxSocket->u.xTCP.usBacklog,\r
+ pxSocket->u.xTCP.usChildCount == 1 ? "" : "ren" ) );\r
prvTCPSendReset( pxNetworkBuffer );\r
}\r
- else if( prvTCPSocketCopy( pxNewSocket, pxSocket ) != pdFALSE )\r
+ else\r
{\r
- /* The socket will be connected immediately, no time for the\r
- owner to setsockopt's, therefore copy properties of the server\r
- socket to the new socket. Only the binding might fail (due to\r
- lack of resources). */\r
- pxReturn = pxNewSocket;\r
+ FreeRTOS_Socket_t *pxNewSocket = ( FreeRTOS_Socket_t * )\r
+ FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );\r
+\r
+ if( ( pxNewSocket == NULL ) || ( pxNewSocket == FREERTOS_INVALID_SOCKET ) )\r
+ {\r
+ FreeRTOS_debug_printf( ( "TCP: Listen: new socket failed\n" ) );\r
+ prvTCPSendReset( pxNetworkBuffer );\r
+ }\r
+ else if( prvTCPSocketCopy( pxNewSocket, pxSocket ) != pdFALSE )\r
+ {\r
+ /* The socket will be connected immediately, no time for the\r
+ owner to setsockopt's, therefore copy properties of the server\r
+ socket to the new socket. Only the binding might fail (due to\r
+ lack of resources). */\r
+ pxReturn = pxNewSocket;\r
+ }\r
}\r
}\r
}\r
\r
- if( pxReturn != NULL )\r
+ if( ( 0 != ulInitialSequenceNumber ) && ( pxReturn != NULL ) )\r
{\r
pxReturn->u.xTCP.usRemotePort = FreeRTOS_htons( pxTCPPacket->xTCPHeader.usSourcePort );\r
pxReturn->u.xTCP.ulRemoteIP = FreeRTOS_htonl( pxTCPPacket->xIPHeader.ulSourceIPAddress );\r
- pxReturn->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulNextInitialSequenceNumber;\r
+ pxReturn->u.xTCP.xTCPWindow.ulOurSequenceNumber = ulInitialSequenceNumber;\r
\r
/* Here is the SYN action. */\r
pxReturn->u.xTCP.xTCPWindow.rx.ulCurrentSequenceNumber = FreeRTOS_ntohl( pxTCPPacket->xTCPHeader.ulSequenceNumber );\r
\r
prvTCPCreateWindow( pxReturn );\r
\r
- /* It is recommended to increase the ISS for each new connection with a value of 0x102. */\r
- ulNextInitialSequenceNumber += INITIAL_SEQUENCE_NUMBER_INCREMENT;\r
-\r
vTCPStateChange( pxReturn, eSYN_FIRST );\r
\r
/* Make a copy of the header up to the TCP header. It is needed later\r
/* A reference to the new socket may be stored and the socket is marked\r
as 'passable'. */\r
\r
- /* When bPassAccept is true, this socket may be returned in a call to\r
+ /* When bPassAccept is pdTRUE_UNSIGNED this socket may be returned in a call to\r
accept(). */\r
pxNewSocket->u.xTCP.bits.bPassAccept = pdTRUE_UNSIGNED;\r
if(pxSocket->u.xTCP.pxPeerSocket == NULL )\r
\r
#endif /* ipconfigUSE_TCP == 1 */\r
\r
+/* Provide access to private members for testing. */\r
+#ifdef AMAZON_FREERTOS_ENABLE_UNIT_TESTS\r
+ #include "iot_freertos_tcp_test_access_tcp_define.h"\r
+#endif\r
+\r
+/* Provide access to private members for verification. */\r
+#ifdef FREERTOS_TCP_ENABLE_VERIFICATION\r
+ #include "aws_freertos_tcp_verification_access_tcp_define.h"\r
+#endif\r
+\r