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