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