git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			594 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			594 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   TCP timer related functions.
 | |
| 
 | |
|   Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
 | |
| 
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php.
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "TcpMain.h"
 | |
| 
 | |
| UINT32    mTcpTick = 1000;
 | |
| 
 | |
| /**
 | |
|   Connect timeout handler.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpConnectTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Timeout handler for TCP retransmission timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpRexmitTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Timeout handler for window probe timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpProbeTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Timeout handler for keepalive timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpKeepaliveTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Timeout handler for FIN_WAIT_2 timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpFinwait2Timeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   );
 | |
| 
 | |
| /**
 | |
|   Timeout handler for 2MSL timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Tcp2MSLTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   );
 | |
| 
 | |
| TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = {
 | |
|   TcpConnectTimeout,
 | |
|   TcpRexmitTimeout,
 | |
|   TcpProbeTimeout,
 | |
|   TcpKeepaliveTimeout,
 | |
|   TcpFinwait2Timeout,
 | |
|   Tcp2MSLTimeout,
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Close the TCP connection.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpClose (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   NetbufFreeList (&Tcb->SndQue);
 | |
|   NetbufFreeList (&Tcb->RcvQue);
 | |
| 
 | |
|   TcpSetState (Tcb, TCP_CLOSED);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Backoff the RTO.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpBackoffRto (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Fold the RTT estimate if too many times, the estimate
 | |
|   // may be wrong, fold it. So the next time a valid
 | |
|   // measurement is sampled, we can start fresh.
 | |
|   //
 | |
|   if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) {
 | |
|     Tcb->RttVar += Tcb->SRtt >> 2;
 | |
|     Tcb->SRtt = 0;
 | |
|   }
 | |
| 
 | |
|   Tcb->Rto <<= 1;
 | |
| 
 | |
|   if (Tcb->Rto < TCP_RTO_MIN) {
 | |
| 
 | |
|     Tcb->Rto = TCP_RTO_MIN;
 | |
|   } else if (Tcb->Rto > TCP_RTO_MAX) {
 | |
| 
 | |
|     Tcb->Rto = TCP_RTO_MAX;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Connect timeout handler.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpConnectTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   if (!TCP_CONNECTED (Tcb->State)) {
 | |
|     DEBUG (
 | |
|       (EFI_D_ERROR,
 | |
|       "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n",
 | |
|       Tcb)
 | |
|       );
 | |
| 
 | |
|     if (EFI_ABORTED == Tcb->Sk->SockError) {
 | |
|       SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
 | |
|     }
 | |
| 
 | |
|     if (TCP_SYN_RCVD == Tcb->State) {
 | |
|       DEBUG (
 | |
|         (EFI_D_WARN,
 | |
|         "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n",
 | |
|         Tcb)
 | |
|         );
 | |
| 
 | |
|       TcpResetConnection (Tcb);
 | |
| 
 | |
|     }
 | |
| 
 | |
|     TcpClose (Tcb);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Timeout handler for TCP retransmission timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpRexmitTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   UINT32  FlightSize;
 | |
| 
 | |
|   DEBUG (
 | |
|     (EFI_D_WARN,
 | |
|     "TcpRexmitTimeout: transmission timeout for TCB %p\n",
 | |
|     Tcb)
 | |
|     );
 | |
| 
 | |
|   //
 | |
|   // Set the congestion window. FlightSize is the
 | |
|   // amount of data that has been sent but not
 | |
|   // yet ACKed.
 | |
|   //
 | |
|   FlightSize        = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna);
 | |
|   Tcb->Ssthresh     = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2);
 | |
| 
 | |
|   Tcb->CWnd         = Tcb->SndMss;
 | |
|   Tcb->LossRecover  = Tcb->SndNxt;
 | |
| 
 | |
|   Tcb->LossTimes++;
 | |
|   if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) {
 | |
| 
 | |
|     DEBUG (
 | |
|       (EFI_D_ERROR,
 | |
|       "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n",
 | |
|       Tcb)
 | |
|       );
 | |
| 
 | |
|     if (EFI_ABORTED == Tcb->Sk->SockError) {
 | |
|       SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
 | |
|     }
 | |
| 
 | |
|     TcpClose (Tcb);
 | |
|     return ;
 | |
|   }
 | |
| 
 | |
|   TcpBackoffRto (Tcb);
 | |
|   TcpRetransmit (Tcb, Tcb->SndUna);
 | |
|   TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
 | |
| 
 | |
|   Tcb->CongestState = TCP_CONGEST_LOSS;
 | |
| 
 | |
|   TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Timeout handler for window probe timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpProbeTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // This is the timer for sender's SWSA. RFC1122 requires
 | |
|   // a timer set for sender's SWSA, and suggest combine it
 | |
|   // with window probe timer. If data is sent, don't set
 | |
|   // the probe timer, since retransmit timer is on.
 | |
|   //
 | |
|   if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) {
 | |
| 
 | |
|     ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0);
 | |
|     Tcb->ProbeTimerOn = FALSE;
 | |
|     return ;
 | |
|   }
 | |
| 
 | |
|   TcpSendZeroProbe (Tcb);
 | |
|   TcpSetProbeTimer (Tcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Timeout handler for keepalive timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpKeepaliveTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   Tcb->KeepAliveProbes++;
 | |
| 
 | |
|   //
 | |
|   // Too many Keep-alive probes, drop the connection
 | |
|   //
 | |
|   if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) {
 | |
| 
 | |
|     if (EFI_ABORTED == Tcb->Sk->SockError) {
 | |
|       SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT);
 | |
|     }
 | |
| 
 | |
|     TcpClose (Tcb);
 | |
|     return ;
 | |
|   }
 | |
| 
 | |
|   TcpSendZeroProbe (Tcb);
 | |
|   TcpSetKeepaliveTimer (Tcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Timeout handler for FIN_WAIT_2 timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpFinwait2Timeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   DEBUG (
 | |
|     (EFI_D_WARN,
 | |
|     "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n",
 | |
|     Tcb)
 | |
|     );
 | |
| 
 | |
|   TcpClose (Tcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Timeout handler for 2MSL timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Tcp2MSLTimeout (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   DEBUG (
 | |
|     (EFI_D_WARN,
 | |
|     "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n",
 | |
|     Tcb)
 | |
|     );
 | |
| 
 | |
|   TcpClose (Tcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Update the timer status and the next expire time according to the timers
 | |
|   to expire in a specific future time slot.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpUpdateTimer (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   UINT16  Index;
 | |
| 
 | |
|   //
 | |
|   // Don't use a too large value to init NextExpire
 | |
|   // since mTcpTick wraps around as sequence no does.
 | |
|   //
 | |
|   Tcb->NextExpire = TCP_EXPIRE_TIME;
 | |
|   TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
 | |
| 
 | |
|   for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
 | |
| 
 | |
|     if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) &&
 | |
|         TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire)
 | |
|         ) {
 | |
| 
 | |
|       Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick);
 | |
|       TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable a TCP timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]       Timer    The index of the timer to be enabled.
 | |
|   @param[in]       TimeOut  The timeout value of this timer.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpSetTimer (
 | |
|   IN OUT TCP_CB *Tcb,
 | |
|   IN     UINT16 Timer,
 | |
|   IN     UINT32 TimeOut
 | |
|   )
 | |
| {
 | |
|   TCP_SET_TIMER (Tcb->EnabledTimer, Timer);
 | |
|   Tcb->Timer[Timer] = mTcpTick + TimeOut;
 | |
| 
 | |
|   TcpUpdateTimer (Tcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear one TCP timer.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
|   @param[in]       Timer    The index of the timer to be cleared.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpClearTimer (
 | |
|   IN OUT TCP_CB *Tcb,
 | |
|   IN     UINT16 Timer
 | |
|   )
 | |
| {
 | |
|   TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer);
 | |
|   TcpUpdateTimer (Tcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clear all TCP timers.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpClearAllTimer (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   Tcb->EnabledTimer = 0;
 | |
|   TcpUpdateTimer (Tcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable the window prober timer and set the timeout value.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpSetProbeTimer (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   if (!Tcb->ProbeTimerOn) {
 | |
|     Tcb->ProbeTime    = Tcb->Rto;
 | |
|     Tcb->ProbeTimerOn = TRUE;
 | |
| 
 | |
|   } else {
 | |
|     Tcb->ProbeTime <<= 1;
 | |
|   }
 | |
| 
 | |
|   if (Tcb->ProbeTime < TCP_RTO_MIN) {
 | |
| 
 | |
|     Tcb->ProbeTime = TCP_RTO_MIN;
 | |
|   } else if (Tcb->ProbeTime > TCP_RTO_MAX) {
 | |
| 
 | |
|     Tcb->ProbeTime = TCP_RTO_MAX;
 | |
|   }
 | |
| 
 | |
|   TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable the keepalive timer and set the timeout value.
 | |
| 
 | |
|   @param[in, out]  Tcb      Pointer to the TCP_CB of this TCP instance.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| TcpSetKeepaliveTimer (
 | |
|   IN OUT TCP_CB *Tcb
 | |
|   )
 | |
| {
 | |
|   if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) {
 | |
|     return ;
 | |
| 
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set the timer to KeepAliveIdle if either
 | |
|   // 1. the keepalive timer is off
 | |
|   // 2. The keepalive timer is on, but the idle
 | |
|   // is less than KeepAliveIdle, that means the
 | |
|   // connection is alive since our last probe.
 | |
|   //
 | |
|   if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) ||
 | |
|       (Tcb->Idle < Tcb->KeepAliveIdle)
 | |
|       ) {
 | |
| 
 | |
|     TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle);
 | |
|     Tcb->KeepAliveProbes = 0;
 | |
| 
 | |
|   } else {
 | |
| 
 | |
|     TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Heart beat timer handler.
 | |
| 
 | |
|   @param[in]  Context        Context of the timer event, ignored.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| TcpTickingDpc (
 | |
|   IN VOID       *Context
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY      *Entry;
 | |
|   LIST_ENTRY      *Next;
 | |
|   TCP_CB          *Tcb;
 | |
|   INT16           Index;
 | |
| 
 | |
|   mTcpTick++;
 | |
|   mTcpGlobalIss += TCP_ISS_INCREMENT_2;
 | |
| 
 | |
|   //
 | |
|   // Don't use LIST_FOR_EACH, which isn't delete safe.
 | |
|   //
 | |
|   for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) {
 | |
| 
 | |
|     Next  = Entry->ForwardLink;
 | |
| 
 | |
|     Tcb   = NET_LIST_USER_STRUCT (Entry, TCP_CB, List);
 | |
| 
 | |
|     if (Tcb->State == TCP_CLOSED) {
 | |
|       continue;
 | |
|     }
 | |
|     //
 | |
|     // The connection is doing RTT measurement.
 | |
|     //
 | |
|     if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
 | |
|       Tcb->RttMeasure++;
 | |
|     }
 | |
| 
 | |
|     Tcb->Idle++;
 | |
| 
 | |
|     if (Tcb->DelayedAck != 0) {
 | |
|       TcpSendAck (Tcb);
 | |
|     }
 | |
| 
 | |
|     if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) {
 | |
|       Tcb->Tick--;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // No timer is active or no timer expired
 | |
|     //
 | |
|     if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) {
 | |
| 
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Call the timeout handler for each expired timer.
 | |
|     //
 | |
|     for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) {
 | |
| 
 | |
|       if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) {
 | |
|         //
 | |
|         // disable the timer before calling the handler
 | |
|         // in case the handler enables it again.
 | |
|         //
 | |
|         TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index);
 | |
|         mTcpTimerHandler[Index](Tcb);
 | |
| 
 | |
|         //
 | |
|         // The Tcb may have been deleted by the timer, or
 | |
|         // no other timer is set.
 | |
|         //
 | |
|         if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // If the Tcb still exist or some timer is set, update the timer
 | |
|     //
 | |
|     if (Index == TCP_TIMER_NUMBER) {
 | |
|       TcpUpdateTimer (Tcb);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Heart beat timer handler, queues the DPC at TPL_CALLBACK.
 | |
| 
 | |
|   @param[in]  Event    Timer event signaled, ignored.
 | |
|   @param[in]  Context  Context of the timer event, ignored.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| TcpTicking (
 | |
|   IN EFI_EVENT Event,
 | |
|   IN VOID      *Context
 | |
|   )
 | |
| {
 | |
|   QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context);
 | |
| }
 | |
| 
 |