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