Home | History | Annotate | Download | only in TcpDxe
      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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    586 TcpTicking (
    587   IN EFI_EVENT Event,
    588   IN VOID      *Context
    589   )
    590 {
    591   QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
    592 }
    593 
    594