mirror of https://github.com/acidanthera/audk.git
571 lines
12 KiB
C
571 lines
12 KiB
C
/** @file
|
|
TCP timer related functions.
|
|
|
|
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#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 (
|
|
(DEBUG_ERROR,
|
|
"TcpConnectTimeout: connection closed because connection 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 (
|
|
(DEBUG_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 (
|
|
(DEBUG_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 (
|
|
(DEBUG_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 (
|
|
(DEBUG_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 (
|
|
(DEBUG_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);
|
|
}
|