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-2019 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 1.9.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 "ipv4/ipv4.h"
41 #include "ipv6/ipv6.h"
42 #include "date_time.h"
43 #include "debug.h"
44 
45 //Check TCP/IP stack configuration
46 #if (TCP_SUPPORT == ENABLED)
47 
48 
49 /**
50  * @brief TCP timer handler
51  *
52  * This routine must be periodically called by the TCP/IP stack to
53  * handle retransmissions and TCP related timers (persist timer,
54  * FIN-WAIT-2 timer and TIME-WAIT timer)
55  *
56  **/
57 
58 void tcpTick(void)
59 {
60  error_t error;
61  uint_t i;
62  uint_t n;
63  uint_t u;
64 
65  //Loop through opened sockets
66  for(i = 0; i < SOCKET_MAX_COUNT; i++)
67  {
68  //Shortcut to the current socket
69  Socket *socket = socketTable + i;
70  //Check socket type
71  if(socket->type != SOCKET_TYPE_STREAM)
72  continue;
73  //Check the current state of the TCP state machine
74  if(socket->state == TCP_STATE_CLOSED)
75  continue;
76 
77  //Is there any packet in the retransmission queue?
78  if(socket->retransmitQueue != NULL)
79  {
80  //Retransmission timeout?
81  if(tcpTimerElapsed(&socket->retransmitTimer))
82  {
83 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
84  //When a TCP sender detects segment loss using the retransmission
85  //timer and the given segment has not yet been resent by way of
86  //the retransmission timer, the value of ssthresh must be updated
87  if(!socket->retransmitCount)
88  {
89  //Amount of data that has been sent but not yet acknowledged
90  uint_t flightSize = socket->sndNxt - socket->sndUna;
91  //Adjust ssthresh value
92  socket->ssthresh = MAX(flightSize / 2, 2 * socket->smss);
93  }
94 
95  //Furthermore, upon a timeout cwnd must be set to no more than
96  //the loss window, LW, which equals 1 full-sized segment
97  socket->cwnd = MIN(TCP_LOSS_WINDOW * socket->smss, socket->txBufferSize);
98 
99  //After a retransmit timeout, record the highest sequence number
100  //transmitted in the variable recover
101  socket->recover = socket->sndNxt - 1;
102 
103  //Enter the fast loss recovery procedure
104  socket->congestState = TCP_CONGEST_STATE_LOSS_RECOVERY;
105 #endif
106  //Make sure the maximum number of retransmissions has not been reached
107  if(socket->retransmitCount < TCP_MAX_RETRIES)
108  {
109  //Debug message
110  TRACE_INFO("%s: TCP segment retransmission #%u (%u data bytes)...\r\n",
111  formatSystemTime(osGetSystemTime(), NULL), socket->retransmitCount + 1,
112  socket->retransmitQueue->length);
113 
114  //Retransmit the earliest segment that has not been
115  //acknowledged by the TCP receiver
117 
118  //Use exponential back-off algorithm to calculate the new RTO
119  socket->rto = MIN(socket->rto * 2, TCP_MAX_RTO);
120  //Restart retransmission timer
121  tcpTimerStart(&socket->retransmitTimer, socket->rto);
122  //Increment retransmission counter
123  socket->retransmitCount++;
124  }
125  else
126  {
127  //The maximum number of retransmissions has been exceeded
129  //Turn off the retransmission timer
130  tcpTimerStop(&socket->retransmitTimer);
131  }
132 
133  //TCP must use Karn's algorithm for taking RTT samples. That is, RTT
134  //samples must not be made using segments that were retransmitted
135  socket->rttBusy = FALSE;
136  }
137  }
138 
139  //Check the current state of the TCP state machine
140  if(socket->state == TCP_STATE_CLOSED)
141  continue;
142 
143  //The persist timer is used when the remote host advertises
144  //a window size of zero
145  if(!socket->sndWnd && socket->wndProbeInterval)
146  {
147  //Time to send a new probe?
148  if(tcpTimerElapsed(&socket->persistTimer))
149  {
150  //Make sure the maximum number of retransmissions has not been reached
151  if(socket->wndProbeCount < TCP_MAX_RETRIES)
152  {
153  //Debug message
154  TRACE_INFO("%s: TCP zero window probe #%u...\r\n",
155  formatSystemTime(osGetSystemTime(), NULL), socket->wndProbeCount + 1);
156 
157  //Zero window probes usually have the sequence number one less than expected
158  tcpSendSegment(socket, TCP_FLAG_ACK, socket->sndNxt - 1, socket->rcvNxt, 0, FALSE);
159  //The interval between successive probes should be increased exponentially
160  socket->wndProbeInterval = MIN(socket->wndProbeInterval * 2, TCP_MAX_PROBE_INTERVAL);
161  //Restart the persist timer
162  tcpTimerStart(&socket->persistTimer, socket->wndProbeInterval);
163  //Increment window probe counter
164  socket->wndProbeCount++;
165  }
166  else
167  {
168  //Enter CLOSED state
170  }
171  }
172  }
173 
174  //To avoid a deadlock, it is necessary to have a timeout to force
175  //transmission of data, overriding the SWS avoidance algorithm. In
176  //practice, this timeout should seldom occur (refer to RFC 1122,
177  //section 4.2.3.4)
178  if(socket->state == TCP_STATE_ESTABLISHED || socket->state == TCP_STATE_CLOSE_WAIT)
179  {
180  //The override timeout occurred?
181  if(socket->sndUser && tcpTimerElapsed(&socket->overrideTimer))
182  {
183  //The amount of data that can be sent at any given time is
184  //limited by the receiver window and the congestion window
185  n = MIN(socket->sndWnd, socket->txBufferSize);
186 
187 #if (TCP_CONGEST_CONTROL_SUPPORT == ENABLED)
188  //Check the congestion window
189  n = MIN(n, socket->cwnd);
190 #endif
191  //Retrieve the size of the usable window
192  u = n - (socket->sndNxt - socket->sndUna);
193 
194  //Send as much data as possible
195  while(socket->sndUser > 0)
196  {
197  //The usable window size may become zero or negative,
198  //preventing packet transmission
199  if((int_t) u <= 0)
200  break;
201 
202  //Calculate the number of bytes to send at a time
203  n = MIN(u, socket->sndUser);
204  n = MIN(n, socket->smss);
205 
206  //Send TCP segment
208  socket->sndNxt, socket->rcvNxt, n, TRUE);
209  //Failed to send TCP segment?
210  if(error)
211  break;
212 
213  //Advance SND.NXT pointer
214  socket->sndNxt += n;
215  //Adjust the number of bytes buffered but not yet sent
216  socket->sndUser -= n;
217  //Update the size of the usable window
218  u -= n;
219  }
220 
221  //Check whether the transmitter can accept more data
223 
224  //Restart override timer if necessary
225  if(socket->sndUser > 0)
226  tcpTimerStart(&socket->overrideTimer, TCP_OVERRIDE_TIMEOUT);
227  }
228  }
229 
230  //The FIN-WAIT-2 timer prevents the connection
231  //from staying in the FIN-WAIT-2 state forever
232  if(socket->state == TCP_STATE_FIN_WAIT_2)
233  {
234  //Maximum FIN-WAIT-2 time has elapsed?
235  if(tcpTimerElapsed(&socket->finWait2Timer))
236  {
237  //Debug message
238  TRACE_WARNING("TCP FIN-WAIT-2 timer elapsed...\r\n");
239  //Enter CLOSED state
241  }
242  }
243 
244  //TIME-WAIT timer
245  if(socket->state == TCP_STATE_TIME_WAIT)
246  {
247  //2MSL time has elapsed?
248  if(tcpTimerElapsed(&socket->timeWaitTimer))
249  {
250  //Debug message
251  TRACE_WARNING("TCP 2MSL timer elapsed (socket %u)...\r\n", i);
252  //Enter CLOSED state
254 
255  //Dispose the socket if the user does not have the ownership anymore
256  if(!socket->ownedFlag)
257  {
258  //Delete the TCB
260  //Mark the socket as closed
261  socket->type = SOCKET_TYPE_UNUSED;
262  }
263  }
264  }
265  }
266 }
267 
268 
269 /**
270  * @brief Start TCP timer
271  * @param[in] timer Pointer to the timer structure
272  * @param[in] delay Time interval
273  **/
274 
275 void tcpTimerStart(TcpTimer *timer, systime_t delay)
276 {
277  //Start timer
278  timer->startTime = osGetSystemTime();
279  timer->interval = delay;
280 
281  //The timer is now running...
282  timer->running = TRUE;
283 }
284 
285 
286 /**
287  * @brief Stop TCP timer
288  * @param[in] timer Pointer to the timer structure
289  **/
290 
291 void tcpTimerStop(TcpTimer *timer)
292 {
293  //Stop timer
294  timer->running = FALSE;
295 }
296 
297 
298 /**
299  * @brief Check whether a TCP timer is running
300  * @param[in] timer Pointer to the timer structure
301  * @return Timer state
302  **/
303 
305 {
306  //Check whether the timer is running
307  return timer->running;
308 }
309 
310 
311 /**
312  * @brief Check whether a TCP timer has elapsed
313  * @param[in] timer Pointer to the timer structure
314  * @return Timer state
315  **/
316 
318 {
319  systime_t time;
320 
321  //Check whether the timer is running
322  if(!timer->running)
323  return FALSE;
324 
325  //Get current time
326  time = osGetSystemTime();
327 
328  //Check whether the specified time interval has elapsed
329  if(timeCompare(time, timer->startTime + timer->interval) >= 0)
330  return TRUE;
331  else
332  return FALSE;
333 }
334 
335 
336 /**
337  * @brief Get current time interval
338  * @param[in] timer Pointer to the timer structure
339  * @return Current time interval
340  **/
341 
343 {
344  //Return current time interval
345  return timer->interval;
346 }
347 
348 #endif
IPv6 (Internet Protocol Version 6)
Date and time management.
@ TCP_STATE_TIME_WAIT
Definition: tcp.h:243
int bool_t
Definition: compiler_port.h:49
#define TCP_MAX_RTO
Definition: tcp.h:131
signed int int_t
Definition: compiler_port.h:44
#define TCP_OVERRIDE_TIMEOUT
Definition: tcp.h:180
#define TRUE
Definition: os_port.h:50
@ TCP_STATE_CLOSE_WAIT
Definition: tcp.h:238
void tcpTimerStop(TcpTimer *timer)
Stop TCP timer.
Definition: tcp_timer.c:291
systime_t interval
Definition: tcp.h:350
const char_t * formatSystemTime(systime_t time, char_t *str)
Format system time.
Definition: date_time.c:77
@ SOCKET_TYPE_STREAM
Definition: socket.h:77
@ TCP_FLAG_ACK
Definition: tcp.h:269
#define timeCompare(t1, t2)
Definition: os_port.h:42
void tcpDeleteControlBlock(Socket *socket)
Delete TCB structure.
Definition: tcp_misc.c:1130
#define TCP_MAX_PROBE_INTERVAL
Definition: tcp.h:173
#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:1725
error_t
Error codes.
Definition: error.h:42
@ TCP_CONGEST_STATE_LOSS_RECOVERY
Definition: tcp.h:255
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:108
error_t tcpRetransmitSegment(Socket *socket)
TCP segment retransmission.
Definition: tcp_misc.c:1493
void tcpUpdateEvents(Socket *socket)
Update TCP related events.
Definition: tcp_misc.c:1756
bool_t tcpTimerRunning(TcpTimer *timer)
Check whether a TCP timer is running.
Definition: tcp_timer.c:304
#define TRACE_INFO(...)
Definition: debug.h:94
#define MIN(a, b)
Definition: os_port.h:62
Socket socketTable[SOCKET_MAX_COUNT]
Definition: socket.c:49
@ TCP_STATE_CLOSED
Definition: tcp.h:233
#define TRACE_WARNING(...)
Definition: debug.h:84
#define MAX(a, b)
Definition: os_port.h:66
systime_t startTime
Definition: tcp.h:349
uint32_t time
systime_t tcpTimerGetInterval(TcpTimer *timer)
Get current time interval.
Definition: tcp_timer.c:342
uint8_t n
TCP (Transmission Control Protocol)
@ SOCKET_TYPE_UNUSED
Definition: socket.h:76
#define TCP_MAX_RETRIES
Definition: tcp.h:110
#define Socket
Definition: socket.h:36
@ TCP_STATE_FIN_WAIT_2
Definition: tcp.h:241
TCP timer.
Definition: tcp.h:346
bool_t running
Definition: tcp.h:348
Socket API.
bool_t tcpTimerElapsed(TcpTimer *timer)
Check whether a TCP timer has elapsed.
Definition: tcp_timer.c:317
void tcpTick(void)
TCP timer handler.
Definition: tcp_timer.c:58
void tcpTimerStart(TcpTimer *timer, systime_t delay)
Start TCP timer.
Definition: tcp_timer.c:275
IPv4 (Internet Protocol Version 4)
TCP timer management.
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
#define SOCKET_MAX_COUNT
Definition: socket.h:45
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:65
uint32_t systime_t
Definition: compiler_port.h:46
@ TCP_STATE_ESTABLISHED
Definition: tcp.h:237
Debugging facilities.
#define TCP_LOSS_WINDOW
Definition: tcp.h:159
@ TCP_FLAG_PSH
Definition: tcp.h:268
systime_t osGetSystemTime(void)
Retrieve system time.