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