| /** @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); | |
| } | |