/** NewReno fast recovery, RFC3782. @param Tcb Pointer to the TCP_CB of this TCP instance. @param Seg Segment that triggers the fast recovery. **/ VOID TcpFastRecover ( IN OUT TCP_CB *Tcb, IN TCP_SEG *Seg ) { UINT32 FlightSize; UINT32 Acked; // // Step 1: Three duplicate ACKs and not in fast recovery // if (Tcb->CongestState != TCP_CONGEST_RECOVER) { // // Step 1A: Invoking fast retransmission. // FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss)); Tcb->Recover = Tcb->SndNxt; Tcb->CongestState = TCP_CONGEST_RECOVER; TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); // // Step 2: Entering fast retransmission // TcpRetransmit (Tcb, Tcb->SndUna); Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss; DEBUG ((EFI_D_INFO, "TcpFastRecover: enter fast retransmission" " for TCB %p, recover point is %d\n", Tcb, Tcb->Recover)); return; }
/** Timeout handler for TCP retransmission timer. @param 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); }
/** Update the timer status and the next expire time according to the timers to expire in a specific future time slot. @param 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 = 65535; 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); } } }
/** Configure the Pcb using CfgData. @param Sk Pointer to the socket of this TCP instance. @param CfgData Pointer to the TCP configuration data. @retval EFI_SUCCESS The operation is completed successfully. @retval EFI_INVALID_PARAMETER A same access point has been configured in another TCP instance. @retval EFI_OUT_OF_RESOURCES Failed due to resource limit. **/ EFI_STATUS Tcp4ConfigurePcb ( IN SOCKET *Sk, IN EFI_TCP4_CONFIG_DATA *CfgData ) { EFI_IP4_CONFIG_DATA IpCfgData; EFI_STATUS Status; EFI_TCP4_OPTION *Option; TCP4_PROTO_DATA *TcpProto; TCP_CB *Tcb; ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL)); TcpProto = (TCP4_PROTO_DATA *) Sk->ProtoReserved; Tcb = TcpProto->TcpPcb; ASSERT (Tcb != NULL); // // Add Ip for send pkt to the peer // CopyMem (&IpCfgData, &mIp4IoDefaultIpConfigData, sizeof (IpCfgData)); IpCfgData.DefaultProtocol = EFI_IP_PROTO_TCP; IpCfgData.UseDefaultAddress = CfgData->AccessPoint.UseDefaultAddress; IpCfgData.StationAddress = CfgData->AccessPoint.StationAddress; IpCfgData.SubnetMask = CfgData->AccessPoint.SubnetMask; IpCfgData.ReceiveTimeout = (UINT32) (-1); // // Configure the IP instance this Tcb consumes. // Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData); if (EFI_ERROR (Status)) { goto OnExit; } // // Get the default address info if the instance is configured to use default address. // if (CfgData->AccessPoint.UseDefaultAddress) { CfgData->AccessPoint.StationAddress = IpCfgData.StationAddress; CfgData->AccessPoint.SubnetMask = IpCfgData.SubnetMask; } // // check if we can bind this endpoint in CfgData // Status = Tcp4Bind (&(CfgData->AccessPoint)); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "Tcp4ConfigurePcb: Bind endpoint failed " "with %r\n", Status)); goto OnExit; } // // Initalize the operating information in this Tcb // ASSERT (Tcb->State == TCP_CLOSED && IsListEmpty (&Tcb->SndQue) && IsListEmpty (&Tcb->RcvQue)); TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); Tcb->State = TCP_CLOSED; Tcb->SndMss = 536; Tcb->RcvMss = TcpGetRcvMss (Sk); Tcb->SRtt = 0; Tcb->Rto = 3 * TCP_TICK_HZ; Tcb->CWnd = Tcb->SndMss; Tcb->Ssthresh = 0xffffffff; Tcb->CongestState = TCP_CONGEST_OPEN; Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN; Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD; Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE; Tcb->MaxRexmit = TCP_MAX_LOSS; Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME; Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME; Tcb->ConnectTimeout = TCP_CONNECT_TIME; // // initialize Tcb in the light of CfgData // Tcb->Ttl = CfgData->TimeToLive; Tcb->Tos = CfgData->TypeOfService; Tcb->UseDefaultAddr = CfgData->AccessPoint.UseDefaultAddress; CopyMem (&Tcb->LocalEnd.Ip, &CfgData->AccessPoint.StationAddress, sizeof (IP4_ADDR)); Tcb->LocalEnd.Port = HTONS (CfgData->AccessPoint.StationPort); IP4_COPY_ADDRESS (&Tcb->SubnetMask, &CfgData->AccessPoint.SubnetMask); if (CfgData->AccessPoint.ActiveFlag) { CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort); } else { Tcb->RemoteEnd.Ip = 0; Tcb->RemoteEnd.Port = 0; } Option = CfgData->ControlOption; if (Option != NULL) { SET_RCV_BUFFSIZE ( Sk, (UINT32) (TCP_COMP_VAL ( TCP_RCV_BUF_SIZE_MIN, TCP_RCV_BUF_SIZE, TCP_RCV_BUF_SIZE, Option->ReceiveBufferSize ) ) ); SET_SND_BUFFSIZE ( Sk, (UINT32) (TCP_COMP_VAL ( TCP_SND_BUF_SIZE_MIN, TCP_SND_BUF_SIZE, TCP_SND_BUF_SIZE, Option->SendBufferSize ) ) ); SET_BACKLOG ( Sk, (UINT32) (TCP_COMP_VAL ( TCP_BACKLOG_MIN, TCP_BACKLOG, TCP_BACKLOG, Option->MaxSynBackLog ) ) ); Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL ( TCP_MAX_LOSS_MIN, TCP_MAX_LOSS, TCP_MAX_LOSS, Option->DataRetries ); Tcb->FinWait2Timeout = TCP_COMP_VAL ( TCP_FIN_WAIT2_TIME, TCP_FIN_WAIT2_TIME_MAX, TCP_FIN_WAIT2_TIME, (UINT32) (Option->FinTimeout * TCP_TICK_HZ) ); if (Option->TimeWaitTimeout != 0) { Tcb->TimeWaitTimeout = TCP_COMP_VAL ( TCP_TIME_WAIT_TIME, TCP_TIME_WAIT_TIME_MAX, TCP_TIME_WAIT_TIME, (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ) ); } else { Tcb->TimeWaitTimeout = 0; } if (Option->KeepAliveProbes != 0) { TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL ( TCP_MAX_KEEPALIVE_MIN, TCP_MAX_KEEPALIVE, TCP_MAX_KEEPALIVE, Option->KeepAliveProbes ); Tcb->KeepAliveIdle = TCP_COMP_VAL ( TCP_KEEPALIVE_IDLE_MIN, TCP_KEEPALIVE_IDLE_MAX, TCP_KEEPALIVE_IDLE_MIN, (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ) ); Tcb->KeepAlivePeriod = TCP_COMP_VAL ( TCP_KEEPALIVE_PERIOD_MIN, TCP_KEEPALIVE_PERIOD, TCP_KEEPALIVE_PERIOD, (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ) ); } Tcb->ConnectTimeout = TCP_COMP_VAL ( TCP_CONNECT_TIME_MIN, TCP_CONNECT_TIME, TCP_CONNECT_TIME, (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ) ); if (!Option->EnableNagle) { TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); } if (!Option->EnableTimeStamp) { TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS); } if (!Option->EnableWindowScaling) { TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS); } } // // The socket is bound, the <SrcIp, SrcPort, DstIp, DstPort> is // determined, construct the IP device path and install it. // Status = TcpInstallDevicePath (Sk); if (EFI_ERROR (Status)) { goto OnExit; } // // update state of Tcb and socket // if (!CfgData->AccessPoint.ActiveFlag) { TcpSetState (Tcb, TCP_LISTEN); SockSetState (Sk, SO_LISTENING); Sk->ConfigureState = SO_CONFIGURED_PASSIVE; } else { Sk->ConfigureState = SO_CONFIGURED_ACTIVE; } TcpInsertTcb (Tcb); OnExit: return Status; }