From 8a67d61da4d5a8f08a656cbeea2d902d0ad9042a Mon Sep 17 00:00:00 2001 From: vanjeff Date: Tue, 24 Jul 2007 08:06:37 +0000 Subject: Import SnpDxe, Tcp4Dxe, Udp4Dxe and MnpDxe. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3416 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Universal/Network/Tcp4Dxe/ComponentName.c | 169 +++ MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c | 1269 +++++++++++++++++ MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h | 84 ++ .../Universal/Network/Tcp4Dxe/SockInterface.c | 980 +++++++++++++ MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h | 518 +++++++ .../Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c | 680 +++++++++ .../Universal/Network/Tcp4Dxe/Tcp4Driver.c | 669 +++++++++ .../Universal/Network/Tcp4Dxe/Tcp4Driver.h | 141 ++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf | 77 + MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa | 90 ++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h | 353 +++++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c | 1486 ++++++++++++++++++++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c | 117 ++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c | 564 ++++++++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h | 176 +++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c | 1091 ++++++++++++++ .../Universal/Network/Tcp4Dxe/Tcp4Option.c | 380 +++++ .../Universal/Network/Tcp4Dxe/Tcp4Option.h | 107 ++ .../Universal/Network/Tcp4Dxe/Tcp4Output.c | 1218 ++++++++++++++++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h | 355 +++++ MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c | 582 ++++++++ 21 files changed, 11106 insertions(+) create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h create mode 100644 MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c (limited to 'MdeModulePkg/Universal/Network/Tcp4Dxe') diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c new file mode 100644 index 0000000000..477af22e1e --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/ComponentName.c @@ -0,0 +1,169 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. 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. + +Module Name: + + ComponentName.c + +Abstract: + + +**/ + +#include "Tcp4Main.h" + +// +// EFI Component Name Functions +// +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + +// +// EFI Component Name Protocol +// +EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName = { + TcpComponentNameGetDriverName, + TcpComponentNameGetControllerName, + "eng" +}; + +static EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = { + { + "eng", + L"Tcp Network Service Driver" + }, + { + NULL, + NULL + } +}; + +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +/*++ + +Routine Description: + + Retrieves a Unicode string that is the user readable name of the EFI Driver. + +Arguments: + + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + Language - A pointer to a three character ISO 639-2 language identifier. + This is the language of the driver name that that the caller + is requesting, and it must match one of the languages + specified in SupportedLanguages. The number of languages + supported by a driver is up to the driver writer. + DriverName - A pointer to the Unicode string to return. This Unicode + string is the name of the driver specified by This in the + language specified by Language. + +Returns: + + EFI_SUCCES - The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - DriverName is NULL. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return LookupUnicodeString ( + Language, + gTcp4ComponentName.SupportedLanguages, + mTcpDriverNameTable, + DriverName + ); +} + +EFI_STATUS +EFIAPI +TcpComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +/*++ + +Routine Description: + + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by an EFI Driver. + +Arguments: + + This - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance. + ControllerHandle - The handle of a controller that the driver specified by + This is managing. This handle specifies the controller + whose name is to be returned. + ChildHandle - The handle of the child controller to retrieve the name + of. This is an optional parameter that may be NULL. + It will be NULL for device drivers. It will also be + NULL for a bus drivers that wish to retrieve the name of + the bus controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a child + controller. + Language - A pointer to a three character ISO 639-2 language + identifier. This is the language of the controller + name that that the caller is requesting, and it must + match one of the languages specified in supported + languages. The number of languages supported by a driver + is up to the driver writer. + ControllerName - A pointer to the Unicode string to return. This Unicode + string is the name of the controller specified by + ControllerHandle and ChildHandle in the language + specified by Language from the point of view of the + driver specified by This. + +Returns: + + EFI_SUCCESS - The Unicode string for the user readable name in + the language specified by Language for the driver + specified by This was returned in DriverName. + EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE. + EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + EFI_INVALID_PARAMETER - Language is NULL. + EFI_INVALID_PARAMETER - ControllerName is NULL. + EFI_UNSUPPORTED - The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + EFI_UNSUPPORTED - The driver specified by This does not support the + language specified by Language. + +--*/ +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c new file mode 100644 index 0000000000..1f81fc39d3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.c @@ -0,0 +1,1269 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + SockImpl.c + +Abstract: + + +**/ + +#include "SockImpl.h" + +STATIC +UINT32 +SockTcpDataToRcv ( + IN SOCK_BUFFER *SockBuffer, + OUT BOOLEAN *IsOOB, + IN UINT32 BufLen + ); + +STATIC +VOID +SockProcessSndToken ( + IN SOCKET *Sock + ); + +VOID +SockFreeFoo ( + IN EFI_EVENT Event + ) +{ + return ; +} + + +/** + Get the length of the data that can be retrieved from the socket + receive buffer. + + @param SockBuffer Pointer to the socket receive buffer. + @param IsUrg Pointer to a BOOLEAN variable. If TRUE the data is + OOB. + @param BufLen The maximum length of the data buffer to store the + received data in socket layer. + + @return The length of the data can be retreived. + +**/ +STATIC +UINT32 +SockTcpDataToRcv ( + IN SOCK_BUFFER *SockBuffer, + OUT BOOLEAN *IsUrg, + IN UINT32 BufLen + ) +{ + NET_BUF *RcvBufEntry; + UINT32 DataLen; + TCP_RSV_DATA *TcpRsvData; + BOOLEAN Urg; + + ASSERT (SockBuffer && IsUrg && (BufLen > 0)); + + RcvBufEntry = SockBufFirst (SockBuffer); + ASSERT (RcvBufEntry); + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + *IsUrg = ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) { + + DataLen = NET_MIN (TcpRsvData->UrgLen, BufLen); + + if (DataLen < TcpRsvData->UrgLen) { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen; + } else { + TcpRsvData->UrgLen = 0; + } + + return DataLen; + + } + + DataLen = RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + + while ((BufLen > DataLen) && (RcvBufEntry != NULL)) { + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + Urg = ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg != Urg) { + break; + } + + if (*IsUrg && TcpRsvData->UrgLen < RcvBufEntry->TotalSize) { + + if (TcpRsvData->UrgLen + DataLen < BufLen) { + TcpRsvData->UrgLen = 0; + } else { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - (BufLen - DataLen); + } + + return NET_MIN (TcpRsvData->UrgLen + DataLen, BufLen); + + } + + DataLen += RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + } + + DataLen = NET_MIN (BufLen, DataLen); + return DataLen; +} + + +/** + Copy data from socket buffer to application provided receive buffer. + + @param Sock Pointer to the socket. + @param TcpRxData Pointer to the application provided receive buffer. + @param RcvdBytes The maximum length of the data can be copied. + @param IsOOB If TURE the data is OOB, else the data is normal. + + @return None. + +**/ +VOID +SockSetTcpRxData ( + IN SOCKET *Sock, + IN VOID *TcpRxData, + IN UINT32 RcvdBytes, + IN BOOLEAN IsOOB + ) +{ + UINT32 Index; + UINT32 CopyBytes; + UINT32 OffSet; + EFI_TCP4_RECEIVE_DATA *RxData; + EFI_TCP4_FRAGMENT_DATA *Fragment; + + RxData = (EFI_TCP4_RECEIVE_DATA *) TcpRxData; + + OffSet = 0; + + ASSERT (RxData->DataLength >= RcvdBytes); + + RxData->DataLength = RcvdBytes; + RxData->UrgentFlag = IsOOB; + + for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) { + + Fragment = &RxData->FragmentTable[Index]; + CopyBytes = NET_MIN (Fragment->FragmentLength, RcvdBytes); + + NetbufQueCopy ( + Sock->RcvBuffer.DataQueue, + OffSet, + CopyBytes, + Fragment->FragmentBuffer + ); + + Fragment->FragmentLength = CopyBytes; + RcvdBytes -= CopyBytes; + OffSet += CopyBytes; + } +} + + +/** + Get received data from the socket layer to the receive token. + + @param Sock Pointer to the socket. + @param RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN SOCKET *Sock, + IN SOCK_IO_TOKEN *RcvToken + ) +{ + UINT32 TokenRcvdBytes; + EFI_TCP4_RECEIVE_DATA *RxData; + BOOLEAN IsUrg; + + ASSERT (Sock); + + ASSERT (SOCK_STREAM == Sock->Type); + + RxData = RcvToken->Packet.RxData; + + TokenRcvdBytes = SockTcpDataToRcv ( + &Sock->RcvBuffer, + &IsUrg, + RxData->DataLength + ); + + // + // Copy data from RcvBuffer of socket to user + // provided RxData and set the fields in TCP RxData + // + SockSetTcpRxData (Sock, RxData, TokenRcvdBytes, IsUrg); + + SOCK_TRIM_RCV_BUFF (Sock, TokenRcvdBytes); + SIGNAL_TOKEN (&(RcvToken->Token), EFI_SUCCESS); + + return TokenRcvdBytes; +} + + +/** + Process the TCP send data, buffer the tcp txdata and append + the buffer to socket send buffer,then try to send it. + + @param Sock Pointer to the socket. + @param TcpTxData Pointer to the tcp txdata. + + @retval EFI_SUCCESS The operation is completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limit. + +**/ +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ) +{ + NET_BUF *SndData; + EFI_STATUS Status; + EFI_TCP4_TRANSMIT_DATA *TxData; + + TxData = (EFI_TCP4_TRANSMIT_DATA *) TcpTxData; + + // + // transform this TxData into a NET_BUFFER + // and insert it into Sock->SndBuffer + // + SndData = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + 0, + 0, + SockFreeFoo, + NULL + ); + + if (NULL == SndData) { + SOCK_DEBUG_ERROR (("SockKProcessSndData: Failed to" + " call NetBufferFromExt\n")); + + return EFI_OUT_OF_RESOURCES; + } + + NetbufQueAppend (Sock->SndBuffer.DataQueue, SndData); + + // + // notify the low layer protocol to handle this send token + // + if (TxData->Urgent) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDURG, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (TxData->Push) { + Status = Sock->ProtoHandler (Sock, SOCK_SNDPUSH, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // low layer protocol should really handle the sending + // process when catching SOCK_SND request + // + Status = Sock->ProtoHandler (Sock, SOCK_SND, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Flush the tokens in the specific token list. + + @param Sock Pointer to the socket. + @param PendingTokenList Pointer to the token list to be flushed. + + @return None. + +**/ +STATIC +VOID +SockFlushPendingToken ( + IN SOCKET *Sock, + IN NET_LIST_ENTRY *PendingTokenList + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *Token; + + ASSERT (Sock && PendingTokenList); + + while (!NetListIsEmpty (PendingTokenList)) { + SockToken = NET_LIST_HEAD ( + PendingTokenList, + SOCK_TOKEN, + TokenList + ); + + Token = SockToken->Token; + SIGNAL_TOKEN (Token, Sock->SockError); + + NetListRemoveEntry (&(SockToken->TokenList)); + NetFreePool (SockToken); + } +} + + +/** + Wake up the connection token while the connection is + successfully established, then try to process any + pending send token. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockWakeConnToken ( + IN SOCKET *Sock + ) +{ + ASSERT (Sock->ConnectionToken != NULL); + + SIGNAL_TOKEN (Sock->ConnectionToken, EFI_SUCCESS); + Sock->ConnectionToken = NULL; + + // + // check to see if some pending send token existed? + // + SockProcessSndToken (Sock); + return ; +} + + +/** + Wake up the listen token while the connection is + established successfully. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockWakeListenToken ( + IN SOCKET *Sock + ) +{ + SOCKET *Parent; + SOCK_TOKEN *SockToken; + EFI_TCP4_LISTEN_TOKEN *ListenToken; + + Parent = Sock->Parent; + + ASSERT (Parent && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock)); + + if (!NetListIsEmpty (&Parent->ListenTokenList)) { + SockToken = NET_LIST_HEAD ( + &Parent->ListenTokenList, + SOCK_TOKEN, + TokenList + ); + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) SockToken->Token; + ListenToken->NewChildHandle = Sock->SockHandle; + + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + NetListRemoveEntry (&SockToken->TokenList); + NetFreePool (SockToken); + + NetListRemoveEntry (&Sock->ConnectionList); + + Parent->ConnCnt--; + SOCK_DEBUG_WARN (("SockWakeListenToken: accept a socket," + "now conncnt is %d", Parent->ConnCnt)); + + Sock->Parent = NULL; + } +} + + +/** + Wake up the receive token while some data is received. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockWakeRcvToken ( + IN SOCKET *Sock + ) +{ + UINT32 RcvdBytes; + UINT32 TokenRcvdBytes; + SOCK_TOKEN *SockToken; + SOCK_IO_TOKEN *RcvToken; + + ASSERT (Sock->RcvBuffer.DataQueue); + + RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize; + + ASSERT (RcvdBytes > 0); + + while (RcvdBytes > 0 && !NetListIsEmpty (&Sock->RcvTokenList)) { + + SockToken = NET_LIST_HEAD ( + &Sock->RcvTokenList, + SOCK_TOKEN, + TokenList + ); + + RcvToken = (SOCK_IO_TOKEN *) SockToken->Token; + TokenRcvdBytes = SockProcessRcvToken (Sock, RcvToken); + + if (0 == TokenRcvdBytes) { + return ; + } + + NetListRemoveEntry (&(SockToken->TokenList)); + NetFreePool (SockToken); + RcvdBytes -= TokenRcvdBytes; + } +} + + +/** + Process the send token. + + @param Sock Pointer to the socket. + + @return None. + +**/ +STATIC +VOID +SockProcessSndToken ( + IN SOCKET *Sock + ) +{ + UINT32 FreeSpace; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + SOCK_IO_TOKEN *SndToken; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + + ASSERT (Sock && (SOCK_STREAM == Sock->Type)); + + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + // + // to determine if process a send token using + // socket layer flow control policy + // + while ((FreeSpace >= Sock->SndBuffer.LowWater) && + !NetListIsEmpty (&Sock->SndTokenList)) { + + SockToken = NET_LIST_HEAD ( + &(Sock->SndTokenList), + SOCK_TOKEN, + TokenList + ); + + // + // process this token + // + NetListRemoveEntry (&(SockToken->TokenList)); + NetListInsertTail ( + &(Sock->ProcessingSndTokenList), + &(SockToken->TokenList) + ); + + // + // Proceess it in the light of SockType + // + SndToken = (SOCK_IO_TOKEN *) SockToken->Token; + TxData = SndToken->Packet.TxData; + + DataLen = TxData->DataLength; + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + goto OnError; + } + + if (DataLen >= FreeSpace) { + FreeSpace = 0; + + } else { + FreeSpace -= DataLen; + + } + } + + return ; + +OnError: + + NetListRemoveEntry (&SockToken->TokenList); + SIGNAL_TOKEN (SockToken->Token, Status); + NetFreePool (SockToken); +} + + +/** + Create a socket with initial data SockInitData. + + @param SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + SOCKET *Parent; + EFI_STATUS Status; + + ASSERT (SockInitData && SockInitData->ProtoHandler); + ASSERT (SockInitData->Type == SOCK_STREAM); + + Parent = SockInitData->Parent; + + if (Parent && (Parent->ConnCnt == Parent->BackLog)) { + SOCK_DEBUG_ERROR ( + ("SockCreate: Socket parent has " + "reached its connection limit with %d ConnCnt and %d BackLog\n", + Parent->ConnCnt, + Parent->BackLog) + ); + + return NULL; + } + + Sock = NetAllocateZeroPool (sizeof (SOCKET)); + if (NULL == Sock) { + + SOCK_DEBUG_ERROR (("SockCreate: No resource to create a new socket\n")); + return NULL; + } + + NetListInit (&Sock->ConnectionList); + NetListInit (&Sock->ListenTokenList); + NetListInit (&Sock->RcvTokenList); + NetListInit (&Sock->SndTokenList); + NetListInit (&Sock->ProcessingSndTokenList); + + NET_LOCK_INIT (&(Sock->Lock)); + + Sock->SndBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->SndBuffer.DataQueue) { + SOCK_DEBUG_ERROR (("SockCreate: No resource to allocate" + " SndBuffer for new socket\n")); + + goto OnError; + } + + Sock->RcvBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->RcvBuffer.DataQueue) { + SOCK_DEBUG_ERROR (("SockCreate: No resource to allocate " + "RcvBuffer for new socket\n")); + + goto OnError; + } + + Sock->Signature = SOCK_SIGNATURE; + + Sock->Parent = Parent; + Sock->BackLog = SockInitData->BackLog; + Sock->ProtoHandler = SockInitData->ProtoHandler; + Sock->SndBuffer.HighWater = SockInitData->SndBufferSize; + Sock->RcvBuffer.HighWater = SockInitData->RcvBufferSize; + Sock->Type = SockInitData->Type; + Sock->DriverBinding = SockInitData->DriverBinding; + Sock->State = SockInitData->State; + + Sock->SockError = EFI_ABORTED; + Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER; + Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER; + + // + // Install protocol on Sock->SockHandle + // + NetCopyMem ( + &(Sock->NetProtocol.TcpProtocol), + SockInitData->Protocol, + sizeof (EFI_TCP4_PROTOCOL) + ); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Sock->SockHandle, + &gEfiTcp4ProtocolGuid, + &(Sock->NetProtocol.TcpProtocol), + NULL + ); + + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockCreate: Install TCP protocol in " + "socket failed with %r\n", Status)); + + goto OnError; + } + + if (Parent != NULL) { + ASSERT (Parent->BackLog > 0); + ASSERT (SOCK_IS_LISTENING (Parent)); + + // + // need to add it into Parent->ConnectionList + // if the Parent->ConnCnt < Parent->BackLog + // + Parent->ConnCnt++; + + SOCK_DEBUG_WARN (("SockCreate: Create a new socket and" + "add to parent, now conncnt is %d\n", Parent->ConnCnt)); + + NetListInsertTail (&Parent->ConnectionList, &Sock->ConnectionList); + } + + return Sock; + +OnError: + if (NULL != Sock) { + + if (NULL != Sock->SndBuffer.DataQueue) { + NetbufQueFree (Sock->SndBuffer.DataQueue); + } + + if (NULL != Sock->RcvBuffer.DataQueue) { + NetbufQueFree (Sock->RcvBuffer.DataQueue); + } + + NetFreePool (Sock); + } + + return NULL; +} + + +/** + Destroy a socket. + + @param Sock Pointer to the socket. + + @return None. + +**/ +VOID +SockDestroy ( + IN SOCKET *Sock + ) +{ + VOID *SockProtocol; + EFI_GUID *ProtocolGuid; + EFI_STATUS Status; + + ASSERT (SOCK_STREAM == Sock->Type); + + // + // Flush the completion token buffered + // by sock and rcv, snd buffer + // + if (!SOCK_IS_UNCONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + Sock->ConfigureState = SO_UNCONFIGURED; + + } + // + // Destory the RcvBuffer Queue and SendBuffer Queue + // + NetbufQueFree (Sock->RcvBuffer.DataQueue); + NetbufQueFree (Sock->SndBuffer.DataQueue); + + // + // Remove it from parent connection list if needed + // + if (Sock->Parent) { + + NetListRemoveEntry (&(Sock->ConnectionList)); + (Sock->Parent->ConnCnt)--; + + SOCK_DEBUG_WARN (("SockDestory: Delete a unaccepted socket from parent" + "now conncnt is %d\n", Sock->Parent->ConnCnt)); + + Sock->Parent = NULL; + } + + // + // Set the protocol guid and driver binding handle + // in the light of Sock->SockType + // + ProtocolGuid = &gEfiTcp4ProtocolGuid; + + // + // Retrieve the protocol installed on this sock + // + Status = gBS->OpenProtocol ( + Sock->SockHandle, + ProtocolGuid, + &SockProtocol, + Sock->DriverBinding, + Sock->SockHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockDestroy: Open protocol installed " + "on socket failed with %r\n", Status)); + + goto FreeSock; + } + + // + // Uninstall the protocol installed on this sock + // in the light of Sock->SockType + // + gBS->UninstallMultipleProtocolInterfaces ( + Sock->SockHandle, + ProtocolGuid, + SockProtocol, + NULL + ); + +FreeSock: + NetFreePool (Sock); + return ; +} + + +/** + Flush the socket. + + @param Sock Pointer to the socket. + + @return None. + +**/ +VOID +SockConnFlush ( + IN SOCKET *Sock + ) +{ + SOCKET *Child; + + ASSERT (Sock); + + // + // Clear the flag in this socket + // + Sock->Flag = 0; + + // + // Flush the SndBuffer and RcvBuffer of Sock + // + NetbufQueFlush (Sock->SndBuffer.DataQueue); + NetbufQueFlush (Sock->RcvBuffer.DataQueue); + + // + // Signal the pending token + // + if (Sock->ConnectionToken != NULL) { + SIGNAL_TOKEN (Sock->ConnectionToken, Sock->SockError); + Sock->ConnectionToken = NULL; + } + + if (Sock->CloseToken != NULL) { + SIGNAL_TOKEN (Sock->CloseToken, Sock->SockError); + Sock->CloseToken = NULL; + } + + SockFlushPendingToken (Sock, &(Sock->ListenTokenList)); + SockFlushPendingToken (Sock, &(Sock->RcvTokenList)); + SockFlushPendingToken (Sock, &(Sock->SndTokenList)); + SockFlushPendingToken (Sock, &(Sock->ProcessingSndTokenList)); + + // + // Destroy the pending connection, if it is a listening socket + // + if (SOCK_IS_LISTENING (Sock)) { + while (!NetListIsEmpty (&Sock->ConnectionList)) { + Child = NET_LIST_HEAD ( + &Sock->ConnectionList, + SOCKET, + ConnectionList + ); + + SockDestroyChild (Child); + } + + Sock->ConnCnt = 0; + } + + return ; +} + + +/** + Set the state of the socket. + + @param Sock Pointer to the socket. + @param State The new state to be set. + + @return None. + +**/ +VOID +SockSetState ( + IN SOCKET *Sock, + IN SOCK_STATE State + ) +{ + Sock->State = State; +} + + +/** + Clone a new socket including its associated protocol control block. + + @param Sock Pointer to the socket to be cloned. + + @retval * Pointer to the newly cloned socket. If NULL, error + condition occurred. + +**/ +SOCKET * +SockClone ( + IN SOCKET *Sock + ) +{ + SOCKET *ClonedSock; + SOCK_INIT_DATA InitData; + + InitData.BackLog = Sock->BackLog; + InitData.Parent = Sock; + InitData.State = Sock->State; + InitData.ProtoHandler = Sock->ProtoHandler; + InitData.Type = Sock->Type; + InitData.RcvBufferSize = Sock->RcvBuffer.HighWater; + InitData.SndBufferSize = Sock->SndBuffer.HighWater; + InitData.DriverBinding = Sock->DriverBinding; + InitData.Protocol = &(Sock->NetProtocol); + + ClonedSock = SockCreate (&InitData); + + if (NULL == ClonedSock) { + SOCK_DEBUG_ERROR (("SockClone: no resource to create a cloned sock\n")); + return NULL; + } + + NetCopyMem ( + ClonedSock->ProtoReserved, + Sock->ProtoReserved, + PROTO_RESERVED_LEN + ); + + SockSetState (ClonedSock, SO_CONNECTING); + ClonedSock->ConfigureState = Sock->ConfigureState; + + return ClonedSock; +} + + +/** + Called by the low layer protocol to indicate the socket + a connection is established. This function just changes + the socket's state to SO_CONNECTED and signals the token + used for connection establishment. + + @param Sock Pointer to the socket associated with the + established connection. + + @return None. + +**/ +VOID +SockConnEstablished ( + IN SOCKET *Sock + ) +{ + + ASSERT (SO_CONNECTING == Sock->State); + + SockSetState (Sock, SO_CONNECTED); + + if (NULL == Sock->Parent) { + SockWakeConnToken (Sock); + } else { + SockWakeListenToken (Sock); + } + + return ; +} + + +/** + Called by the low layer protocol to indicate the connection + is closed. This function flushes the socket, sets the state + to SO_CLOSED and signals the close token. + + @param Sock Pointer to the socket associated with the closed + connection. + + @return None. + +**/ +VOID +SockConnClosed ( + IN SOCKET *Sock + ) +{ + if (Sock->CloseToken) { + SIGNAL_TOKEN (Sock->CloseToken, EFI_SUCCESS); + Sock->CloseToken = NULL; + } + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + if (Sock->Parent != NULL) { + SockDestroyChild (Sock); + } + +} + + +/** + Called by low layer protocol to indicate that some + data is sent or processed. This function trims the + sent data in the socket send buffer, signals the + data token if proper + + @param Sock Pointer to the socket. + @param Count The length of the data processed or sent, in bytes. + + @return None. + +**/ +VOID +SockDataSent ( + IN SOCKET *Sock, + IN UINT32 Count + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *SndToken; + + ASSERT (!NetListIsEmpty (&Sock->ProcessingSndTokenList)); + ASSERT (Count <= (Sock->SndBuffer.DataQueue)->BufSize); + + NetbufQueTrim (Sock->SndBuffer.DataQueue, Count); + + // + // To check if we can signal some snd token in this socket + // + while (Count > 0) { + SockToken = NET_LIST_HEAD ( + &(Sock->ProcessingSndTokenList), + SOCK_TOKEN, + TokenList + ); + + SndToken = SockToken->Token; + + if (SockToken->RemainDataLen <= Count) { + + NetListRemoveEntry (&(SockToken->TokenList)); + SIGNAL_TOKEN (SndToken, EFI_SUCCESS); + Count -= SockToken->RemainDataLen; + NetFreePool (SockToken); + } else { + + SockToken->RemainDataLen -= Count; + Count = 0; + } + } + + // + // to judge if we can process some send token in + // Sock->SndTokenList, if so process those send token + // + SockProcessSndToken (Sock); + return ; +} + + +/** + Called by the low layer protocol to copy some data in socket send + buffer starting from the specific offset to a buffer provided by + the caller. + + @param Sock Pointer to the socket. + @param Offset The start point of the data to be copied. + @param Len The length of the data to be copied. + @param Dest Pointer to the destination to copy the data. + + @return The data size copied. + +**/ +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + IN UINT8 *Dest + ) +{ + ASSERT (Sock && SOCK_STREAM == Sock->Type); + + return NetbufQueCopy ( + Sock->SndBuffer.DataQueue, + Offset, + Len, + Dest + ); +} + + +/** + Called by the low layer protocol to deliver received data + to socket layer. This function will append the data to the + socket receive buffer, set ther urgent data length and then + check if any receive token can be signaled. + + @param Sock Pointer to the socket. + @param NetBuffer Pointer to the buffer that contains the received + data. + @param UrgLen The length of the urgent data in the received data. + + @return None. + +**/ +VOID +SockDataRcvd ( + IN SOCKET *Sock, + IN NET_BUF *NetBuffer, + IN UINT32 UrgLen + ) +{ + ASSERT (Sock && Sock->RcvBuffer.DataQueue && + UrgLen <= NetBuffer->TotalSize); + + NET_GET_REF (NetBuffer); + + ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen; + + NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer); + + SockWakeRcvToken (Sock); + return ; +} + + +/** + Get the length of the free space of the specific socket buffer. + + @param Sock Pointer to the socket. + @param Which Flag to indicate which socket buffer to check, + either send buffer or receive buffer. + + @return The length of the free space, in bytes. + +**/ +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ) +{ + UINT32 BufferCC; + SOCK_BUFFER *SockBuffer; + + ASSERT (Sock && ((SOCK_SND_BUF == Which) || (SOCK_RCV_BUF == Which))); + + if (SOCK_SND_BUF == Which) { + SockBuffer = &(Sock->SndBuffer); + } else { + SockBuffer = &(Sock->RcvBuffer); + } + + BufferCC = (SockBuffer->DataQueue)->BufSize; + + if (BufferCC >= SockBuffer->HighWater) { + + return 0; + } + + return SockBuffer->HighWater - BufferCC; +} + + +/** + Signal the receive token with the specific error or + set socket error code after error is received. + + @param Sock Pointer to the socket. + @param Error The error code received. + + @return None. + +**/ +VOID +SockRcvdErr ( + IN SOCKET *Sock, + IN EFI_STATUS Error + ) +{ + SOCK_TOKEN *SockToken; + + if (!NetListIsEmpty (&Sock->RcvTokenList)) { + + SockToken = NET_LIST_HEAD ( + &Sock->RcvTokenList, + SOCK_TOKEN, + TokenList + ); + + NetListRemoveEntry (&SockToken->TokenList); + + SIGNAL_TOKEN (SockToken->Token, Error); + + NetFreePool (SockToken); + } else { + + SOCK_ERROR (Sock, Error); + } +} + + +/** + Called by the low layer protocol to indicate that there + will be no more data from the communication peer. This + function set the socket's state to SO_NO_MORE_DATA and + signal all queued IO tokens with the error status + EFI_CONNECTION_FIN. + + @param Sock Pointer to the socket. + + @return None. + +**/ +VOID +SockNoMoreData ( + IN SOCKET *Sock + ) +{ + EFI_STATUS Err; + + SOCK_NO_MORE_DATA (Sock); + + if (!NetListIsEmpty (&Sock->RcvTokenList)) { + + ASSERT (0 == GET_RCV_DATASIZE (Sock)); + + Err = Sock->SockError; + + SOCK_ERROR (Sock, EFI_CONNECTION_FIN); + + SockFlushPendingToken (Sock, &Sock->RcvTokenList); + + SOCK_ERROR (Sock, Err); + + } + +} + + +/** + Get the first buffer block in the specific socket buffer. + + @param Sockbuf Pointer to the socket buffer. + + @return Pointer to the first buffer in the queue. NULL if the queue is empty. + +**/ +NET_BUF * +SockBufFirst ( + IN SOCK_BUFFER *Sockbuf + ) +{ + NET_LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if (NetListIsEmpty (NetbufList)) { + return NULL; + } + + return NET_LIST_HEAD (NetbufList, NET_BUF, List); +} + + +/** + Get the next buffer block in the specific socket buffer. + + @param Sockbuf Pointer to the socket buffer. + @param SockEntry Pointer to the buffer block prior to the required + one. + + @return Pointer to the buffer block next to SockEntry. NULL if SockEntry is the tail or head entry. + +**/ +NET_BUF * +SockBufNext ( + IN SOCK_BUFFER *Sockbuf, + IN NET_BUF *SockEntry + ) +{ + NET_LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if ((SockEntry->List.ForwardLink == NetbufList) || + (SockEntry->List.BackLink == &SockEntry->List) || + (SockEntry->List.ForwardLink == &SockEntry->List) + ) { + + return NULL; + } + + return NET_LIST_USER_STRUCT (SockEntry->List.ForwardLink, NET_BUF, List); +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h new file mode 100644 index 0000000000..69a1ac6222 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockImpl.h @@ -0,0 +1,84 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + SockImpl.h + +Abstract: + + +**/ + +#ifndef _SOCK_IMPL_H_ +#define _SOCK_IMPL_H_ + +#include "Socket.h" + +#define SOCK_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR("Sock", PrintArg) +#define SOCK_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING("Sock", PrintArg) +#define SOCK_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE("Sock", PrintArg) + +#define SOCK_TRIM_RCV_BUFF(Sock, Len) \ + (NetbufQueTrim ((Sock)->RcvBuffer.DataQueue, (Len))) + +#define SIGNAL_TOKEN(Token, TokenStatus) \ + do { \ + (Token)->Status = (TokenStatus); \ + gBS->SignalEvent ((Token)->Event); \ + } while (0) + +#define SOCK_HEADER_SPACE (60 + 60 + 72) + +// +// Supporting function for both SockImpl and SockInterface +// +VOID +SockFreeFoo ( + IN EFI_EVENT Event + ); + +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ); + +VOID +SockSetTcpRxData ( + IN SOCKET *Sock, + IN VOID *TcpRxData, + IN UINT32 RcvdBytes, + IN BOOLEAN IsOOB + ); + +UINT32 +SockProcessRcvToken ( + IN SOCKET *Sock, + IN SOCK_IO_TOKEN *RcvToken + ); + +VOID +SockConnFlush ( + IN SOCKET *Sock + ); + +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ); + +VOID +SockDestroy ( + IN SOCKET *Sock + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c new file mode 100644 index 0000000000..0b71996e14 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/SockInterface.c @@ -0,0 +1,980 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + SockInterface.c + +Abstract: + + +**/ + + +#include "SockImpl.h" + + +/** + Check whether the Event is in the List. + + @param List Pointer to the token list to be searched. + @param Event The event to be checked. + + @retval BOOLEAN If TRUE, the specific Event exists in the List. If + FALSE, the specific Event is not in the List. + +**/ +STATIC +BOOLEAN +SockTokenExistedInList ( + IN NET_LIST_ENTRY *List, + IN EFI_EVENT Event + ) +{ + NET_LIST_ENTRY *ListEntry; + SOCK_TOKEN *SockToken; + + NET_LIST_FOR_EACH (ListEntry, List) { + SockToken = NET_LIST_USER_STRUCT ( + ListEntry, + SOCK_TOKEN, + TokenList + ); + + if (Event == SockToken->Token->Event) { + return TRUE; + } + } + + return FALSE; +} + + +/** + Call SockTokenExistedInList() to check whether the Event is + in the related socket's lists. + + @param Sock Pointer to the instance's socket. + @param Event The event to be checked. + + @return The specific Event exists in one of socket's lists or not. + +**/ +BOOLEAN +SockTokenExisted ( + IN SOCKET *Sock, + IN EFI_EVENT Event + ) +{ + + if (SockTokenExistedInList (&Sock->SndTokenList, Event) || + SockTokenExistedInList (&Sock->ProcessingSndTokenList, Event) || + SockTokenExistedInList (&Sock->RcvTokenList, Event) || + SockTokenExistedInList (&Sock->ListenTokenList, Event) + ) { + + return TRUE; + } + + if ((Sock->ConnectionToken != NULL) && + (Sock->ConnectionToken->Event == Event)) { + + return TRUE; + } + + if ((Sock->CloseToken != NULL) && (Sock->CloseToken->Event == Event)) { + return TRUE; + } + + return FALSE; +} + + +/** + Buffer a token into the specific list of socket Sock. + + @param Sock Pointer to the instance's socket. + @param List Pointer to the list to store the token. + @param Token Pointer to the token to be buffered. + @param DataLen The data length of the buffer contained in Token. + + @return Pointer to the token that wraps Token. If NULL, error condition occurred. + +**/ +SOCK_TOKEN * +SockBufferToken ( + IN SOCKET *Sock, + IN NET_LIST_ENTRY *List, + IN VOID *Token, + IN UINT32 DataLen + ) +{ + SOCK_TOKEN *SockToken; + + SockToken = NetAllocatePool (sizeof (SOCK_TOKEN)); + if (NULL == SockToken) { + + SOCK_DEBUG_ERROR (("SockBufferIOToken: No Memory " + "to allocate SockToken\n")); + + return NULL; + } + + SockToken->Sock = Sock; + SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token; + SockToken->RemainDataLen = DataLen; + NetListInsertTail (List, &SockToken->TokenList); + + return SockToken; +} + + +/** + Destory the socket Sock and its associated protocol control block. + + @param Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock is destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT (Sock && Sock->ProtoHandler); + + if (Sock->IsDestroyed) { + return EFI_SUCCESS; + } + + Sock->IsDestroyed = TRUE; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockDestroyChild: Get the lock to " + "access socket failed with %r\n", Status)); + + return EFI_ACCESS_DENIED; + } + + // + // force protocol layer to detach the PCB + // + Status = Sock->ProtoHandler (Sock, SOCK_DETACH, NULL); + + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockDestroyChild: Protocol detach socket" + " failed with %r\n", Status)); + + Sock->IsDestroyed = FALSE; + } else if (SOCK_IS_CONFIGURED (Sock)) { + + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + } + + NET_UNLOCK (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + return Status; + } + + SockDestroy (Sock); + return EFI_SUCCESS; +} + + +/** + Create a socket and its associated protocol control block + with the intial data SockInitData and protocol specific + data ProtoData. + + @param SockInitData Inital data to setting the socket. + @param ProtoData Pointer to the protocol specific data. + @param Len Length of the protocol specific data. + + @return Pointer to the newly created socket. If NULL, error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData, + IN VOID *ProtoData, + IN UINT32 Len + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + ASSERT (ProtoData && (Len <= PROTO_RESERVED_LEN)); + + // + // create a new socket + // + Sock = SockCreate (SockInitData); + if (NULL == Sock) { + + SOCK_DEBUG_ERROR (("SockCreateChild: No resource to " + "create a new socket\n")); + + return NULL; + } + + // + // Open the + // + + // + // copy the protodata into socket + // + NetCopyMem (Sock->ProtoReserved, ProtoData, Len); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockCreateChild: Get the lock to " + "access socket failed with %r\n", Status)); + + SockDestroy (Sock); + return NULL; + } + // + // inform the protocol layer to attach the socket + // with a new protocol control block + // + Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockCreateChild: Protocol failed to" + " attach a socket with %r\n", Status)); + + SockDestroy (Sock); + Sock = NULL; + } + + NET_UNLOCK (&(Sock->Lock)); + return Sock; +} + + +/** + Configure the specific socket Sock using configuration data + ConfigData. + + @param Sock Pointer to the socket to be configured. + @param ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket is configured successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket or the + socket is already configured. + +**/ +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ) +{ + EFI_STATUS Status; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockConfigure: Get the access for " + "socket failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_CONFIGURED (Sock)) { + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + ASSERT (Sock->State == SO_CLOSED); + + Status = Sock->ProtoHandler (Sock, SOCK_CONFIGURE, ConfigData); + +OnExit: + NET_UNLOCK (&(Sock->Lock)); + + return Status; +} + + +/** + Initiate a connection establishment process. + + @param Sock Pointer to the socket to initiate the initate the + connection. + @param Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection is initialized successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be an active one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockConnect: Get the access for " + "socket failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto OnExit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto OnExit; + } + + if (!SOCK_IS_CLOSED (Sock) || !SOCK_IS_CONFIGURED_ACTIVE (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto OnExit; + } + + Sock->ConnectionToken = (SOCK_COMPLETION_TOKEN *) Token; + SockSetState (Sock, SO_CONNECTING); + Status = Sock->ProtoHandler (Sock, SOCK_CONNECT, NULL); + +OnExit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Issue a listen token to get an existed connected network instance + or wait for a connection if there is none. + + @param Sock Pointer to the socket to accept connections. + @param Token The token to accept a connection. + + @retval EFI_SUCCESS Either a connection is accpeted or the Token is + buffered for further acception. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not configured to + be a passive one, or the token is already in one of + this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the Token due to memory limit. + +**/ +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_TCP4_LISTEN_TOKEN *ListenToken; + NET_LIST_ENTRY *ListEntry; + EFI_STATUS Status; + SOCKET *Socket; + EFI_EVENT Event; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockAccept: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!SOCK_IS_LISTENING (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + ListenToken = (EFI_TCP4_LISTEN_TOKEN *) Token; + + // + // Check if a connection has already in this Sock->ConnectionList + // + NET_LIST_FOR_EACH (ListEntry, &Sock->ConnectionList) { + + Socket = NET_LIST_USER_STRUCT (ListEntry, SOCKET, ConnectionList); + + if (SOCK_IS_CONNECTED (Socket)) { + ListenToken->NewChildHandle = Socket->SockHandle; + SIGNAL_TOKEN (&(ListenToken->CompletionToken), EFI_SUCCESS); + + NetListRemoveEntry (ListEntry); + + ASSERT (Socket->Parent); + + Socket->Parent->ConnCnt--; + + SOCK_DEBUG_WARN (("SockAccept: Accept a socket," + "now conncount is %d", Socket->Parent->ConnCnt) + ); + Socket->Parent = NULL; + + goto Exit; + } + } + + // + // Buffer this token for latter incoming connection request + // + if (NULL == SockBufferToken (Sock, &(Sock->ListenTokenList), Token, 0)) { + + Status = EFI_OUT_OF_RESOURCES; + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + + return Status; +} + + +/** + Issue a token with data to the socket to send out. + + @param Sock Pointer to the socket to process the token with + data. + @param Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token is processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit. + +**/ +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *SndToken; + EFI_EVENT Event; + UINT32 FreeSpace; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockSend: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + SndToken = (SOCK_IO_TOKEN *) Token; + TxData = (EFI_TCP4_TRANSMIT_DATA *) SndToken->Packet.TxData; + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTING (Sock) || SOCK_IS_CONNECTED (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // check if a token is already in the token buffer + // + Event = SndToken->Token.Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + DataLen = TxData->DataLength; + + // + // process this sending token now or buffer it only? + // + FreeSpace = SockGetFreeSpace (Sock, SOCK_SND_BUF); + + if ((FreeSpace < Sock->SndBuffer.LowWater) || !SOCK_IS_CONNECTED (Sock)) { + + SockToken = SockBufferToken ( + Sock, + &Sock->SndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + Status = EFI_OUT_OF_RESOURCES; + } + } else { + + SockToken = SockBufferToken ( + Sock, + &Sock->ProcessingSndTokenList, + SndToken, + DataLen + ); + + if (NULL == SockToken) { + SOCK_DEBUG_ERROR (("SockSend: Failed to buffer IO token into" + " socket processing SndToken List\n", Status)); + + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Status = SockProcessTcpSndData (Sock, TxData); + + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockSend: Failed to process " + "Snd Data\n", Status)); + + NetListRemoveEntry (&(SockToken->TokenList)); + NetFreePool (SockToken); + } + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Issue a token to get data from the socket. + + @param Sock Pointer to the socket to get data from. + @param Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token is processed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + @retval EFI_CONNECTION_FIN The connection is closed and there is no more data. + @retval EFI_OUT_OF_RESOURCE Failed to buffer the token due to memory limit. + +**/ +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + SOCK_IO_TOKEN *RcvToken; + UINT32 RcvdBytes; + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockRcv: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (!(SOCK_IS_CONNECTED (Sock) || SOCK_IS_CONNECTING (Sock))) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + + // + // check if a token is already in the token buffer of this socket + // + Event = RcvToken->Token.Event; + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + RcvToken = (SOCK_IO_TOKEN *) Token; + RcvdBytes = GET_RCV_DATASIZE (Sock); + + // + // check whether an error has happened before + // + if (EFI_ABORTED != Sock->SockError) { + + SIGNAL_TOKEN (&(RcvToken->Token), Sock->SockError); + Sock->SockError = EFI_ABORTED; + goto Exit; + } + + // + // check whether can not receive and there is no any + // data buffered in Sock->RcvBuffer + // + if (SOCK_IS_NO_MORE_DATA (Sock) && (0 == RcvdBytes)) { + + Status = EFI_CONNECTION_FIN; + goto Exit; + } + + if (RcvdBytes != 0) { + Status = SockProcessRcvToken (Sock, RcvToken); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_CONSUMED, NULL); + } else { + + if (NULL == SockBufferToken (Sock, &Sock->RcvTokenList, RcvToken, 0)) { + Status = EFI_OUT_OF_RESOURCES; + } + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Reset the socket and its associated protocol control block. + + @param Sock Pointer to the socket to be flushed. + + @retval EFI_SUCCESS The socket is flushed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockFlush ( + IN SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockFlush: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (!SOCK_IS_CONFIGURED (Sock)) { + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL); + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockFlush: Protocol failed handling" + " SOCK_FLUSH with %r", Status)); + + goto Exit; + } + + SOCK_ERROR (Sock, EFI_ABORTED); + SockConnFlush (Sock); + SockSetState (Sock, SO_CLOSED); + + Sock->ConfigureState = SO_UNCONFIGURED; + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Close or abort the socket associated connection. + + @param Sock Pointer to the socket of the connection to close or + abort. + @param Token The token for close operation. + @param OnAbort TRUE for aborting the connection, FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation is initialized + successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket, or the + socket is closed, or the socket is not in a + synchronized state , or the token is already in one + of this socket's lists. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockClose ( + IN SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SOCK_STREAM == Sock->Type); + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockClose: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (SOCK_IS_DISCONNECTING (Sock)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Event = ((SOCK_COMPLETION_TOKEN *) Token)->Event; + + if (SockTokenExisted (Sock, Event)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Sock->CloseToken = Token; + SockSetState (Sock, SO_DISCONNECTING); + + if (OnAbort) { + Status = Sock->ProtoHandler (Sock, SOCK_ABORT, NULL); + } else { + Status = Sock->ProtoHandler (Sock, SOCK_CLOSE, NULL); + } + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Get the mode data of the low layer protocol. + + @param Sock Pointer to the socket to get mode data from. + @param Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data is got successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN VOID *Mode + ) +{ + return Sock->ProtoHandler (Sock, SOCK_MODE, Mode); +} + + +/** + Configure the low level protocol to join a multicast group for + this socket's connection. + + @param Sock Pointer to the socket of the connection to join the + specific multicast group. + @param GroupInfo Pointer to the multicast group info. + + @retval EFI_SUCCESS The configuration is done successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ) +{ + EFI_STATUS Status; + + Status = NET_TRYLOCK (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + + SOCK_DEBUG_ERROR (("SockGroup: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_GROUP, GroupInfo); + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} + + +/** + Add or remove route information in IP route table associated + with this socket. + + @param Sock Pointer to the socket associated with the IP route + table to operate on. + @param RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table is updated successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + @retval EFI_NO_MAPPING The IP address configuration operation is not + finished. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ) +{ + EFI_STATUS Status; + + Status = NET_TRYLOCK (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + SOCK_DEBUG_ERROR (("SockRoute: Get the access for socket" + " failed with %r", Status)); + + return EFI_ACCESS_DENIED; + } + + if (SOCK_IS_NO_MAPPING (Sock)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + if (SOCK_IS_UNCONFIGURED (Sock)) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_ROUTE, RouteInfo); + +Exit: + NET_UNLOCK (&(Sock->Lock)); + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h new file mode 100644 index 0000000000..f3978541fb --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Socket.h @@ -0,0 +1,518 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Socket.h + +Abstract: + + +**/ + +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SOCK_SND_BUF 0 +#define SOCK_RCV_BUF 1 + +#define SOCK_BUFF_LOW_WATER 2 * 1024 +#define SOCK_RCV_BUFF_SIZE 8 * 1024 +#define SOCK_SND_BUFF_SIZE 8 * 1024 +#define SOCK_BACKLOG 5 + +#define PROTO_RESERVED_LEN 20 + +#define SO_NO_MORE_DATA 0x0001 + +// +// +// +// When a socket is created it enters into SO_UNCONFIGURED, +// no actions can be taken on this socket, only after calling +// SockConfigure. The state transition diagram of socket is +// as following: +// +// SO_UNCONFIGURED --- SO_CONFIGURED --- SO_CONNECTING +// ^ | | +// | ---> SO_LISTENING | +// | | +// |------------------SO_DISCONNECTING<-- SO_CONNECTED +// +// A passive socket can only go into SO_LISTENING and +// SO_UNCONFIGURED state. SO_XXXING state is a middle state +// when a socket is undergoing a protocol procedure such +// as requesting a TCP connection. +// +// +// +typedef enum { + SO_CLOSED = 0, + SO_LISTENING, + SO_CONNECTING, + SO_CONNECTED, + SO_DISCONNECTING +} SOCK_STATE; + +typedef enum { + SO_UNCONFIGURED = 0, + SO_CONFIGURED_ACTIVE, + SO_CONFIGURED_PASSIVE, + SO_NO_MAPPING +} SOCK_CONFIGURE_STATE; + +#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA) + +#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED) + +#define SOCK_IS_CONFIGURED(Sock) \ + (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \ + ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)) + +#define SOCK_IS_CONFIGURED_ACTIVE(Sock) \ + ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) + +#define SOCK_IS_CONNECTED_PASSIVE(Sock) \ + ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE) + +#define SOCK_IS_NO_MAPPING(Sock) \ + ((Sock)->ConfigureState == SO_NO_MAPPING) + +#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED) + +#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING) + +#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING) + +#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED) + +#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING) + +#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA)) + +#define SOCK_SIGNATURE EFI_SIGNATURE_32 ('S', 'O', 'C', 'K') + +#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE) + +#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size)) + +#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater) + +#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize) + +#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size)) + +#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater) + +#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize) + +#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value)) + +#define GET_BACKLOG(Sock) ((Sock)->BackLog) + +#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error)) + +#define SND_BUF_HDR_LEN(Sock) \ + ((SockBufFirst (&((Sock)->SndBuffer)))->TotalSize) + +#define RCV_BUF_HDR_LEN(Sock) \ + ((SockBufFirst (&((Sock)->RcvBuffer)))->TotalSize) + +#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock) + +#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) \ + ((Type *) (((SOCK_TOKEN *) (SockToken))->Token)) + +typedef struct _SOCKET SOCKET; + +typedef struct _SOCK_COMPLETION_TOKEN { + EFI_EVENT Event; + EFI_STATUS Status; +} SOCK_COMPLETION_TOKEN; + +typedef struct _SOCK_IO_TOKEN { + SOCK_COMPLETION_TOKEN Token; + union { + VOID *RxData; + VOID *TxData; + } Packet; +} SOCK_IO_TOKEN; + +// +// the request issued from socket layer to protocol layer +// +typedef enum { + SOCK_ATTACH, // attach current socket to a new PCB + SOCK_DETACH, // detach current socket from the PCB + SOCK_CONFIGURE, // configure attached PCB + SOCK_FLUSH, // flush attached PCB + SOCK_SND, // need protocol to send something + SOCK_SNDPUSH, // need protocol to send pushed data + SOCK_SNDURG, // need protocol to send urgent data + SOCK_CONSUMED, // application has retrieved data from socket + SOCK_CONNECT, // need to connect to a peer + SOCK_CLOSE, // need to close the protocol process + SOCK_ABORT, // need to reset the protocol process + SOCK_POLL, // need to poll to the protocol layer + SOCK_ROUTE, // need to add a route information + SOCK_MODE, // need to get the mode data of the protocol + SOCK_GROUP // need to join a mcast group +} SOCK_REQUEST; + +// +// the socket type +// +typedef enum { + SOCK_DGRAM, // this socket providing datagram service + SOCK_STREAM // this socket providing stream service +} SOCK_TYPE; + +// +// the handler of protocol for request from socket +// +typedef +EFI_STATUS +(*SOCK_PROTO_HANDLER) ( + IN SOCKET * Socket, // the socket issuing the request to protocol + IN SOCK_REQUEST Request, // the request issued by socket + IN VOID *RequestData // the request related data + ); + +// +// the buffer structure of rcvd data and send data used by socket +// +typedef struct _SOCK_BUFFER { + UINT32 HighWater; // the buffersize upper limit of sock_buffer + UINT32 LowWater; // the low warter mark of sock_buffer + NET_BUF_QUEUE *DataQueue; // the queue to buffer data +} SOCK_BUFFER; + +// +// the initialize data for create a new socket +// +typedef struct _SOCK_INIT_DATA { + SOCK_TYPE Type; + SOCK_STATE State; + + SOCKET *Parent; // the parent of this socket + UINT32 BackLog; // the connection limit for listening socket + UINT32 SndBufferSize; // the high warter mark of send buffer + UINT32 RcvBufferSize; // the high warter mark of receive buffer + VOID *Protocol; // the pointer to protocol function template + // wanted to install on socket + + SOCK_PROTO_HANDLER ProtoHandler; + + EFI_HANDLE DriverBinding; // the driver binding handle +} SOCK_INIT_DATA; + +// +// socket provided oprerations for low layer protocol +// + +// +// socket provided operations for user interface +// +VOID +SockSetState ( + IN SOCKET *Sock, + IN SOCK_STATE State + ); + +// +// when the connection establishment process for a Sock +// is finished low layer protocol calling this function +// to notify socket layer +// +VOID +SockConnEstablished ( + IN SOCKET *Sock + ); + +VOID +SockConnClosed ( + IN SOCKET *Sock + ); + +// +// called by low layer protocol to trim send buffer of +// Sock, when Count data is sent out completely +// +VOID +SockDataSent ( + IN SOCKET *Sock, + IN UINT32 Count + ); + +// +// called by low layer protocol to get Len of data from +// socket to send and copy it in Dest +// +UINT32 +SockGetDataToSend ( + IN SOCKET *Sock, + IN UINT32 Offset, + IN UINT32 Len, + IN UINT8 *Dest + ); + +// +// called by low layer protocol to notify socket no more data can be +// received +// +VOID +SockNoMoreData ( + IN SOCKET *Sock + ); + +// +// called by low layer protocol to append a NetBuffer +// to rcv buffer of sock +// +VOID +SockDataRcvd ( + IN SOCKET *Sock, + IN NET_BUF *NetBuffer, + IN UINT32 UrgLen + ); + +UINT32 +SockGetFreeSpace ( + IN SOCKET *Sock, + IN UINT32 Which + ); + +SOCKET * +SockClone ( + IN SOCKET *Sock + ); + +VOID +SockRcvdErr ( + IN SOCKET *Sock, + IN EFI_STATUS Error + ); + +// +// the socket structure representing a network service access point +// +typedef struct _SOCKET { + + // + // socket description information + // + UINT32 Signature; + EFI_HANDLE SockHandle; // the virtual handle of the socket + EFI_HANDLE DriverBinding; // socket't driver binding protocol + SOCK_CONFIGURE_STATE ConfigureState; + SOCK_TYPE Type; + SOCK_STATE State; + UINT16 Flag; + NET_LOCK Lock; // the lock of socket + SOCK_BUFFER SndBuffer; // send buffer of application's data + SOCK_BUFFER RcvBuffer; // receive buffer of received data + EFI_STATUS SockError; // the error returned by low layer protocol + BOOLEAN IsDestroyed; + + // + // fields used to manage the connection request + // + UINT32 BackLog; // the limit of connection to this socket + UINT32 ConnCnt; // the current count of connections to it + SOCKET *Parent; // listening parent that accept the connection + NET_LIST_ENTRY ConnectionList; // the connections maintained by this socket + // + // the queue to buffer application's asynchronous token + // + NET_LIST_ENTRY ListenTokenList; + NET_LIST_ENTRY RcvTokenList; + NET_LIST_ENTRY SndTokenList; + NET_LIST_ENTRY ProcessingSndTokenList; + + SOCK_COMPLETION_TOKEN *ConnectionToken; // app's token to signal if connected + SOCK_COMPLETION_TOKEN *CloseToken; // app's token to signal if closed + + // + // interface for low level protocol + // + SOCK_PROTO_HANDLER ProtoHandler; // the request handler of protocol + UINT8 ProtoReserved[PROTO_RESERVED_LEN]; // Data fields reserved for protocol + union { + EFI_TCP4_PROTOCOL TcpProtocol; + EFI_UDP4_PROTOCOL UdpProtocol; + } NetProtocol; +} SOCKET; + +// +// the token structure buffered in socket layer +// +typedef struct _SOCK_TOKEN { + NET_LIST_ENTRY TokenList; // the entry to add in the token list + SOCK_COMPLETION_TOKEN *Token; // The application's token + UINT32 RemainDataLen; // unprocessed data length + SOCKET *Sock; // the poninter to the socket this token + // belongs to +} SOCK_TOKEN; + +// +// reserved data to access the NET_BUF delivered by UDP driver +// +typedef struct _UDP_RSV_DATA { + EFI_TIME TimeStamp; + EFI_UDP4_SESSION_DATA Session; +} UDP_RSV_DATA; + +// +// reserved data to access the NET_BUF delivered by TCP driver +// +typedef struct _TCP_RSV_DATA { + UINT32 UrgLen; +} TCP_RSV_DATA; + +// +// call it to creat a socket and attach it to a PCB +// +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData, + IN VOID *ProtoData, + IN UINT32 Len + ); + +// +// call it to destroy a socket and its related PCB +// +EFI_STATUS +SockDestroyChild ( + IN SOCKET *Sock + ); + +// +// call it to configure a socket and its related PCB +// +EFI_STATUS +SockConfigure ( + IN SOCKET *Sock, + IN VOID *ConfigData + ); + +// +// call it to connect a socket to the peer +// +EFI_STATUS +SockConnect ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// call it to issue an asynchronous listen token to the socket +// +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// Call it to send data using this socket +// +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// Call it to receive data from this socket +// +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ); + +// +// Call it to flush a socket +// +EFI_STATUS +SockFlush ( + IN SOCKET *Sock + ); + +// +// Call it to close a socket in the light of policy in Token +// +EFI_STATUS +SockClose ( + IN SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ); + +// +// Call it to get the mode data of low layer protocol +// +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN VOID *Mode + ); + +// +// call it to add this socket instance into a group +// +EFI_STATUS +SockGroup ( + IN SOCKET *Sock, + IN VOID *GroupInfo + ); + +// +// call it to add a route entry for this socket instance +// +EFI_STATUS +SockRoute ( + IN SOCKET *Sock, + IN VOID *RouteInfo + ); + +// +// Supporting function to operate on socket buffer +// +NET_BUF * +SockBufFirst ( + IN SOCK_BUFFER *Sockbuf + ); + +NET_BUF * +SockBufNext ( + IN SOCK_BUFFER *Sockbuf, + IN NET_BUF *SockEntry + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c new file mode 100644 index 0000000000..9039905be6 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dispatcher.c @@ -0,0 +1,680 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Dispatcher.c + +Abstract: + + +**/ + +#include "Tcp4Main.h" + +#define TCP_COMP_VAL(Min, Max, Default, Val) \ + ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default)) + +STATIC +EFI_STATUS +Tcp4Route ( + IN TCP_CB *Tcb, + IN TCP4_ROUTE_INFO *RouteInfo + ) +/*++ + +Routine Description: + + Add or remove a route entry in the IP route table associated + with this TCP instance. + +Arguments: + + Tcb - Pointer to the TCP_CB of this TCP instance. + RouteInfo - Pointer to the route info to be processed. + +Returns: + + EFI_SUCCESS - The operation completed successfully. + EFI_NOT_STARTED - The driver instance has not been started. + EFI_NO_MAPPING - When using the default address, configuration(DHCP, + BOOTP, RARP, etc.) is not finished yet. + EFI_OUT_OF_RESOURCES - Could not add the entry to the routing table. + EFI_NOT_FOUND - This route is not in the routing table + (when RouteInfo->DeleteRoute is TRUE). + EFI_ACCESS_DENIED - The route is already defined in the routing table + (when RouteInfo->DeleteRoute is FALSE). + +--*/ +{ + EFI_IP4_PROTOCOL *Ip; + + Ip = Tcb->IpInfo->Ip; + + ASSERT (Ip); + + return Ip->Routes ( + Ip, + RouteInfo->DeleteRoute, + RouteInfo->SubnetAddress, + RouteInfo->SubnetMask, + RouteInfo->GatewayAddress + ); + +} + + +/** + Get the operational settings of this TCP instance. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data is read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +STATIC +EFI_STATUS +Tcp4GetMode ( + IN TCP_CB *Tcb, + IN 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) { + *(Mode->Tcp4State) = Tcb->State; + } + + if (Mode->Tcp4ConfigData) { + + ConfigData = Mode->Tcp4ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TypeOfService = Tcb->TOS; + ConfigData->TimeToLive = Tcb->TTL; + + AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr; + + EFI_IP4 (AccessPoint->StationAddress) = Tcb->LocalEnd.Ip; + AccessPoint->SubnetMask = Tcb->SubnetMask; + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + + EFI_IP4 (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 = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); + Option->EnableTimeStamp = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS); + Option->EnableWindowScaling = !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip; + ASSERT (Ip); + + return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + + +/** + If AP->StationPort isn't zero, check whether the access point + is registered, else generate a random station port for this + access point. + + @param AP Pointer to the access point. + + @retval EFI_SUCCESS The check is 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. + +**/ +STATIC +EFI_STATUS +Tcp4Bind ( + IN EFI_TCP4_ACCESS_POINT *AP + ) +{ + BOOLEAN Cycle; + + if (0 != AP->StationPort) { + // + // check if a same endpoint is bound + // + if (TcpFindTcbByPeer (&AP->StationAddress, AP->StationPort)) { + + return EFI_INVALID_PARAMETER; + } + } else { + // + // generate a random port + // + Cycle = FALSE; + + if (TCP4_PORT_USER_RESERVED == mTcp4RandomPort) { + mTcp4RandomPort = TCP4_PORT_KNOWN; + } + + mTcp4RandomPort++; + + while (TcpFindTcbByPeer (&AP->StationAddress, mTcp4RandomPort)) { + + mTcp4RandomPort++; + + if (mTcp4RandomPort <= TCP4_PORT_KNOWN) { + + if (Cycle) { + TCP4_DEBUG_ERROR (("Tcp4Bind: no port can be allocated " + "for this pcb\n")); + + return EFI_OUT_OF_RESOURCES; + } + + mTcp4RandomPort = TCP4_PORT_KNOWN + 1; + + Cycle = TRUE; + } + + } + + AP->StationPort = mTcp4RandomPort; + } + + return EFI_SUCCESS; +} + + +/** + Flush the Tcb add its associated protocols.. + + @param Tcb Pointer to the TCP_CB to be flushed. + + @retval EFI_SUCCESS The operation is completed successfully. + +**/ +STATIC +VOID +Tcp4FlushPcb ( + IN TCP_CB *Tcb + ) +{ + SOCKET *Sock; + TCP4_PROTO_DATA *TcpProto; + + IpIoConfigIp (Tcb->IpInfo, NULL); + + Sock = Tcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + + if (SOCK_IS_CONFIGURED (Sock)) { + NetListRemoveEntry (&Tcb->List); + + TcpSetVariableData (TcpProto->TcpService); + } + + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); +} + +STATIC +EFI_STATUS +Tcp4AttachPcb ( + IN SOCKET *Sk + ) +{ + TCP_CB *Tcb; + TCP4_PROTO_DATA *ProtoData; + IP_IO *IpIo; + + Tcb = NetAllocateZeroPool (sizeof (TCP_CB)); + + if (Tcb == NULL) { + + TCP4_DEBUG_ERROR (("Tcp4ConfigurePcb: failed to allocate a TCB\n")); + + return EFI_OUT_OF_RESOURCES; + } + + ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved; + IpIo = ProtoData->TcpService->IpIo; + + // + // Create an IpInfo for this Tcb. + // + Tcb->IpInfo = IpIoAddIp (IpIo); + if (Tcb->IpInfo == NULL) { + + NetFreePool (Tcb); + return EFI_OUT_OF_RESOURCES; + } + + NetListInit (&Tcb->List); + NetListInit (&Tcb->SndQue); + NetListInit (&Tcb->RcvQue); + + Tcb->State = TCP_CLOSED; + Tcb->Sk = Sk; + ProtoData->TcpPcb = Tcb; + + return EFI_SUCCESS; +} + +STATIC +VOID +Tcp4DetachPcb ( + IN SOCKET *Sk + ) +{ + TCP4_PROTO_DATA *ProtoData; + TCP_CB *Tcb; + + ProtoData = (TCP4_PROTO_DATA *) Sk->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + ASSERT (Tcb != NULL); + + Tcp4FlushPcb (Tcb); + + IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo); + + NetFreePool (Tcb); + + ProtoData->TcpPcb = NULL; +} + + +/** + Configure the Tcb using CfgData. + + @param Sk Pointer to the socket of this TCP instance. + @param SkTcb Pointer to the TCP_CB 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. + +**/ +STATIC +EFI_STATUS +Tcp4ConfigurePcb ( + IN SOCKET *Sk, + IN EFI_TCP4_CONFIG_DATA *CfgData + ) +{ + IP_IO *IpIo; + EFI_IP4_CONFIG_DATA IpCfgData; + EFI_STATUS Status; + EFI_TCP4_OPTION *Option; + TCP4_PROTO_DATA *TcpProto; + TCP_CB *Tcb; + + ASSERT (CfgData && Sk && Sk->SockHandle); + + TcpProto = (TCP4_PROTO_DATA *) Sk->ProtoReserved; + Tcb = TcpProto->TcpPcb; + IpIo = TcpProto->TcpService->IpIo; + + ASSERT (Tcb != NULL); + + // + // Add Ip for send pkt to the peer + // + IpCfgData = mIpIoDefaultIpConfigData; + 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)) { + TCP4_DEBUG_ERROR (("Tcp4ConfigurePcb: Bind endpoint failed " + "with %r\n", Status)); + + goto OnExit; + } + + // + // Initalize the operating information in this Tcb + // + ASSERT (Tcb->State == TCP_CLOSED && + NetListIsEmpty (&Tcb->SndQue) && + NetListIsEmpty (&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->LocalEnd.Ip = EFI_IP4 (CfgData->AccessPoint.StationAddress); + Tcb->LocalEnd.Port = HTONS (CfgData->AccessPoint.StationPort); + Tcb->SubnetMask = CfgData->AccessPoint.SubnetMask; + + Tcb->RemoteEnd.Ip = EFI_IP4 (CfgData->AccessPoint.RemoteAddress); + Tcb->RemoteEnd.Port = HTONS (CfgData->AccessPoint.RemotePort); + + Option = CfgData->ControlOption; + + if (Option != NULL) { + SET_RCV_BUFFSIZE ( + Sk, + TCP_COMP_VAL (TCP_RCV_BUF_SIZE_MIN, + TCP_RCV_BUF_SIZE, + TCP_RCV_BUF_SIZE, + Option->ReceiveBufferSize) + ); + SET_SND_BUFFSIZE ( + Sk, + TCP_COMP_VAL (TCP_SND_BUF_SIZE_MIN, + TCP_SND_BUF_SIZE, + TCP_SND_BUF_SIZE, + Option->SendBufferSize) + ); + + SET_BACKLOG ( + Sk, + 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, + 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, + 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, + Option->KeepAliveTime * TCP_TICK_HZ + ); + Tcb->KeepAlivePeriod = TCP_COMP_VAL ( + TCP_KEEPALIVE_PERIOD_MIN, + TCP_KEEPALIVE_PERIOD, + TCP_KEEPALIVE_PERIOD, + Option->KeepAliveInterval * TCP_TICK_HZ + ); + } + + Tcb->ConnectTimeout = TCP_COMP_VAL ( + TCP_CONNECT_TIME_MIN, + TCP_CONNECT_TIME, + TCP_CONNECT_TIME, + Option->ConnectionTimeout * TCP_TICK_HZ + ); + + if (Option->EnableNagle == FALSE) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); + } + + if (Option->EnableTimeStamp == FALSE) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS); + } + + if (Option->EnableWindowScaling == FALSE) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS); + } + } + + // + // update state of Tcb and socket + // + if (CfgData->AccessPoint.ActiveFlag == FALSE) { + + TcpSetState (Tcb, TCP_LISTEN); + SockSetState (Sk, SO_LISTENING); + + Sk->ConfigureState = SO_CONFIGURED_PASSIVE; + } else { + + Sk->ConfigureState = SO_CONFIGURED_ACTIVE; + } + + TcpInsertTcb (Tcb); + +OnExit: + + return Status; +} + + +/** + The procotol handler provided to the socket layer, used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param Sock Pointer to the socket of this TCP instance. + @param Request The code of this operation request. + @param Data Pointer to the operation specific data passed in + together with the operation request. + + @retval EFI_SUCCESS The socket request is completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +Tcp4Dispatcher ( + IN SOCKET *Sock, + IN SOCK_REQUEST Request, + IN VOID *Data OPTIONAL + ) +{ + TCP_CB *Tcb; + TCP4_PROTO_DATA *ProtoData; + EFI_IP4_PROTOCOL *Ip; + + ProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + switch (Request) { + case SOCK_POLL: + Ip = ProtoData->TcpService->IpIo->Ip; + Ip->Poll (Ip); + 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); + TcpOnAppConsume (Tcb); + break; + + case SOCK_SND: + + ASSERT (Tcb); + 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 Tcp4AttachPcb (Sock); + + break; + + case SOCK_FLUSH: + + Tcp4FlushPcb (Tcb); + + break; + + case SOCK_DETACH: + + Tcp4DetachPcb (Sock); + + break; + + case SOCK_CONFIGURE: + + return Tcp4ConfigurePcb ( + Sock, + (EFI_TCP4_CONFIG_DATA *) Data + ); + + break; + + case SOCK_MODE: + + ASSERT (Data && Tcb); + + return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data); + + break; + + case SOCK_ROUTE: + + ASSERT (Data && Tcb); + + return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data); + + } + + return EFI_SUCCESS; + +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c new file mode 100644 index 0000000000..2927849285 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.c @@ -0,0 +1,669 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Driver.c + +Abstract: + + +**/ + +#include "Tcp4Main.h" + + +UINT16 mTcp4RandomPort; +extern EFI_COMPONENT_NAME_PROTOCOL gTcp4ComponentName; + +TCP4_HEARTBEAT_TIMER mTcp4Timer = { + NULL, + 0 +}; + +EFI_TCP4_PROTOCOL mTcp4ProtocolTemplate = { + Tcp4GetModeData, + Tcp4Configure, + Tcp4Routes, + Tcp4Connect, + Tcp4Accept, + Tcp4Transmit, + Tcp4Receive, + Tcp4Close, + Tcp4Cancel, + Tcp4Poll +}; + +SOCK_INIT_DATA mTcp4DefaultSockData = { + SOCK_STREAM, + 0, + NULL, + TCP_BACKLOG, + TCP_SND_BUF_SIZE, + TCP_RCV_BUF_SIZE, + &mTcp4ProtocolTemplate, + Tcp4Dispatcher, + NULL, +}; + +EFI_DRIVER_BINDING_PROTOCOL mTcp4DriverBinding = { + Tcp4DriverBindingSupported, + Tcp4DriverBindingStart, + Tcp4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL mTcp4ServiceBinding = { + Tcp4ServiceBindingCreateChild, + Tcp4ServiceBindingDestroyChild +}; + + +/** + Create and start the heartbeat timer for TCP driver. + + None. + + @retval EFI_SUCCESS The timer is successfully created and started. + @retval other The timer is not created. + +**/ +STATIC +EFI_STATUS +Tcp4CreateTimer ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (mTcp4Timer.RefCnt == 0) { + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + NET_TPL_TIMER, + TcpTicking, + NULL, + &mTcp4Timer.TimerEvent + ); + if (!EFI_ERROR (Status)) { + + Status = gBS->SetTimer ( + mTcp4Timer.TimerEvent, + TimerPeriodic, + (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ) + ); + } + } + + if (!EFI_ERROR (Status)) { + + mTcp4Timer.RefCnt++; + } + + return Status; +} + + +/** + Stop and destroy the heartbeat timer for TCP driver. + + None. + + @return None. + +**/ +STATIC +VOID +Tcp4DestroyTimer ( + VOID + ) +{ + ASSERT (mTcp4Timer.RefCnt > 0); + + mTcp4Timer.RefCnt--; + + if (mTcp4Timer.RefCnt > 0) { + return; + } + + gBS->SetTimer (mTcp4Timer.TimerEvent, TimerCancel, 0); + gBS->CloseEvent (mTcp4Timer.TimerEvent); + mTcp4Timer.TimerEvent = NULL; +} + +//@MT: EFI_DRIVER_ENTRY_POINT (Tcp4DriverEntryPoint) + +EFI_STATUS +EFIAPI +Tcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +/*++ + +Routine Description: + + The entry point for Tcp4 driver. used to install + Tcp4 driver on the ImageHandle. + +Arguments: + + ImageHandle - The firmware allocated handle for this + driver image. + SystemTable - Pointer to the EFI system table. + +Returns: + + EFI_SUCCESS - Driver loaded. + other - Driver not loaded. + +--*/ +{ + EFI_STATUS Status; + UINT32 Seed; + + // + // Install the TCP4 Driver Binding Protocol + // + Status = NetLibInstallAllDriverProtocols ( + ImageHandle, + SystemTable, + &mTcp4DriverBinding, + ImageHandle, + &gTcp4ComponentName, + NULL, + NULL + ); + + // + // Initialize ISS and random port. + // + Seed = NetRandomInitSeed (); + mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss; + mTcp4RandomPort = TCP4_PORT_KNOWN + + (UINT16) (NET_RANDOM(Seed) % TCP4_PORT_KNOWN); + + return Status; +} + + +/** + Test to see if this driver supports ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the Tcp4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Test for the Ip4 Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver is added to ControllerHandle. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to start the + driver. + @retval other The driver cannot be added to ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + TCP4_SERVICE_DATA *TcpServiceData; + IP_IO_OPEN_DATA OpenData; + + TcpServiceData = NetAllocateZeroPool (sizeof (TCP4_SERVICE_DATA)); + + if (NULL == TcpServiceData) { + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Have no enough" + " resource to create a Tcp Servcie Data!\n")); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Create a new IP IO to Consume it + // + TcpServiceData->IpIo = IpIoCreate (This->DriverBindingHandle, ControllerHandle); + if (NULL == TcpServiceData->IpIo) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Have no enough" + " resource to create an Ip Io!\n")); + + Status = EFI_OUT_OF_RESOURCES; + goto ReleaseServiceData; + } + + // + // Configure and start IpIo. + // + NetZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA)); + + OpenData.IpConfigData = mIpIoDefaultIpConfigData; + OpenData.IpConfigData.DefaultProtocol = EFI_IP_PROTO_TCP; + + OpenData.PktRcvdNotify = Tcp4RxCallback; + Status = IpIoOpen (TcpServiceData->IpIo, &OpenData); + + if (EFI_ERROR (Status)) { + goto ReleaseServiceData; + } + + // + // Create the timer event used by TCP driver + // + Status = Tcp4CreateTimer (); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Create TcpTimer" + " Event failed with %r\n", Status)); + + goto ReleaseIpIo; + } + + // + // Install the Tcp4ServiceBinding Protocol on the + // controller handle + // + TcpServiceData->Tcp4ServiceBinding = mTcp4ServiceBinding; + + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + &TcpServiceData->Tcp4ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStart: Install Tcp4 Service Binding" + " Protocol failed for %r\n", Status)); + + goto ReleaseTimer; + } + + // + // Initialize member in TcpServiceData + // + TcpServiceData->ControllerHandle = ControllerHandle; + TcpServiceData->Signature = TCP4_DRIVER_SIGNATURE; + TcpServiceData->DriverBindingHandle = This->DriverBindingHandle; + + TcpSetVariableData (TcpServiceData); + + return EFI_SUCCESS; + +ReleaseTimer: + + Tcp4DestroyTimer (); + +ReleaseIpIo: + + IpIoDestroy (TcpServiceData->IpIo); + +ReleaseServiceData: + + NetFreePool (TcpServiceData); + + return Status; +} + + +/** + Stop this driver on ControllerHandle. + + @param This Protocol instance pointer. + @param ControllerHandle Handle of device to stop driver on. + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed from ControllerHandle. + @retval other This driver is not removed from ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +Tcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + EFI_HANDLE NicHandle; + EFI_SERVICE_BINDING_PROTOCOL *Tcp4ServiceBinding; + TCP4_SERVICE_DATA *TcpServiceData; + TCP_CB *TcpPcb; + SOCKET *Sock; + TCP4_PROTO_DATA *TcpProto; + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *NextEntry; + + // Find the NicHandle where Tcp4 ServiceBinding Protocol is installed. + // + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid); + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + // + // Retrieve the TCP driver Data Structure + // + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + (VOID **) &Tcp4ServiceBinding, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Locate Tcp4 Service " + " Binding Protocol failed with %r\n", Status)); + + return Status; + } + + TcpServiceData = TCP4_FROM_THIS (Tcp4ServiceBinding); + + // + // Kill TCP driver + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mTcpRunQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + // + // Try to destroy this child + // + Sock = TcpPcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + + if (TcpProto->TcpService == TcpServiceData) { + Status = SockDestroyChild (Sock); + + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Destroy Tcp " + "instance failed with %r\n", Status)); + return Status; + } + } + } + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mTcpListenQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + // + // Try to destroy this child + // + Sock = TcpPcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + + if (TcpProto->TcpService == TcpServiceData) { + Status = SockDestroyChild (TcpPcb->Sk); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Destroy Tcp " + "instance failed with %r\n", Status)); + return Status; + } + } + } + + // + // Uninstall TCP servicebinding protocol + // + Status = gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + Tcp4ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + + TCP4_DEBUG_ERROR (("Tcp4DriverBindingStop: Uninstall TCP service " + "binding protocol failed with %r\n", Status)); + return Status; + } + + // + // Destroy the IpIO consumed by TCP driver + // + Status = IpIoDestroy (TcpServiceData->IpIo); + + // + // Destroy the heartbeat timer. + // + Tcp4DestroyTimer (); + + // + // Clear the variable. + // + TcpClearVariableData (TcpServiceData); + + // + // Release the TCP service data + // + NetFreePool (TcpServiceData); + + return Status; +} + + +/** + Creates a child handle with a set of TCP4 services. + + @param This Protocol instance pointer. + @param ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it is + not NULL, then the I/O services are added to the + existing child handle. + + @retval EFI_SUCCESS The child handle is created. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to create the + child. + +**/ +EFI_STATUS +EFIAPI +Tcp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + SOCKET *Sock; + TCP4_SERVICE_DATA *TcpServiceData; + TCP4_PROTO_DATA TcpProto; + EFI_STATUS Status; + VOID *Ip4; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + TcpServiceData = TCP4_FROM_THIS (This); + TcpProto.TcpService = TcpServiceData; + TcpProto.TcpPcb = NULL; + + // + // Create a tcp instance with defualt Tcp default + // sock init data and TcpProto + // + mTcp4DefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle; + + Sock = SockCreateChild (&mTcp4DefaultSockData, &TcpProto, sizeof (TCP4_PROTO_DATA)); + if (NULL == Sock) { + TCP4_DEBUG_ERROR (("Tcp4DriverBindingCreateChild: " + "No resource to create a Tcp Child\n")); + + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + *ChildHandle = Sock->SockHandle; + + // + // Open the default Ip4 protocol of IP_IO BY_DRIVER. + // + Status = gBS->OpenProtocol ( + TcpServiceData->IpIo->ChildHandle, + &gEfiIp4ProtocolGuid, + (VOID **) &Ip4, + TcpServiceData->DriverBindingHandle, + Sock->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + SockDestroyChild (Sock); + } + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} + + +/** + Destroys a child handle with a set of UDP4 services. + + @param This Protocol instance pointer. + @param ChildHandle Handle of the child to be destroyed. + + @retval EFI_SUCCESS The TCP4 services are removed from the child + handle. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval other The child handle is not destroyed. + +**/ +EFI_STATUS +EFIAPI +Tcp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + EFI_TCP4_PROTOCOL *Tcp4; + SOCKET *Sock; + TCP4_PROTO_DATA *TcpProtoData; + TCP4_SERVICE_DATA *TcpServiceData; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = NET_RAISE_TPL (NET_TPL_LOCK); + + // + // retrieve the Tcp4 protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp4ProtocolGuid, + (VOID **) &Tcp4, + mTcp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + goto ON_EXIT; + } + + // + // destroy this sock and related Tcp protocol control + // block + // + Sock = SOCK_FROM_THIS (Tcp4); + TcpProtoData = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + TcpServiceData = TcpProtoData->TcpService; + + Status = SockDestroyChild (Sock); + + // + // Close the Ip4 protocol. + // + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + &gEfiIp4ProtocolGuid, + TcpServiceData->DriverBindingHandle, + ChildHandle + ); + +ON_EXIT: + NET_RESTORE_TPL (OldTpl); + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h new file mode 100644 index 0000000000..af3444ef58 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Driver.h @@ -0,0 +1,141 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Driver.h + +Abstract: + + +**/ + +#ifndef _TCP4_DRIVER_H_ +#define _TCP4_DRIVER_H_ + +#include +#include + +#define TCP4_DRIVER_SIGNATURE EFI_SIGNATURE_32 ('T', 'C', 'P', '4') + +#define TCP4_PORT_KNOWN 1024 +#define TCP4_PORT_USER_RESERVED 65535 + +typedef struct _TCP4_HEARTBEAT_TIMER { + EFI_EVENT TimerEvent; + INTN RefCnt; +} TCP4_HEARTBEAT_TIMER; + +typedef struct _TCP4_SERVICE_DATA { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + IP_IO *IpIo; // IP Io consumed by TCP4 + EFI_SERVICE_BINDING_PROTOCOL Tcp4ServiceBinding; + EFI_HANDLE DriverBindingHandle; + CHAR16 *MacString; +} TCP4_SERVICE_DATA; + +// +// Prototype for TCP4 driver Rcv callback function registered to IP_IO +// +VOID +Tcp4RxCallback ( + IN EFI_STATUS Status, + IN ICMP_ERROR IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ); + +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dest + ); + +EFI_STATUS +Tcp4Dispatcher ( + IN SOCKET *Sock, + IN SOCK_REQUEST Request, + IN VOID *Data OPTIONAL + ); + +typedef struct _TCP4_PROTO_DATA { + TCP4_SERVICE_DATA *TcpService; + TCP_CB *TcpPcb; +} TCP4_PROTO_DATA; + +#define TCP4_FROM_THIS(a) \ + CR ( \ + (a), \ + TCP4_SERVICE_DATA, \ + Tcp4ServiceBinding, \ + TCP4_DRIVER_SIGNATURE \ + ) + +// +// Function prototype for the driver's entry point +// +EFI_STATUS +EFIAPI +Tcp4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Drivr Binding Protocol +// +EFI_STATUS +EFIAPI +Tcp4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Tcp4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +EFI_STATUS +EFIAPI +Tcp4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// +EFI_STATUS +EFIAPI +Tcp4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +EFI_STATUS +EFIAPI +Tcp4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf new file mode 100644 index 0000000000..9baea4e388 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf @@ -0,0 +1,77 @@ +#/** @file +# Component name for module Tcp4 +# +# FIX ME! +# Copyright (c) 2006, Intel Corporation. All right reserved. +# +# All rights reserved. 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. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Tcp4Dxe + FILE_GUID = 6d6963ab-906d-4a65-a7ca-bd40e5d6af4d + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + EDK_RELEASE_VERSION = 0x00020000 + EFI_SPECIFICATION_VERSION = 0x00020000 + + ENTRY_POINT = Tcp4DriverEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + SockImpl.c + SockInterface.c + Tcp4Proto.h + Tcp4Main.h + SockImpl.h + Tcp4Output.c + Tcp4Timer.c + Tcp4Option.h + Tcp4Dispatcher.c + Tcp4Input.c + Tcp4Misc.c + Tcp4Main.c + Socket.h + ComponentName.c + Tcp4Driver.h + Tcp4Io.c + Tcp4Driver.c + Tcp4Func.h + Tcp4Option.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DebugLib + NetLib + IpIoLib + +[Protocols] + gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa new file mode 100644 index 0000000000..fec1934bb7 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.msa @@ -0,0 +1,90 @@ + + + Tcp4Dxe + DXE_DRIVER + 6d6963ab-906d-4a65-a7ca-bd40e5d6af4d + 1.0 + Component name for module Tcp4 + FIX ME! + Copyright (c) 2006, Intel Corporation. All right reserved. + All rights reserved. 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. + FRAMEWORK_BUILD_PACKAGING_SPECIFICATION 0x00000052 + + + IA32 X64 IPF EBC + false + Tcp4Dxe + + + + DebugLib + + + UefiRuntimeServicesTableLib + + + UefiDriverEntryPoint + + + UefiBootServicesTableLib + + + BaseLib + + + UefiLib + + + + Tcp4Option.c + Tcp4Func.h + Tcp4Driver.c + Tcp4Io.c + Tcp4Driver.h + ComponentName.c + Socket.h + Tcp4Main.c + Tcp4Misc.c + Tcp4Input.c + Tcp4Dispatcher.c + Tcp4Option.h + Tcp4Timer.c + Tcp4Output.c + SockImpl.h + Tcp4Main.h + Tcp4Proto.h + SockInterface.c + SockImpl.c + + + + + + + + gEfiTcp4ProtocolGuid + + + gEfiIp4ServiceBindingProtocolGuid + + + gEfiTcp4ServiceBindingProtocolGuid + + + gEfiIp4ProtocolGuid + + + + EFI_SPECIFICATION_VERSION 0x00020000 + EDK_RELEASE_VERSION 0x00020000 + + Tcp4DriverEntryPoint + + + \ No newline at end of file diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h new file mode 100644 index 0000000000..6441c13c64 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Func.h @@ -0,0 +1,353 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Func.h + +Abstract: + + +**/ + +#ifndef _TCP4_FUNC_H_ +#define _TCP4_FUNC_H_ + +// +// Declaration of all the functions in TCP +// protocol. It is intended to keep tcp.h +// clear. +// + +// +// Functions in tcp.c +// +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IPv4_ADDRESS *Addr, + IN TCP_PORTNO Port + ); + +TCP_CB * +TcpLocateTcb ( + IN TCP_PORTNO LocalPort, + IN UINT32 LocalIp, + IN TCP_PORTNO RemotePort, + IN UINT32 RemoteIp, + IN BOOLEAN Syn + ); + +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ); + +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ); + +TCP_SEQNO +TcpGetIss ( + VOID + ); + +VOID +TcpInitTcbLocal ( + IN TCP_CB *Tcb + ); + +VOID +TcpInitTcbPeer ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ); + +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ); + +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ); + +// +// Functions in Tcp4Output.c +// +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dst + ); + +INTN +TcpToSendData ( + IN TCP_CB *Tcb, + IN INTN Force + ); + +VOID +TcpToSendAck ( + IN TCP_CB *Tcb + ); + +VOID +TcpSendAck ( + IN TCP_CB *Tcb + ); + +INTN +TcpSendZeroProbe ( + IN TCP_CB *Tcb + ); + +INTN +TcpDeliverData ( + IN TCP_CB *Tcb + ); + +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN UINT32 Local, + IN UINT32 Remote + ); + +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ); + +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ); + +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ); + +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ); + +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ); + +INTN +TcpCheckSndQue ( + IN NET_LIST_ENTRY *Head + ); + +NET_BUF * +TcpGetSegmentSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ); + +NET_BUF * +TcpGetSegmentSock ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ); + +NET_BUF * +TcpGetSegment ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ); + +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ); + +// +// Functions from Tcp4Input.c +// +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN ICMP_ERROR IcmpErr, + IN UINT32 Src, + IN UINT32 Dst + ); + +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dst + ); + +INTN +TcpSeqAcceptable ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ); + +VOID +TcpFastRecover ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ); + +VOID +TcpFastLossRecover ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ); + +VOID +TcpComputeRtt ( + IN TCP_CB *Tcb, + IN UINT32 Measure + ); + +INTN +TcpTrimInWnd ( + IN TCP_CB *Tcb, + IN NET_BUF *Buf + ); + +VOID +TcpQueueData ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +VOID +TcpAdjustSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Ack + ); + +// +// Functions from Tcp4Misc.c +// +UINT16 +TcpChecksum ( + IN NET_BUF *Buf, + IN UINT16 HeadChecksum + ); + +TCP_SEG * +TcpFormatNetbuf ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ); + +VOID +TcpOnAppConnect ( + IN TCP_CB *Tcb + ); + +INTN +TcpOnAppConsume ( + IN TCP_CB *Tcb + ); + +VOID +TcpOnAppClose ( + IN TCP_CB *Tcb + ); + +INTN +TcpOnAppSend ( + IN TCP_CB *Tcb + ); + +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ); + +VOID +TcpResetConnection ( + IN TCP_CB *Tcb + ); + +// +// Functions in Tcp4Timer.c +// +VOID +TcpClose ( + IN TCP_CB *Tcb + ); + +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +VOID +TcpSetTimer ( + IN TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ); + +VOID +TcpClearTimer ( + IN TCP_CB *Tcb, + IN UINT16 Timer + ); + +VOID +TcpClearAllTimer ( + IN TCP_CB *Tcb + ); + +VOID +TcpSetProbeTimer ( + IN TCP_CB *Tcb + ); + +VOID +TcpSetKeepaliveTimer ( + IN TCP_CB *Tcb + ); + +VOID +TcpBackoffRto ( + IN TCP_CB *Tcb + ); + +EFI_STATUS +TcpSetVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ); + +VOID +TcpClearVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c new file mode 100644 index 0000000000..b636016207 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Input.c @@ -0,0 +1,1486 @@ +/** @file + +Copyright (c) 2005 - 2007, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Input.c + +Abstract: + + TCP input process routines. + + +**/ + +#include "Tcp4Main.h" + + +/** + Check whether the sequence number of the incoming segment + is acceptable. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Pointer to the incoming segment. + + @return 1 if the sequence number is acceptable, otherwise 0. + +**/ +INTN +TcpSeqAcceptable ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd)); +} + + +/** + NewReno fast recovery, RFC3782. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Segment that triggers the fast recovery. + + @return None. + +**/ +VOID +TcpFastRecover ( + IN 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 = NET_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; + + TCP4_DEBUG_TRACE (("TcpFastRecover: enter fast retransmission" + " for TCB %x, recover point is %d\n", Tcb, Tcb->Recover)); + return; + } + + // + // During fast recovery, execute Step 3, 4, 5 of RFC3782 + // + if (Seg->Ack == Tcb->SndUna) { + + // + // Step 3: Fast Recovery, + // If this is a duplicated ACK, increse Cwnd by SMSS. + // + + // Step 4 is skipped here only to be executed later + // by TcpToSendData + // + Tcb->CWnd += Tcb->SndMss; + TCP4_DEBUG_TRACE (("TcpFastRecover: received another" + " duplicated ACK (%d) for TCB %x\n", Seg->Ack, Tcb)); + + } else { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->Recover)) { + + // + // Step 5 - Full ACK: + // deflate the congestion window, and exit fast recovery + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->CWnd = NET_MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss); + + Tcb->CongestState = TCP_CONGEST_OPEN; + TCP4_DEBUG_TRACE (("TcpFastRecover: received a full ACK(%d)" + " for TCB %x, exit fast recovery\n", Seg->Ack, Tcb)); + + } else { + + // + // Step 5 - Partial ACK: + // fast retransmit the first unacknowledge field + // , then deflate the CWnd + // + TcpRetransmit (Tcb, Seg->Ack); + Acked = TCP_SUB_SEQ (Seg->Ack, Tcb->SndUna); + + // + // Deflate the CWnd by the amount of new data + // ACKed by SEG.ACK. If more than one SMSS data + // is ACKed, add back SMSS byte to CWnd after + // + if (Acked >= Tcb->SndMss) { + Acked -= Tcb->SndMss; + + } + + Tcb->CWnd -= Acked; + + TCP4_DEBUG_TRACE (("TcpFastRecover: received a partial" + " ACK(%d) for TCB %x\n", Seg->Ack, Tcb)); + + } + } +} + + +/** + NewReno fast loss recovery, RFC3792. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Segment that triggers the fast loss recovery. + + @return None. + +**/ +VOID +TcpFastLossRecover ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + // + // New data is ACKed, check whether it is a + // full ACK or partial ACK + // + if (TCP_SEQ_GEQ (Seg->Ack, Tcb->LossRecover)) { + + // + // Full ACK: exit the loss recovery. + // + Tcb->LossTimes = 0; + Tcb->CongestState = TCP_CONGEST_OPEN; + + TCP4_DEBUG_TRACE (("TcpFastLossRecover: received a " + "full ACK(%d) for TCB %x\n", Seg->Ack, Tcb)); + + } else { + + // + // Partial ACK: + // fast retransmit the first unacknowledge field. + // + TcpRetransmit (Tcb, Seg->Ack); + TCP4_DEBUG_TRACE (("TcpFastLossRecover: received a " + "partial ACK(%d) for TCB %x\n", Seg->Ack, Tcb)); + } + } +} + + +/** + Compute the RTT as specified in RFC2988 + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Measure Currently measured RTT in heart beats. + + @return None. + +**/ +VOID +TcpComputeRtt ( + IN TCP_CB *Tcb, + IN UINT32 Measure + ) +{ + INT32 Var; + + // + // Step 2.3: Compute the RTO for subsequent RTT measurement. + // + if (Tcb->SRtt != 0) { + + Var = Tcb->SRtt - (Measure << TCP_RTT_SHIFT); + + if (Var < 0) { + Var = -Var; + } + + Tcb->RttVar = (3 * Tcb->RttVar + Var) >> 2; + Tcb->SRtt = 7 * (Tcb->SRtt >> 3) + Measure; + + } else { + // + // Step 2.2: compute the first RTT measure + // + Tcb->SRtt = Measure << TCP_RTT_SHIFT; + Tcb->RttVar = Measure << (TCP_RTT_SHIFT - 1); + } + + Tcb->Rto = (Tcb->SRtt + NET_MAX (8, 4 * Tcb->RttVar)) >> TCP_RTT_SHIFT; + + // + // Step 2.4: Limit the RTO to at least 1 second + // Step 2.5: Limit the RTO to a maxium value that + // is at least 60 second + // + if (Tcb->Rto < TCP_RTO_MIN) { + Tcb->Rto = TCP_RTO_MIN; + + } else if (Tcb->Rto > TCP_RTO_MAX) { + Tcb->Rto = TCP_RTO_MAX; + + } + + TCP4_DEBUG_TRACE (("TcpComputeRtt: new RTT for TCB %x" + " computed SRTT: %d RTTVAR: %d RTO: %d\n", + Tcb, Tcb->SRtt, Tcb->RttVar, Tcb->Rto)); + +} + + +/** + Trim the data, SYN and FIN to fit into the window defined by + Left and Right. + + @param Nbuf Buffer that contains received TCP segment without IP header. + @param Left The sequence number of the window's left edge. + @param Right The sequence number of the window's right edge. + + @return 0, the data is successfully trimmed. + +**/ +STATIC +INTN +TcpTrimSegment ( + IN NET_BUF *Nbuf, + IN TCP_SEQNO Left, + IN TCP_SEQNO Right + ) +{ + TCP_SEG *Seg; + TCP_SEQNO Urg; + UINT32 Drop; + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // If the segment is completely out of window, + // truncate every thing, include SYN and FIN. + // + if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + + Seg->Seq = Seg->End; + NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD); + return 0; + } + + // + // Adjust the buffer header + // + if (TCP_SEQ_LT (Seg->Seq, Left)) { + + Drop = TCP_SUB_SEQ (Left, Seg->Seq); + Urg = Seg->Seq + Seg->Urg; + Seg->Seq = Left; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN); + Drop--; + } + + // + // Adjust the urgent point + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG)) { + + if (TCP_SEQ_LT (Urg, Seg->Seq)) { + + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + } else { + Seg->Urg = (UINT16) TCP_SUB_SEQ (Urg, Seg->Seq); + } + } + + if (Drop) { + NetbufTrim (Nbuf, Drop, NET_BUF_HEAD); + } + } + + // + // Adjust the buffer tail + // + if (TCP_SEQ_GT (Seg->End, Right)) { + + Drop = TCP_SUB_SEQ (Seg->End, Right); + Seg->End = Right; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN); + Drop--; + } + + if (Drop) { + NetbufTrim (Nbuf, Drop, NET_BUF_TAIL); + } + } + + ASSERT (TcpVerifySegment (Nbuf)); + return 0; +} + + +/** + Trim off the data outside the tcb's receive window. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the NET_BUF containing the received tcp segment. + + @return 0, the data is trimmed. + +**/ +INTN +TcpTrimInWnd ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + return TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd); +} + + +/** + Process the data and FIN flag, check whether to deliver + data to the socket layer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 No error occurred to deliver data. + @retval -1 Error condition occurred. Proper response is to reset the + connection. + +**/ +INTN +TcpDeliverData ( + IN TCP_CB *Tcb + ) +{ + NET_LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + TCP_SEG *Seg; + UINT32 Urgent; + + ASSERT (Tcb && Tcb->Sk); + + // + // make sure there is some data queued, + // and TCP is in a proper state + // + if (NetListIsEmpty (&Tcb->RcvQue) || !TCP_CONNECTED (Tcb->State)) { + + return 0; + } + + // + // Deliver data to the socket layer + // + Entry = Tcb->RcvQue.ForwardLink; + Seq = Tcb->RcvNxt; + + while (Entry != &Tcb->RcvQue) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seg = TCPSEG_NETBUF (Nbuf); + + ASSERT (TcpVerifySegment (Nbuf)); + ASSERT (Nbuf->Tcp == NULL); + + if (TCP_SEQ_GT (Seg->Seq, Seq)) { + break; + } + + Entry = Entry->ForwardLink; + Seq = Seg->End; + Tcb->RcvNxt = Seq; + + NetListRemoveEntry (&Nbuf->List); + + // + // RFC793 Eighth step: process FIN in sequence + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + // + // The peer sends to us junky data after FIN, + // reset the connection. + // + if (!NetListIsEmpty (&Tcb->RcvQue)) { + TCP4_DEBUG_ERROR (("TcpDeliverData: data received after" + " FIN from peer of TCB %x, reset connection\n", Tcb)); + + NetbufFree (Nbuf); + return -1; + } + + TCP4_DEBUG_TRACE (("TcpDeliverData: processing FIN " + "from peer of TCB %x\n", Tcb)); + + switch (Tcb->State) { + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + + TcpSetState (Tcb, TCP_CLOSE_WAIT); + break; + + case TCP_FIN_WAIT_1: + + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSING); + break; + } + + // + // fall through + // + case TCP_FIN_WAIT_2: + + TcpSetState (Tcb, TCP_TIME_WAIT); + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + TCP4_DEBUG_WARN (("Connection closed immediately " + "because app disables TIME_WAIT timer for %x\n", Tcb)); + + TcpSendAck (Tcb); + TcpClose (Tcb); + } + break; + + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + // + // The peer sends to us junk FIN byte. Discard + // the buffer then reset the connection + // + NetbufFree (Nbuf); + return -1; + break; + } + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + Seg->End--; + } + + // + // Don't delay the ack if PUSH flag is on. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_PSH)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + if (Nbuf->TotalSize) { + Urgent = 0; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvUp)) { + + if (TCP_SEQ_LEQ (Seg->End, Tcb->RcvUp)) { + Urgent = Nbuf->TotalSize; + } else { + Urgent = TCP_SUB_SEQ (Tcb->RcvUp, Seg->Seq) + 1; + } + } + + SockDataRcvd (Tcb->Sk, Nbuf, Urgent); + } + + if (TCP_FIN_RCVD (Tcb->State)) { + + SockNoMoreData (Tcb->Sk); + } + + NetbufFree (Nbuf); + } + + return 0; +} + + +/** + Store the data into the reassemble queue. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer containing the data to be queued. + + @return None. + +**/ +VOID +TcpQueueData ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Prev; + NET_LIST_ENTRY *Cur; + NET_BUF *Node; + + ASSERT (Tcb && Nbuf && (Nbuf->Tcp == NULL)); + + NET_GET_REF (Nbuf); + + Seg = TCPSEG_NETBUF (Nbuf); + Head = &Tcb->RcvQue; + + // + // Fast path to process normal case. That is, + // no out-of-order segments are received. + // + if (NetListIsEmpty (Head)) { + + NetListInsertTail (Head, &Nbuf->List); + return ; + } + + // + // Find the point to insert the buffer + // + for (Prev = Head, Cur = Head->ForwardLink; + Cur != Head; + Prev = Cur, Cur = Cur->ForwardLink) { + + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->Seq)) { + break; + } + } + + // + // Check whether the current segment overlaps with the + // previous segment. + // + if (Prev != Head) { + Node = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + + if (TCP_SEQ_LT (Seg->Seq, TCPSEG_NETBUF (Node)->End)) { + + if (TCP_SEQ_LEQ (Seg->End, TCPSEG_NETBUF (Node)->End)) { + + NetbufFree (Nbuf); + return ; + } + + TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End); + } + } + + NetListInsertHead (Prev, &Nbuf->List); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + // + // Check the segments after the insert point. + // + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->End, Seg->End)) { + + Cur = Cur->ForwardLink; + + NetListRemoveEntry (&Node->List); + NetbufFree (Node); + continue; + } + + if (TCP_SEQ_LT (TCPSEG_NETBUF (Node)->Seq, Seg->End)) { + + if (TCP_SEQ_LEQ (TCPSEG_NETBUF (Node)->Seq, Seg->Seq)) { + + NetListRemoveEntry (&Nbuf->List); + NetbufFree (Nbuf); + return ; + } + + TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq); + break; + } + + Cur = Cur->ForwardLink; + } +} + + +/** + Ajust the send queue or the retransmit queue. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Ack The acknowledge seuqence number of the received segment. + + @return None. + +**/ +VOID +TcpAdjustSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Ack + ) +{ + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + + Head = &Tcb->SndQue; + Cur = Head->ForwardLink; + + while (Cur != Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_GEQ (Seg->Seq, Ack)) { + break; + } + + // + // Remove completely ACKed segments + // + if (TCP_SEQ_LEQ (Seg->End, Ack)) { + Cur = Cur->ForwardLink; + + NetListRemoveEntry (&Node->List); + NetbufFree (Node); + continue; + } + + TcpTrimSegment (Node, Ack, Seg->End); + break; + } +} + + +/** + Process the received TCP segments. + + @param Nbuf Buffer that contains received TCP segment without IP header. + @param Src Source address of the segment, or the peer's IP address. + @param Dst Destination address of the segment, or the local end's IP + address. + + @retval 0 Segment is processed successfully. It is either accepted or + discarded. But no connection is reset by the segment. + @retval -1 A connection is reset by the segment. + +**/ +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dst + ) +{ + TCP_CB *Tcb; + TCP_CB *Parent; + TCP_OPTION Option; + TCP_HEAD *Head; + INT32 Len; + TCP_SEG *Seg; + TCP_SEQNO Right; + TCP_SEQNO Urg; + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Parent = NULL; + Tcb = NULL; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + Len = Nbuf->TotalSize - (Head->HeadLen << 2); + + if ((Head->HeadLen < 5) || (Len < 0) || + TcpChecksum (Nbuf, NetPseudoHeadChecksum (Src, Dst, 6, 0))) { + + TCP4_DEBUG_TRACE (("TcpInput: received an mal-formated packet\n")); + goto DISCARD; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_FIN)) { + Len++; + } + + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN) + ); + + if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) { + TCP4_DEBUG_TRACE (("TcpInput: send reset because no TCB find\n")); + + Tcb = NULL; + goto SEND_RESET; + } + + Seg = TcpFormatNetbuf (Tcb, Nbuf); + + // + // RFC1122 recommended reaction to illegal option + // (in fact, an illegal option length) is reset. + // + if (TcpParseOption (Nbuf->Tcp, &Option) == -1) { + TCP4_DEBUG_ERROR (("TcpInput: reset the peer because" + " of mal-format option for Tcb %x\n", Tcb)); + + goto SEND_RESET; + } + + // + // From now on, the segment is headless + // + NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + // + // TODO: add fast path process here + // + + // + // Process the segment in LISTEN state. + // + if (Tcb->State == TCP_LISTEN) { + // + // First step: Check RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + TCP4_DEBUG_WARN (("TcpInput: discard a reset segment " + "for TCB %x in listening\n", Tcb)); + + goto DISCARD; + } + + // + // Second step: Check ACK. + // Any ACK sent to TCP in LISTEN is reseted. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + TCP4_DEBUG_WARN (("TcpInput: send reset because of" + " segment with ACK for TCB %x in listening\n", Tcb)); + + goto SEND_RESET; + } + + // + // Third step: Check SYN + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + // + // create a child TCB to handle the data + // + Parent = Tcb; + + Tcb = TcpCloneTcb (Parent); + if (Tcb == NULL) { + TCP4_DEBUG_ERROR (("TcpInput: discard a segment because" + "failed to clone a child for TCB%x\n", Tcb)); + + goto DISCARD; + } + + TCP4_DEBUG_TRACE (("TcpInput: create a child for TCB %x" + " in listening\n", Tcb)); + + // + // init the TCB structure + // + Tcb->LocalEnd.Ip = Dst; + Tcb->LocalEnd.Port = Head->DstPort; + Tcb->RemoteEnd.Ip = Src; + Tcb->RemoteEnd.Port = Head->SrcPort; + + TcpInitTcbLocal (Tcb); + TcpInitTcbPeer (Tcb, Seg, &Option); + + TcpSetState (Tcb, TCP_SYN_RCVD); + TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout); + TcpTrimInWnd (Tcb, Nbuf); + + goto StepSix; + } + + goto DISCARD; + + } else if (Tcb->State == TCP_SYN_SENT) { + // + // First step: Check ACK bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK) && (Seg->Ack != Tcb->Iss + 1)) { + + TCP4_DEBUG_WARN (("TcpInput: send reset because of " + "wrong ACK received for TCB %x in SYN_SENT\n", Tcb)); + + goto SEND_RESET; + } + + // + // Second step: Check RST bit + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset by" + " peer for TCB%x in SYN_SENT\n", Tcb)); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto DROP_CONNECTION; + } else { + + TCP4_DEBUG_WARN (("TcpInput: discard a reset segment " + "because of no ACK for TCB%x in SYN_SENT\n", Tcb)); + + goto DISCARD; + } + } + + // + // Third step: Check security and precedence. Skipped + // + + // + // Fourth step: Check SYN. Pay attention to sitimulatous open + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + TcpInitTcbPeer (Tcb, Seg, &Option); + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + + Tcb->SndUna = Seg->Ack; + } + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + + if (TCP_SEQ_GT (Tcb->SndUna, Tcb->Iss)) { + + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + TcpTrimInWnd (Tcb, Nbuf); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + + TCP4_DEBUG_TRACE (("TcpInput: connection established" + " for TCB %x in SYN_SENT\n", Tcb)); + + goto StepSix; + } else { + // + // Received a SYN segment without ACK, simultanous open. + // + TcpSetState (Tcb, TCP_SYN_RCVD); + + ASSERT (Tcb->SndNxt == Tcb->Iss + 1); + TcpAdjustSndQue (Tcb, Tcb->SndNxt); + + TcpTrimInWnd (Tcb, Nbuf); + + TCP4_DEBUG_WARN (("TcpInput: simultanous open " + "for TCB %x in SYN_SENT\n", Tcb)); + + goto StepSix; + } + } + + goto DISCARD; + } + + // + // Process segment in SYN_RCVD or TCP_CONNECTED states + // + + // + // First step: Check whether SEG.SEQ is acceptable + // + if (!TcpSeqAcceptable (Tcb, Seg)) { + TCP4_DEBUG_WARN (("TcpInput: sequence acceptance" + " test failed for segment of TCB %x\n", Tcb)); + + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + TcpSendAck (Tcb); + } + + goto DISCARD; + } + + if ((TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2)) && + (Tcb->RcvWl2 == Seg->End) && + !TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN | TCP_FLG_FIN)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + + // + // Second step: Check the RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset for TCB %x\n", Tcb)); + + if (Tcb->State == TCP_SYN_RCVD) { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_REFUSED); + + // + // This TCB comes from either a LISTEN TCB, + // or active open TCB with simultanous open. + // Do NOT signal user CONNECTION refused + // if it comes from a LISTEN TCB. + // + } else if ((Tcb->State == TCP_ESTABLISHED) || + (Tcb->State == TCP_FIN_WAIT_1) || + (Tcb->State == TCP_FIN_WAIT_2) || + (Tcb->State == TCP_CLOSE_WAIT) + ) { + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + + } else { + // + // TODO: set socket error to CLOSED + // + } + + goto DROP_CONNECTION; + } + + // + // Trim the data and flags. + // + TcpTrimInWnd (Tcb, Nbuf); + + // + // Third step: Check security and precedence, Ignored + // + + // + // Fourth step: Check the SYN bit. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset " + "because received extra SYN for TCB %x\n", Tcb)); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto RESET_THEN_DROP; + } + + // + // Fifth step: Check the ACK + // + if (!TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + TCP4_DEBUG_WARN (("TcpInput: segment discard because" + " of no ACK for connected TCB %x\n", Tcb)); + + goto DISCARD; + + } + + if (Tcb->State == TCP_SYN_RCVD) { + + if (TCP_SEQ_LT (Tcb->SndUna, Seg->Ack) && + TCP_SEQ_LEQ (Seg->Ack, Tcb->SndNxt)) { + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = NET_MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + TCP4_DEBUG_TRACE (("TcpInput: connection established " + " for TCB %x in SYN_RCVD\n", Tcb)); + + // + // Continue the process as ESTABLISHED state + // + } else { + TCP4_DEBUG_WARN (("TcpInput: send reset because of" + " wrong ACK for TCB %x in SYN_RCVD\n", Tcb)); + + goto SEND_RESET; + } + } + + if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) { + + TCP4_DEBUG_WARN (("TcpInput: ignore the out-of-data" + " ACK for connected TCB %x\n", Tcb)); + + goto StepSix; + + } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) { + + TCP4_DEBUG_WARN (("TcpInput: discard segment for " + "future ACK for connected TCB %x\n", Tcb)); + + TcpSendAck (Tcb); + goto DISCARD; + } + + // + // From now on: SND.UNA <= SEG.ACK <= SND.NXT. + // + if (TCP_FLG_ON (Option.Flag, TCP_OPTION_RCVD_TS)) { + // + // update TsRecent as specified in page 16 RFC1323. + // RcvWl2 equals to the variable "LastAckSent" + // defined there. + // + if (TCP_SEQ_LEQ (Seg->Seq, Tcb->RcvWl2) && + TCP_SEQ_LT (Tcb->RcvWl2, Seg->End)) { + + Tcb->TsRecent = Option.TSVal; + Tcb->TsRecentAge = mTcpTick; + } + + TcpComputeRtt (Tcb, TCP_SUB_TIME (mTcpTick, Option.TSEcr)); + + } else if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + ASSERT (Tcb->CongestState == TCP_CONGEST_OPEN); + + TcpComputeRtt (Tcb, Tcb->RttMeasure); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + } + + if (Seg->Ack == Tcb->SndNxt) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + } else { + + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Count duplicate acks. + // + if ((Seg->Ack == Tcb->SndUna) && + (Tcb->SndUna != Tcb->SndNxt) && + (Seg->Wnd == Tcb->SndWnd) && + (0 == Len)) { + + Tcb->DupAck++; + } else { + + Tcb->DupAck = 0; + } + + // + // Congestion avoidance, fast recovery and fast retransmission. + // + if (((Tcb->CongestState == TCP_CONGEST_OPEN) && (Tcb->DupAck < 3)) || + (Tcb->CongestState == TCP_CONGEST_LOSS)) { + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + if (Tcb->CWnd < Tcb->Ssthresh) { + + Tcb->CWnd += Tcb->SndMss; + } else { + + Tcb->CWnd += NET_MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1); + } + + Tcb->CWnd = NET_MIN (Tcb->CWnd, TCP_MAX_WIN << Tcb->SndWndScale); + } + + if (Tcb->CongestState == TCP_CONGEST_LOSS) { + TcpFastLossRecover (Tcb, Seg); + } + } else { + + TcpFastRecover (Tcb, Seg); + } + + if (TCP_SEQ_GT (Seg->Ack, Tcb->SndUna)) { + + TcpAdjustSndQue (Tcb, Seg->Ack); + Tcb->SndUna = Seg->Ack; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && + (TCP_SEQ_LT (Tcb->SndUp, Seg->Ack))) { + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + } + } + + // + // Update window info + // + if (TCP_SEQ_LT (Tcb->SndWl1, Seg->Seq) || + ((Tcb->SndWl1 == Seg->Seq) && TCP_SEQ_LEQ (Tcb->SndWl2, Seg->Ack))) { + + Right = Seg->Ack + Seg->Wnd; + + if (TCP_SEQ_LT (Right, Tcb->SndWl2 + Tcb->SndWnd)) { + + if ((Tcb->SndWl1 == Seg->Seq) && + (Tcb->SndWl2 == Seg->Ack) && + (Len == 0)) { + + goto NO_UPDATE; + } + + TCP4_DEBUG_WARN (("TcpInput: peer shrinks the" + " window for connected TCB %x\n", Tcb)); + + if ((Tcb->CongestState == TCP_CONGEST_RECOVER) && + (TCP_SEQ_LT (Right, Tcb->Recover))) { + + Tcb->Recover = Right; + } + + if ((Tcb->CongestState == TCP_CONGEST_LOSS) && + (TCP_SEQ_LT (Right, Tcb->LossRecover))) { + + Tcb->LossRecover = Right; + } + + if (TCP_SEQ_LT (Right, Tcb->SndNxt)) { + + Tcb->SndNxt = Right; + + if (Right == Tcb->SndUna) { + + TcpClearTimer (Tcb, TCP_TIMER_REXMIT); + TcpSetProbeTimer (Tcb); + } + } + } + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = NET_MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + } + +NO_UPDATE: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT) && + (Tcb->SndUna == Tcb->SndNxt)) { + + TCP4_DEBUG_TRACE (("TcpInput: local FIN is ACKed by" + " peer for connected TCB %x\n", Tcb)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED); + } + + // + // Transit the state if proper. + // + switch (Tcb->State) { + case TCP_FIN_WAIT_1: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_FIN_WAIT_2); + + TcpClearAllTimer (Tcb); + TcpSetTimer (Tcb, TCP_TIMER_FINWAIT2, Tcb->FinWait2Timeout); + } + + case TCP_FIN_WAIT_2: + + break; + + case TCP_CLOSE_WAIT: + break; + + case TCP_CLOSING: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_TIME_WAIT); + + TcpClearAllTimer (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + TCP4_DEBUG_WARN (("Connection closed immediately " + "because app disables TIME_WAIT timer for %x\n", Tcb)); + + TcpClose (Tcb); + } + } + break; + + case TCP_LAST_ACK: + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_ACKED)) { + + TcpSetState (Tcb, TCP_CLOSED); + } + + break; + + case TCP_TIME_WAIT: + + TcpSendAck (Tcb); + + if (Tcb->TimeWaitTimeout != 0) { + + TcpSetTimer (Tcb, TCP_TIMER_2MSL, Tcb->TimeWaitTimeout); + } else { + + TCP4_DEBUG_WARN (("Connection closed immediately " + "because app disables TIME_WAIT timer for %x\n", Tcb)); + + TcpClose (Tcb); + } + break; + } + + // + // Sixth step: Check the URG bit.update the Urg point + // if in TCP_CAN_RECV, otherwise, leave the RcvUp intact. + // +StepSix: + + Tcb->Idle = 0; + TcpSetKeepaliveTimer (Tcb); + + if (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_PROBE)) { + + TcpClearTimer (Tcb, TCP_TIMER_PROBE); + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_URG) && + !TCP_FIN_RCVD (Tcb->State)) { + + TCP4_DEBUG_TRACE (("TcpInput: received urgent data " + "from peer for connected TCB %x\n", Tcb)); + + Urg = Seg->Seq + Seg->Urg; + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG) && + TCP_SEQ_GT (Urg, Tcb->RcvUp)) { + + Tcb->RcvUp = Urg; + } else { + + Tcb->RcvUp = Urg; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_URG); + } + } + + // + // Seventh step: Process the segment data + // + if (Seg->End != Seg->Seq) { + + if (TCP_FIN_RCVD (Tcb->State)) { + + TCP4_DEBUG_WARN (("TcpInput: connection reset because" + " data is lost for connected TCB %x\n", Tcb)); + + goto RESET_THEN_DROP; + } + + if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) { + TCP4_DEBUG_WARN (("TcpInput: connection reset because" + " data is lost for connected TCB %x\n", Tcb)); + + goto RESET_THEN_DROP; + } + + TcpQueueData (Tcb, Nbuf); + if (TcpDeliverData (Tcb) == -1) { + goto RESET_THEN_DROP; + } + + if (!NetListIsEmpty (&Tcb->RcvQue)) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + } + } + + // + // Eighth step: check the FIN. + // This step is moved to TcpDeliverData. FIN will be + // processed in sequence there. Check the comments in + // the beginning of the file header for information. + // + + // + // Tcb is a new child of the listening Parent, + // commit it. + // + if (Parent) { + Tcb->Parent = Parent; + TcpInsertTcb (Tcb); + } + + if ((Tcb->State != TCP_CLOSED) && + (!TcpToSendData (Tcb, 0)) && + (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || Nbuf->TotalSize)) { + + TcpToSendAck (Tcb); + } + + NetbufFree (Nbuf); + return 0; + +RESET_THEN_DROP: + TcpSendReset (Tcb, Head, Len, Dst, Src); + +DROP_CONNECTION: + ASSERT (Tcb && Tcb->Sk); + + NetbufFree (Nbuf); + TcpClose (Tcb); + + return -1; + +SEND_RESET: + + TcpSendReset (Tcb, Head, Len, Dst, Src); + +DISCARD: + + // + // Tcb is a child of Parent, and it doesn't survive + // + TCP4_DEBUG_WARN (("Tcp4Input: Discard a packet\n")); + NetbufFree (Nbuf); + + if (Parent && Tcb) { + + ASSERT (Tcb->Sk); + TcpClose (Tcb); + } + + return 0; +} + + +/** + Process the received ICMP error messages for TCP. + + @param Nbuf Buffer that contains part of the TCP segment without IP header + truncated from the ICMP error packet. + @param IcmpErr The ICMP error code interpreted from ICMP error packet. + @param Src Source address of the ICMP error message. + @param Dst Destination address of the ICMP error message. + + @return None. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN ICMP_ERROR IcmpErr, + IN UINT32 Src, + IN UINT32 Dst + ) +{ + TCP_HEAD *Head; + TCP_CB *Tcb; + TCP_SEQNO Seq; + EFI_STATUS IcmpErrStatus; + BOOLEAN IcmpErrIsHard; + BOOLEAN IcmpErrNotify; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + FALSE + ); + if (Tcb == NULL || Tcb->State == TCP_CLOSED) { + + goto CLEAN_EXIT; + } + + // + // Validate the sequence number. + // + Seq = NTOHL (Head->Seq); + if (!(TCP_SEQ_LEQ (Tcb->SndUna, Seq) && TCP_SEQ_LT (Seq, Tcb->SndNxt))) { + + goto CLEAN_EXIT; + } + + IcmpErrStatus = IpIoGetIcmpErrStatus (IcmpErr, &IcmpErrIsHard, &IcmpErrNotify); + + if (IcmpErrNotify) { + + SOCK_ERROR (Tcb->Sk, IcmpErrStatus); + } + + if (IcmpErrIsHard) { + + TcpClose (Tcb); + } + +CLEAN_EXIT: + NetbufFree (Nbuf); +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c new file mode 100644 index 0000000000..2f7a49fcb8 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Io.c @@ -0,0 +1,117 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Io.c + +Abstract: + + I/O interfaces between TCP and IpIo. + + +**/ + + +#include "Tcp4Main.h" + + +/** + Packet receive callback function provided to IP_IO, used to call + the proper function to handle the packet received by IP. + + @param Status Status of the received packet. + @param IcmpErr ICMP error number. + @param NetSession Pointer to the net session of this packet. + @param Pkt Pointer to the recieved packet. + @param Context Pointer to the context configured in IpIoOpen(), not used + now. + + @return None + +**/ +VOID +Tcp4RxCallback ( + IN EFI_STATUS Status, + IN ICMP_ERROR IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ) +{ + if (EFI_SUCCESS == Status) { + TcpInput (Pkt, NetSession->Source, NetSession->Dest); + } else { + TcpIcmpInput (Pkt, IcmpErr, NetSession->Source, NetSession->Dest); + } +} + + +/** + Send the segment to IP via IpIo function. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the TCP segment to be sent. + @param Src Source address of the TCP segment. + @param Dest Destination address of the TCP segment. + + @retval 0 The segment was sent out successfully. + @retval -1 The segment was failed to send. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN UINT32 Src, + IN UINT32 Dest + ) +{ + EFI_STATUS Status; + IP_IO *IpIo; + IP_IO_OVERRIDE Override; + SOCKET *Sock; + VOID *IpSender; + TCP4_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + + IpIo = NULL; + IpSender = IpIoFindSender (&IpIo, Src); + + if (IpSender == NULL) { + TCP4_DEBUG_WARN (("TcpSendIpPacket: No appropriate IpSender.\n")); + return -1; + } + } else { + + Sock = Tcb->Sk; + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + IpSender = Tcb->IpInfo; + } + + Override.TypeOfService = 0; + Override.TimeToLive = 255; + Override.DoNotFragment = FALSE; + Override.Protocol = EFI_IP_PROTO_TCP; + EFI_IP4 (Override.GatewayAddress) = 0; + EFI_IP4 (Override.SourceAddress) = Src; + + Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override); + + if (EFI_ERROR (Status)) { + TCP4_DEBUG_ERROR (("TcpSendIpPacket: return %r error\n", Status)); + return -1; + } + + return 0; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c new file mode 100644 index 0000000000..8a8cd8a9e3 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.c @@ -0,0 +1,564 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Main.c + +Abstract: + + Implementation of TCP4 protocol services. + + +**/ + +#include "Tcp4Main.h" + + +/** + Check the integrity of the data buffer. + + @param DataLen The total length of the data buffer. + @param FragmentCount The fragment count of the fragment table. + @param FragmentTable Pointer to the fragment table of the data + buffer. + + @retval EFI_SUCCESS The integrity check is passed. + @retval EFI_INVALID_PARAMETER The integrity check is failed. + +**/ +STATIC +EFI_STATUS +Tcp4ChkDataBuf ( + IN UINT32 DataLen, + IN UINT32 FragmentCount, + IN EFI_TCP4_FRAGMENT_DATA *FragmentTable + ) +{ + UINT32 Index; + + UINT32 Len; + + for (Index = 0, Len = 0; Index < FragmentCount; Index++) { + Len = Len + FragmentTable[Index].FragmentLength; + } + + if (DataLen != Len) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +/** + Get the current operational status. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param Tcp4State Pointer to the buffer to receive the current TCP + state. + @param Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. + @param Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. + @param MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. + @param SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + @retval EFI_INVALID_PARAMETER This is NULL. + +**/ +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN CONST EFI_TCP4_PROTOCOL * This, + OUT EFI_TCP4_CONNECTION_STATE * Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA * Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA * Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA * MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE * SnpModeData OPTIONAL + ) +{ + TCP4_MODE_DATA TcpMode; + SOCKET *Sock; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + TcpMode.Tcp4State = Tcp4State; + TcpMode.Tcp4ConfigData = Tcp4ConfigData; + TcpMode.Ip4ModeData = Ip4ModeData; + TcpMode.MnpConfigData = MnpConfigData; + TcpMode.SnpModeData = SnpModeData; + + return SockGetMode (Sock, &TcpMode); +} + + +/** + Initialize or brutally reset the operational parameters for + this EFI TCPv4 instance. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param TcpConfigData Pointer to the configure data to configure the + instance. + + @retval EFI_SUCCESS The operational settings are set, changed, or + reset successfully. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is already + configured. + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + @retval EFI_UNSUPPORTED One or more of the control options are not + supported in the implementation. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough system resources. + +**/ +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ) +{ + EFI_TCP4_OPTION *Option; + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != TcpConfigData) { + if ((EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) != 0) && + !Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.RemoteAddress), 0)) { + return EFI_INVALID_PARAMETER; + } + + if (!TcpConfigData->AccessPoint.UseDefaultAddress) { + if (!Ip4IsUnicast (EFI_NTOHL (TcpConfigData->AccessPoint.StationAddress), 0) || + !IP4_IS_VALID_NETMASK (EFI_NTOHL (TcpConfigData->AccessPoint.SubnetMask)) + ) { + return EFI_INVALID_PARAMETER; + } + } + + if (TcpConfigData->AccessPoint.ActiveFlag && + (0 == TcpConfigData->AccessPoint.RemotePort || + (EFI_IP4 (TcpConfigData->AccessPoint.RemoteAddress) == 0)) + ) { + return EFI_INVALID_PARAMETER; + } + + Option = TcpConfigData->ControlOption; + if ((NULL != Option) && + (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { + return EFI_UNSUPPORTED; + } + } + + Sock = SOCK_FROM_THIS (This); + + if (NULL == TcpConfigData) { + return SockFlush (Sock); + } + + Status = SockConfigure (Sock, TcpConfigData); + + if (EFI_NO_MAPPING == Status) { + Sock->ConfigureState = SO_NO_MAPPING; + } + + return Status; +} + + +/** + Add or delete routing entries. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param SubnetAddress The destination network. + @param SubnetMask The subnet mask for the destination network. + @param GatewayAddress The gateway address for this route. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance has not been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (through DHCP, BOOTP, RARP, etc.) is not + finished. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to add the + entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table. + @retval EFI_ACCESS_DENIED This route is already in the routing table. + @retval EFI_UNSUPPORTED The TCP driver does not support this operation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ) +{ + SOCKET *Sock; + TCP4_ROUTE_INFO RouteInfo; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + RouteInfo.DeleteRoute = DeleteRoute; + RouteInfo.SubnetAddress = SubnetAddress; + RouteInfo.SubnetMask = SubnetMask; + RouteInfo.GatewayAddress = GatewayAddress; + + return SockRoute (Sock, &RouteInfo); +} + + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param ConnectionToken Pointer to the connection token to return when + the TCP three way handshake finishes. + + @retval EFI_SUCCESS The connection request is successfully + initiated. + @retval EFI_NOT_STARTED This EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instance is not configured as an active one + or it is not in Tcp4StateClosed state. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resource to + initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ) +{ + SOCKET *Sock; + + if (NULL == This || + NULL == ConnectionToken || + NULL == ConnectionToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockConnect (Sock, ConnectionToken); +} + + +/** + Listen on the passive instance to accept an incoming connection request. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param ListenToken Pointer to the listen token to return when + operation finishes. + + @retval EFI_SUCCESS The listen token has been queued successfully. + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED The instatnce is not a passive one or it is not + in Tcp4StateListen state or a same listen token + has already existed in the listen token queue of + this TCP instance. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish + the operation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ) +{ + SOCKET *Sock; + + if (NULL == This || + NULL == ListenToken || + NULL == ListenToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockAccept (Sock, ListenToken); +} + + +/** + Queues outgoing data into the transmit queue + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param Token Pointer to the completion token to queue to the + transmit queue + + @retval EFI_SUCCESS The data has been queued for transmission + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A transmit completion token with the same + Token-> CompletionToken.Event was already in the + transmission queue. * The current instance is in + Tcp4StateClosed state * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_NOT_READY The completion token could not be queued because + the transmit queue is full. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data because of + resource shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or + address. + +**/ +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.TxData || + 0 == Token->Packet.TxData->FragmentCount || + 0 == Token->Packet.TxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = Tcp4ChkDataBuf ( + Token->Packet.TxData->DataLength, + Token->Packet.TxData->FragmentCount, + Token->Packet.TxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockSend (Sock, Token); + +} + + +/** + Place an asynchronous receive request into the receiving queue. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param Token Pointer to a token that is associated with the + receive data descriptor. + + @retval EFI_SUCCESS The receive completion token was cached + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_NO_MAPPING When using a default address, configuration + (DHCP, BOOTP, RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued + due to a lack of system resources. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_ACCESS_DENIED One or more of the following conditions is TRUE: + * A receive completion token with the same + Token->CompletionToken.Event was already in the + receive queue. * The current instance is in + Tcp4StateClosed state. * The current instance is + a passive one and it is in Tcp4StateListen + state. * User has called Close() to disconnect + this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection + and there is no any buffered data in the receive + buffer of this instance. + @retval EFI_NOT_READY The receive request could not be queued because + the receive queue is full. + +**/ +EFI_STATUS +EFIAPI +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This || + NULL == Token || + NULL == Token->CompletionToken.Event || + NULL == Token->Packet.RxData || + 0 == Token->Packet.RxData->FragmentCount || + 0 == Token->Packet.RxData->DataLength + ) { + return EFI_INVALID_PARAMETER; + } + + Status = Tcp4ChkDataBuf ( + Token->Packet.RxData->DataLength, + Token->Packet.RxData->FragmentCount, + Token->Packet.RxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockRcv (Sock, Token); + +} + + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance + @param CloseToken Pointer to the close token to return when + operation finishes. + + @retval EFI_SUCCESS The operation completed successfully + @retval EFI_NOT_STARTED The EFI_TCP4_PROTOCOL instance hasn't been + configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: * + Configure() has been called with TcpConfigData + set to NULL and this function has not returned. + * Previous Close() call on this instance has not + finished. + @retval EFI_INVALID_PARAMETER One ore more parameters are invalid + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the + operation + @retval EFI_DEVICE_ERROR Any unexpected and not belonged to above + category error. + +**/ +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ) +{ + SOCKET *Sock; + + if (NULL == This || + NULL == CloseToken || + NULL == CloseToken->CompletionToken.Event) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + return SockClose (Sock, CloseToken, CloseToken->AbortOnClose); +} + + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + @param Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by above four + functions will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in current + implementation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_COMPLETION_TOKEN * Token OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param This Pointer to the EFI_TCP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or + receive queue. Consider increasing the polling + rate. + +**/ +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + Status = Sock->ProtoHandler (Sock, SOCK_POLL, NULL); + + return Status; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h new file mode 100644 index 0000000000..dba46aa80a --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Main.h @@ -0,0 +1,176 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Main.h + +Abstract: + + +**/ + +#ifndef _TCP4_MAIN_H_ +#define _TCP4_MAIN_H_ + +#include "Socket.h" + +#include "Tcp4Proto.h" +#include "Tcp4Driver.h" + + +extern UINT16 mTcp4RandomPort; + +// +// Driver Produced Protocol Prototypes +// +//@MT:#include EFI_PROTOCOL_PRODUCER (Tcp4) + +#define TCP4_DEBUG_ERROR(PrintArg) NET_DEBUG_ERROR ("Tcp", PrintArg) +#define TCP4_DEBUG_WARN(PrintArg) NET_DEBUG_WARNING ("Tcp", PrintArg) +#define TCP4_DEBUG_TRACE(PrintArg) NET_DEBUG_TRACE ("Tcp", PrintArg) + +// +// Function prototype for the Tcp4 socket request handler +// +EFI_STATUS +Tcp4Dispatcher ( + IN SOCKET *Sock, + IN SOCK_REQUEST Request, + IN VOID *Data OPTIONAL + ); + +typedef struct _TCP4_MODE_DATA { + EFI_TCP4_CONNECTION_STATE *Tcp4State; + EFI_TCP4_CONFIG_DATA *Tcp4ConfigData; + EFI_IP4_MODE_DATA *Ip4ModeData; + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData; + EFI_SIMPLE_NETWORK_MODE *SnpModeData; +} TCP4_MODE_DATA; + +typedef struct _TCP4_ROUTE_INFO { + BOOLEAN DeleteRoute; + EFI_IPv4_ADDRESS *SubnetAddress; + EFI_IPv4_ADDRESS *SubnetMask; + EFI_IPv4_ADDRESS *GatewayAddress; +} TCP4_ROUTE_INFO; + +// +// Get the mode data of a TCP instance +// +EFI_STATUS +EFIAPI +Tcp4GetModeData ( + IN CONST EFI_TCP4_PROTOCOL * This, + OUT EFI_TCP4_CONNECTION_STATE * Tcp4State OPTIONAL, + OUT EFI_TCP4_CONFIG_DATA * Tcp4ConfigData OPTIONAL, + OUT EFI_IP4_MODE_DATA * Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA * MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE * SnpModeData OPTIONAL + ); + +// +// Initialize or reset a TCP instance +// +EFI_STATUS +EFIAPI +Tcp4Configure ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_CONFIG_DATA * TcpConfigData OPTIONAL + ); + +// +// Add a route entry to the route table +// +EFI_STATUS +EFIAPI +Tcp4Routes ( + IN EFI_TCP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +// +// Issue an asynchronous connection establishment +// request to the peer +// +EFI_STATUS +EFIAPI +Tcp4Connect ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CONNECTION_TOKEN *ConnectionToken + ); + +// +// Issue an asynchronous listent token to accept an +// incoming connection reques +// +EFI_STATUS +EFIAPI +Tcp4Accept ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_LISTEN_TOKEN *ListenToken + ); + +// +// Issue an asynchronous IO token to transmit some data +// through this TCP instance +// +EFI_STATUS +EFIAPI +Tcp4Transmit ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +// +// Issue an asynchronous IO token to receive some data +// through this TCP instance +// +EFI_STATUS +Tcp4Receive ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_IO_TOKEN *Token + ); + +// +// Issue an asynchronous CloseToken to close a TCP +// connection represented by instance +// +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ); + +// +// cancle an connect, listent or IO token +// +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL * This, + IN EFI_TCP4_COMPLETION_TOKEN * Token OPTIONAL + ); + +// +// poll data from NIC for receive +// +EFI_STATUS +EFIAPI +Tcp4Poll ( + IN EFI_TCP4_PROTOCOL *This + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c new file mode 100644 index 0000000000..7f1f141bc8 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Misc.c @@ -0,0 +1,1091 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Misc.c + +Abstract: + + Misc support routines for tcp. + + +**/ + + +#include "Tcp4Main.h" + +NET_LIST_ENTRY mTcpRunQue = { + &mTcpRunQue, + &mTcpRunQue +}; + +NET_LIST_ENTRY mTcpListenQue = { + &mTcpListenQue, + &mTcpListenQue +}; + +TCP_SEQNO mTcpGlobalIss = 0x4d7e980b; + +STATIC CHAR16 *mTcpStateName[] = { + L"TCP_CLOSED", + L"TCP_LISTEN", + L"TCP_SYN_SENT", + L"TCP_SYN_RCVD", + L"TCP_ESTABLISHED", + L"TCP_FIN_WAIT_1", + L"TCP_FIN_WAIT_2", + L"TCP_CLOSING", + L"TCP_TIME_WAIT", + L"TCP_CLOSE_WAIT", + L"TCP_LAST_ACK" +}; + + +/** + Initialize the Tcb local related members. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None + +**/ +VOID +TcpInitTcbLocal ( + IN TCP_CB *Tcb + ) +{ + // + // Compute the checksum of the fixed parts of pseudo header + // + Tcb->HeadSum = NetPseudoHeadChecksum ( + Tcb->LocalEnd.Ip, + Tcb->RemoteEnd.Ip, + 0x06, + 0 + ); + + Tcb->Iss = TcpGetIss (); + Tcb->SndUna = Tcb->Iss; + Tcb->SndNxt = Tcb->Iss; + + Tcb->SndWl2 = Tcb->Iss; + Tcb->SndWnd = 536; + + Tcb->RcvWnd = GET_RCV_BUFFSIZE (Tcb->Sk); + + // + // Fisrt window size is never scaled + // + Tcb->RcvWndScale = 0; +} + + +/** + Initialize the peer related members. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seg Pointer to the segment that contains the peer's + intial info. + @param Opt Pointer to the options announced by the peer. + + @return None + +**/ +VOID +TcpInitTcbPeer ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ) +{ + UINT16 RcvMss; + + ASSERT (Tcb && Seg && Opt); + ASSERT (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)); + + Tcb->SndWnd = Seg->Wnd; + Tcb->SndWndMax = Tcb->SndWnd; + Tcb->SndWl1 = Seg->Seq; + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_ACK)) { + Tcb->SndWl2 = Seg->Ack; + } else { + Tcb->SndWl2 = Tcb->Iss + 1; + } + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_MSS)) { + Tcb->SndMss = NET_MAX (64, Opt->Mss); + + RcvMss = TcpGetRcvMss (Tcb->Sk); + if (Tcb->SndMss > RcvMss) { + Tcb->SndMss = RcvMss; + } + + } else { + // + // One end doesn't support MSS option, use default. + // + Tcb->RcvMss = 536; + } + + Tcb->CWnd = Tcb->SndMss; + + Tcb->Irs = Seg->Seq; + Tcb->RcvNxt = Tcb->Irs + 1; + + Tcb->RcvWl2 = Tcb->RcvNxt; + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_WS) && + !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)) { + + Tcb->SndWndScale = Opt->WndScale; + + Tcb->RcvWndScale = TcpComputeScale (Tcb); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS); + + } else { + // + // One end doesn't support window scale option. use zero. + // + Tcb->RcvWndScale = 0; + } + + if (TCP_FLG_ON (Opt->Flag, TCP_OPTION_RCVD_TS) && + !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)) { + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_TS); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS); + + // + // Compute the effective SndMss per RFC1122 + // section 4.2.2.6. If timestamp option is + // enabled, it will always occupy 12 bytes. + // + Tcb->SndMss -= TCP_OPTION_TS_ALIGNED_LEN; + } +} + + +/** + Locate a listen TCB that matchs the Local and Remote. + + @param Local Pointer to the local (IP, Port). + @param Remote Pointer to the remote (IP, Port). + + @return Pointer to the TCP_CB with the least number of wildcard, if NULL no match is found. + +**/ +STATIC +TCP_CB * +TcpLocateListenTcb ( + IN TCP_PEER *Local, + IN TCP_PEER *Remote + ) +{ + NET_LIST_ENTRY *Entry; + TCP_CB *Node; + TCP_CB *Match; + INTN Last; + INTN Cur; + + Last = 4; + Match = NULL; + + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Local->Port != Node->LocalEnd.Port) || + !TCP_PEER_MATCH (Remote, &Node->RemoteEnd) || + !TCP_PEER_MATCH (Local, &Node->LocalEnd) + ) { + + continue; + } + + // + // Compute the number of wildcard + // + Cur = 0; + if (Node->RemoteEnd.Ip == 0) { + Cur++; + } + + if (Node->RemoteEnd.Port == 0) { + Cur++; + } + + if (Node->LocalEnd.Ip == 0) { + Cur++; + } + + if (Cur < Last) { + if (Cur == 0) { + return Node; + } + + Last = Cur; + Match = Node; + } + } + + return Match; +} + + +/** + Try to find one Tcb whose equals to . + + @param Addr Pointer to the IP address needs to match. + @param Port The port number needs to match. + + @return The Tcb which matches the paire exists or not. + +**/ +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IPv4_ADDRESS *Addr, + IN TCP_PORTNO Port + ) +{ + TCP_PORTNO LocalPort; + NET_LIST_ENTRY *Entry; + TCP_CB *Tcb; + + ASSERT ((Addr != NULL) && (Port != 0)); + + LocalPort = HTONS (Port); + + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip) && + (LocalPort == Tcb->LocalEnd.Port)) { + + return TRUE; + } + } + + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (((EFI_IP4 (*Addr) == Tcb->LocalEnd.Ip)) && + (LocalPort == Tcb->LocalEnd.Port)) { + + return TRUE; + } + } + + return FALSE; +} + + +/** + Locate the TCP_CB related to the socket pair. + + @param LocalPort The local port number. + @param LocalIp The local IP address. + @param RemotePort The remote port number. + @param RemoteIp The remote IP address. + @param Syn Whether to search the listen sockets, if TRUE, the + listen sockets are searched. + + @return Pointer to the related TCP_CB, if NULL no match is found. + +**/ +TCP_CB * +TcpLocateTcb ( + IN TCP_PORTNO LocalPort, + IN UINT32 LocalIp, + IN TCP_PORTNO RemotePort, + IN UINT32 RemoteIp, + IN BOOLEAN Syn + ) +{ + TCP_PEER Local; + TCP_PEER Remote; + NET_LIST_ENTRY *Entry; + TCP_CB *Tcb; + + Local.Port = LocalPort; + Local.Ip = LocalIp; + + Remote.Port = RemotePort; + Remote.Ip = RemoteIp; + + // + // First check for exact match. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd) && + TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd)) { + + NetListRemoveEntry (&Tcb->List); + NetListInsertHead (&mTcpRunQue, &Tcb->List); + + return Tcb; + } + } + + // + // Only check listen queue when SYN flag is on + // + if (Syn) { + return TcpLocateListenTcb (&Local, &Remote); + } + + return NULL; +} + + +/** + Insert a Tcb into the proper queue. + + @param Tcb Pointer to the TCP_CB to be inserted. + + @retval 0 The Tcb is inserted successfully. + @retval -1 Error condition occurred. + +**/ +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Head; + TCP_CB *Node; + TCP4_PROTO_DATA *TcpProto; + + ASSERT ( + Tcb && + ( + (Tcb->State == TCP_LISTEN) || + (Tcb->State == TCP_SYN_SENT) || + (Tcb->State == TCP_SYN_RCVD) || + (Tcb->State == TCP_CLOSED) + ) + ); + + if (Tcb->LocalEnd.Port == 0) { + return -1; + } + + Head = &mTcpRunQue; + + if (Tcb->State == TCP_LISTEN) { + Head = &mTcpListenQue; + } + + // + // Check that Tcb isn't already on the list. + // + NET_LIST_FOR_EACH (Entry, Head) { + Node = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (TCP_PEER_EQUAL (&Tcb->LocalEnd, &Node->LocalEnd) && + TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd)) { + + return -1; + } + } + + NetListInsertHead (Head, &Tcb->List); + + TcpProto = (TCP4_PROTO_DATA *) Tcb->Sk->ProtoReserved; + TcpSetVariableData (TcpProto->TcpService); + + return 0; +} + + +/** + Clone a TCP_CB from Tcb. + + @param Tcb Pointer to the TCP_CB to be cloned. + + @return Pointer to the new cloned TCP_CB, if NULL error condition occurred. + +**/ +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ) +{ + TCP_CB *Clone; + + Clone = NetAllocatePool (sizeof (TCP_CB)); + + if (Clone == NULL) { + return NULL; + + } + + NetCopyMem (Clone, Tcb, sizeof (TCP_CB)); + + // + // Increate the reference count of the shared IpInfo. + // + NET_GET_REF (Tcb->IpInfo); + + NetListInit (&Clone->List); + NetListInit (&Clone->SndQue); + NetListInit (&Clone->RcvQue); + + Clone->Sk = SockClone (Tcb->Sk); + if (Clone->Sk == NULL) { + TCP4_DEBUG_ERROR (("TcpCloneTcb: failed to clone a sock\n")); + NetFreePool (Clone); + return NULL; + } + + ((TCP4_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone; + + return Clone; +} + + +/** + Compute an ISS to be used by a new connection. + + None + + @return The result ISS. + +**/ +TCP_SEQNO +TcpGetIss ( + VOID + ) +{ + mTcpGlobalIss += 2048; + return mTcpGlobalIss; +} + + +/** + Get the local mss. + + None + + @return The mss size. + +**/ +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ) +{ + EFI_SIMPLE_NETWORK_MODE SnpMode; + TCP4_PROTO_DATA *TcpProto; + EFI_IP4_PROTOCOL *Ip; + + ASSERT (Sock); + + TcpProto = (TCP4_PROTO_DATA *) Sock->ProtoReserved; + Ip = TcpProto->TcpService->IpIo->Ip; + ASSERT (Ip); + + Ip->GetModeData (Ip, NULL, NULL, &SnpMode); + + return (UINT16) (SnpMode.MaxPacketSize - 40); +} + + +/** + Set the Tcb's state. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param State The state to be set. + + @return None + +**/ +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ) +{ + TCP4_DEBUG_TRACE ( + ("Tcb (%x) state %s --> %s\n", + Tcb, + mTcpStateName[Tcb->State], + mTcpStateName[State]) + ); + + Tcb->State = State; + + switch (State) { + case TCP_ESTABLISHED: + + SockConnEstablished (Tcb->Sk); + break; + + case TCP_CLOSED: + + SockConnClosed (Tcb->Sk); + + break; + } +} + + +/** + Compute the TCP segment's checksum. + + @param Nbuf Pointer to the buffer that contains the TCP + segment. + @param HeadSum The checksum value of the fixed part of pseudo + header. + + @return The checksum value. + +**/ +UINT16 +TcpChecksum ( + IN NET_BUF *Nbuf, + IN UINT16 HeadSum + ) +{ + UINT16 Checksum; + + Checksum = NetbufChecksum (Nbuf); + Checksum = NetAddChecksum (Checksum, HeadSum); + + Checksum = NetAddChecksum ( + Checksum, + HTONS ((UINT16) Nbuf->TotalSize) + ); + + return ~Checksum; +} + + +/** + Translate the information from the head of the received TCP + segment Nbuf contains and fill it into a TCP_SEG structure. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer contains the TCP segment. + + @return Pointer to the TCP_SEG that contains the translated TCP head information. + +**/ +TCP_SEG * +TcpFormatNetbuf ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + TCP_HEAD *Head; + + Seg = TCPSEG_NETBUF (Nbuf); + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + Nbuf->Tcp = Head; + + Seg->Seq = NTOHL (Head->Seq); + Seg->Ack = NTOHL (Head->Ack); + Seg->End = Seg->Seq + (Nbuf->TotalSize - (Head->HeadLen << 2)); + + Seg->Urg = NTOHS (Head->Urg); + Seg->Wnd = (NTOHS (Head->Wnd) << Tcb->SndWndScale); + Seg->Flag = Head->Flag; + + // + // SYN and FIN flag occupy one sequence space each. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + // + // RFC requires that initial window not be scaled + // + Seg->Wnd = NTOHS (Head->Wnd); + Seg->End++; + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + Seg->End++; + } + + return Seg; +} + + +/** + Reset the connection related with Tcb. + + @param Tcb Pointer to the TCP_CB of the connection to be + reset. + + @return None + +**/ +VOID +TcpResetConnection ( + IN TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return ; + } + + Nhead = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_TAIL + ); + + ASSERT (Nhead != NULL); + + Nbuf->Tcp = Nhead; + + Nhead->Flag = TCP_FLG_RST; + Nhead->Seq = HTONL (Tcb->SndNxt); + Nhead->Ack = HTONL (Tcb->RcvNxt); + Nhead->SrcPort = Tcb->LocalEnd.Port; + Nhead->DstPort = Tcb->RemoteEnd.Port; + Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + Nhead->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip); + + NetbufFree (Nbuf); +} + + +/** + Initialize an active connection, + + @param Tcb Pointer to the TCP_CB that wants to initiate a + connection. + + @return None + +**/ +VOID +TcpOnAppConnect ( + IN TCP_CB *Tcb + ) +{ + TcpInitTcbLocal (Tcb); + TcpSetState (Tcb, TCP_SYN_SENT); + + TcpSetTimer (Tcb, TCP_TIMER_CONNECT, Tcb->ConnectTimeout); + TcpToSendData (Tcb, 1); +} + + +/** + Initiate the connection close procedure, called when + applications want to close the connection. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpOnAppClose ( + IN TCP_CB *Tcb + ) +{ + ASSERT (Tcb); + + if (!NetListIsEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk)) { + + TCP4_DEBUG_WARN (("TcpOnAppClose: connection reset " + "because data is lost for TCB %x\n", Tcb)); + + TcpResetConnection (Tcb); + TcpClose (Tcb); + return; + } + + switch (Tcb->State) { + case TCP_CLOSED: + case TCP_LISTEN: + case TCP_SYN_SENT: + TcpSetState (Tcb, TCP_CLOSED); + break; + + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + TcpSetState (Tcb, TCP_FIN_WAIT_1); + break; + + case TCP_CLOSE_WAIT: + TcpSetState (Tcb, TCP_LAST_ACK); + break; + } + + TcpToSendData (Tcb, 1); +} + + +/** + Check whether the application's newly delivered data + can be sent out. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 Whether the data is sent out or is buffered for + further sending. + @retval -1 The Tcb is not in a state that data is permitted to + be sent out. + +**/ +INTN +TcpOnAppSend ( + IN TCP_CB *Tcb + ) +{ + + switch (Tcb->State) { + case TCP_CLOSED: + return -1; + break; + + case TCP_LISTEN: + return -1; + break; + + case TCP_SYN_SENT: + case TCP_SYN_RCVD: + return 0; + break; + + case TCP_ESTABLISHED: + case TCP_CLOSE_WAIT: + TcpToSendData (Tcb, 0); + return 0; + break; + + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + return -1; + break; + } + + return 0; +} + + +/** + Application has consumed some data, check whether + to send a window updata ack or a delayed ack. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + +**/ +INTN +TcpOnAppConsume ( + IN TCP_CB *Tcb + ) +{ + + switch (Tcb->State) { + case TCP_CLOSED: + return -1; + break; + + case TCP_LISTEN: + return -1; + break; + + case TCP_SYN_SENT: + case TCP_SYN_RCVD: + return 0; + break; + + case TCP_ESTABLISHED: + if (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb)) { + + if (TcpRcvWinOld (Tcb) < Tcb->RcvMss) { + + TCP4_DEBUG_TRACE (("TcpOnAppConsume: send a window" + " update for a window closed Tcb(%x)\n", Tcb)); + + TcpSendAck (Tcb); + } else if (Tcb->DelayedAck == 0) { + + TCP4_DEBUG_TRACE (("TcpOnAppConsume: scheduled a delayed" + " ACK to update window for Tcb(%x)\n", Tcb)); + + Tcb->DelayedAck = 1; + } + } + + break; + + case TCP_CLOSE_WAIT: + return 0; + break; + + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + return -1; + break; + } + + return -1; +} + + +/** + Abort the connection by sending a reset segment, called + when the application wants to abort the connection. + + @param Tcb Pointer to the TCP_CB of the TCP instance. + + @return None. + +**/ +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ) +{ + TCP4_DEBUG_WARN (("TcpOnAppAbort: connection reset " + "issued by application for TCB %x\n", Tcb)); + + switch (Tcb->State) { + case TCP_SYN_RCVD: + case TCP_ESTABLISHED: + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSE_WAIT: + TcpResetConnection (Tcb); + break; + } + + TcpSetState (Tcb, TCP_CLOSED); +} + + +/** + Set the Tdp4 variable data. + + @param Tcp4Service Tcp4 service data. + + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable. + @retval other Set variable failed. + +**/ +EFI_STATUS +TcpSetVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ) +{ + UINT32 NumConfiguredInstance; + NET_LIST_ENTRY *Entry; + TCP_CB *TcpPcb; + TCP4_PROTO_DATA *TcpProto; + UINTN VariableDataSize; + EFI_TCP4_VARIABLE_DATA *Tcp4VariableData; + EFI_TCP4_SERVICE_POINT *Tcp4ServicePoint; + CHAR16 *NewMacString; + EFI_STATUS Status; + + NumConfiguredInstance = 0; + + // + // Go through the running queue to count the instances. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + NumConfiguredInstance++; + } + } + + // + // Go through the listening queue to count the instances. + // + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + NumConfiguredInstance++; + } + } + + // + // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child, + // we should add extra buffer for the service points only if the number of configured + // children is more than 1. + // + VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1); + } + + Tcp4VariableData = NetAllocatePool (VariableDataSize); + if (Tcp4VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcp4VariableData->DriverHandle = Tcp4Service->DriverBindingHandle; + Tcp4VariableData->ServiceCount = NumConfiguredInstance; + + Tcp4ServicePoint = &Tcp4VariableData->Services[0]; + + // + // Go through the running queue to fill the service points. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip; + Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip; + Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp4ServicePoint++; + } + } + + // + // Go through the listening queue to fill the service points. + // + NET_LIST_FOR_EACH (Entry, &mTcpListenQue) { + TcpPcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + TcpProto = (TCP4_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == Tcp4Service) { + // + // This tcp instance belongs to the Tcp4Service. + // + Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + EFI_IP4 (Tcp4ServicePoint->LocalAddress) = TcpPcb->LocalEnd.Ip; + Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + EFI_IP4 (Tcp4ServicePoint->RemoteAddress) = TcpPcb->RemoteEnd.Ip; + Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp4ServicePoint++; + } + } + + // + // Get the mac string. + // + Status = NetLibGetMacString ( + Tcp4Service->ControllerHandle, + Tcp4Service->DriverBindingHandle, + &NewMacString + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (Tcp4Service->MacString != NULL) { + // + // The variable is set already, we're going to update it. + // + if (StrCmp (Tcp4Service->MacString, NewMacString) != 0) { + // + // The mac address is changed, delete the previous variable first. + // + gRT->SetVariable ( + Tcp4Service->MacString, + &gEfiTcp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + } + + NetFreePool (Tcp4Service->MacString); + } + + Tcp4Service->MacString = NewMacString; + + Status = gRT->SetVariable ( + Tcp4Service->MacString, + &gEfiTcp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableDataSize, + (VOID *) Tcp4VariableData + ); + +ON_ERROR: + + NetFreePool (Tcp4VariableData); + + return Status; +} + + +/** + Clear the variable and free the resource. + + @param Tcp4Service Tcp4 service data. + + @return None. + +**/ +VOID +TcpClearVariableData ( + IN TCP4_SERVICE_DATA *Tcp4Service + ) +{ + ASSERT (Tcp4Service->MacString != NULL); + + gRT->SetVariable ( + Tcp4Service->MacString, + &gEfiTcp4ServiceBindingProtocolGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + + NetFreePool (Tcp4Service->MacString); + Tcp4Service->MacString = NULL; +} + diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c new file mode 100644 index 0000000000..d430a2e10d --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.c @@ -0,0 +1,380 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Option.c + +Abstract: + + Routines to process TCP option. + + +**/ + +#include "Tcp4Main.h" + +STATIC +UINT16 +TcpGetUint16 ( + IN UINT8 *Buf + ) +{ + UINT16 Value; + NetCopyMem (&Value, Buf, sizeof (UINT16)); + return NTOHS (Value); +} + +STATIC +VOID +TcpPutUint16 ( + IN UINT8 *Buf, + IN UINT16 Data + ) +{ + Data = HTONS (Data); + NetCopyMem (Buf, &Data, sizeof (UINT16)); +} + +STATIC +UINT32 +TcpGetUint32 ( + IN UINT8 *Buf + ) +{ + UINT32 Value; + NetCopyMem (&Value, Buf, sizeof (UINT32)); + return NTOHL (Value); +} + +STATIC +VOID +TcpPutUint32 ( + IN UINT8 *Buf, + IN UINT32 Data + ) +{ + Data = HTONL (Data); + NetCopyMem (Buf, &Data, sizeof (UINT32)); +} + + +/** + Compute the window scale value according to the given + buffer size. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval UINT8 The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ) +{ + UINT8 Scale; + UINT32 BufSize; + + ASSERT (Tcb && Tcb->Sk); + + BufSize = GET_RCV_BUFFSIZE (Tcb->Sk); + + Scale = 0; + while ((Scale < TCP_OPTION_MAX_WS) && + ((UINT32) (TCP_OPTION_MAX_WIN << Scale) < BufSize)) { + + Scale++; + } + + return Scale; +} + + +/** + Build the TCP option in three-way handshake. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + char *Data; + UINT16 Len; + + ASSERT (Tcb && Nbuf && !Nbuf->Tcp); + + Len = 0; + + // + // Add timestamp option if not disabled by application + // and it is the first SYN segment or the peer has sent + // us its timestamp. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_TS))) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, 0); + } + + // + // Build window scale option, only when are configured + // to send WS option, and either we are doing active + // open or we have received WS option from peer. + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS) && + (!TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_ACK) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RCVD_WS))) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_WS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data); + + Len += TCP_OPTION_WS_ALIGNED_LEN; + TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb)); + } + + // + // Build MSS option + // + Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1); + ASSERT (Data); + + Len += TCP_OPTION_MSS_LEN; + TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss); + + return Len; +} + + +/** + Build the TCP option in synchronized states. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer to store the options. + + @return The total length of the TCP option field. + +**/ +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + char *Data; + UINT16 Len; + + ASSERT (Tcb && Nbuf && !Nbuf->Tcp); + Len = 0; + + // + // Build Timestamp option + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_TS) && + !TCP_FLG_ON (TCPSEG_NETBUF (Nbuf)->Flag, TCP_FLG_RST)) { + + Data = NetbufAllocSpace ( + Nbuf, + TCP_OPTION_TS_ALIGNED_LEN, + NET_BUF_HEAD + ); + + ASSERT (Data); + Len += TCP_OPTION_TS_ALIGNED_LEN; + + TcpPutUint32 (Data, TCP_OPTION_TS_FAST); + TcpPutUint32 (Data + 4, mTcpTick); + TcpPutUint32 (Data + 8, Tcb->TsRecent); + } + + return Len; +} + + +/** + Parse the supported options. + + @param Tcp Pointer to the TCP_CB of this TCP instance. + @param Option Pointer to the TCP_OPTION used to store the successfully pasrsed + options. + + @retval 0 The options are successfully pasrsed. + @retval -1 Ilegal option was found. + +**/ +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN TCP_OPTION *Option + ) +{ + UINT8 *Head; + UINT8 TotalLen; + UINT8 Cur; + UINT8 Type; + UINT8 Len; + + ASSERT (Tcp && Option); + + Option->Flag = 0; + + TotalLen = (Tcp->HeadLen << 2) - sizeof (TCP_HEAD); + if (TotalLen <= 0) { + return 0; + } + + Head = (UINT8 *) (Tcp + 1); + + // + // Fast process of timestamp option + // + if ((TotalLen == TCP_OPTION_TS_ALIGNED_LEN) && + (TcpGetUint32 (Head) == TCP_OPTION_TS_FAST)) { + + Option->TSVal = TcpGetUint32 (Head + 4); + Option->TSEcr = TcpGetUint32 (Head + 8); + Option->Flag = TCP_OPTION_RCVD_TS; + + return 0; + } + + // + // Slow path to process the options. + // + Cur = 0; + + while (Cur < TotalLen) { + Type = Head[Cur]; + + switch (Type) { + case TCP_OPTION_MSS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_MSS_LEN) || + (TotalLen - Cur < TCP_OPTION_MSS_LEN)) { + + return -1; + } + + Option->Mss = TcpGetUint16 (&Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_MSS); + + Cur += TCP_OPTION_MSS_LEN; + break; + + case TCP_OPTION_WS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_WS_LEN) || + (TotalLen - Cur < TCP_OPTION_WS_LEN)) { + + return -1; + } + + Option->WndScale = NET_MIN (14, Head[Cur + 2]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_WS); + + Cur += TCP_OPTION_WS_LEN; + break; + + case TCP_OPTION_TS: + Len = Head[Cur + 1]; + + if ((Len != TCP_OPTION_TS_LEN) || + (TotalLen - Cur < TCP_OPTION_TS_LEN)) { + + return -1; + } + + Option->TSVal = TcpGetUint32 (&Head[Cur + 2]); + Option->TSEcr = TcpGetUint32 (&Head[Cur + 6]); + TCP_SET_FLG (Option->Flag, TCP_OPTION_RCVD_TS); + + Cur += TCP_OPTION_TS_LEN; + break; + + case TCP_OPTION_NOP: + Cur++; + break; + + case TCP_OPTION_EOP: + Cur = TotalLen; + break; + + default: + Len = Head[Cur + 1]; + + if (TotalLen - Cur < Len || Len < 2) { + return -1; + } + + Cur = Cur + Len; + break; + } + + } + + return 0; +} + + +/** + Check the segment against PAWS. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param TSVal The timestamp value. + + @retval 1 The segment passed the PAWS check. + @retval 0 The segment failed to pass the PAWS check. + +**/ +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ) +{ + // + // PAWS as defined in RFC1323, buggy... + // + if (TCP_TIME_LT (TSVal, Tcb->TsRecent) && + TCP_TIME_LT (Tcb->TsRecentAge + TCP_PAWS_24DAY, mTcpTick)) { + + return 0; + + } + + return 1; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h new file mode 100644 index 0000000000..8074cb5564 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Option.h @@ -0,0 +1,107 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Option.h + +Abstract: + + +**/ + +#ifndef _TCP4_OPTION_H_ +#define _TCP4_OPTION_H_ + +// +// The structure to store the parse option value. +// ParseOption only parse the options, don't process them. +// +typedef struct s_TCP_OPTION { + UINT8 Flag; // flag such as TCP_OPTION_RCVD_MSS + UINT8 WndScale; // the WndScale received + UINT16 Mss; // the Mss received + UINT32 TSVal; // the TSVal field in a timestamp option + UINT32 TSEcr; // the TSEcr field in a timestamp option +} TCP_OPTION; + +enum { + + // + // supported TCP option type and their length + // + TCP_OPTION_EOP = 0, // End Of oPtion + TCP_OPTION_NOP = 1, // No-Option. + TCP_OPTION_MSS = 2, // Maximum Segment Size + TCP_OPTION_WS = 3, // Window scale + TCP_OPTION_TS = 8, // Timestamp + TCP_OPTION_MSS_LEN = 4, // length of MSS option + TCP_OPTION_WS_LEN = 3, // length of window scale option + TCP_OPTION_TS_LEN = 10, // length of timestamp option + TCP_OPTION_WS_ALIGNED_LEN = 4, // length of window scale option, aligned + TCP_OPTION_TS_ALIGNED_LEN = 12, // length of timestamp option, aligned + + // + // recommend format of timestamp window scale + // option for fast process. + // + TCP_OPTION_TS_FAST = ((TCP_OPTION_NOP << 24) | + (TCP_OPTION_NOP << 16) | + (TCP_OPTION_TS << 8) | + TCP_OPTION_TS_LEN), + + TCP_OPTION_WS_FAST = ((TCP_OPTION_NOP << 24) | + (TCP_OPTION_WS << 16) | + (TCP_OPTION_WS_LEN << 8)), + + TCP_OPTION_MSS_FAST = ((TCP_OPTION_MSS << 24) | + (TCP_OPTION_MSS_LEN << 16)), + + // + // Other misc definations + // + TCP_OPTION_MAX_WS = 14, // Maxium window scale value + TCP_OPTION_MAX_WIN = 0xffff, // max window size in TCP header + TCP_OPTION_RCVD_MSS = 0x01, + TCP_OPTION_RCVD_WS = 0x02, + TCP_OPTION_RCVD_TS = 0x04, +}; + +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ); + +UINT16 +TcpSynBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Buf + ); + +UINT16 +TcpBuildOption ( + IN TCP_CB *Tcb, + IN NET_BUF *Buf + ); + +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN TCP_OPTION *Option + ); + +UINT32 +TcpPawsOK ( + IN TCP_CB *Tcb, + IN UINT32 TSVal + ); + +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c new file mode 100644 index 0000000000..f2987e769c --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c @@ -0,0 +1,1218 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Output.c + +Abstract: + + TCP output process routines. + + +**/ + +#include "Tcp4Main.h" + +STATIC UINT8 mTcpOutFlag[] = { + 0, // TCP_CLOSED + 0, // TCP_LISTEN + TCP_FLG_SYN, // TCP_SYN_SENT + TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD + TCP_FLG_ACK, // TCP_ESTABLISHED + TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1 + TCP_FLG_ACK, // TCP_FIN_WAIT_2 + TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING + TCP_FLG_ACK, // TCP_TIME_WAIT + TCP_FLG_ACK, // TCP_CLOSE_WAIT + TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK +}; + + +/** + Compute the sequence space left in the old receive window. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence space left in the old receive window. + +**/ +UINT32 +TcpRcvWinOld ( + IN TCP_CB *Tcb + ) +{ + UINT32 OldWin; + + OldWin = 0; + + if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) { + + OldWin = TCP_SUB_SEQ ( + Tcb->RcvWl2 + Tcb->RcvWnd, + Tcb->RcvNxt + ); + } + + return OldWin; +} + + +/** + Compute the current receive window. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return The size of the current receive window, in bytes. + +**/ +UINT32 +TcpRcvWinNow ( + IN TCP_CB *Tcb + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Increase; + UINT32 OldWin; + + Sk = Tcb->Sk; + ASSERT (Sk); + + OldWin = TcpRcvWinOld (Tcb); + + Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF); + + Increase = 0; + if (Win > OldWin) { + Increase = Win - OldWin; + } + + // + // Receiver's SWS: don't advertise a bigger window + // unless it can be increased by at least one Mss or + // half of the receive buffer. + // + if ((Increase > Tcb->SndMss) || + (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) { + + return Win; + } + + return OldWin; +} + + +/** + Compute the value to fill in the window size field + of the outgoing segment. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Syn The flag to indicate whether the outgoing segment is a SYN + segment. + + @return The value of the local receive window size used to fill the outing segment. + +**/ +STATIC +UINT16 +TcpComputeWnd ( + IN TCP_CB *Tcb, + IN BOOLEAN Syn + ) +{ + UINT32 Wnd; + + // + // RFC requires that initial window not be scaled + // + if (Syn) { + + Wnd = GET_RCV_BUFFSIZE (Tcb->Sk); + } else { + + Wnd = TcpRcvWinNow (Tcb); + + Tcb->RcvWnd = Wnd; + } + + Wnd = NET_MIN (Wnd >> Tcb->RcvWndScale, 0xffff); + return NTOHS ((UINT16) Wnd); +} + + +/** + Get the maximum SndNxt. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return The sequence number of the maximum SndNxt. + +**/ +TCP_SEQNO +TcpGetMaxSndNxt ( + IN TCP_CB *Tcb + ) +{ + NET_LIST_ENTRY *Entry; + NET_BUF *Nbuf; + + if (NetListIsEmpty (&Tcb->SndQue)) { + return Tcb->SndNxt; + } + + Entry = Tcb->SndQue.BackLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt)); + return TCPSEG_NETBUF (Nbuf)->End; +} + + +/** + Compute how much data to send. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Force Whether to ignore the sender's SWS avoidance algorithm and send + out data by force. + + @return The length of the data can be sent, if 0, no data can be sent. + +**/ +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ) +{ + SOCKET *Sk; + UINT32 Win; + UINT32 Len; + UINT32 Left; + UINT32 Limit; + + Sk = Tcb->Sk; + ASSERT (Sk); + + // + // TCP should NOT send data beyond the send window + // and congestion window. The right edge of send + // window is defined as SND.WL2 + SND.WND. The right + // edge of congestion window is defined as SND.UNA + + // CWND. + // + Win = 0; + Limit = Tcb->SndWl2 + Tcb->SndWnd; + + if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) { + + Limit = Tcb->SndUna + Tcb->CWnd; + } + + if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) { + Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt); + } + + // + // The data to send contains two parts: the data on the + // socket send queue, and the data on the TCB's send + // buffer. The later can be non-zero if the peer shrinks + // its advertised window. + // + Left = GET_SND_DATASIZE (Sk) + + TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt); + + Len = NET_MIN (Win, Left); + + if (Len > Tcb->SndMss) { + Len = Tcb->SndMss; + } + + if (Force || (Len == 0 && Left == 0)) { + return Len; + } + + if (Len == 0 && Left != 0) { + goto SetPersistTimer; + } + + // + // Sender's SWS avoidance: Don't send a small segment unless + // a)A full-sized segment can be sent, + // b)at least one-half of the maximum sized windows that + // the other end has ever advertised. + // c)It can send everything it has and either it isn't + // expecting an ACK or the Nagle algorithm is disabled. + // + if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) { + + return Len; + } + + if ((Len == Left) && + ((Tcb->SndNxt == Tcb->SndUna) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))) { + + return Len; + } + + // + // RFC1122 suggests to set a timer when SWSA forbids TCP + // sending more data, and combine it with probe timer. + // +SetPersistTimer: + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + + TCP4_DEBUG_WARN ( + ("TcpDataToSend: enter persistent state for TCB %x\n", + Tcb) + ); + + TcpSetProbeTimer (Tcb); + } + + return 0; +} + + +/** + Build the TCP header of the TCP segment and transmit the + segment by IP. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Nbuf Pointer to the buffer containing the segment to be sent out. + + @retval 0 The segment is sent out successfully. + @retval other Error condition occurred. + +**/ +STATIC +INTN +TcpTransmitSegment ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT16 Len; + TCP_HEAD *Head; + TCP_SEG *Seg; + BOOLEAN Syn; + UINT32 DataLen; + + ASSERT (Nbuf && (Nbuf->Tcp == NULL) && TcpVerifySegment (Nbuf)); + + DataLen = Nbuf->TotalSize; + + Seg = TCPSEG_NETBUF (Nbuf); + Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN); + + if (Syn) { + + Len = TcpSynBuildOption (Tcb, Nbuf); + } else { + + Len = TcpBuildOption (Tcb, Nbuf); + } + + ASSERT ((Len % 4 == 0) && (Len <= 40)); + + Len += sizeof (TCP_HEAD); + + Head = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_HEAD + ); + + ASSERT (Head != NULL); + + Nbuf->Tcp = Head; + + Head->SrcPort = Tcb->LocalEnd.Port; + Head->DstPort = Tcb->RemoteEnd.Port; + Head->Seq = NTOHL (Seg->Seq); + Head->Ack = NTOHL (Tcb->RcvNxt); + Head->HeadLen = (UINT8) (Len >> 2); + Head->Res = 0; + Head->Wnd = TcpComputeWnd (Tcb, Syn); + Head->Checksum = 0; + + // + // Check whether to set the PSH flag. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH); + + if (DataLen != 0) { + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) && + TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + } else if ((Seg->End == Tcb->SndNxt) && + (GET_SND_DATASIZE (Tcb->Sk) == 0)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH); + } + } + + // + // Check whether to set the URG flag and the urgent pointer. + // + TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) && + TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) { + + TCP_SET_FLG (Seg->Flag, TCP_FLG_URG); + + if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) { + + Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq); + } else { + + Seg->Urg = (UINT16) NET_MIN ( + TCP_SUB_SEQ (Tcb->SndUp, + Seg->Seq), + 0xffff + ); + } + } + + Head->Flag = Seg->Flag; + Head->Urg = NTOHS (Seg->Urg); + Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum); + + // + // update the TCP session's control information + // + Tcb->RcvWl2 = Tcb->RcvNxt; + if (Syn) { + Tcb->RcvWnd = NTOHS (Head->Wnd); + } + + // + // clear delayedack flag + // + Tcb->DelayedAck = 0; + + return TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip); +} + + +/** + Get a segment from the Tcb's SndQue. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment. + @param Len The maximum length of the segment. + + @return Pointer to the segment, if NULL some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_LIST_ENTRY *Head; + NET_LIST_ENTRY *Cur; + NET_BUF *Node; + TCP_SEG *Seg; + NET_BUF *Nbuf; + TCP_SEQNO End; + UINT8 *Data; + UINT8 Flag; + INT32 Offset; + INT32 CopyLen; + + ASSERT (Tcb && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0)); + + // + // Find the segment that contains the Seq. + // + Head = &Tcb->SndQue; + + Node = NULL; + Seg = NULL; + + NET_LIST_FOR_EACH (Cur, Head) { + Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Seg = TCPSEG_NETBUF (Node); + + if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) { + + break; + } + } + + ASSERT (Cur != Head); + + // + // Return the buffer if it can be returned without + // adjustment: + // + if ((Seg->Seq == Seq) && + TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) && + !NET_BUF_SHARED (Node)) { + + NET_GET_REF (Node); + return Node; + } + + // + // Create a new buffer and copy data there. + // + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Flag = Seg->Flag; + End = Seg->End; + + if (TCP_SEQ_LT (Seq + Len, Seg->End)) { + End = Seq + Len; + } + + CopyLen = TCP_SUB_SEQ (End, Seq); + Offset = TCP_SUB_SEQ (Seq, Seg->Seq); + + // + // If SYN is set and out of the range, clear the flag. + // Becuase the sequence of the first byte is SEG.SEQ+1, + // adjust Offset by -1. If SYN is in the range, copy + // one byte less. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + + if (TCP_SEQ_LT (Seg->Seq, Seq)) { + + TCP_CLEAR_FLG (Flag, TCP_FLG_SYN); + Offset--; + } else { + + CopyLen--; + } + } + + // + // If FIN is set and in the range, copy one byte less, + // and if it is out of the range, clear the flag. + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + + if (Seg->End == End) { + + CopyLen--; + } else { + + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + ASSERT (CopyLen >= 0); + + // + // copy data to the segment + // + if (CopyLen) { + Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL); + ASSERT (Data); + + if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) { + goto OnError; + } + } + + NetCopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG)); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = End; + TCPSEG_NETBUF (Nbuf)->Flag = Flag; + + return Nbuf; + +OnError: + NetbufFree (Nbuf); + return NULL; +} + + +/** + Get a segment from the Tcb's socket buffer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment. + @param Len The maximum length of the segment. + + @return Pointer to the segment, if NULL some error occurred. + +**/ +NET_BUF * +TcpGetSegmentSock ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + UINT8 *Data; + UINT32 DataGet; + + ASSERT (Tcb && Tcb->Sk); + + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + TCP4_DEBUG_ERROR (("TcpGetSegmentSock: failed to allocate " + "a netbuf for TCB %x\n",Tcb)); + + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + DataGet = 0; + + if (Len) { + // + // copy data to the segment. + // + Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL); + ASSERT (Data); + + DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data); + } + + NET_GET_REF (Nbuf); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = Seq + Len; + + NetListInsertTail (&(Tcb->SndQue), &(Nbuf->List)); + + if (DataGet != 0) { + + SockDataSent (Tcb->Sk, DataGet); + } + + return Nbuf; +} + + +/** + Get a segment starting from sequence Seq of a maximum + length of Len. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment. + @param Len The maximum length of the segment. + + @return Pointer to the segment, if NULL some error occurred. + +**/ +NET_BUF * +TcpGetSegment ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq, + IN UINT32 Len + ) +{ + NET_BUF *Nbuf; + + ASSERT (Tcb); + + // + // Compare the SndNxt with the max sequence number sent. + // + if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) { + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + } else { + + Nbuf = TcpGetSegmentSock (Tcb, Seq, Len); + } + + ASSERT (TcpVerifySegment (Nbuf)); + return Nbuf; +} + + +/** + Retransmit the segment from sequence Seq. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Seq The sequence number of the segment to be retransmitted. + + @retval 0 Retransmission succeeded. + @retval -1 Error condition occurred. + +**/ +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ) +{ + NET_BUF *Nbuf; + UINT32 Len; + + // + // Compute the maxium length of retransmission. It is + // limited by three factors: + // 1. Less than SndMss + // 2. must in the current send window + // 3. will not change the boundaries of queued segments. + // + if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) { + TCP4_DEBUG_WARN (("TcpRetransmit: retransmission cancelled " + "because send window too small for TCB %x\n", Tcb)); + + return 0; + } + + Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq); + Len = NET_MIN (Len, Tcb->SndMss); + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + if (Nbuf == NULL) { + return -1; + } + + ASSERT (TcpVerifySegment (Nbuf)); + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + goto OnError; + } + + // + // The retransmitted buffer may be on the SndQue, + // trim TCP head because all the buffer on SndQue + // are headless. + // + ASSERT (Nbuf->Tcp); + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + return 0; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return -1; +} + + +/** + Check whether to send data/SYN/FIN and piggy back an ACK. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Force Whether to ignore the sender's SWS avoidance algorithm and send + out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN TCP_CB *Tcb, + IN INTN Force + ) +{ + UINT32 Len; + INTN Sent; + UINT8 Flag; + NET_BUF *Nbuf; + TCP_SEG *Seg; + TCP_SEQNO Seq; + TCP_SEQNO End; + + ASSERT (Tcb && Tcb->Sk && (Tcb->State != TCP_LISTEN)); + + Sent = 0; + + if ((Tcb->State == TCP_CLOSED) || + TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) { + + return 0; + } + +SEND_AGAIN: + // + // compute how much data can be sent + // + Len = TcpDataToSend (Tcb, Force); + Seq = Tcb->SndNxt; + + Flag = mTcpOutFlag[Tcb->State]; + + if (Flag & TCP_FLG_SYN) { + + Seq = Tcb->Iss; + Len = 0; + } + + // + // only send a segment without data if SYN or + // FIN is set. + // + if ((Len == 0) && !(Flag & (TCP_FLG_SYN | TCP_FLG_FIN))) { + return Sent; + } + + Nbuf = TcpGetSegment (Tcb, Seq, Len); + + if (Nbuf == NULL) { + TCP4_DEBUG_ERROR ( + ("TcpToSendData: failed to get a segment for TCB %x\n", + Tcb) + ); + + goto OnError; + } + + Seg = TCPSEG_NETBUF (Nbuf); + + // + // Set the TcpSeg in Nbuf. + // + Len = Nbuf->TotalSize; + End = Seq + Len; + if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) { + End++; + } + + if (Flag & TCP_FLG_FIN) { + // + // Send FIN if all data is sent, and FIN is + // in the window + // + if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) && + (GET_SND_DATASIZE (Tcb->Sk) == 0) && + TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2) + ) { + + TCP4_DEBUG_TRACE (("TcpToSendData: send FIN " + "to peer for TCB %x in state %d\n", Tcb, Tcb->State)); + + End++; + } else { + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + Seg->Seq = Seq; + Seg->End = End; + Seg->Flag = Flag; + + ASSERT (TcpVerifySegment (Nbuf)); + ASSERT (TcpCheckSndQue (&Tcb->SndQue)); + + // + // don't send an empty segment here. + // + if (Seg->End == Seg->Seq) { + TCP4_DEBUG_WARN (("TcpToSendData: created a empty" + " segment for TCB %x, free it now\n", Tcb)); + + NetbufFree (Nbuf); + return Sent; + } + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + // + // TODO: double check this + // + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + if (Flag & TCP_FLG_FIN) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + goto OnError; + } + + Sent += TCP_SUB_SEQ (End, Seq); + + // + // All the buffer in the SndQue is headless + // + ASSERT (Nbuf->Tcp); + + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + + // + // update status in TCB + // + Tcb->DelayedAck = 0; + + if (Flag & TCP_FLG_FIN) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + if (TCP_SEQ_GT (End, Tcb->SndNxt)) { + Tcb->SndNxt = End; + } + + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + } + + // + // Enable RTT measurement only if not in retransmit. + // Karn's algorithm reqires not to update RTT when in loss. + // + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && + !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + TCP4_DEBUG_TRACE (("TcpToSendData: set RTT measure " + "sequence %d for TCB %x\n", Seq, Tcb)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + Tcb->RttSeq = Seq; + Tcb->RttMeasure = 0; + } + + if (Len == Tcb->SndMss) { + goto SEND_AGAIN; + } + + return Sent; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return Sent; +} + + +/** + Send an ACK immediately. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpSendAck ( + IN TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt; + Seg->End = Tcb->SndNxt; + Seg->Flag = TCP_FLG_ACK; + + if (TcpTransmitSegment (Tcb, Nbuf) == 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW); + Tcb->DelayedAck = 0; + } + + NetbufFree (Nbuf); +} + + +/** + Send a zero probe segment. It can be used by keepalive + and zero window probe. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other Error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN TCP_CB *Tcb + ) +{ + NET_BUF *Nbuf; + TCP_SEG *Seg; + INTN Result; + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + // + // SndNxt-1 is out of window. The peer should respond + // with an ACK. + // + Seg = TCPSEG_NETBUF (Nbuf); + Seg->Seq = Tcb->SndNxt - 1; + Seg->End = Tcb->SndNxt - 1; + Seg->Flag = TCP_FLG_ACK; + + Result = TcpTransmitSegment (Tcb, Nbuf); + NetbufFree (Nbuf); + + return Result; +} + + +/** + Check whether to send an ACK or delayed ACK. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpToSendAck ( + IN TCP_CB *Tcb + ) +{ + // + // Generally, TCP should send a delayed ACK unless: + // 1. ACK at least every other FULL sized segment received, + // 2. Packets received out of order + // 3. Receiving window is open + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || + (Tcb->DelayedAck >= 1) || + (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb)) + ) { + TcpSendAck (Tcb); + return; + } + + TCP4_DEBUG_TRACE (("TcpToSendAck: scheduled a delayed" + " ACK for TCB %x\n", Tcb)); + + // + // schedule a delayed ACK + // + Tcb->DelayedAck++; +} + + +/** + Send a RESET segment in response to the segment received. + + @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL. + @param Head TCP header of the segment that triggers the reset. + @param Len Length of the segment that triggers the reset. + @param Local Local IP address. + @param Remote Remote peer's IP address. + + @retval 0 A reset is sent or no need to send it. + @retval -1 No reset is sent. + +**/ +INTN +TcpSendReset ( + IN TCP_CB *Tcb, + IN TCP_HEAD *Head, + IN INT32 Len, + IN UINT32 Local, + IN UINT32 Remote + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + UINT16 HeadSum; + + // + // Don't respond to a Reset with reset + // + if (Head->Flag & TCP_FLG_RST) { + return 0; + } + + Nbuf = NetbufAlloc (TCP_MAX_HEAD); + + if (Nbuf == NULL) { + return -1; + } + + Nhead = (TCP_HEAD *) NetbufAllocSpace ( + Nbuf, + sizeof (TCP_HEAD), + NET_BUF_TAIL + ); + + ASSERT (Nhead != NULL); + + Nbuf->Tcp = Nhead; + Nhead->Flag = TCP_FLG_RST; + + // + // Derive Seq/ACK from the segment if no TCB + // associated with it, otherwise from the Tcb + // + if (Tcb == NULL) { + + if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) { + Nhead->Seq = Head->Ack; + Nhead->Ack = 0; + } else { + Nhead->Seq = 0; + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len); + } + } else { + + Nhead->Seq = HTONL (Tcb->SndNxt); + Nhead->Ack = HTONL (Tcb->RcvNxt); + TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK); + } + + Nhead->SrcPort = Head->DstPort; + Nhead->DstPort = Head->SrcPort; + Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + + HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0); + Nhead->Checksum = TcpChecksum (Nbuf, HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, Local, Remote); + + NetbufFree (Nbuf); + return 0; +} + + +/** + Verify that the segment is in good shape. + + @param Nbuf Buffer that contains the segment to be checked. + + @retval 0 The segment is broken. + @retval 1 The segment is in good shape. + +**/ +INTN +TcpVerifySegment ( + IN NET_BUF *Nbuf + ) +{ + TCP_HEAD *Head; + TCP_SEG *Seg; + UINT32 Len; + + if (Nbuf == NULL) { + return 1; + } + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Seg = TCPSEG_NETBUF (Nbuf); + Len = Nbuf->TotalSize; + Head = Nbuf->Tcp; + + if (Head != NULL) { + if (Head->Flag != Seg->Flag) { + return 0; + } + + Len -= (Head->HeadLen << 2); + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) { + Len++; + } + + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) { + Len++; + } + + if (Seg->Seq + Len != Seg->End) { + return 0; + } + + return 1; +} + + +/** + Verify that all the segments in SndQue are in good shape. + + @param Head Pointer to the head node of the SndQue. + + @retval 0 At least one segment is broken. + @retval 1 All segments in the specific queue are in good shape. + +**/ +INTN +TcpCheckSndQue ( + IN NET_LIST_ENTRY *Head + ) +{ + NET_LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + + if (NetListIsEmpty (Head)) { + return 1; + } + // + // Initialize the Seq + // + Entry = Head->ForwardLink; + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Seq = TCPSEG_NETBUF (Nbuf)->Seq; + + NET_LIST_FOR_EACH (Entry, Head) { + Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + if (TcpVerifySegment (Nbuf) == 0) { + return 0; + } + + // + // All the node in the SndQue should has: + // SEG.SEQ = LAST_SEG.END + // + if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) { + return 0; + } + + Seq = TCPSEG_NETBUF (Nbuf)->End; + } + + return 1; +} diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h new file mode 100644 index 0000000000..03daa6f04e --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Proto.h @@ -0,0 +1,355 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Proto.h + +Abstract: + + +**/ + +#ifndef _TCP4_PROTO_H_ +#define _TCP4_PROTO_H_ + +typedef struct _TCP_CB TCP_CB; + +#include "Tcp4Driver.h" +#include "Socket.h" +#include "Tcp4Option.h" + +// +// tcp states, Don't change their order, it is used as +// index to mTcpOutFlag and other macros +// +enum { + TCP_CLOSED = 0, + TCP_LISTEN, + TCP_SYN_SENT, + TCP_SYN_RCVD, + TCP_ESTABLISHED, + TCP_FIN_WAIT_1, + TCP_FIN_WAIT_2, + TCP_CLOSING, + TCP_TIME_WAIT, + TCP_CLOSE_WAIT, + TCP_LAST_ACK, +}; + +// +// flags in the TCP header +// +enum { + + TCP_FLG_FIN = 0x01, + TCP_FLG_SYN = 0x02, + TCP_FLG_RST = 0x04, + TCP_FLG_PSH = 0x08, + TCP_FLG_ACK = 0x10, + TCP_FLG_URG = 0x20, + TCP_FLG_FLAG = 0x3F, // mask for all the flags +}; + +enum { + + // + // TCP error status + // + TCP_CONNECT_REFUSED = -1, + TCP_CONNECT_RESET = -2, + TCP_CONNECT_CLOSED = -3, + + // + // Current congestion status as suggested by RFC3782. + // + TCP_CONGEST_RECOVER = 1, // during the NewReno fast recovery + TCP_CONGEST_LOSS = 2, // retxmit because of retxmit time out + TCP_CONGEST_OPEN = 3, // TCP is opening its congestion window + + // + // TCP control flags + // + TCP_CTRL_NO_NAGLE = 0x0001, // disable Nagle algorithm + TCP_CTRL_NO_KEEPALIVE = 0x0002, // disable keepalive timer + TCP_CTRL_NO_WS = 0x0004, // disable window scale option + TCP_CTRL_RCVD_WS = 0x0008, // rcvd a wnd scale option in syn + TCP_CTRL_NO_TS = 0x0010, // disable Timestamp option + TCP_CTRL_RCVD_TS = 0x0020, // rcvd a Timestamp option in syn + TCP_CTRL_SND_TS = 0x0040, // Send Timestamp option to remote + TCP_CTRL_SND_URG = 0x0080, // in urgent send mode + TCP_CTRL_RCVD_URG = 0x0100, // in urgent receive mode + TCP_CTRL_SND_PSH = 0x0200, // in PUSH send mode + TCP_CTRL_FIN_SENT = 0x0400, // FIN is sent + TCP_CTRL_FIN_ACKED = 0x0800, // FIN is ACKed. + TCP_CTRL_TIMER_ON = 0x1000, // At least one of the timer is on + TCP_CTRL_RTT_ON = 0x2000, // The RTT measurement is on + TCP_CTRL_ACK_NOW = 0x4000, // Send the ACK now, don't delay + + // + // Timer related values + // + TCP_TIMER_CONNECT = 0, // Connection establishment timer + TCP_TIMER_REXMIT = 1, // retransmit timer + TCP_TIMER_PROBE = 2, // Window probe timer + TCP_TIMER_KEEPALIVE = 3, // Keepalive timer + TCP_TIMER_FINWAIT2 = 4, // FIN_WAIT_2 timer + TCP_TIMER_2MSL = 5, // TIME_WAIT tiemr + TCP_TIMER_NUMBER = 6, // the total number of TCP timer. + TCP_TICK = 200, // every TCP tick is 200ms + TCP_TICK_HZ = 5, // the frequence of TCP tick + TCP_RTT_SHIFT = 3, // SRTT & RTTVAR scaled by 8 + TCP_RTO_MIN = TCP_TICK_HZ, // the minium value of RTO + TCP_RTO_MAX = TCP_TICK_HZ *60, // the maxium value of RTO + TCP_FOLD_RTT = 4, // timeout threshod to fold RTT + + // + // default values for some timers + // + TCP_MAX_LOSS = 12, // default max times to retxmit + TCP_KEEPALIVE_IDLE_MIN = TCP_TICK_HZ *60 *60 *2, // First keep alive + TCP_KEEPALIVE_PERIOD = TCP_TICK_HZ *60, + TCP_MAX_KEEPALIVE = 8, + TCP_FIN_WAIT2_TIME = 2 *TCP_TICK_HZ, // * 60, + TCP_TIME_WAIT_TIME = 2 *TCP_TICK_HZ, + TCP_PAWS_24DAY = 24 *24 *60 *60 *TCP_TICK_HZ, + TCP_CONNECT_TIME = 75 *TCP_TICK_HZ, + + // + // The header space to be reserved before TCP data to accomodate : + // 60byte IP head + 60byte TCP head + link layer head + // + TCP_MAX_HEAD = 192, + + // + // value ranges for some control option + // + TCP_RCV_BUF_SIZE = 2 *1024 *1024, + TCP_RCV_BUF_SIZE_MIN = 8 *1024, + TCP_SND_BUF_SIZE = 2 *1024 *1024, + TCP_SND_BUF_SIZE_MIN = 8 *1024, + TCP_BACKLOG = 10, + TCP_BACKLOG_MIN = 5, + TCP_MAX_LOSS_MIN = 6, + TCP_CONNECT_TIME_MIN = 60 *TCP_TICK_HZ, + TCP_MAX_KEEPALIVE_MIN = 4, + TCP_KEEPALIVE_IDLE_MAX = TCP_TICK_HZ *60 *60 *4, + TCP_KEEPALIVE_PERIOD_MIN= TCP_TICK_HZ *30, + TCP_FIN_WAIT2_TIME_MAX = 4 *TCP_TICK_HZ, + TCP_TIME_WAIT_TIME_MAX = 60 *TCP_TICK_HZ, +}; + +typedef struct _TCP_SEG { + TCP_SEQNO Seq; // Starting sequence number + TCP_SEQNO End; // The sequence of the last byte + 1, + // include SYN/FIN. End-Seq = SEG.LEN + TCP_SEQNO Ack; // ACK fild in the segment + UINT8 Flag; // TCP header flags + UINT16 Urg; // Valid if URG flag is set. + UINT32 Wnd; // TCP window size field +} TCP_SEG; + +typedef struct _TCP_PEER { + UINT32 Ip; // Network byte order + TCP_PORTNO Port; // Network byte order +} TCP_PEER; + +// +// tcp control block, it includes various states +// +typedef struct _TCP_CB { + NET_LIST_ENTRY List; + TCP_CB *Parent; + + SOCKET *Sk; + TCP_PEER LocalEnd; + TCP_PEER RemoteEnd; + + NET_LIST_ENTRY SndQue; // retxmission queue + NET_LIST_ENTRY RcvQue; // reassemble queue + UINT32 CtrlFlag; // control flags, such as NO_NAGLE + INT32 Error; // soft error status,TCP_CONNECT_RESET... + + // + // RFC793 and RFC1122 defined variables + // + UINT8 State; // TCP state, such as SYN_SENT, LISTEN + UINT8 DelayedAck; // number of delayed ACKs + UINT16 HeadSum; // checksum of the fixed parts of pesudo + // header: Src IP, Dst IP, 0, Protocol, + // not include the TCP length. + + TCP_SEQNO Iss; // Initial Sending Sequence + TCP_SEQNO SndUna; // first unacknowledged data + TCP_SEQNO SndNxt; // next data sequence to send. + TCP_SEQNO SndPsh; // Send PUSH point + TCP_SEQNO SndUp; // Send urgent point + UINT32 SndWnd; // Window advertised by the remote peer + UINT32 SndWndMax; // max send window advertised by the peer + TCP_SEQNO SndWl1; // Seq number used for last window update + TCP_SEQNO SndWl2; // ack no of last window update + UINT16 SndMss; // Max send segment size + TCP_SEQNO RcvNxt; // Next sequence no to receive + UINT32 RcvWnd; // Window advertised by the local peer + TCP_SEQNO RcvWl2; // The RcvNxt (or ACK) of last window update. + // It is necessary because of delayed ACK + + TCP_SEQNO RcvUp; // urgent point; + TCP_SEQNO Irs; // Initial Receiving Sequence + UINT16 RcvMss; // Max receive segment size + UINT16 EnabledTimer; // which timer is currently enabled + UINT32 Timer[TCP_TIMER_NUMBER]; // when the timer will expire + INT32 NextExpire; // count down offset for the nearest timer + UINT32 Idle; // How long the connection is in idle + UINT32 ProbeTime; // the time out value for current window prober + + // + // RFC1323 defined variables, about window scale, + // timestamp and PAWS + // + UINT8 SndWndScale; // Wndscale received from the peer + UINT8 RcvWndScale; // Wndscale used to scale local buffer + UINT32 TsRecent; // TsRecent to echo to the remote peer + UINT32 TsRecentAge; // When this TsRecent is updated + + // TCP_SEQNO LastAckSent; + // It isn't necessary to add LastAckSent here, + // since it is the same as RcvWl2 + + // + // RFC2988 defined variables. about RTT measurement + // + TCP_SEQNO RttSeq; // the seq of measured segment now + UINT32 RttMeasure; // currently measured RTT in heart beats + UINT32 SRtt; // Smoothed RTT, scaled by 8 + UINT32 RttVar; // RTT variance, scaled by 8 + UINT32 Rto; // Current RTO, not scaled + + // + // RFC2581, and 3782 variables. + // Congestion control + NewReno fast recovery. + // + UINT32 CWnd; // Sender's congestion window + UINT32 Ssthresh; // Slow start threshold. + TCP_SEQNO Recover; // recover point for NewReno + UINT16 DupAck; // number of duplicate ACKs + UINT8 CongestState; // the current congestion state(RFC3782) + UINT8 LossTimes; // number of retxmit timeouts in a row + TCP_SEQNO LossRecover; // recover point for retxmit + + // + // configuration parameters, for EFI_TCP4_PROTOCOL specification + // + UINT32 KeepAliveIdle; // idle time before sending first probe + UINT32 KeepAlivePeriod; // interval for subsequent keep alive probe + UINT8 MaxKeepAlive; // Maxium keep alive probe times. + UINT8 KeepAliveProbes; // the number of keep alive probe. + UINT16 MaxRexmit; // The maxium number of retxmit before abort + UINT32 FinWait2Timeout; // The FIN_WAIT_2 time out + UINT32 TimeWaitTimeout; // The TIME_WAIT time out + UINT32 ConnectTimeout; + + // + // configuration for tcp provided by user + // + BOOLEAN UseDefaultAddr; + UINT8 TOS; + UINT8 TTL; + EFI_IPv4_ADDRESS SubnetMask; + + // + // pointer reference to Ip used to send pkt + // + IP_IO_IP_INFO *IpInfo; +} TCP_CB; + +extern NET_LIST_ENTRY mTcpRunQue; +extern NET_LIST_ENTRY mTcpListenQue; +extern TCP_SEQNO mTcpGlobalIss; +extern UINT32 mTcpTick; + +// +// TCP_CONNECTED: both ends have synchronized their ISN. +// +#define TCP_CONNECTED(state) ((state) > TCP_SYN_RCVD) + +#define TCP_FIN_RCVD(State) \ + (((State) == TCP_CLOSE_WAIT) || \ + ((State) == TCP_LAST_ACK) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT)) + +#define TCP_LOCAL_CLOSED(State) \ + (((State) == TCP_FIN_WAIT_1) || \ + ((State) == TCP_FIN_WAIT_2) || \ + ((State) == TCP_CLOSING) || \ + ((State) == TCP_TIME_WAIT) || \ + ((State) == TCP_LAST_ACK)) + +// +// Get the TCP_SEG point from a net buffer's ProtoData +// +#define TCPSEG_NETBUF(NBuf) ((TCP_SEG *) ((NBuf)->ProtoData)) + +// +// macros to compare sequence no +// +#define TCP_SEQ_LT(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) < 0) +#define TCP_SEQ_LEQ(SeqA, SeqB) ((INT32) ((SeqA) - (SeqB)) <= 0) +#define TCP_SEQ_GT(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) < 0) +#define TCP_SEQ_GEQ(SeqA, SeqB) ((INT32) ((SeqB) - (SeqA)) <= 0) + +// +// TCP_SEQ_BETWEEN return whether b <= m <= e +// +#define TCP_SEQ_BETWEEN(b, m, e) ((e) - (b) >= (m) - (b)) + +// +// TCP_SUB_SEQ returns Seq1 - Seq2. Make sure Seq1 >= Seq2 +// +#define TCP_SUB_SEQ(Seq1, Seq2) ((UINT32) ((Seq1) - (Seq2))) + +#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0)) +#define TCP_SET_FLG(Value, Flag) ((Value) |= (Flag)) +#define TCP_CLEAR_FLG(Value, Flag) ((Value) &= ~(Flag)) + +// +// test whether two peers are equal +// +#define TCP_PEER_EQUAL(Pa, Pb) \ + (((Pa)->Ip == (Pb)->Ip) && ((Pa)->Port == (Pb)->Port)) + +// +// test whether Pa matches Pb, or Pa is more specific +// than pb. Zero means wildcard. +// +#define TCP_PEER_MATCH(Pa, Pb) \ + ((((Pb)->Ip == 0) || ((Pb)->Ip == (Pa)->Ip)) && \ + (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port))) + +#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer))) +#define TCP_SET_TIMER(Flag, Timer) ((Flag) |= (1 << (Timer))) +#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) &= ~(1 << (Timer))) + +#define TCP_TIME_LT(Ta, Tb) ((INT32) ((Ta) - (Tb)) < 0) +#define TCP_TIME_LEQ(Ta, Tb) ((INT32) ((Ta) - (Tb)) <= 0) +#define TCP_SUB_TIME(Ta, Tb) ((UINT32) ((Ta) - (Tb))) + +#define TCP_MAX_WIN 0xFFFFU + +typedef +VOID +(*TCP_TIMER_HANDLER) ( + IN TCP_CB * Tcb + ); + +#include "Tcp4Func.h" +#endif diff --git a/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c new file mode 100644 index 0000000000..20679b4f95 --- /dev/null +++ b/MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Timer.c @@ -0,0 +1,582 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation +All rights reserved. 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. + +Module Name: + + Tcp4Timer.c + +Abstract: + + TCP timer related functions. + + +**/ + +#include "Tcp4Main.h" + +UINT32 mTcpTick = 1000; + +STATIC +VOID +TcpConnectTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpRexmitTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpProbeTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpKeepaliveTimeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +TcpFinwait2Timeout ( + IN TCP_CB *Tcb + ); + +STATIC +VOID +Tcp2MSLTimeout ( + IN TCP_CB *Tcb + ); + +TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = { + TcpConnectTimeout, + TcpRexmitTimeout, + TcpProbeTimeout, + TcpKeepaliveTimeout, + TcpFinwait2Timeout, + Tcp2MSLTimeout, +}; + + +/** + Close the TCP connection. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpClose ( + IN TCP_CB *Tcb + ) +{ + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + + TcpSetState (Tcb, TCP_CLOSED); +} + + +/** + Connect timeout handler. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpConnectTimeout ( + IN TCP_CB *Tcb + ) +{ + if (!TCP_CONNECTED (Tcb->State)) { + TCP4_DEBUG_ERROR (("TcpConnectTimeout: connection closed " + "because conenction timer timeout for TCB %x\n", Tcb)); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + if (TCP_SYN_RCVD == Tcb->State) { + TCP4_DEBUG_WARN (("TcpConnectTimeout: send reset because " + "connection timer timeout for TCB %x\n", Tcb)); + + TcpResetConnection (Tcb); + + } + + TcpClose (Tcb); + } +} + + +/** + Timeout handler for TCP retransmission timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpRexmitTimeout ( + IN TCP_CB *Tcb + ) +{ + UINT32 FlightSize; + + TCP4_DEBUG_WARN (("TcpRexmitTimeout: transmission " + "timeout for TCB %x\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 = NET_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)) { + + TCP4_DEBUG_ERROR (("TcpRexmitTimeout: connection closed " + "because too many timeouts for TCB %x\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 Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpProbeTimeout ( + IN 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)); + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetProbeTimer (Tcb); +} + + +/** + Timeout handler for keepalive timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpKeepaliveTimeout ( + IN 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 Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpFinwait2Timeout ( + IN TCP_CB *Tcb + ) +{ + TCP4_DEBUG_WARN (("TcpFinwait2Timeout: connection closed " + "because FIN_WAIT2 timer timeouts for TCB %x\n", Tcb)); + + TcpClose (Tcb); +} + + +/** + Timeout handler for 2MSL timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +Tcp2MSLTimeout ( + IN TCP_CB *Tcb + ) +{ + TCP4_DEBUG_WARN (("Tcp2MSLTimeout: connection closed " + "because TIME_WAIT timer timeouts for TCB %x\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 Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +STATIC +VOID +TcpUpdateTimer ( + IN 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); + } + } +} + + +/** + Enable a TCP timer. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + @param Timer The index of the timer to be enabled. + @param TimeOut The timeout value of this timer. + + @return None. + +**/ +VOID +TcpSetTimer ( + IN 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 Tcb Pointer to the TCP_CB of this TCP instance. + @param Timer The index of the timer to be cleared. + + @return None. + +**/ +VOID +TcpClearTimer ( + IN TCP_CB *Tcb, + IN UINT16 Timer + ) +{ + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer); + TcpUpdateTimer (Tcb); +} + + +/** + Clear all TCP timers. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpClearAllTimer ( + IN TCP_CB *Tcb + ) +{ + Tcb->EnabledTimer = 0; + TcpUpdateTimer (Tcb); +} + + +/** + Enable the window prober timer and set the timeout value. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpSetProbeTimer ( + IN TCP_CB *Tcb + ) +{ + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_PROBE)) { + Tcb->ProbeTime = Tcb->Rto; + + } 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 Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN 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); + } +} + + +/** + Backoff the RTO. + + @param Tcb Pointer to the TCP_CB of this TCP instance. + + @return None. + +**/ +VOID +TcpBackoffRto ( + IN 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) { + 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; + } +} + + +/** + Heart beat timer handler. + + @param Event Timer event signaled, ignored. + @param Context Context of the timer event, ignored. + + @return None. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + NET_LIST_ENTRY *Entry; + NET_LIST_ENTRY *Next; + TCP_CB *Tcb; + INT16 Index; + + mTcpTick++; + mTcpGlobalIss += 100; + + // + // 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) { + TcpSendAck (Tcb); + } + + // + // 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)) { + + goto NextConnection; + } + } + } + + TcpUpdateTimer (Tcb); +NextConnection: + ; + } +} -- cgit v1.2.3