]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/TcpDxe/TcpTimer.c
5d2e124977d9251d569bc6d8c9b821de6ce9bdf5
[mirror_edk2.git] / NetworkPkg / TcpDxe / TcpTimer.c
1 /** @file
2 TCP timer related functions.
3
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "TcpMain.h"
11
12 UINT32 mTcpTick = 1000;
13
14 /**
15 Connect timeout handler.
16
17 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
18
19 **/
20 VOID
21 TcpConnectTimeout (
22 IN OUT TCP_CB *Tcb
23 );
24
25 /**
26 Timeout handler for TCP retransmission timer.
27
28 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
29
30 **/
31 VOID
32 TcpRexmitTimeout (
33 IN OUT TCP_CB *Tcb
34 );
35
36 /**
37 Timeout handler for window probe timer.
38
39 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
40
41 **/
42 VOID
43 TcpProbeTimeout (
44 IN OUT TCP_CB *Tcb
45 );
46
47 /**
48 Timeout handler for keepalive timer.
49
50 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
51
52 **/
53 VOID
54 TcpKeepaliveTimeout (
55 IN OUT TCP_CB *Tcb
56 );
57
58 /**
59 Timeout handler for FIN_WAIT_2 timer.
60
61 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
62
63 **/
64 VOID
65 TcpFinwait2Timeout (
66 IN OUT TCP_CB *Tcb
67 );
68
69 /**
70 Timeout handler for 2MSL timer.
71
72 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
73
74 **/
75 VOID
76 Tcp2MSLTimeout (
77 IN OUT TCP_CB *Tcb
78 );
79
80 TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {
81 TcpConnectTimeout,
82 TcpRexmitTimeout,
83 TcpProbeTimeout,
84 TcpKeepaliveTimeout,
85 TcpFinwait2Timeout,
86 Tcp2MSLTimeout,
87 };
88
89 /**
90 Close the TCP connection.
91
92 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
93
94 **/
95 VOID
96 TcpClose (
97 IN OUT TCP_CB *Tcb
98 )
99 {
100 NetbufFreeList (&Tcb->SndQue);
101 NetbufFreeList (&Tcb->RcvQue);
102
103 TcpSetState (Tcb, TCP_CLOSED);
104 }
105
106 /**
107 Backoff the RTO.
108
109 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
110
111 **/
112 VOID
113 TcpBackoffRto (
114 IN OUT TCP_CB *Tcb
115 )
116 {
117 //
118 // Fold the RTT estimate if too many times, the estimate
119 // may be wrong, fold it. So the next time a valid
120 // measurement is sampled, we can start fresh.
121 //
122 if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {
123 Tcb->RttVar += Tcb->SRtt >> 2;
124 Tcb->SRtt = 0;
125 }
126
127 Tcb->Rto <<= 1;
128
129 if (Tcb->Rto < TCP_RTO_MIN) {
130 Tcb->Rto = TCP_RTO_MIN;
131 } else if (Tcb->Rto > TCP_RTO_MAX) {
132 Tcb->Rto = TCP_RTO_MAX;
133 }
134 }
135
136 /**
137 Connect timeout handler.
138
139 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
140
141 **/
142 VOID
143 TcpConnectTimeout (
144 IN OUT TCP_CB *Tcb
145 )
146 {
147 if (!TCP_CONNECTED (Tcb->State)) {
148 DEBUG (
149 (DEBUG_ERROR,
150 "TcpConnectTimeout: connection closed because connection timer timeout for TCB %p\n",
151 Tcb)
152 );
153
154 if (EFI_ABORTED == Tcb->Sk->SockError) {
155 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
156 }
157
158 if (TCP_SYN_RCVD == Tcb->State) {
159 DEBUG (
160 (DEBUG_WARN,
161 "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",
162 Tcb)
163 );
164
165 TcpResetConnection (Tcb);
166 }
167
168 TcpClose (Tcb);
169 }
170 }
171
172 /**
173 Timeout handler for TCP retransmission timer.
174
175 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
176
177 **/
178 VOID
179 TcpRexmitTimeout (
180 IN OUT TCP_CB *Tcb
181 )
182 {
183 UINT32 FlightSize;
184
185 DEBUG (
186 (DEBUG_WARN,
187 "TcpRexmitTimeout: transmission timeout for TCB %p\n",
188 Tcb)
189 );
190
191 //
192 // Set the congestion window. FlightSize is the
193 // amount of data that has been sent but not
194 // yet ACKed.
195 //
196 FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
197 Tcb->Ssthresh = MAX ((UINT32)(2 * Tcb->SndMss), FlightSize / 2);
198
199 Tcb->CWnd = Tcb->SndMss;
200 Tcb->LossRecover = Tcb->SndNxt;
201
202 Tcb->LossTimes++;
203 if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {
204 DEBUG (
205 (DEBUG_ERROR,
206 "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",
207 Tcb)
208 );
209
210 if (EFI_ABORTED == Tcb->Sk->SockError) {
211 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
212 }
213
214 TcpClose (Tcb);
215 return;
216 }
217
218 TcpBackoffRto (Tcb);
219 TcpRetransmit (Tcb, Tcb->SndUna);
220 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
221
222 Tcb->CongestState = TCP_CONGEST_LOSS;
223
224 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
225 }
226
227 /**
228 Timeout handler for window probe timer.
229
230 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
231
232 **/
233 VOID
234 TcpProbeTimeout (
235 IN OUT TCP_CB *Tcb
236 )
237 {
238 //
239 // This is the timer for sender's SWSA. RFC1122 requires
240 // a timer set for sender's SWSA, and suggest combine it
241 // with window probe timer. If data is sent, don't set
242 // the probe timer, since retransmit timer is on.
243 //
244 if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {
245 ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);
246 Tcb->ProbeTimerOn = FALSE;
247 return;
248 }
249
250 TcpSendZeroProbe (Tcb);
251 TcpSetProbeTimer (Tcb);
252 }
253
254 /**
255 Timeout handler for keepalive timer.
256
257 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
258
259 **/
260 VOID
261 TcpKeepaliveTimeout (
262 IN OUT TCP_CB *Tcb
263 )
264 {
265 Tcb->KeepAliveProbes++;
266
267 //
268 // Too many Keep-alive probes, drop the connection
269 //
270 if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {
271 if (EFI_ABORTED == Tcb->Sk->SockError) {
272 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
273 }
274
275 TcpClose (Tcb);
276 return;
277 }
278
279 TcpSendZeroProbe (Tcb);
280 TcpSetKeepaliveTimer (Tcb);
281 }
282
283 /**
284 Timeout handler for FIN_WAIT_2 timer.
285
286 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
287
288 **/
289 VOID
290 TcpFinwait2Timeout (
291 IN OUT TCP_CB *Tcb
292 )
293 {
294 DEBUG (
295 (DEBUG_WARN,
296 "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",
297 Tcb)
298 );
299
300 TcpClose (Tcb);
301 }
302
303 /**
304 Timeout handler for 2MSL timer.
305
306 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
307
308 **/
309 VOID
310 Tcp2MSLTimeout (
311 IN OUT TCP_CB *Tcb
312 )
313 {
314 DEBUG (
315 (DEBUG_WARN,
316 "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",
317 Tcb)
318 );
319
320 TcpClose (Tcb);
321 }
322
323 /**
324 Update the timer status and the next expire time according to the timers
325 to expire in a specific future time slot.
326
327 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
328
329 **/
330 VOID
331 TcpUpdateTimer (
332 IN OUT TCP_CB *Tcb
333 )
334 {
335 UINT16 Index;
336
337 //
338 // Don't use a too large value to init NextExpire
339 // since mTcpTick wraps around as sequence no does.
340 //
341 Tcb->NextExpire = TCP_EXPIRE_TIME;
342 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
343
344 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
345 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
346 TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)
347 )
348 {
349 Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);
350 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
351 }
352 }
353 }
354
355 /**
356 Enable a TCP timer.
357
358 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
359 @param[in] Timer The index of the timer to be enabled.
360 @param[in] TimeOut The timeout value of this timer.
361
362 **/
363 VOID
364 TcpSetTimer (
365 IN OUT TCP_CB *Tcb,
366 IN UINT16 Timer,
367 IN UINT32 TimeOut
368 )
369 {
370 TCP_SET_TIMER (Tcb->EnabledTimer, Timer);
371 Tcb->Timer[Timer] = mTcpTick + TimeOut;
372
373 TcpUpdateTimer (Tcb);
374 }
375
376 /**
377 Clear one TCP timer.
378
379 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
380 @param[in] Timer The index of the timer to be cleared.
381
382 **/
383 VOID
384 TcpClearTimer (
385 IN OUT TCP_CB *Tcb,
386 IN UINT16 Timer
387 )
388 {
389 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);
390 TcpUpdateTimer (Tcb);
391 }
392
393 /**
394 Clear all TCP timers.
395
396 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
397
398 **/
399 VOID
400 TcpClearAllTimer (
401 IN OUT TCP_CB *Tcb
402 )
403 {
404 Tcb->EnabledTimer = 0;
405 TcpUpdateTimer (Tcb);
406 }
407
408 /**
409 Enable the window prober timer and set the timeout value.
410
411 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
412
413 **/
414 VOID
415 TcpSetProbeTimer (
416 IN OUT TCP_CB *Tcb
417 )
418 {
419 if (!Tcb->ProbeTimerOn) {
420 Tcb->ProbeTime = Tcb->Rto;
421 Tcb->ProbeTimerOn = TRUE;
422 } else {
423 Tcb->ProbeTime <<= 1;
424 }
425
426 if (Tcb->ProbeTime < TCP_RTO_MIN) {
427 Tcb->ProbeTime = TCP_RTO_MIN;
428 } else if (Tcb->ProbeTime > TCP_RTO_MAX) {
429 Tcb->ProbeTime = TCP_RTO_MAX;
430 }
431
432 TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);
433 }
434
435 /**
436 Enable the keepalive timer and set the timeout value.
437
438 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
439
440 **/
441 VOID
442 TcpSetKeepaliveTimer (
443 IN OUT TCP_CB *Tcb
444 )
445 {
446 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {
447 return;
448 }
449
450 //
451 // Set the timer to KeepAliveIdle if either
452 // 1. the keepalive timer is off
453 // 2. The keepalive timer is on, but the idle
454 // is less than KeepAliveIdle, that means the
455 // connection is alive since our last probe.
456 //
457 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||
458 (Tcb->Idle < Tcb->KeepAliveIdle)
459 )
460 {
461 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);
462 Tcb->KeepAliveProbes = 0;
463 } else {
464 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);
465 }
466 }
467
468 /**
469 Heart beat timer handler.
470
471 @param[in] Context Context of the timer event, ignored.
472
473 **/
474 VOID
475 EFIAPI
476 TcpTickingDpc (
477 IN VOID *Context
478 )
479 {
480 LIST_ENTRY *Entry;
481 LIST_ENTRY *Next;
482 TCP_CB *Tcb;
483 INT16 Index;
484
485 mTcpTick++;
486 mTcpGlobalIss += TCP_ISS_INCREMENT_2;
487
488 //
489 // Don't use LIST_FOR_EACH, which isn't delete safe.
490 //
491 for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {
492 Next = Entry->ForwardLink;
493
494 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
495
496 if (Tcb->State == TCP_CLOSED) {
497 continue;
498 }
499
500 //
501 // The connection is doing RTT measurement.
502 //
503 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
504 Tcb->RttMeasure++;
505 }
506
507 Tcb->Idle++;
508
509 if (Tcb->DelayedAck != 0) {
510 TcpSendAck (Tcb);
511 }
512
513 if ((Tcb->IpInfo->IpVersion == IP_VERSION_6) && (Tcb->Tick > 0)) {
514 Tcb->Tick--;
515 }
516
517 //
518 // No timer is active or no timer expired
519 //
520 if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {
521 continue;
522 }
523
524 //
525 // Call the timeout handler for each expired timer.
526 //
527 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
528 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {
529 //
530 // disable the timer before calling the handler
531 // in case the handler enables it again.
532 //
533 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);
534 mTcpTimerHandler[Index](Tcb);
535
536 //
537 // The Tcb may have been deleted by the timer, or
538 // no other timer is set.
539 //
540 if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {
541 break;
542 }
543 }
544 }
545
546 //
547 // If the Tcb still exist or some timer is set, update the timer
548 //
549 if (Index == TCP_TIMER_NUMBER) {
550 TcpUpdateTimer (Tcb);
551 }
552 }
553 }
554
555 /**
556 Heart beat timer handler, queues the DPC at TPL_CALLBACK.
557
558 @param[in] Event Timer event signaled, ignored.
559 @param[in] Context Context of the timer event, ignored.
560
561 **/
562 VOID
563 EFIAPI
564 TcpTicking (
565 IN EFI_EVENT Event,
566 IN VOID *Context
567 )
568 {
569 QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
570 }