1 /* 2 * FreeRTOS+TCP <DEVELOPMENT BRANCH> 3 * Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 * 5 * SPDX-License-Identifier: MIT 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy of 8 * this software and associated documentation files (the "Software"), to deal in 9 * the Software without restriction, including without limitation the rights to 10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 * the Software, and to permit persons to whom the Software is furnished to do so, 12 * subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in all 15 * copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * http://aws.amazon.com/freertos 25 * http://www.FreeRTOS.org 26 */ 27 28 /*============================================================================= 29 * 30 * ##### # ##### #### ###### 31 * # # # # # # # # # # # 32 * # # # # # # 33 * # ### ##### # # # # # # 34 * # # # # # # # # ##### 35 * # # # # # # #### # # # 36 * # # # # # # # # # # 37 * # # # # #### # # # # 38 * #### ##### # # # #### #### #### 39 * # 40 * ### 41 * Tiny-TCP: TCP without sliding windows. 42 * 43 *=============================================================================*/ 44 45 /** 46 * @file FreeRTOS_TINY_TCP.c 47 * @brief Module which handles TCP when windowing is disabled 48 * 49 * In this module all ports and IP addresses and sequence numbers are 50 * being stored in host byte-order. 51 */ 52 53 54 /* Standard includes. */ 55 #include <stdint.h> 56 57 /* FreeRTOS includes. */ 58 #include "FreeRTOS.h" 59 #include "task.h" 60 61 /* FreeRTOS+TCP includes. */ 62 #include "FreeRTOS_IP.h" 63 #include "FreeRTOS_UDP_IP.h" 64 #include "FreeRTOS_Sockets.h" 65 #include "FreeRTOS_IP_Private.h" 66 67 #if ( ipconfigUSE_TCP == 1 ) 68 69 #if ( ipconfigUSE_TCP_WIN == 0 ) 70 71 /** @brief Logging verbosity level. */ 72 BaseType_t xTCPWindowLoggingLevel = 0; 73 74 static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, 75 uint32_t b ); 76 77 /** 78 * @brief Test if a>=b. This function is required since the sequence numbers can roll over. 79 * 80 * @param[in] a The first sequence number. 81 * @param[in] b The second sequence number. 82 * 83 * @return pdTRUE if a>=b, else pdFALSE. 84 */ 85 xSequenceGreaterThanOrEqual(uint32_t a,uint32_t b)86 static portINLINE BaseType_t xSequenceGreaterThanOrEqual( uint32_t a, 87 uint32_t b ) 88 { 89 BaseType_t xResult = pdFALSE; 90 91 /* Test if a >= b */ 92 if( ( ( a - b ) & 0x80000000U ) == 0U ) 93 { 94 xResult = pdTRUE; 95 } 96 97 return xResult; 98 } 99 100 static portINLINE void vTCPTimerSet( TCPTimer_t * pxTimer ); 101 102 /** 103 * @brief Set the timer's "born" time. 104 * 105 * @param[in] pxTimer The TCP timer. 106 */ vTCPTimerSet(TCPTimer_t * pxTimer)107 static portINLINE void vTCPTimerSet( TCPTimer_t * pxTimer ) 108 { 109 pxTimer->uxBorn = xTaskGetTickCount(); 110 } 111 /*-----------------------------------------------------------*/ 112 113 static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer ); 114 115 /** 116 * @brief Get the timer age in milliseconds. 117 * 118 * @param[in] pxTimer The timer whose age is to be fetched. 119 * 120 * @return The time in milliseconds since the timer was born. 121 */ ulTimerGetAge(const TCPTimer_t * pxTimer)122 static portINLINE uint32_t ulTimerGetAge( const TCPTimer_t * pxTimer ) 123 { 124 TickType_t uxNow = xTaskGetTickCount(); 125 TickType_t uxDiff = uxNow - pxTimer->uxBorn; 126 127 return uxDiff * portTICK_PERIOD_MS; 128 } 129 /*-----------------------------------------------------------*/ 130 131 /** 132 * @brief Data was received at 'ulSequenceNumber'. See if it was expected 133 * and if there is enough space to store the new data. 134 * 135 * @param[in] pxWindow The window to be checked. 136 * @param[in] ulSequenceNumber Sequence number of the data received. 137 * @param[in] ulLength Length of the data received. 138 * @param[in] ulSpace Space in the buffer. 139 * 140 * @return A 0 is returned if there is enough space and the sequence number is correct, 141 * if not then a -1 is returned. 142 * 143 * @note if true may be passed directly to user (segment expected and window is empty). 144 * But pxWindow->ackno should always be used to set "BUF->ackno". 145 */ lTCPWindowRxCheck(TCPWindow_t * pxWindow,uint32_t ulSequenceNumber,uint32_t ulLength,uint32_t ulSpace,uint32_t * pulSkipCount)146 int32_t lTCPWindowRxCheck( TCPWindow_t * pxWindow, 147 uint32_t ulSequenceNumber, 148 uint32_t ulLength, 149 uint32_t ulSpace, 150 uint32_t * pulSkipCount ) 151 { 152 int32_t lReturn = -1; 153 154 *pulSkipCount = 0; 155 156 /* Data was received at 'ulSequenceNumber'. See if it was expected 157 * and if there is enough space to store the new data. */ 158 if( ( pxWindow->rx.ulCurrentSequenceNumber != ulSequenceNumber ) || ( ulSpace < ulLength ) ) 159 { 160 lReturn = -1; 161 } 162 else 163 { 164 pxWindow->rx.ulCurrentSequenceNumber += ( uint32_t ) ulLength; 165 lReturn = 0; 166 } 167 168 return lReturn; 169 } 170 #endif /* ipconfigUSE_TCP_WIN == 0 */ 171 /*-----------------------------------------------------------*/ 172 173 #if ( ipconfigUSE_TCP_WIN == 0 ) 174 175 /** 176 * @brief Add data to the Tx Window. 177 * 178 * @param[in] pxWindow The window to which the data is to be added. 179 * @param[in] ulLength The length of the data to be added. 180 * @param[in] lPosition Position in the stream. 181 * @param[in] lMax Size of the Tx stream. 182 * 183 * @return The data actually added. 184 */ lTCPWindowTxAdd(TCPWindow_t * pxWindow,uint32_t ulLength,int32_t lPosition,int32_t lMax)185 int32_t lTCPWindowTxAdd( TCPWindow_t * pxWindow, 186 uint32_t ulLength, 187 int32_t lPosition, 188 int32_t lMax ) 189 { 190 TCPSegment_t * pxSegment = &( pxWindow->xTxSegment ); 191 int32_t lResult; 192 193 /* Data is being scheduled for transmission. */ 194 195 /* lMax would indicate the size of the txStream. */ 196 ( void ) lMax; 197 198 /* This is tiny TCP: there is only 1 segment for outgoing data. 199 * As long as 'lDataLength' is unequal to zero, the segment is still occupied. */ 200 if( pxSegment->lDataLength > 0 ) 201 { 202 lResult = 0L; 203 } 204 else 205 { 206 if( ulLength > ( uint32_t ) pxSegment->lMaxLength ) 207 { 208 if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) ) 209 { 210 FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: can only store %u / %d bytes\n", ( unsigned ) ulLength, ( int ) pxSegment->lMaxLength ) ); 211 } 212 213 ulLength = ( uint32_t ) pxSegment->lMaxLength; 214 } 215 216 if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) ) 217 { 218 FreeRTOS_debug_printf( ( "lTCPWindowTxAdd: SeqNr %u (%u) Len %u\n", 219 ( unsigned ) ( pxWindow->ulNextTxSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), 220 ( unsigned ) ( pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), 221 ( unsigned ) ulLength ) ); 222 } 223 224 /* The sequence number of the first byte in this packet. */ 225 pxSegment->ulSequenceNumber = pxWindow->ulNextTxSequenceNumber; 226 pxSegment->lDataLength = ( int32_t ) ulLength; 227 pxSegment->lStreamPos = lPosition; 228 pxSegment->u.ulFlags = 0U; 229 vTCPTimerSet( &( pxSegment->xTransmitTimer ) ); 230 231 /* Increase the sequence number of the next data to be stored for 232 * transmission. */ 233 pxWindow->ulNextTxSequenceNumber += ulLength; 234 lResult = ( int32_t ) ulLength; 235 } 236 237 return lResult; 238 } 239 #endif /* ipconfigUSE_TCP_WIN == 0 */ 240 /*-----------------------------------------------------------*/ 241 242 #if ( ipconfigUSE_TCP_WIN == 0 ) 243 244 /** 245 * @brief Fetches data to be sent. 246 * 247 * @param[in] pxWindow The window for the connection. 248 * @param[in] ulWindowSize The size of the window. 249 * @param[out] plPosition plPosition will point to a location with the circular data buffer: txStream. 250 * 251 * @return return the amount of data which may be sent along with the position in the txStream. 252 */ ulTCPWindowTxGet(TCPWindow_t * pxWindow,uint32_t ulWindowSize,int32_t * plPosition)253 uint32_t ulTCPWindowTxGet( TCPWindow_t * pxWindow, 254 uint32_t ulWindowSize, 255 int32_t * plPosition ) 256 { 257 TCPSegment_t * pxSegment = &( pxWindow->xTxSegment ); 258 uint32_t ulLength = ( uint32_t ) pxSegment->lDataLength; 259 uint32_t ulMaxTime; 260 261 if( ulLength != 0U ) 262 { 263 /* _HT_ Still under investigation */ 264 ( void ) ulWindowSize; 265 266 if( pxSegment->u.bits.bOutstanding != pdFALSE_UNSIGNED ) 267 { 268 /* As 'ucTransmitCount' has a minimum of 1, take 2 * RTT */ 269 ulMaxTime = ( ( uint32_t ) 1U ) << pxSegment->u.bits.ucTransmitCount; 270 ulMaxTime *= ( uint32_t ) pxWindow->lSRTT; 271 272 if( ulTimerGetAge( &( pxSegment->xTransmitTimer ) ) < ulMaxTime ) 273 { 274 ulLength = 0U; 275 } 276 } 277 278 if( ulLength != 0U ) 279 { 280 pxSegment->u.bits.bOutstanding = pdTRUE_UNSIGNED; 281 pxSegment->u.bits.ucTransmitCount++; 282 vTCPTimerSet( &pxSegment->xTransmitTimer ); 283 pxWindow->ulOurSequenceNumber = pxSegment->ulSequenceNumber; 284 *plPosition = pxSegment->lStreamPos; 285 } 286 } 287 288 return ulLength; 289 } 290 #endif /* ipconfigUSE_TCP_WIN == 0 */ 291 /*-----------------------------------------------------------*/ 292 293 #if ( ipconfigUSE_TCP_WIN == 0 ) 294 295 /** 296 * @brief Has the transmission completed. 297 * 298 * @param[in] pxWindow The window whose transmission window is to be checked. 299 * 300 * @return If there is no outstanding data then pdTRUE is returned, 301 * else pdFALSE. 302 */ xTCPWindowTxDone(const TCPWindow_t * pxWindow)303 BaseType_t xTCPWindowTxDone( const TCPWindow_t * pxWindow ) 304 { 305 BaseType_t xReturn; 306 307 /* Has the outstanding data been sent because user wants to shutdown? */ 308 if( pxWindow->xTxSegment.lDataLength == 0 ) 309 { 310 xReturn = pdTRUE; 311 } 312 else 313 { 314 xReturn = pdFALSE; 315 } 316 317 return xReturn; 318 } 319 #endif /* ipconfigUSE_TCP_WIN == 0 */ 320 /*-----------------------------------------------------------*/ 321 322 #if ( ipconfigUSE_TCP_WIN == 0 ) 323 static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * pxWindow, 324 uint32_t ulWindowSize ); 325 326 /** 327 * @brief Check if the window has space for one message. 328 * 329 * @param[in] pxWindow The window to be checked. 330 * @param[in] ulWindowSize Size of the window. 331 * 332 * @return pdTRUE if the window has space, pdFALSE otherwise. 333 */ prvTCPWindowTxHasSpace(TCPWindow_t const * pxWindow,uint32_t ulWindowSize)334 static BaseType_t prvTCPWindowTxHasSpace( TCPWindow_t const * pxWindow, 335 uint32_t ulWindowSize ) 336 { 337 BaseType_t xReturn; 338 339 if( ulWindowSize >= pxWindow->usMSSInit ) 340 { 341 xReturn = pdTRUE; 342 } 343 else 344 { 345 xReturn = pdFALSE; 346 } 347 348 return xReturn; 349 } 350 #endif /* ipconfigUSE_TCP_WIN == 0 */ 351 /*-----------------------------------------------------------*/ 352 353 #if ( ipconfigUSE_TCP_WIN == 0 ) 354 355 /** 356 * @brief Check data to be sent and calculate the time period the process may sleep. 357 * 358 * @param[in] pxWindow The window to be checked. 359 * @param[in] ulWindowSize Size of the window. 360 * @param[out] pulDelay The time period (in ticks) that the process may sleep. 361 * 362 * @return pdTRUE if the process should sleep or pdFALSE. 363 */ xTCPWindowTxHasData(TCPWindow_t const * pxWindow,uint32_t ulWindowSize,TickType_t * pulDelay)364 BaseType_t xTCPWindowTxHasData( TCPWindow_t const * pxWindow, 365 uint32_t ulWindowSize, 366 TickType_t * pulDelay ) 367 { 368 TCPSegment_t const * pxSegment = &( pxWindow->xTxSegment ); 369 BaseType_t xReturn; 370 TickType_t ulAge, ulMaxAge; 371 372 /* Check data to be sent. */ 373 *pulDelay = ( TickType_t ) 0; 374 375 if( pxSegment->lDataLength == 0 ) 376 { 377 /* Got nothing to send right now. */ 378 xReturn = pdFALSE; 379 } 380 else 381 { 382 if( pxSegment->u.bits.bOutstanding != pdFALSE_UNSIGNED ) 383 { 384 ulAge = ulTimerGetAge( &pxSegment->xTransmitTimer ); 385 ulMaxAge = ( ( TickType_t ) 1U << pxSegment->u.bits.ucTransmitCount ) * ( ( uint32_t ) pxWindow->lSRTT ); 386 387 if( ulMaxAge > ulAge ) 388 { 389 *pulDelay = ulMaxAge - ulAge; 390 } 391 392 xReturn = pdTRUE; 393 } 394 else if( prvTCPWindowTxHasSpace( pxWindow, ulWindowSize ) == pdFALSE ) 395 { 396 /* Too many outstanding messages. */ 397 xReturn = pdFALSE; 398 } 399 else 400 { 401 xReturn = pdTRUE; 402 } 403 } 404 405 return xReturn; 406 } 407 #endif /* ipconfigUSE_TCP_WIN == 0 */ 408 /*-----------------------------------------------------------*/ 409 410 #if ( ipconfigUSE_TCP_WIN == 0 ) 411 412 /** 413 * @brief Receive a normal ACK. 414 * 415 * @param[in] pxWindow The window for this particular connection. 416 * @param[in] ulSequenceNumber The sequence number of the packet. 417 * 418 * @return Number of bytes to send. 419 */ ulTCPWindowTxAck(TCPWindow_t * pxWindow,uint32_t ulSequenceNumber)420 uint32_t ulTCPWindowTxAck( TCPWindow_t * pxWindow, 421 uint32_t ulSequenceNumber ) 422 { 423 TCPSegment_t * pxSegment = &( pxWindow->xTxSegment ); 424 uint32_t ulDataLength = ( uint32_t ) pxSegment->lDataLength; 425 426 /* Receive a normal ACK */ 427 428 if( ulDataLength != 0U ) 429 { 430 if( ulSequenceNumber < ( pxWindow->tx.ulCurrentSequenceNumber + ulDataLength ) ) 431 { 432 if( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) 433 { 434 FreeRTOS_debug_printf( ( "win_tx_ack: acked %u expc %u len %u\n", 435 ( unsigned ) ( ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), 436 ( unsigned ) ( pxWindow->tx.ulCurrentSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), 437 ( unsigned ) ulDataLength ) ); 438 } 439 440 /* Nothing to send right now. */ 441 ulDataLength = 0U; 442 } 443 else 444 { 445 pxWindow->tx.ulCurrentSequenceNumber += ulDataLength; 446 447 if( ( xTCPWindowLoggingLevel != 0 ) && ( ipconfigTCP_MAY_LOG_PORT( pxWindow->usOurPortNumber ) != pdFALSE ) ) 448 { 449 FreeRTOS_debug_printf( ( "win_tx_ack: acked seqnr %u len %u\n", 450 ( unsigned ) ( ulSequenceNumber - pxWindow->tx.ulFirstSequenceNumber ), 451 ( unsigned ) ulDataLength ) ); 452 } 453 454 pxSegment->lDataLength = 0; 455 } 456 } 457 458 return ulDataLength; 459 } 460 #endif /* ipconfigUSE_TCP_WIN == 0 */ 461 /*-----------------------------------------------------------*/ 462 463 #if ( ipconfigUSE_TCP_WIN == 0 ) 464 465 /** 466 * @brief This function will be called as soon as a FIN is received to check 467 * whether all transmit queues are empty or not. 468 * 469 * @param[in] pxWindow The window to be checked. 470 * 471 * @return It will return true if there are no 'open' reception segments. 472 */ xTCPWindowRxEmpty(const TCPWindow_t * pxWindow)473 BaseType_t xTCPWindowRxEmpty( const TCPWindow_t * pxWindow ) 474 { 475 /* Return true if 'ulCurrentSequenceNumber >= ulHighestSequenceNumber' 476 * 'ulCurrentSequenceNumber' is the highest sequence number stored, 477 * 'ulHighestSequenceNumber' is the highest sequence number seen. */ 478 return xSequenceGreaterThanOrEqual( pxWindow->rx.ulCurrentSequenceNumber, pxWindow->rx.ulHighestSequenceNumber ); 479 } 480 #endif /* ipconfigUSE_TCP_WIN == 0 */ 481 /*-----------------------------------------------------------*/ 482 483 #if ( ipconfigUSE_TCP_WIN == 0 ) 484 485 /** 486 * @brief Destroy a window. 487 * 488 * @param[in] pxWindow Pointer to the window to be destroyed. 489 * 490 * @return Always returns a NULL. 491 */ vTCPWindowDestroy(const TCPWindow_t * pxWindow)492 void vTCPWindowDestroy( const TCPWindow_t * pxWindow ) 493 { 494 /* As in tiny TCP there are no shared segments descriptors, there is 495 * nothing to release. */ 496 ( void ) pxWindow; 497 } 498 #endif /* ipconfigUSE_TCP_WIN == 0 */ 499 /*-----------------------------------------------------------*/ 500 501 #endif /* ipconfigUSE_TCP == 1 */ 502