1/** @file
2  TCP timer related functions.
3
4Copyright (c) 2005 - 2010, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php<BR>
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "Tcp4Main.h"
16
17UINT32    mTcpTick = 1000;
18
19/**
20  Connect timeout handler.
21
22  @param  Tcb      Pointer to the TCP_CB of this TCP instance.
23
24**/
25VOID
26TcpConnectTimeout (
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**/
36VOID
37TcpRexmitTimeout (
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**/
47VOID
48TcpProbeTimeout (
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**/
58VOID
59TcpKeepaliveTimeout (
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**/
69VOID
70TcpFinwait2Timeout (
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**/
80VOID
81Tcp2MSLTimeout (
82  IN OUT TCP_CB *Tcb
83  );
84
85TCP_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**/
100VOID
101TcpClose (
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**/
118VOID
119TcpConnectTimeout (
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**/
150VOID
151TcpRexmitTimeout (
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**/
201VOID
202TcpProbeTimeout (
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**/
230VOID
231TcpKeepaliveTimeout (
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**/
261VOID
262TcpFinwait2Timeout (
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**/
279VOID
280Tcp2MSLTimeout (
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**/
298VOID
299TcpUpdateTimer (
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**/
332VOID
333TcpSetTimer (
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**/
353VOID
354TcpClearTimer (
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**/
370VOID
371TcpClearAllTimer (
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**/
386VOID
387TcpSetProbeTimer (
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**/
417VOID
418TcpSetKeepaliveTimer (
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**/
453VOID
454TcpBackoffRto (
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**/
486VOID
487EFIAPI
488TcpTickingDpc (
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**/
575VOID
576EFIAPI
577TcpTicking (
578  IN EFI_EVENT Event,
579  IN VOID      *Context
580  )
581{
582  QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
583}
584
585