tcp.c
Go to the documentation of this file.
1 /**
2  * @file tcp.c
3  * @brief TCP (Transmission Control Protocol)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2023 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.2.4
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL TCP_TRACE_LEVEL
33 
34 //Dependencies
35 #include <string.h>
36 #include "core/net.h"
37 #include "core/socket.h"
38 #include "core/socket_misc.h"
39 #include "core/tcp.h"
40 #include "core/tcp_misc.h"
41 #include "core/tcp_timer.h"
42 #include "mibs/mib2_module.h"
43 #include "mibs/tcp_mib_module.h"
44 #include "debug.h"
45 
46 //Check TCP/IP stack configuration
47 #if (TCP_SUPPORT == ENABLED)
48 
49 //Tick counter to handle periodic operations
51 
52 //Ephemeral ports are used for dynamic port assignment
53 static uint16_t tcpDynamicPort;
54 
55 
56 /**
57  * @brief TCP related initialization
58  * @return Error code
59  **/
60 
62 {
63  //Reset ephemeral port number
64  tcpDynamicPort = 0;
65 
66  //Successful initialization
67  return NO_ERROR;
68 }
69 
70 
71 /**
72  * @brief Get an ephemeral port number
73  * @return Ephemeral port
74  **/
75 
76 uint16_t tcpGetDynamicPort(void)
77 {
78  uint_t port;
79 
80  //Retrieve current port number
81  port = tcpDynamicPort;
82 
83  //Invalid port number?
84  if(port < SOCKET_EPHEMERAL_PORT_MIN || port > SOCKET_EPHEMERAL_PORT_MAX)
85  {
86  //Generate a random port number
89  }
90 
91  //Next dynamic port to use
93  {
94  //Increment port number
95  tcpDynamicPort = port + 1;
96  }
97  else
98  {
99  //Wrap around if necessary
100  tcpDynamicPort = SOCKET_EPHEMERAL_PORT_MIN;
101  }
102 
103  //Return an ephemeral port number
104  return port;
105 }
106 
107 
108 /**
109  * @brief Establish a TCP connection
110  * @param[in] socket Handle to an unconnected socket
111  * @param[in] remoteIpAddr IP address of the remote host
112  * @param[in] remotePort Remote port number that will be used to establish the connection
113  * @return Error code
114  **/
115 
116 error_t tcpConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
117 {
118  error_t error;
119  uint_t event;
120 
121  //Check current TCP state
122  if(socket->state == TCP_STATE_CLOSED && !socket->resetFlag)
123  {
124  //Save port number and IP address of the remote host
125  socket->remoteIpAddr = *remoteIpAddr;
126  socket->remotePort = remotePort;
127 
128  //Select the source address and the relevant network interface
129  //to use when establishing the connection
130  error = ipSelectSourceAddr(&socket->interface,
131  &socket->remoteIpAddr, &socket->localIpAddr);
132  //Any error to report?
133  if(error)
134  return error;
135 
136  //Make sure the source address is valid
137  if(ipIsUnspecifiedAddr(&socket->localIpAddr))
138  return ERROR_NOT_CONFIGURED;
139 
140  //The user owns the socket
141  socket->ownedFlag = TRUE;
142 
143  //Number of chunks that comprise the TX and the RX buffers
144  socket->txBuffer.maxChunkCount = arraysize(socket->txBuffer.chunk);
145  socket->rxBuffer.maxChunkCount = arraysize(socket->rxBuffer.chunk);
146 
147  //Allocate transmit buffer
148  error = netBufferSetLength((NetBuffer *) &socket->txBuffer,
149  socket->txBufferSize);
150 
151  //Allocate receive buffer
152  if(!error)
153  {
154  error = netBufferSetLength((NetBuffer *) &socket->rxBuffer,
155  socket->rxBufferSize);
156  }
157 
158  //Failed to allocate memory?
159  if(error)
160  {
161  //Free any previously allocated memory
163  //Report an error to the caller
164  return error;
165  }
166 
167  //The SMSS is the size of the largest segment that the sender can
168  //transmit
170 
171  //The RMSS is the size of the largest segment the receiver is willing
172  //to accept
173  socket->rmss = MIN(socket->rxBufferSize, TCP_MAX_MSS);
174 
175  //Generate the initial sequence number
176  socket->iss = tcpGenerateInitialSeqNum(&socket->localIpAddr,
177  socket->localPort, &socket->remoteIpAddr, socket->remotePort);
178 
179  //Initialize TCP control block
180  socket->sndUna = socket->iss;
181  socket->sndNxt = socket->iss + 1;
182  socket->rcvUser = 0;
183  socket->rcvWnd = socket->rxBufferSize;
184 
185  //Default retransmission timeout
186  socket->rto = TCP_INITIAL_RTO;
187 
188 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
189  //Default congestion state
190  socket->congestState = TCP_CONGEST_STATE_IDLE;
191  //Initial congestion window
192  socket->cwnd = MIN(TCP_INITIAL_WINDOW * socket->smss, socket->txBufferSize);
193  //Slow start threshold should be set arbitrarily high
194  socket->ssthresh = UINT16_MAX;
195  //Recover is set to the initial send sequence number
196  socket->recover = socket->iss;
197 #endif
198 
199  //Send a SYN segment
200  error = tcpSendSegment(socket, TCP_FLAG_SYN, socket->iss, 0, 0, TRUE);
201  //Failed to send TCP segment?
202  if(error)
203  return error;
204 
205  //Switch to the SYN-SENT state
207 
208  //Number of times TCP connections have made a direct transition to
209  //the SYN-SENT state from the CLOSED state
210  MIB2_TCP_INC_COUNTER32(tcpActiveOpens, 1);
211  TCP_MIB_INC_COUNTER32(tcpActiveOpens, 1);
212  }
213 
214  //Wait for the connection to be established
216  SOCKET_EVENT_CLOSED, socket->timeout);
217 
218  //Connection successfully established?
219  if(event == SOCKET_EVENT_CONNECTED)
220  return NO_ERROR;
221  //Failed to establish connection?
222  else if(event == SOCKET_EVENT_CLOSED)
224  //Timeout exception?
225  else
226  return ERROR_TIMEOUT;
227 }
228 
229 
230 /**
231  * @brief Place a socket in the listening state
232  *
233  * Place a socket in a state in which it is listening for an incoming connection
234  *
235  * @param[in] socket Socket to place in the listening state
236  * @param[in] backlog backlog The maximum length of the pending connection queue.
237  * If this parameter is zero, then the default backlog value is used instead
238  * @return Error code
239  **/
240 
242 {
243  //Socket already connected?
244  if(socket->state != TCP_STATE_CLOSED)
246 
247  //Set the size of the SYN queue
248  socket->synQueueSize = (backlog > 0) ? backlog : TCP_DEFAULT_SYN_QUEUE_SIZE;
249  //Limit the number of pending connections
250  socket->synQueueSize = MIN(socket->synQueueSize, TCP_MAX_SYN_QUEUE_SIZE);
251 
252  //Place the socket in the listening state
254 
255  //Successful processing
256  return NO_ERROR;
257 }
258 
259 
260 /**
261  * @brief Permit an incoming connection attempt on a TCP socket
262  * @param[in] socket Handle to a socket previously placed in a listening state
263  * @param[out] clientIpAddr IP address of the client
264  * @param[out] clientPort Port number used by the client
265  * @return Handle to the socket in which the actual connection is made
266  **/
267 
268 Socket *tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
269 {
270  error_t error;
271  Socket *newSocket;
272  TcpSynQueueItem *queueItem;
273 
274  //Ensure the socket was previously placed in the listening state
276  return NULL;
277 
278  //Get exclusive access
280 
281  //Wait for an connection attempt
282  while(1)
283  {
284  //The SYN queue is empty?
285  if(socket->synQueue == NULL)
286  {
287  //Set the events the application is interested in
288  socket->eventMask = SOCKET_EVENT_RX_READY;
289  //Reset the event object
290  osResetEvent(&socket->event);
291 
292  //Release exclusive access
294  //Wait until a SYN message is received from a client
295  osWaitForEvent(&socket->event, socket->timeout);
296  //Get exclusive access
298  }
299 
300  //Check whether the queue is still empty
301  if(socket->synQueue == NULL)
302  {
303  //Timeout error
304  newSocket = NULL;
305  //Exit immediately
306  break;
307  }
308 
309  //Point to the first item in the SYN queue
310  queueItem = socket->synQueue;
311 
312  //The function optionally returns the IP address of the client
313  if(clientIpAddr != NULL)
314  {
315  *clientIpAddr = queueItem->srcAddr;
316  }
317 
318  //The function optionally returns the port number used by the client
319  if(clientPort != NULL)
320  {
321  *clientPort = queueItem->srcPort;
322  }
323 
324  //Create a new socket to handle the incoming connection request
326 
327  //Socket successfully created?
328  if(newSocket != NULL)
329  {
330  //The user owns the socket
331  newSocket->ownedFlag = TRUE;
332 
333  //Inherit parameters from the listening socket
334  newSocket->txBufferSize = socket->txBufferSize;
335  newSocket->rxBufferSize = socket->rxBufferSize;
336 
337 #if (TCP_KEEP_ALIVE_SUPPORT == ENABLED)
338  //Inherit keep-alive parameters from the listening socket
339  newSocket->keepAliveEnabled = socket->keepAliveEnabled;
340  newSocket->keepAliveIdle = socket->keepAliveIdle;
341  newSocket->keepAliveInterval = socket->keepAliveInterval;
342  newSocket->keepAliveMaxProbes = socket->keepAliveMaxProbes;
343 #endif
344  //Number of chunks that comprise the TX and the RX buffers
345  newSocket->txBuffer.maxChunkCount = arraysize(newSocket->txBuffer.chunk);
346  newSocket->rxBuffer.maxChunkCount = arraysize(newSocket->rxBuffer.chunk);
347 
348  //Allocate transmit buffer
349  error = netBufferSetLength((NetBuffer *) &newSocket->txBuffer,
350  newSocket->txBufferSize);
351 
352  //Check status code
353  if(!error)
354  {
355  //Allocate receive buffer
356  error = netBufferSetLength((NetBuffer *) &newSocket->rxBuffer,
357  newSocket->rxBufferSize);
358  }
359 
360  //Transmit and receive buffers successfully allocated?
361  if(!error)
362  {
363  //Bind the newly created socket to the appropriate interface
364  newSocket->interface = queueItem->interface;
365 
366  //Bind the socket to the specified address
367  newSocket->localIpAddr = queueItem->destAddr;
368  newSocket->localPort = socket->localPort;
369 
370  //Save the port number and the IP address of the remote host
371  newSocket->remoteIpAddr = queueItem->srcAddr;
372  newSocket->remotePort = queueItem->srcPort;
373 
374  //The SMSS is the size of the largest segment that the sender can
375  //transmit
376  newSocket->smss = queueItem->mss;
377 
378  //The RMSS is the size of the largest segment the receiver is
379  //willing to accept
380  newSocket->rmss = MIN(newSocket->rxBufferSize, TCP_MAX_MSS);
381 
382  //Generate the initial sequence number
383  newSocket->iss = tcpGenerateInitialSeqNum(&socket->localIpAddr,
384  socket->localPort, &socket->remoteIpAddr, socket->remotePort);
385 
386  //Initialize TCP control block
387  newSocket->irs = queueItem->isn;
388  newSocket->sndUna = newSocket->iss;
389  newSocket->sndNxt = newSocket->iss + 1;
390  newSocket->rcvNxt = newSocket->irs + 1;
391  newSocket->rcvUser = 0;
392  newSocket->rcvWnd = newSocket->rxBufferSize;
393 
394  //Default retransmission timeout
395  newSocket->rto = TCP_INITIAL_RTO;
396 
397 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
398  //Default congestion state
399  newSocket->congestState = TCP_CONGEST_STATE_IDLE;
400 
401  //Initial congestion window
402  newSocket->cwnd = MIN(TCP_INITIAL_WINDOW * newSocket->smss,
403  newSocket->txBufferSize);
404 
405  //Slow start threshold should be set arbitrarily high
406  newSocket->ssthresh = UINT16_MAX;
407  //Recover is set to the initial send sequence number
408  newSocket->recover = newSocket->iss;
409 #endif
410 
411 #if (TCP_SACK_SUPPORT == ENABLED)
412  //The SACK Permitted option can be sent in a SYN segment to
413  //indicate that the SACK option can be used once the connection
414  //is established
415  newSocket->sackPermitted = queueItem->sackPermitted;
416 #endif
417  //The connection state should be changed to SYN-RECEIVED
419 
420  //Number of times TCP connections have made a direct transition to
421  //the SYN-RECEIVED state from the LISTEN state
422  MIB2_TCP_INC_COUNTER32(tcpPassiveOpens, 1);
423  TCP_MIB_INC_COUNTER32(tcpPassiveOpens, 1);
424 
425  //Send a SYN ACK control segment
426  error = tcpSendSegment(newSocket, TCP_FLAG_SYN | TCP_FLAG_ACK,
427  newSocket->iss, newSocket->rcvNxt, 0, TRUE);
428 
429  //TCP segment successfully sent?
430  if(!error)
431  {
432  //Remove the item from the SYN queue
433  socket->synQueue = queueItem->next;
434  //Deallocate memory buffer
435  memPoolFree(queueItem);
436  //Update the state of events
438 
439  //We are done
440  break;
441  }
442  }
443 
444  //Dispose the socket
445  tcpAbort(newSocket);
446  }
447 
448  //Debug message
449  TRACE_WARNING("Cannot accept TCP connection!\r\n");
450 
451  //Remove the item from the SYN queue
452  socket->synQueue = queueItem->next;
453  //Deallocate memory buffer
454  memPoolFree(queueItem);
455 
456  //Wait for the next connection attempt
457  }
458 
459  //Release exclusive access
461 
462  //Return a handle to the newly created socket
463  return newSocket;
464 }
465 
466 
467 /**
468  * @brief Send data to a connected socket
469  * @param[in] socket Handle that identifies a connected socket
470  * @param[in] data Pointer to a buffer containing the data to be transmitted
471  * @param[in] length Number of bytes to be transmitted
472  * @param[out] written Actual number of bytes written (optional parameter)
473  * @param[in] flags Set of flags that influences the behavior of this function
474  * @return Error code
475  **/
476 
477 error_t tcpSend(Socket *socket, const uint8_t *data,
478  size_t length, size_t *written, uint_t flags)
479 {
480  uint_t n;
481  uint_t totalLength;
482  uint_t event;
483 
484  //Check whether the socket is in the listening state
485  if(socket->state == TCP_STATE_LISTEN)
486  return ERROR_NOT_CONNECTED;
487 
488  //Actual number of bytes written
489  totalLength = 0;
490 
491  //Send as much data as possible
492  do
493  {
494  //Wait until there is more room in the send buffer
496 
497  //A timeout exception occurred?
498  if(event != SOCKET_EVENT_TX_READY)
499  return ERROR_TIMEOUT;
500 
501  //Check current TCP state
502  switch(socket->state)
503  {
504  //ESTABLISHED or CLOSE-WAIT state?
507  //The send buffer is now available for writing
508  break;
509 
510  //LAST-ACK, FIN-WAIT-1, FIN-WAIT-2, CLOSING or TIME-WAIT state?
511  case TCP_STATE_LAST_ACK:
514  case TCP_STATE_CLOSING:
515  case TCP_STATE_TIME_WAIT:
516  //The connection is being closed
518 
519  //CLOSED state?
520  default:
521  //The connection was reset by remote side?
522  return (socket->resetFlag) ? ERROR_CONNECTION_RESET : ERROR_NOT_CONNECTED;
523  }
524 
525  //Determine the actual number of bytes in the send buffer
526  n = socket->sndUser + socket->sndNxt - socket->sndUna;
527  //Exit immediately if the transmission buffer is full (sanity check)
528  if(n >= socket->txBufferSize)
529  return ERROR_FAILURE;
530 
531  //Number of bytes available for writing
532  n = socket->txBufferSize - n;
533  //Calculate the number of bytes to copy at a time
534  n = MIN(n, length - totalLength);
535 
536  //Any data to copy?
537  if(n > 0)
538  {
539  //Copy user data to send buffer
540  tcpWriteTxBuffer(socket, socket->sndNxt + socket->sndUser, data, n);
541 
542  //Update the number of data buffered but not yet sent
543  socket->sndUser += n;
544  //Advance data pointer
545  data += n;
546  //Update byte counter
547  totalLength += n;
548 
549  //Total number of data that have been written
550  if(written != NULL)
551  *written = totalLength;
552 
553  //Update TX events
555 
556  //To avoid a deadlock, it is necessary to have a timeout to force
557  //transmission of data, overriding the SWS avoidance algorithm. In
558  //practice, this timeout should seldom occur (refer to RFC 1122,
559  //section 4.2.3.4)
560  if(socket->sndUser == n)
561  {
562  netStartTimer(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT);
563  }
564  }
565 
566  //The Nagle algorithm should be implemented to coalesce short segments
567  //(refer to RFC 1122 4.2.3.4)
569 
570  //Send as much data as possible
571  } while(totalLength < length);
572 
573  //The SOCKET_FLAG_WAIT_ACK flag causes the function to wait for
574  //acknowledgment from the remote side
575  if((flags & SOCKET_FLAG_WAIT_ACK) != 0)
576  {
577  //Wait for the data to be acknowledged
579 
580  //A timeout exception occurred?
581  if(event != SOCKET_EVENT_TX_ACKED)
582  return ERROR_TIMEOUT;
583 
584  //The connection closed before an acknowledgment was received?
585  if(socket->state != TCP_STATE_ESTABLISHED && socket->state != TCP_STATE_CLOSE_WAIT)
586  return ERROR_NOT_CONNECTED;
587  }
588 
589  //Successful write operation
590  return NO_ERROR;
591 }
592 
593 
594 /**
595  * @brief Receive data from a connected socket
596  * @param[in] socket Handle that identifies a connected socket
597  * @param[out] data Buffer where to store the incoming data
598  * @param[in] size Maximum number of bytes that can be received
599  * @param[out] received Number of bytes that have been received
600  * @param[in] flags Set of flags that influences the behavior of this function
601  * @return Error code
602  **/
603 
605  size_t size, size_t *received, uint_t flags)
606 {
607  uint_t i;
608  uint_t n;
609  uint_t event;
610  uint32_t seqNum;
611  systime_t timeout;
612 
613  //Retrieve the break character code
614  char_t c = LSB(flags);
615  //No data has been read yet
616  *received = 0;
617 
618  //Check whether the socket is in the listening state
619  if(socket->state == TCP_STATE_LISTEN)
620  return ERROR_NOT_CONNECTED;
621 
622  //Read as much data as possible
623  while(*received < size)
624  {
625  //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation
626  timeout = (flags & SOCKET_FLAG_DONT_WAIT) ? 0 : socket->timeout;
627  //Wait for data to be available for reading
628  event = tcpWaitForEvents(socket, SOCKET_EVENT_RX_READY, timeout);
629 
630  //A timeout exception occurred?
631  if(event != SOCKET_EVENT_RX_READY)
632  return ERROR_TIMEOUT;
633 
634  //Check current TCP state
635  switch(socket->state)
636  {
637  //ESTABLISHED, FIN-WAIT-1 or FIN-WAIT-2 state?
641  //Sequence number of the first byte to read
642  seqNum = socket->rcvNxt - socket->rcvUser;
643  //Data is available in the receive buffer
644  break;
645 
646  //CLOSE-WAIT, LAST-ACK, CLOSING or TIME-WAIT state?
648  case TCP_STATE_LAST_ACK:
649  case TCP_STATE_CLOSING:
650  case TCP_STATE_TIME_WAIT:
651  //The user must be satisfied with data already on hand
652  if(socket->rcvUser == 0)
653  {
654  if(*received > 0)
655  return NO_ERROR;
656  else
657  return ERROR_END_OF_STREAM;
658  }
659 
660  //Sequence number of the first byte to read
661  seqNum = (socket->rcvNxt - 1) - socket->rcvUser;
662  //Data is available in the receive buffer
663  break;
664 
665  //CLOSED state?
666  default:
667  //The connection was reset by remote side?
668  if(socket->resetFlag)
669  return ERROR_CONNECTION_RESET;
670  //The connection has not yet been established?
671  if(!socket->closedFlag)
672  return ERROR_NOT_CONNECTED;
673 
674  //The user must be satisfied with data already on hand
675  if(socket->rcvUser == 0)
676  {
677  if(*received > 0)
678  return NO_ERROR;
679  else
680  return ERROR_END_OF_STREAM;
681  }
682 
683  //Sequence number of the first byte to read
684  seqNum = (socket->rcvNxt - 1) - socket->rcvUser;
685  //Data is available in the receive buffer
686  break;
687  }
688 
689  //Sanity check
690  if(socket->rcvUser == 0)
691  return ERROR_FAILURE;
692 
693  //Calculate the number of bytes to read at a time
694  n = MIN(socket->rcvUser, size - *received);
695  //Copy data from circular buffer
697 
698  //Read data until a break character is encountered?
699  if((flags & SOCKET_FLAG_BREAK_CHAR) != 0)
700  {
701  //Search for the specified break character
702  for(i = 0; i < n && data[i] != c; i++);
703  //Adjust the number of data to read
704  n = MIN(n, i + 1);
705  }
706 
707  //Total number of data that have been read
708  *received += n;
709  //Remaining data still available in the receive buffer
710  socket->rcvUser -= n;
711 
712  //Update the receive window
714  //Update RX event state
716 
717  //The SOCKET_FLAG_BREAK_CHAR flag causes the function to stop reading
718  //data as soon as the specified break character is encountered
719  if((flags & SOCKET_FLAG_BREAK_CHAR) != 0)
720  {
721  //Check whether a break character has been found
722  if(data[n - 1] == c)
723  break;
724  }
725  //The SOCKET_FLAG_WAIT_ALL flag causes the function to return
726  //only when the requested number of bytes have been read
727  else if((flags & SOCKET_FLAG_WAIT_ALL) == 0)
728  {
729  break;
730  }
731 
732  //Advance data pointer
733  data += n;
734  }
735 
736  //Successful read operation
737  return NO_ERROR;
738 }
739 
740 
741 /**
742  * @brief Shutdown gracefully reception, transmission, or both
743  *
744  * Note that socketShutdown() does not close the socket, and resources attached
745  * to the socket will not be freed until socketClose() is invoked
746  *
747  * @param[in] socket Handle to a socket
748  * @param[in] how Flag that describes what types of operation will no longer be allowed
749  * @return Error code
750  **/
751 
753 {
754  error_t error;
755  uint_t event;
756 
757  //Disable transmission?
758  if(how == SOCKET_SD_SEND || how == SOCKET_SD_BOTH)
759  {
760  //Check current state
761  switch(socket->state)
762  {
763  //CLOSED or LISTEN state?
764  case TCP_STATE_CLOSED:
765  case TCP_STATE_LISTEN:
766  //The connection does not exist
767  return ERROR_NOT_CONNECTED;
768 
769  //SYN-RECEIVED or ESTABLISHED state?
772  //Flush the send buffer
773  error = tcpSend(socket, NULL, 0, NULL, SOCKET_FLAG_NO_DELAY);
774  //Any error to report?
775  if(error)
776  return error;
777 
778  //Make sure all the data has been sent out
780  //Timeout error?
781  if(event != SOCKET_EVENT_TX_DONE)
782  return ERROR_TIMEOUT;
783 
784  //Send a FIN segment
786  socket->sndNxt, socket->rcvNxt, 0, TRUE);
787  //Failed to send FIN segment?
788  if(error)
789  return error;
790 
791  //Sequence number expected to be received
792  socket->sndNxt++;
793  //Switch to the FIN-WAIT1 state
795 
796  //Wait for the FIN to be acknowledged
798  //Timeout interval elapsed?
799  if(event != SOCKET_EVENT_TX_SHUTDOWN)
800  return ERROR_TIMEOUT;
801 
802  //Continue processing
803  break;
804 
805  //CLOSE-WAIT state?
807  //Flush the send buffer
808  error = tcpSend(socket, NULL, 0, NULL, SOCKET_FLAG_NO_DELAY);
809  //Any error to report?
810  if(error)
811  return error;
812 
813  //Make sure all the data has been sent out
815  //Timeout error?
816  if(event != SOCKET_EVENT_TX_DONE)
817  return ERROR_TIMEOUT;
818 
819  //Send a FIN segment
821  socket->sndNxt, socket->rcvNxt, 0, TRUE);
822  //Failed to send FIN segment?
823  if(error)
824  return error;
825 
826  //Sequence number expected to be received
827  socket->sndNxt++;
828  //Switch to the LAST-ACK state
830 
831  //Wait for the FIN to be acknowledged
833  //Timeout interval elapsed?
834  if(event != SOCKET_EVENT_TX_SHUTDOWN)
835  return ERROR_TIMEOUT;
836 
837  //Continue processing
838  break;
839 
840  //FIN-WAIT-1, CLOSING or LAST-ACK state?
842  case TCP_STATE_CLOSING:
843  case TCP_STATE_LAST_ACK:
844  //Wait for the FIN to be acknowledged
846  //Timeout interval elapsed?
847  if(event != SOCKET_EVENT_TX_SHUTDOWN)
848  return ERROR_TIMEOUT;
849 
850  //Continue processing
851  break;
852 
853  //SYN-SENT, FIN-WAIT-2 or TIME-WAIT state?
854  default:
855  //Continue processing
856  break;
857  }
858  }
859 
860  //Disable reception?
861  if(how == SOCKET_SD_RECEIVE || how == SOCKET_SD_BOTH)
862  {
863  //Check current state
864  switch(socket->state)
865  {
866  //LISTEN state?
867  case TCP_STATE_LISTEN:
868  //The connection does not exist
869  return ERROR_NOT_CONNECTED;
870 
871  //SYN-SENT, SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1 or FIN-WAIT-2 state?
872  case TCP_STATE_SYN_SENT:
877  //Wait for a FIN to be received
879  //Timeout interval elapsed?
880  if(event != SOCKET_EVENT_RX_SHUTDOWN)
881  return ERROR_TIMEOUT;
882  //A FIN segment has been received
883  break;
884 
885  //CLOSING, TIME-WAIT, CLOSE-WAIT, LAST-ACK or CLOSED state?
886  default:
887  //A FIN segment has already been received
888  break;
889  }
890  }
891 
892  //Successful operation
893  return NO_ERROR;
894 }
895 
896 
897 /**
898  * @brief Abort an existing TCP connection
899  * @param[in] socket Handle identifying the socket to close
900  * @return Error code
901  **/
902 
904 {
905  error_t error;
906 
907  //Check current state
908  switch(socket->state)
909  {
910  //SYN-RECEIVED, ESTABLISHED, FIN-WAIT-1
911  //FIN-WAIT-2 or CLOSE-WAIT state?
917  //Send a reset segment
918  error = tcpSendResetSegment(socket, socket->sndNxt);
919  //Enter CLOSED state
921  //Delete TCB
923  //Mark the socket as closed
924  socket->type = SOCKET_TYPE_UNUSED;
925  //Return status code
926  return error;
927 
928  //TIME-WAIT state?
929  case TCP_STATE_TIME_WAIT:
930 #if (TCP_2MSL_TIMER > 0)
931  //The user doe not own the socket anymore...
932  socket->ownedFlag = FALSE;
933  //TCB will be deleted and socket will be closed
934  //when the 2MSL timer will elapse
935  return NO_ERROR;
936 #else
937  //Enter CLOSED state
939  //Delete TCB
941  //Mark the socket as closed
942  socket->type = SOCKET_TYPE_UNUSED;
943  //No error to report
944  return NO_ERROR;
945 #endif
946 
947  //Any other state?
948  default:
949  //Enter CLOSED state
951  //Delete TCB
953  //Mark the socket as closed
954  socket->type = SOCKET_TYPE_UNUSED;
955  //No error to report
956  return NO_ERROR;
957  }
958 }
959 
960 
961 /**
962  * @brief Get the current state of the TCP FSM
963  * @param[in] socket Handle identifying the socket
964  * @return TCP FSM state
965  **/
966 
968 {
969  TcpState state;
970 
971  //Get exclusive access
973 
974  //Get TCP FSM current state
975  state = socket->state;
976 
977  //Release exclusive access
979 
980  //Return current state
981  return state;
982 }
983 
984 
985 /**
986  * @brief Kill the oldest socket in the TIME-WAIT state
987  * @return Handle identifying the oldest TCP connection in the TIME-WAIT state.
988  * NULL is returned if no socket is currently in the TIME-WAIT state
989  **/
990 
992 {
993  uint_t i;
994  systime_t time;
995  Socket *socket;
996  Socket *oldestSocket;
997 
998  //Get current time
999  time = osGetSystemTime();
1000 
1001  //Keep track of the oldest socket in the TIME-WAIT state
1002  oldestSocket = NULL;
1003 
1004  //Loop through socket descriptors
1005  for(i = 0; i < SOCKET_MAX_COUNT; i++)
1006  {
1007  //Point to the current socket descriptor
1008  socket = &socketTable[i];
1009 
1010  //TCP connection found?
1011  if(socket->type == SOCKET_TYPE_STREAM)
1012  {
1013  //Check current state
1014  if(socket->state == TCP_STATE_TIME_WAIT)
1015  {
1016  //Keep track of the oldest socket in the TIME-WAIT state
1017  if(oldestSocket == NULL)
1018  {
1019  //Save socket handle
1020  oldestSocket = socket;
1021  }
1022  if((time - socket->timeWaitTimer.startTime) >
1023  (time - oldestSocket->timeWaitTimer.startTime))
1024  {
1025  //Save socket handle
1026  oldestSocket = socket;
1027  }
1028  }
1029  }
1030  }
1031 
1032  //Any connection in the TIME-WAIT state?
1033  if(oldestSocket != NULL)
1034  {
1035  //Enter CLOSED state
1036  tcpChangeState(oldestSocket, TCP_STATE_CLOSED);
1037  //Delete TCB
1038  tcpDeleteControlBlock(oldestSocket);
1039  //Mark the socket as closed
1040  oldestSocket->type = SOCKET_TYPE_UNUSED;
1041  }
1042 
1043  //The oldest connection in the TIME-WAIT state can be reused
1044  //when the socket table runs out of space
1045  return oldestSocket;
1046 }
1047 
1048 #endif
@ TCP_CONGEST_STATE_IDLE
Definition: tcp.h:288
void tcpUpdateReceiveWindow(Socket *socket)
Update receive window so as to avoid Silly Window Syndrome.
Definition: tcp_misc.c:1623
uint8_t length
Definition: coap_common.h:193
MIB-II module.
void netStartTimer(NetTimer *timer, systime_t interval)
Start timer.
Definition: net_misc.c:749
@ TCP_STATE_TIME_WAIT
Definition: tcp.h:278
#define TCP_INITIAL_WINDOW
Definition: tcp.h:159
@ SOCKET_FLAG_WAIT_ALL
Definition: socket.h:121
void memPoolFree(void *p)
Release a memory block.
Definition: net_mem.c:166
uint8_t data[]
Definition: ethernet.h:220
#define netMutex
Definition: net_legacy.h:195
IP network address.
Definition: ip.h:79
@ TCP_FLAG_FIN
Definition: tcp.h:300
@ SOCKET_FLAG_WAIT_ACK
Definition: socket.h:125
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
error_t tcpReceive(Socket *socket, uint8_t *data, size_t size, size_t *received, uint_t flags)
Receive data from a connected socket.
Definition: tcp.c:604
@ TCP_STATE_FIN_WAIT_1
Definition: tcp.h:275
uint16_t mss
Definition: tcp.h:403
#define TCP_OVERRIDE_TIMEOUT
Definition: tcp.h:187
#define TRUE
Definition: os_port.h:52
IpAddr srcAddr
Definition: tcp.h:399
@ ERROR_ALREADY_CONNECTED
Definition: error.h:82
@ ERROR_CONNECTION_RESET
Definition: error.h:79
@ TCP_STATE_CLOSE_WAIT
Definition: tcp.h:273
IpAddr destAddr
Definition: tcp.h:401
error_t tcpSendResetSegment(Socket *socket, uint32_t seqNum)
Send a TCP reset segment.
Definition: tcp_misc.c:364
#define TCP_MAX_MSS
Definition: tcp.h:54
@ ERROR_NOT_CONFIGURED
Definition: error.h:217
uint16_t srcPort
Definition: tcp.h:400
struct _TcpSynQueueItem * next
Definition: tcp.h:397
@ ERROR_END_OF_STREAM
Definition: error.h:210
error_t tcpListen(Socket *socket, uint_t backlog)
Place a socket in the listening state.
Definition: tcp.c:241
@ SOCKET_TYPE_STREAM
Definition: socket.h:78
@ TCP_FLAG_ACK
Definition: tcp.h:304
#define SOCKET_EPHEMERAL_PORT_MIN
Definition: socket.h:53
void tcpDeleteControlBlock(Socket *socket)
Delete TCB structure.
Definition: tcp_misc.c:1358
@ SOCKET_SD_SEND
Definition: socket.h:143
void tcpWriteTxBuffer(Socket *socket, uint32_t seqNum, const uint8_t *data, size_t length)
Copy incoming data to the send buffer.
Definition: tcp_misc.c:2195
TcpState
TCP FSM states.
Definition: tcp.h:267
#define FALSE
Definition: os_port.h:48
Helper functions for TCP.
#define SOCKET_EPHEMERAL_PORT_MAX
Definition: socket.h:60
void tcpChangeState(Socket *socket, TcpState newState)
Update TCP FSM current state.
Definition: tcp_misc.c:1978
void osResetEvent(OsEvent *event)
Set the specified event object to the nonsignaled state.
uint32_t netGenerateRandRange(uint32_t min, uint32_t max)
Generate a random value in the specified range.
Definition: net_misc.c:900
error_t
Error codes.
Definition: error.h:43
uint32_t seqNum
Definition: tcp.h:339
Socket * tcpKillOldestConnection(void)
Kill the oldest socket in the TIME-WAIT state.
Definition: tcp.c:991
error_t tcpSend(Socket *socket, const uint8_t *data, size_t length, size_t *written, uint_t flags)
Send data to a connected socket.
Definition: tcp.c:477
int_t socket(int_t family, int_t type, int_t protocol)
Create a socket that is bound to a specific transport service provider.
Definition: bsd_socket.c:63
@ SOCKET_EVENT_TX_READY
Definition: socket.h:158
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
error_t ipSelectSourceAddr(NetInterface **interface, const IpAddr *destAddr, IpAddr *srcAddr)
IP source address selection.
Definition: ip.c:117
TcpState tcpGetState(Socket *socket)
Get the current state of the TCP FSM.
Definition: tcp.c:967
NetInterface * interface
Definition: tcp.h:398
#define TCP_MAX_SYN_QUEUE_SIZE
Definition: tcp.h:103
@ SOCKET_EVENT_RX_SHUTDOWN
Definition: socket.h:163
bool_t ipIsUnspecifiedAddr(const IpAddr *ipAddr)
Compare an IP address against the unspecified address.
Definition: ip.c:165
#define TCP_MIB_INC_COUNTER32(name, value)
void tcpUpdateEvents(Socket *socket)
Update TCP related events.
Definition: tcp_misc.c:2009
@ TCP_STATE_SYN_SENT
Definition: tcp.h:270
@ TCP_FLAG_SYN
Definition: tcp.h:301
uint32_t isn
Definition: tcp.h:402
#define LSB(x)
Definition: os_port.h:57
@ TCP_STATE_LAST_ACK
Definition: tcp.h:274
#define MIN(a, b)
Definition: os_port.h:65
@ SOCKET_EVENT_TX_DONE
Definition: socket.h:159
@ TCP_STATE_CLOSING
Definition: tcp.h:277
Socket socketTable[SOCKET_MAX_COUNT]
Definition: socket.c:50
@ ERROR_CONNECTION_CLOSING
Definition: error.h:78
systime_t tcpTickCounter
Definition: tcp.c:50
@ SOCKET_FLAG_BREAK_CHAR
Definition: socket.h:123
uint32_t systime_t
System time.
@ TCP_STATE_CLOSED
Definition: tcp.h:268
uint16_t port
Definition: dns_common.h:251
@ TCP_STATE_LISTEN
Definition: tcp.h:269
uint8_t flags
Definition: tcp.h:349
#define TRACE_WARNING(...)
Definition: debug.h:85
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:48
Socket * socketAllocate(uint_t type, uint_t protocol)
Allocate a socket.
Definition: socket_misc.c:53
uint16_t tcpGetDynamicPort(void)
Get an ephemeral port number.
Definition: tcp.c:76
@ SOCKET_EVENT_TX_ACKED
Definition: socket.h:160
uint32_t time
Helper functions for sockets.
void tcpReadRxBuffer(Socket *socket, uint32_t seqNum, uint8_t *data, size_t length)
Copy data from the receive buffer.
Definition: tcp_misc.c:2310
error_t tcpInit(void)
TCP related initialization.
Definition: tcp.c:61
@ SOCKET_FLAG_NO_DELAY
Definition: socket.h:126
@ SOCKET_EVENT_RX_READY
Definition: socket.h:162
@ ERROR_NOT_CONNECTED
Definition: error.h:80
uint8_t n
bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
Wait until the specified event is in the signaled state.
TCP (Transmission Control Protocol)
TCP MIB module.
@ SOCKET_EVENT_TX_SHUTDOWN
Definition: socket.h:161
@ SOCKET_TYPE_UNUSED
Definition: socket.h:77
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
#define TCP_DEFAULT_SYN_QUEUE_SIZE
Definition: tcp.h:96
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
@ SOCKET_SD_RECEIVE
Definition: socket.h:142
error_t tcpAbort(Socket *socket)
Abort an existing TCP connection.
Definition: tcp.c:903
error_t tcpNagleAlgo(Socket *socket, uint_t flags)
Nagle algorithm implementation.
Definition: tcp_misc.c:1849
#define Socket
Definition: socket.h:36
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:320
@ TCP_STATE_FIN_WAIT_2
Definition: tcp.h:276
Socket API.
@ TCP_STATE_SYN_RECEIVED
Definition: tcp.h:271
error_t tcpConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort)
Establish a TCP connection.
Definition: tcp.c:116
SYN queue item.
Definition: tcp.h:396
error_t tcpShutdown(Socket *socket, uint_t how)
Shutdown gracefully reception, transmission, or both.
Definition: tcp.c:752
#define TCP_INITIAL_RTO
Definition: tcp.h:117
TCP timer management.
Socket * tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort)
Permit an incoming connection attempt on a TCP socket.
Definition: tcp.c:268
#define MIB2_TCP_INC_COUNTER32(name, value)
Definition: mib2_module.h:178
@ SOCKET_EVENT_CONNECTED
Definition: socket.h:156
@ ERROR_CONNECTION_FAILED
Definition: error.h:76
unsigned int uint_t
Definition: compiler_port.h:50
@ SOCKET_FLAG_DONT_WAIT
Definition: socket.h:122
TCP/IP stack core.
@ SOCKET_EVENT_CLOSED
Definition: socket.h:157
#define SOCKET_MAX_COUNT
Definition: socket.h:46
#define TCP_DEFAULT_MSS
Definition: tcp.h:251
uint32_t tcpGenerateInitialSeqNum(const IpAddr *localIpAddr, uint16_t localPort, const IpAddr *remoteIpAddr, uint16_t remotePort)
Initial sequence number generation.
Definition: tcp_misc.c:675
@ SOCKET_SD_BOTH
Definition: socket.h:144
error_t tcpSendSegment(Socket *socket, uint8_t flags, uint32_t seqNum, uint32_t ackNum, size_t length, bool_t addToQueue)
Send a TCP segment.
Definition: tcp_misc.c:70
@ SOCKET_IP_PROTO_TCP
Definition: socket.h:93
bool_t sackPermitted
Definition: tcp.h:405
@ TCP_STATE_ESTABLISHED
Definition: tcp.h:272
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t c
Definition: ndp.h:512
Debugging facilities.
uint_t tcpWaitForEvents(Socket *socket, uint_t eventMask, systime_t timeout)
Wait for a particular TCP event.
Definition: tcp_misc.c:2157
#define arraysize(a)
Definition: os_port.h:73
systime_t osGetSystemTime(void)
Retrieve system time.