]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/TcpDxe/TcpTimer.c
NetworkPkg: Replace BSD License with BSD+Patent License
[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
131 Tcb->Rto = TCP_RTO_MIN;
132 } else if (Tcb->Rto > TCP_RTO_MAX) {
133
134 Tcb->Rto = TCP_RTO_MAX;
135 }
136 }
137
138 /**
139 Connect timeout handler.
140
141 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
142
143 **/
144 VOID
145 TcpConnectTimeout (
146 IN OUT TCP_CB *Tcb
147 )
148 {
149 if (!TCP_CONNECTED (Tcb->State)) {
150 DEBUG (
151 (EFI_D_ERROR,
152 "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n",
153 Tcb)
154 );
155
156 if (EFI_ABORTED == Tcb->Sk->SockError) {
157 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
158 }
159
160 if (TCP_SYN_RCVD == Tcb->State) {
161 DEBUG (
162 (EFI_D_WARN,
163 "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",
164 Tcb)
165 );
166
167 TcpResetConnection (Tcb);
168
169 }
170
171 TcpClose (Tcb);
172 }
173 }
174
175
176 /**
177 Timeout handler for TCP retransmission timer.
178
179 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
180
181 **/
182 VOID
183 TcpRexmitTimeout (
184 IN OUT TCP_CB *Tcb
185 )
186 {
187 UINT32 FlightSize;
188
189 DEBUG (
190 (EFI_D_WARN,
191 "TcpRexmitTimeout: transmission timeout for TCB %p\n",
192 Tcb)
193 );
194
195 //
196 // Set the congestion window. FlightSize is the
197 // amount of data that has been sent but not
198 // yet ACKed.
199 //
200 FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
201 Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);
202
203 Tcb->CWnd = Tcb->SndMss;
204 Tcb->LossRecover = Tcb->SndNxt;
205
206 Tcb->LossTimes++;
207 if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {
208
209 DEBUG (
210 (EFI_D_ERROR,
211 "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",
212 Tcb)
213 );
214
215 if (EFI_ABORTED == Tcb->Sk->SockError) {
216 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
217 }
218
219 TcpClose (Tcb);
220 return ;
221 }
222
223 TcpBackoffRto (Tcb);
224 TcpRetransmit (Tcb, Tcb->SndUna);
225 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
226
227 Tcb->CongestState = TCP_CONGEST_LOSS;
228
229 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
230 }
231
232 /**
233 Timeout handler for window probe timer.
234
235 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
236
237 **/
238 VOID
239 TcpProbeTimeout (
240 IN OUT TCP_CB *Tcb
241 )
242 {
243 //
244 // This is the timer for sender's SWSA. RFC1122 requires
245 // a timer set for sender's SWSA, and suggest combine it
246 // with window probe timer. If data is sent, don't set
247 // the probe timer, since retransmit timer is on.
248 //
249 if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {
250
251 ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);
252 Tcb->ProbeTimerOn = FALSE;
253 return ;
254 }
255
256 TcpSendZeroProbe (Tcb);
257 TcpSetProbeTimer (Tcb);
258 }
259
260 /**
261 Timeout handler for keepalive timer.
262
263 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
264
265 **/
266 VOID
267 TcpKeepaliveTimeout (
268 IN OUT TCP_CB *Tcb
269 )
270 {
271 Tcb->KeepAliveProbes++;
272
273 //
274 // Too many Keep-alive probes, drop the connection
275 //
276 if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {
277
278 if (EFI_ABORTED == Tcb->Sk->SockError) {
279 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
280 }
281
282 TcpClose (Tcb);
283 return ;
284 }
285
286 TcpSendZeroProbe (Tcb);
287 TcpSetKeepaliveTimer (Tcb);
288 }
289
290 /**
291 Timeout handler for FIN_WAIT_2 timer.
292
293 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
294
295 **/
296 VOID
297 TcpFinwait2Timeout (
298 IN OUT TCP_CB *Tcb
299 )
300 {
301 DEBUG (
302 (EFI_D_WARN,
303 "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",
304 Tcb)
305 );
306
307 TcpClose (Tcb);
308 }
309
310 /**
311 Timeout handler for 2MSL timer.
312
313 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
314
315 **/
316 VOID
317 Tcp2MSLTimeout (
318 IN OUT TCP_CB *Tcb
319 )
320 {
321 DEBUG (
322 (EFI_D_WARN,
323 "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",
324 Tcb)
325 );
326
327 TcpClose (Tcb);
328 }
329
330 /**
331 Update the timer status and the next expire time according to the timers
332 to expire in a specific future time slot.
333
334 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
335
336 **/
337 VOID
338 TcpUpdateTimer (
339 IN OUT TCP_CB *Tcb
340 )
341 {
342 UINT16 Index;
343
344 //
345 // Don't use a too large value to init NextExpire
346 // since mTcpTick wraps around as sequence no does.
347 //
348 Tcb->NextExpire = TCP_EXPIRE_TIME;
349 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
350
351 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
352
353 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
354 TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)
355 ) {
356
357 Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);
358 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
359 }
360 }
361 }
362
363 /**
364 Enable a TCP timer.
365
366 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
367 @param[in] Timer The index of the timer to be enabled.
368 @param[in] TimeOut The timeout value of this timer.
369
370 **/
371 VOID
372 TcpSetTimer (
373 IN OUT TCP_CB *Tcb,
374 IN UINT16 Timer,
375 IN UINT32 TimeOut
376 )
377 {
378 TCP_SET_TIMER (Tcb->EnabledTimer, Timer);
379 Tcb->Timer[Timer] = mTcpTick + TimeOut;
380
381 TcpUpdateTimer (Tcb);
382 }
383
384 /**
385 Clear one TCP timer.
386
387 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
388 @param[in] Timer The index of the timer to be cleared.
389
390 **/
391 VOID
392 TcpClearTimer (
393 IN OUT TCP_CB *Tcb,
394 IN UINT16 Timer
395 )
396 {
397 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);
398 TcpUpdateTimer (Tcb);
399 }
400
401 /**
402 Clear all TCP timers.
403
404 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
405
406 **/
407 VOID
408 TcpClearAllTimer (
409 IN OUT TCP_CB *Tcb
410 )
411 {
412 Tcb->EnabledTimer = 0;
413 TcpUpdateTimer (Tcb);
414 }
415
416 /**
417 Enable the window prober timer and set the timeout value.
418
419 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
420
421 **/
422 VOID
423 TcpSetProbeTimer (
424 IN OUT TCP_CB *Tcb
425 )
426 {
427 if (!Tcb->ProbeTimerOn) {
428 Tcb->ProbeTime = Tcb->Rto;
429 Tcb->ProbeTimerOn = TRUE;
430
431 } else {
432 Tcb->ProbeTime <<= 1;
433 }
434
435 if (Tcb->ProbeTime < TCP_RTO_MIN) {
436
437 Tcb->ProbeTime = TCP_RTO_MIN;
438 } else if (Tcb->ProbeTime > TCP_RTO_MAX) {
439
440 Tcb->ProbeTime = TCP_RTO_MAX;
441 }
442
443 TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);
444 }
445
446 /**
447 Enable the keepalive timer and set the timeout value.
448
449 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance.
450
451 **/
452 VOID
453 TcpSetKeepaliveTimer (
454 IN OUT TCP_CB *Tcb
455 )
456 {
457 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {
458 return ;
459
460 }
461
462 //
463 // Set the timer to KeepAliveIdle if either
464 // 1. the keepalive timer is off
465 // 2. The keepalive timer is on, but the idle
466 // is less than KeepAliveIdle, that means the
467 // connection is alive since our last probe.
468 //
469 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||
470 (Tcb->Idle < Tcb->KeepAliveIdle)
471 ) {
472
473 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);
474 Tcb->KeepAliveProbes = 0;
475
476 } else {
477
478 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);
479 }
480 }
481
482 /**
483 Heart beat timer handler.
484
485 @param[in] Context Context of the timer event, ignored.
486
487 **/
488 VOID
489 EFIAPI
490 TcpTickingDpc (
491 IN VOID *Context
492 )
493 {
494 LIST_ENTRY *Entry;
495 LIST_ENTRY *Next;
496 TCP_CB *Tcb;
497 INT16 Index;
498
499 mTcpTick++;
500 mTcpGlobalIss += TCP_ISS_INCREMENT_2;
501
502 //
503 // Don't use LIST_FOR_EACH, which isn't delete safe.
504 //
505 for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {
506
507 Next = Entry->ForwardLink;
508
509 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
510
511 if (Tcb->State == TCP_CLOSED) {
512 continue;
513 }
514 //
515 // The connection is doing RTT measurement.
516 //
517 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
518 Tcb->RttMeasure++;
519 }
520
521 Tcb->Idle++;
522
523 if (Tcb->DelayedAck != 0) {
524 TcpSendAck (Tcb);
525 }
526
527 if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) {
528 Tcb->Tick--;
529 }
530
531 //
532 // No timer is active or no timer expired
533 //
534 if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {
535
536 continue;
537 }
538
539 //
540 // Call the timeout handler for each expired timer.
541 //
542 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
543
544 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {
545 //
546 // disable the timer before calling the handler
547 // in case the handler enables it again.
548 //
549 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);
550 mTcpTimerHandler[Index](Tcb);
551
552 //
553 // The Tcb may have been deleted by the timer, or
554 // no other timer is set.
555 //
556 if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {
557 break;
558 }
559 }
560 }
561
562 //
563 // If the Tcb still exist or some timer is set, update the timer
564 //
565 if (Index == TCP_TIMER_NUMBER) {
566 TcpUpdateTimer (Tcb);
567 }
568 }
569 }
570
571 /**
572 Heart beat timer handler, queues the DPC at TPL_CALLBACK.
573
574 @param[in] Event Timer event signaled, ignored.
575 @param[in] Context Context of the timer event, ignored.
576
577 **/
578 VOID
579 EFIAPI
580 TcpTicking (
581 IN EFI_EVENT Event,
582 IN VOID *Context
583 )
584 {
585 QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
586 }
587