/** @file
The implementation of a dispatch routine for processing TCP requests.
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "TcpMain.h"
/**
Add or remove a route entry in the IP route table associated with this TCP instance.
@param[in] Tcb Pointer to the TCP_CB of this TCP instance.
@param[in] RouteInfo Pointer to the route information to be processed.
@retval EFI_SUCCESS The operation completed successfully.
@retval EFI_NOT_STARTED The driver instance has not been started.
@retval EFI_NO_MAPPING When using the default address, configuration(DHCP,
BOOTP, RARP, etc.) is not finished yet.
@retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
@retval EFI_NOT_FOUND This route is not in the routing table
(when RouteInfo->DeleteRoute is TRUE).
@retval EFI_ACCESS_DENIED The route is already defined in the routing table
(when RouteInfo->DeleteRoute is FALSE).
**/
EFI_STATUS
Tcp4Route (
IN TCP_CB *Tcb,
IN TCP4_ROUTE_INFO *RouteInfo
)
{
IP_IO_IP_PROTOCOL Ip;
Ip = Tcb->IpInfo->Ip;
ASSERT (Ip.Ip4!= NULL);
return Ip.Ip4->Routes (
Ip.Ip4,
RouteInfo->DeleteRoute,
RouteInfo->SubnetAddress,
RouteInfo->SubnetMask,
RouteInfo->GatewayAddress
);
}
/**
Get the operational settings of this TCPv4 instance.
@param[in] Tcb Pointer to the TCP_CB of this TCP instance.
@param[in, out] Mode Pointer to the buffer to store the operational
settings.
@retval EFI_SUCCESS The mode data was read.
@retval EFI_NOT_STARTED No configuration data is available because this
instance hasn't been started.
**/
EFI_STATUS
Tcp4GetMode (
IN TCP_CB *Tcb,
IN OUT TCP4_MODE_DATA *Mode
)
{
SOCKET *Sock;
EFI_TCP4_CONFIG_DATA *ConfigData;
EFI_TCP4_ACCESS_POINT *AccessPoint;
EFI_TCP4_OPTION *Option;
EFI_IP4_PROTOCOL *Ip;
Sock = Tcb->Sk;
if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {
return EFI_NOT_STARTED;
}
if (Mode->Tcp4State != NULL) {
*(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State;
}
if (Mode->Tcp4ConfigData != NULL) {
ConfigData = Mode->Tcp4ConfigData;
AccessPoint = &(ConfigData->AccessPoint);
Option = ConfigData->ControlOption;
ConfigData->TypeOfService = Tcb->Tos;
ConfigData->TimeToLive = Tcb->Ttl;
AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr;
IP4_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);
IP4_COPY_ADDRESS (&AccessPoint->SubnetMask, &Tcb->SubnetMask);
AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
IP4_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);
AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
if (Option != NULL) {
Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
Option->DataRetries = Tcb->MaxRexmit;
Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
Option->KeepAliveProbes = Tcb->MaxKeepAlive;
Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));
Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));
Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));
Option->EnableSelectiveAck = FALSE;
Option->EnablePathMtuDiscovery = FALSE;
}
}
Ip = Tcb->IpInfo->Ip.Ip4;
ASSERT (Ip != NULL);
return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);
}
/**
Get the operational settings of this TCPv6 instance.
@param[in] Tcb Pointer to the TCP_CB of this TCP instance.
@param[in, out] Mode Pointer to the buffer to store the operational
settings.
@retval EFI_SUCCESS The mode data was read.
@retval EFI_NOT_STARTED No configuration data is available because this
instance hasn't been started.
**/
EFI_STATUS
Tcp6GetMode (
IN TCP_CB *Tcb,
IN OUT TCP6_MODE_DATA *Mode
)
{
SOCKET *Sock;
EFI_TCP6_CONFIG_DATA *ConfigData;
EFI_TCP6_ACCESS_POINT *AccessPoint;
EFI_TCP6_OPTION *Option;
EFI_IP6_PROTOCOL *Ip;
Sock = Tcb->Sk;
if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) {
return EFI_NOT_STARTED;
}
if (Mode->Tcp6State != NULL) {
*(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State);
}
if (Mode->Tcp6ConfigData != NULL) {
ConfigData = Mode->Tcp6ConfigData;
AccessPoint = &(ConfigData->AccessPoint);
Option = ConfigData->ControlOption;
ConfigData->TrafficClass = Tcb->Tos;
ConfigData->HopLimit = Tcb->Ttl;
AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);
IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);
if (Option != NULL) {
Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
Option->DataRetries = Tcb->MaxRexmit;
Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
Option->KeepAliveProbes = Tcb->MaxKeepAlive;
Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));
Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));
Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));
Option->EnableSelectiveAck = FALSE;
Option->EnablePathMtuDiscovery = FALSE;
}
}
Ip = Tcb->IpInfo->Ip.Ip6;
ASSERT (Ip != NULL);
return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData);
}
/**
If TcpAp->StationPort isn't zero, check whether the access point
is registered, else generate a random station port for this
access point.
@param[in] TcpAp Pointer to the access point.
@param[in] IpVersion IP_VERSION_4 or IP_VERSION_6
@retval EFI_SUCCESS The check passed or the port is assigned.
@retval EFI_INVALID_PARAMETER The non-zero station port is already used.
@retval EFI_OUT_OF_RESOURCES No port can be allocated.
**/
EFI_STATUS
TcpBind (
IN TCP_ACCESS_POINT *TcpAp,
IN UINT8 IpVersion
)
{
BOOLEAN Cycle;
EFI_IP_ADDRESS Local;
UINT16 *Port;
UINT16 *RandomPort;
if (IpVersion == IP_VERSION_4) {
IP4_COPY_ADDRESS (&Local, &TcpAp->Tcp4Ap.StationAddress);
Port = &TcpAp->Tcp4Ap.StationPort;
RandomPort = &mTcp4RandomPort;
} else {
IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress);
Port = &TcpAp->Tcp6Ap.StationPort;
RandomPort = &mTcp6RandomPort;
}
if (0 != *Port) {
//
// Check if a same endpoing is bound.
//
if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) {
return EFI_INVALID_PARAMETER;
}
} else {
//
// generate a random port
//
Cycle = FALSE;
if (TCP_PORT_USER_RESERVED == *RandomPort) {
*RandomPort = TCP_PORT_KNOWN;
}
(*RandomPort)++;
while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) {
(*RandomPort)++;
if (*RandomPort <= TCP_PORT_KNOWN) {
if (Cycle) {
DEBUG (
(EFI_D_ERROR,
"TcpBind: no port can be allocated for this pcb\n")
);
return EFI_OUT_OF_RESOURCES;
}
*RandomPort = TCP_PORT_KNOWN + 1;
Cycle = TRUE;
}
}
*Port = *RandomPort;
}
return EFI_SUCCESS;
}
/**
Flush the Tcb add its associated protocols.
@param[in, out] Tcb Pointer to the TCP_CB to be flushed.
**/
VOID
TcpFlushPcb (
IN OUT TCP_CB *Tcb
)
{
SOCKET *Sock;
IpIoConfigIp (Tcb->IpInfo, NULL);
Sock = Tcb->Sk;
if (SOCK_IS_CONFIGURED (Sock)) {
RemoveEntryList (&Tcb->List);
if (Sock->DevicePath != NULL) {
//
// Uninstall the device path protocl.
//
gBS->UninstallProtocolInterface (
Sock->SockHandle,
&gEfiDevicePathProtocolGuid,
Sock->DevicePath
);
FreePool (Sock->DevicePath);
Sock->DevicePath = NULL;
}
}
NetbufFreeList (&Tcb->SndQue);
NetbufFreeList (&Tcb->RcvQue);
Tcb->State = TCP_CLOSED;
Tcb->RemoteIpZero = FALSE;
}
/**
Attach a Pcb to the socket.
@param[in] Sk Pointer to the socket of this TCP instance.
@retval EFI_SUCCESS The operation completed successfully.
@retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
**/
EFI_STATUS
TcpAttachPcb (
IN SOCKET *Sk
)
{
TCP_CB *Tcb;
TCP_PROTO_DATA *ProtoData;
IP_IO *IpIo;
EFI_STATUS Status;
VOID *Ip;
EFI_GUID *IpProtocolGuid;
if (Sk->IpVersion == IP_VERSION_4) {
IpProtocolGuid = &gEfiIp4ProtocolGuid;
} else {
IpProtocolGuid = &gEfiIp6ProtocolGuid;
}
Tcb = AllocateZeroPool (sizeof (TCP_CB));
if (Tcb == NULL) {
DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n"));
return EFI_OUT_OF_RESOURCES;
}
ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;
IpIo = ProtoData->TcpService->IpIo;
//
// Create an IpInfo for this Tcb.
//
Tcb->IpInfo = IpIoAddIp (IpIo);
if (Tcb->IpInfo == NULL) {
FreePool (Tcb);
return EFI_OUT_OF_RESOURCES;
}
//
// Open the new created IP instance BY_CHILD.
//
Status = gBS->OpenProtocol (
Tcb->IpInfo->ChildHandle,
IpProtocolGuid,
&Ip,
IpIo->Image,
Sk->SockHandle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
IpIoRemoveIp (IpIo, Tcb->IpInfo);
FreePool (Tcb);
return Status;
}
InitializeListHead (&Tcb->List);
InitializeListHead (&Tcb->SndQue);
InitializeListHead (&Tcb->RcvQue);
Tcb->State = TCP_CLOSED;
Tcb->Sk = Sk;
ProtoData->TcpPcb = Tcb;
return EFI_SUCCESS;
}
/**
Detach the Pcb of the socket.
@param[in, out] Sk Pointer to the socket of this TCP instance.
**/
VOID
TcpDetachPcb (
IN OUT SOCKET *Sk
)
{
TCP_PROTO_DATA *ProtoData;
TCP_CB *Tcb;
ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;
Tcb = ProtoData->TcpPcb;
ASSERT (Tcb != NULL);
TcpFlushPcb (Tcb);
IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);
FreePool (Tcb);
ProtoData->TcpPcb = NULL;
}
/**
Configure the Pcb using CfgData.
@param[in] Sk Pointer to the socket of this TCP instance.
@param[in] CfgData Pointer to the TCP configuration data.
@retval EFI_SUCCESS The operation 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 limits.
**/
EFI_STATUS
TcpConfigurePcb (
IN SOCKET *Sk,
IN TCP_CONFIG_DATA *CfgData
)
{
IP_IO_IP_CONFIG_DATA IpCfgData;
EFI_STATUS Status;
EFI_TCP4_OPTION *Option;
TCP_PROTO_DATA *TcpProto;
TCP_CB *Tcb;
TCP_ACCESS_POINT *TcpAp;
ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL));
TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved;
Tcb = TcpProto->TcpPcb;
ASSERT (Tcb != NULL);
if (Sk->IpVersion == IP_VERSION_4) {
//
// Add Ip for send pkt to the peer
//
CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA));
IpCfgData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
IpCfgData.Ip4CfgData.TypeOfService = CfgData->Tcp4CfgData.TypeOfService;
IpCfgData.Ip4CfgData.TimeToLive = CfgData->Tcp4CfgData.TimeToLive;
IpCfgData.Ip4CfgData.UseDefaultAddress = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;
IP4_COPY_ADDRESS (
&IpCfgData.Ip4CfgData.SubnetMask,
&CfgData->Tcp4CfgData.AccessPoint.SubnetMask
);
IpCfgData.Ip4CfgData.ReceiveTimeout = (UINT32) (-1);
IP4_COPY_ADDRESS (
&IpCfgData.Ip4CfgData.StationAddress,
&CfgData->Tcp4CfgData.AccessPoint.StationAddress
);
} else {
ASSERT (Sk->IpVersion == IP_VERSION_6);
CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA));
IpCfgData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
IpCfgData.Ip6CfgData.TrafficClass = CfgData->Tcp6CfgData.TrafficClass;
IpCfgData.Ip6CfgData.HopLimit = CfgData->Tcp6CfgData.HopLimit;
IpCfgData.Ip6CfgData.ReceiveTimeout = (UINT32) (-1);
IP6_COPY_ADDRESS (
&IpCfgData.Ip6CfgData.StationAddress,
&CfgData->Tcp6CfgData.AccessPoint.StationAddress
);
IP6_COPY_ADDRESS (
&IpCfgData.Ip6CfgData.DestinationAddress,
&CfgData->Tcp6CfgData.AccessPoint.RemoteAddress
);
}
//
// Configure the IP instance this Tcb consumes.
//
Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);
if (EFI_ERROR (Status)) {
goto OnExit;
}
if (Sk->IpVersion == IP_VERSION_4) {
//
// Get the default address information if the instance is configured to use default address.
//
IP4_COPY_ADDRESS (
&CfgData->Tcp4CfgData.AccessPoint.StationAddress,
&IpCfgData.Ip4CfgData.StationAddress
);
IP4_COPY_ADDRESS (
&CfgData->Tcp4CfgData.AccessPoint.SubnetMask,
&IpCfgData.Ip4CfgData.SubnetMask
);
TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint;
} else {
IP6_COPY_ADDRESS (
&CfgData->Tcp6CfgData.AccessPoint.StationAddress,
&IpCfgData.Ip6CfgData.StationAddress
);
TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint;
}
//
// check if we can bind this endpoint in CfgData
//
Status = TcpBind (TcpAp, Sk->IpVersion);
if (EFI_ERROR (Status)) {
DEBUG (
(EFI_D_ERROR,
"TcpConfigurePcb: 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;
if (Sk->IpVersion == IP_VERSION_4) {
//
// initialize Tcb in the light of CfgData
//
Tcb->Ttl = CfgData->Tcp4CfgData.TimeToLive;
Tcb->Tos = CfgData->Tcp4CfgData.TypeOfService;
Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;
CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR));
Tcb->LocalEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort);
IP4_COPY_ADDRESS (&Tcb->SubnetMask, &CfgData->Tcp4CfgData.AccessPoint.SubnetMask);
CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort);
Option = CfgData->Tcp4CfgData.ControlOption;
} else {
Tcb->Ttl = CfgData->Tcp6CfgData.HopLimit;
Tcb->Tos = CfgData->Tcp6CfgData.TrafficClass;
IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress);
Tcb->LocalEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort);
IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress);
Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort);
//
// Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same.
//
Option = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.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 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 (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) ||
((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag)
) {
TcpSetState (Tcb, TCP_LISTEN);
SockSetState (Sk, SO_LISTENING);
Sk->ConfigureState = SO_CONFIGURED_PASSIVE;
} else {
Sk->ConfigureState = SO_CONFIGURED_ACTIVE;
}
if (Sk->IpVersion == IP_VERSION_6) {
Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK;
if (NetIp6IsUnspecifiedAddr (&Tcb->RemoteEnd.Ip.v6)) {
Tcb->RemoteIpZero = TRUE;
}
}
TcpInsertTcb (Tcb);
OnExit:
return Status;
}
/**
The procotol handler provided to the socket layer, which is used to
dispatch the socket level requests by calling the corresponding
TCP layer functions.
@param[in] Sock Pointer to the socket of this TCP instance.
@param[in] Request The code of this operation request.
@param[in] Data Pointer to the operation specific data passed in
together with the operation request. This is an
optional parameter that may be NULL.
@retval EFI_SUCCESS The socket request completed successfully.
@retval other The error status returned by the corresponding TCP
layer function.
**/
EFI_STATUS
TcpDispatcher (
IN SOCKET *Sock,
IN UINT8 Request,
IN VOID *Data OPTIONAL
)
{
TCP_CB *Tcb;
TCP_PROTO_DATA *ProtoData;
ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved;
Tcb = ProtoData->TcpPcb;
switch (Request) {
case SOCK_POLL:
if (Tcb->Sk->IpVersion == IP_VERSION_4) {
ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4);
} else {
ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6);
}
break;
case SOCK_CONSUMED:
//
// After user received data from socket buffer, socket will
// notify TCP using this message to give it a chance to send out
// window update information
//
ASSERT (Tcb != NULL);
TcpOnAppConsume (Tcb);
break;
case SOCK_SND:
ASSERT (Tcb != NULL);
TcpOnAppSend (Tcb);
break;
case SOCK_CLOSE:
TcpOnAppClose (Tcb);
break;
case SOCK_ABORT:
TcpOnAppAbort (Tcb);
break;
case SOCK_SNDPUSH:
Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk);
TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);
break;
case SOCK_SNDURG:
Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1;
TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG);
break;
case SOCK_CONNECT:
TcpOnAppConnect (Tcb);
break;
case SOCK_ATTACH:
return TcpAttachPcb (Sock);
break;
case SOCK_FLUSH:
TcpFlushPcb (Tcb);
break;
case SOCK_DETACH:
TcpDetachPcb (Sock);
break;
case SOCK_CONFIGURE:
return TcpConfigurePcb (
Sock,
(TCP_CONFIG_DATA *) Data
);
break;
case SOCK_MODE:
ASSERT ((Data != NULL) && (Tcb != NULL));
if (Tcb->Sk->IpVersion == IP_VERSION_4) {
return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data);
} else {
return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data);
}
break;
case SOCK_ROUTE:
ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4));
return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data);
default:
return EFI_UNSUPPORTED;
}
return EFI_SUCCESS;
}