tcp_timer.c
Go to the documentation of this file.
1 /**
2  * @file tcp_timer.c
3  * @brief TCP timer management
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 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.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL TCP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "core/socket.h"
37 #include "core/tcp.h"
38 #include "core/tcp_misc.h"
39 #include "core/tcp_timer.h"
40 #include "date_time.h"
41 #include "debug.h"
42 
43 //Check TCP/IP stack configuration
44 #if (TCP_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief TCP timer handler
49  *
50  * This routine must be periodically called by the TCP/IP stack to
51  * handle retransmissions and TCP related timers (persist timer,
52  * FIN-WAIT-2 timer and TIME-WAIT timer)
53  *
54  **/
55 
56 void tcpTick(void)
57 {
58  uint_t i;
59  Socket *socket;
60 
61  //Loop through opened sockets
62  for(i = 0; i < SOCKET_MAX_COUNT; i++)
63  {
64  //Point to the current socket
65  socket = &socketTable[i];
66 
67  //TCP socket?
68  if(socket->type == SOCKET_TYPE_STREAM)
69  {
70  //Check current TCP state
71  if(socket->state != TCP_STATE_CLOSED)
72  {
73  //Check retransmission timer
75  //Check persist timer
77  //Check TCP keep-alive timer
79  //Check override timer
81  //Check FIN-WAIT-2 timer
83  //Check 2MSL timer
85  }
86  }
87  }
88 }
89 
90 
91 /**
92  * @brief Check retransmission timer
93  * @param[in] socket Handle referencing the socket
94  **/
95 
97 {
98  //Check current TCP state
99  if(socket->state != TCP_STATE_CLOSED)
100  {
101  //Any packet in the retransmission queue?
102  if(socket->retransmitQueue != NULL)
103  {
104  //Retransmission timeout?
105  if(netTimerExpired(&socket->retransmitTimer))
106  {
107 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
108  //When a TCP sender detects segment loss using the retransmission
109  //timer and the given segment has not yet been resent by way of
110  //the retransmission timer, the value of ssthresh must be updated
111  if(socket->retransmitCount == 0)
112  {
113  uint32_t flightSize;
114 
115  //Amount of data that has been sent but not yet acknowledged
116  flightSize = socket->sndNxt - socket->sndUna;
117  //Adjust ssthresh value
118  socket->ssthresh = MAX(flightSize / 2, 2 * socket->smss);
119  }
120 
121  //Furthermore, upon a timeout cwnd must be set to no more than the
122  //loss window, LW, which equals 1 full-sized segment
123  socket->cwnd = MIN(TCP_LOSS_WINDOW * socket->smss,
124  socket->txBufferSize);
125 
126  //After a retransmit timeout, record the highest sequence number
127  //transmitted in the variable recover
128  socket->recover = socket->sndNxt - 1;
129 
130  //Enter the fast loss recovery procedure
131  socket->congestState = TCP_CONGEST_STATE_LOSS_RECOVERY;
132 #endif
133  //Make sure the maximum number of retransmissions has not been
134  //reached
135  if(socket->retransmitCount < TCP_MAX_RETRIES)
136  {
137  //Debug message
138  TRACE_INFO("%s: TCP segment retransmission #%u (%u data bytes)...\r\n",
140  socket->retransmitCount + 1,
141  socket->retransmitQueue->length);
142 
143  //Retransmit the earliest segment that has not been acknowledged
144  //by the TCP receiver
146 
147  //Use exponential back-off algorithm to calculate the new RTO
148  socket->rto = MIN(socket->rto * 2, TCP_MAX_RTO);
149  //Restart retransmission timer
150  netStartTimer(&socket->retransmitTimer, socket->rto);
151  //Increment retransmission counter
152  socket->retransmitCount++;
153  }
154  else
155  {
156  //Send a reset segment
158  //Turn off the retransmission timer
159  netStopTimer(&socket->retransmitTimer);
160  //The maximum number of retransmissions has been exceeded
162  }
163 
164  //TCP must use Karn's algorithm for taking RTT samples. That is, RTT
165  //samples must not be made using segments that were retransmitted
166  socket->rttBusy = FALSE;
167  }
168  }
169  }
170 }
171 
172 
173 /**
174  * @brief Check persist timer
175  *
176  * The TCP persist timer is set by one end of a connection when it has data to
177  * send, but has been stopped because the other end has advertised a zero-sized
178  * window
179  *
180  * @param[in] socket Handle referencing the socket
181  **/
182 
184 {
185  //Check current TCP state
186  if(socket->state != TCP_STATE_CLOSED)
187  {
188  //Check whether the remote host advertises a window size of zero
189  if(socket->sndWnd == 0 && socket->wndProbeInterval != 0)
190  {
191  //Persist timer expired?
192  if(netTimerExpired(&socket->persistTimer))
193  {
194  //Make sure the maximum number of retransmissions has not been
195  //reached
196  if(socket->wndProbeCount < TCP_MAX_RETRIES)
197  {
198  //Debug message
199  TRACE_INFO("%s: TCP zero window probe #%u...\r\n",
200  formatSystemTime(osGetSystemTime(), NULL), socket->wndProbeCount + 1);
201 
202  //Zero window probes usually have the sequence number one less
203  //than expected
205  socket->rcvNxt, 0, FALSE);
206 
207  //The interval between successive probes should be increased
208  //exponentially
209  socket->wndProbeInterval = MIN(socket->wndProbeInterval * 2,
211 
212  //Restart the persist timer
213  netStartTimer(&socket->persistTimer, socket->wndProbeInterval);
214  //Increment window probe counter
215  socket->wndProbeCount++;
216  }
217  else
218  {
219  //Send a reset segment
221  //Enter CLOSED state
223  }
224  }
225  }
226  }
227 }
228 
229 
230 /**
231  * @brief Check TCP keep-alive timer
232  *
233  * The TCP keep-alive timer feature provides a mechanism to identify dead
234  * connections. The other useful goal of keep-alive is to prevent inactivity
235  * from disconnecting the channel
236  *
237  * @param[in] socket Handle referencing the socket
238  **/
239 
241 {
242 #if (TCP_KEEP_ALIVE_SUPPORT == ENABLED)
243  systime_t time;
244 
245  //Check current TCP state
246  if(socket->state == TCP_STATE_ESTABLISHED)
247  {
248  //Check whether TCP keep-alive mechanism is enabled
249  if(socket->keepAliveEnabled)
250  {
251  //Get current time
252  time = osGetSystemTime();
253 
254  //Idle condition?
255  if(socket->keepAliveProbeCount == 0)
256  {
257  //Check keep-alive idle timer
258  if(timeCompare(time, socket->keepAliveTimestamp +
259  socket->keepAliveIdle) >= 0)
260  {
261  //Keep-alive probes are sent with a sequence number one less than
262  //the sequence number the receiver is expecting
264  socket->rcvNxt, 0, FALSE);
265 
266  //Initialize window probe counter
267  socket->keepAliveProbeCount = 1;
268  //Restart keep-alive timer
269  socket->keepAliveTimestamp = time;
270  }
271  }
272  else
273  {
274  //Check keep-alive probe timer
275  if(timeCompare(time, socket->keepAliveTimestamp +
276  MIN(socket->keepAliveInterval, socket->keepAliveIdle)) >= 0)
277  {
278  //Check the number of unacknowledged keep-alive probes
279  if(socket->keepAliveProbeCount < socket->keepAliveMaxProbes)
280  {
281  //Keep-alive probes are sent with a sequence number one less
282  //than the sequence number the receiver is expecting
284  socket->rcvNxt, 0, FALSE);
285 
286  //Increment window probe counter
287  socket->keepAliveProbeCount++;
288  //Restart keep-alive timer
289  socket->keepAliveTimestamp = time;
290  }
291  else
292  {
293  //Debug message
294  TRACE_WARNING("%s: TCP dead peer detected...\r\n",
296 
297  //Send a reset segment
299  //The TCP connection is dead
301  }
302  }
303  }
304  }
305  }
306 #endif
307 }
308 
309 
310 /**
311  * @brief Check override timer
312  *
313  * To avoid a deadlock, it is necessary to have a timeout to force transmission
314  * of data, overriding the SWS avoidance algorithm. In practice, this timeout
315  * should seldom occur (refer to RFC 1122, section 4.2.3.4)
316  *
317  * @param[in] socket Handle referencing the socket
318  **/
319 
321 {
322  error_t error;
323  uint32_t n;
324  uint32_t u;
325 
326  //Check current TCP state
327  if(socket->state == TCP_STATE_ESTABLISHED ||
328  socket->state == TCP_STATE_CLOSE_WAIT)
329  {
330  //Override timer expired?
331  if(socket->sndUser && netTimerExpired(&socket->overrideTimer))
332  {
333  //The amount of data that can be sent at any given time is limited by
334  //the receiver window and the congestion window
335  n = MIN(socket->sndWnd, socket->txBufferSize);
336 
337 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
338  //Check the congestion window
339  n = MIN(n, socket->cwnd);
340 #endif
341  //Retrieve the size of the usable window
342  u = n - (socket->sndNxt - socket->sndUna);
343 
344  //Send as much data as possible
345  while(socket->sndUser > 0)
346  {
347  //The usable window size may become zero or negative, preventing
348  //packet transmission
349  if((int32_t) u <= 0)
350  break;
351 
352  //Calculate the number of bytes to send at a time
353  n = MIN(u, socket->sndUser);
354  n = MIN(n, socket->smss);
355 
356  //Send TCP segment
358  socket->sndNxt, socket->rcvNxt, n, TRUE);
359  //Failed to send TCP segment?
360  if(error)
361  break;
362 
363  //Advance SND.NXT pointer
364  socket->sndNxt += n;
365  //Adjust the number of bytes buffered but not yet sent
366  socket->sndUser -= n;
367  //Update the size of the usable window
368  u -= n;
369  }
370 
371  //Check whether the transmitter can accept more data
373 
374  //Restart override timer if necessary
375  if(socket->sndUser > 0)
376  {
377  netStartTimer(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT);
378  }
379  }
380  }
381 }
382 
383 
384 /**
385  * @brief Check FIN-WAIT-2 timer
386  *
387  * The FIN-WAIT-2 timer prevents the connection from staying in the FIN-WAIT-2
388  * state forever
389  *
390  * @param[in] socket Handle referencing the socket
391  **/
392 
394 {
395  //Check current TCP state
396  if(socket->state == TCP_STATE_FIN_WAIT_2)
397  {
398  //FIN-WAIT-2 timer expired?
399  if(netTimerExpired(&socket->finWait2Timer))
400  {
401  //Debug message
402  TRACE_INFO("TCP FIN-WAIT-2 timer elapsed...\r\n");
403  //Enter CLOSED state
405  }
406  }
407 }
408 
409 
410 /**
411  * @brief Check 2MSL timer
412  *
413  * The purpose of the TIME-WAIT timer is to prevent delayed packets from one
414  * connection from being accepted by a later connection
415  *
416  * @param[in] socket Handle referencing the socket
417  **/
418 
420 {
421  //Check current TCP state
422  if(socket->state == TCP_STATE_TIME_WAIT)
423  {
424  //2MSL timer expired?
425  if(netTimerExpired(&socket->timeWaitTimer))
426  {
427  //Debug message
428  TRACE_INFO("TCP 2MSL timer elapsed...\r\n");
429  //Enter CLOSED state
431 
432  //Dispose the socket if the user does not have the ownership anymore
433  if(!socket->ownedFlag)
434  {
435  //Delete the TCB
437  //Mark the socket as closed
438  socket->type = SOCKET_TYPE_UNUSED;
439  }
440  }
441  }
442 }
443 
444 #endif
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:65
unsigned int uint_t
Definition: compiler_port.h:50
const char_t * formatSystemTime(systime_t time, char_t *str)
Format system time.
Definition: date_time.c:77
Date and time management.
Debugging facilities.
#define TRACE_WARNING(...)
Definition: debug.h:85
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t n
uint32_t time
error_t
Error codes.
Definition: error.h:43
uint8_t u
Definition: lldp_ext_med.h:213
TCP/IP stack core.
void netStartTimer(NetTimer *timer, systime_t interval)
Start timer.
Definition: net_misc.c:763
bool_t netTimerExpired(NetTimer *timer)
Check whether the timer has expired.
Definition: net_misc.c:803
void netStopTimer(NetTimer *timer)
Stop timer.
Definition: net_misc.c:777
#define timeCompare(t1, t2)
Definition: os_port.h:40
#define MIN(a, b)
Definition: os_port.h:63
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define MAX(a, b)
Definition: os_port.h:67
systime_t osGetSystemTime(void)
Retrieve system time.
uint32_t systime_t
System time.
Socket socketTable[SOCKET_MAX_COUNT]
Definition: socket.c:49
Socket API.
@ SOCKET_TYPE_UNUSED
Definition: socket.h:84
@ SOCKET_TYPE_STREAM
Definition: socket.h:85
#define Socket
Definition: socket.h:36
#define SOCKET_MAX_COUNT
Definition: socket.h:46
TCP (Transmission Control Protocol)
#define TCP_MAX_RETRIES
Definition: tcp.h:110
@ TCP_STATE_CLOSED
Definition: tcp.h:268
@ TCP_STATE_TIME_WAIT
Definition: tcp.h:278
@ TCP_STATE_ESTABLISHED
Definition: tcp.h:272
@ TCP_STATE_CLOSE_WAIT
Definition: tcp.h:273
@ TCP_STATE_FIN_WAIT_2
Definition: tcp.h:276
@ TCP_FLAG_PSH
Definition: tcp.h:303
@ TCP_FLAG_ACK
Definition: tcp.h:304
#define TCP_OVERRIDE_TIMEOUT
Definition: tcp.h:187
#define TCP_MAX_RTO
Definition: tcp.h:131
@ TCP_CONGEST_STATE_LOSS_RECOVERY
Definition: tcp.h:290
#define TCP_MAX_PROBE_INTERVAL
Definition: tcp.h:180
#define TCP_LOSS_WINDOW
Definition: tcp.h:166
void tcpUpdateEvents(Socket *socket)
Update TCP related events.
Definition: tcp_misc.c:2052
error_t tcpSendResetSegment(Socket *socket, uint32_t seqNum)
Send a TCP reset segment.
Definition: tcp_misc.c:371
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:68
error_t tcpRetransmitSegment(Socket *socket)
TCP segment retransmission.
Definition: tcp_misc.c:1744
void tcpChangeState(Socket *socket, TcpState newState)
Update TCP FSM current state.
Definition: tcp_misc.c:2021
void tcpDeleteControlBlock(Socket *socket)
Delete TCB structure.
Definition: tcp_misc.c:1371
Helper functions for TCP.
void tcpCheckOverrideTimer(Socket *socket)
Check override timer.
Definition: tcp_timer.c:320
void tcpCheckPersistTimer(Socket *socket)
Check persist timer.
Definition: tcp_timer.c:183
void tcpCheckRetransmitTimer(Socket *socket)
Check retransmission timer.
Definition: tcp_timer.c:96
void tcpCheckKeepAliveTimer(Socket *socket)
Check TCP keep-alive timer.
Definition: tcp_timer.c:240
void tcpCheckFinWait2Timer(Socket *socket)
Check FIN-WAIT-2 timer.
Definition: tcp_timer.c:393
void tcpCheckTimeWaitTimer(Socket *socket)
Check 2MSL timer.
Definition: tcp_timer.c:419
void tcpTick(void)
TCP timer handler.
Definition: tcp_timer.c:56
TCP timer management.