From a3bcde70e6dc69000f85cc5deee98101d2ae200a Mon Sep 17 00:00:00 2001 From: hhtian Date: Mon, 1 Nov 2010 06:13:54 +0000 Subject: Add NetworkPkg (P.UDK2010.UP3.Network.P1) git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524 --- NetworkPkg/TcpDxe/ComponentName.c | 304 +++++++ NetworkPkg/TcpDxe/SockImpl.c | 1230 ++++++++++++++++++++++++++++ NetworkPkg/TcpDxe/SockImpl.h | 103 +++ NetworkPkg/TcpDxe/SockInterface.c | 999 +++++++++++++++++++++++ NetworkPkg/TcpDxe/Socket.h | 924 +++++++++++++++++++++ NetworkPkg/TcpDxe/TcpDispatcher.c | 861 ++++++++++++++++++++ NetworkPkg/TcpDxe/TcpDriver.c | 891 +++++++++++++++++++++ NetworkPkg/TcpDxe/TcpDriver.h | 230 ++++++ NetworkPkg/TcpDxe/TcpDxe.inf | 83 ++ NetworkPkg/TcpDxe/TcpFunc.h | 724 +++++++++++++++++ NetworkPkg/TcpDxe/TcpInput.c | 1592 +++++++++++++++++++++++++++++++++++++ NetworkPkg/TcpDxe/TcpIo.c | 190 +++++ NetworkPkg/TcpDxe/TcpMain.c | 1074 +++++++++++++++++++++++++ NetworkPkg/TcpDxe/TcpMain.h | 758 ++++++++++++++++++ NetworkPkg/TcpDxe/TcpMisc.c | 1281 +++++++++++++++++++++++++++++ NetworkPkg/TcpDxe/TcpOption.c | 374 +++++++++ NetworkPkg/TcpDxe/TcpOption.h | 145 ++++ NetworkPkg/TcpDxe/TcpOutput.c | 1219 ++++++++++++++++++++++++++++ NetworkPkg/TcpDxe/TcpProto.h | 342 ++++++++ NetworkPkg/TcpDxe/TcpTimer.c | 593 ++++++++++++++ 20 files changed, 13917 insertions(+) create mode 100644 NetworkPkg/TcpDxe/ComponentName.c create mode 100644 NetworkPkg/TcpDxe/SockImpl.c create mode 100644 NetworkPkg/TcpDxe/SockImpl.h create mode 100644 NetworkPkg/TcpDxe/SockInterface.c create mode 100644 NetworkPkg/TcpDxe/Socket.h create mode 100644 NetworkPkg/TcpDxe/TcpDispatcher.c create mode 100644 NetworkPkg/TcpDxe/TcpDriver.c create mode 100644 NetworkPkg/TcpDxe/TcpDriver.h create mode 100644 NetworkPkg/TcpDxe/TcpDxe.inf create mode 100644 NetworkPkg/TcpDxe/TcpFunc.h create mode 100644 NetworkPkg/TcpDxe/TcpInput.c create mode 100644 NetworkPkg/TcpDxe/TcpIo.c create mode 100644 NetworkPkg/TcpDxe/TcpMain.c create mode 100644 NetworkPkg/TcpDxe/TcpMain.h create mode 100644 NetworkPkg/TcpDxe/TcpMisc.c create mode 100644 NetworkPkg/TcpDxe/TcpOption.c create mode 100644 NetworkPkg/TcpDxe/TcpOption.h create mode 100644 NetworkPkg/TcpDxe/TcpOutput.c create mode 100644 NetworkPkg/TcpDxe/TcpProto.h create mode 100644 NetworkPkg/TcpDxe/TcpTimer.c (limited to 'NetworkPkg/TcpDxe') diff --git a/NetworkPkg/TcpDxe/ComponentName.c b/NetworkPkg/TcpDxe/ComponentName.c new file mode 100644 index 0000000000..956792afec --- /dev/null +++ b/NetworkPkg/TcpDxe/ComponentName.c @@ -0,0 +1,304 @@ +/** @file + Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +// +// EFI Component Name Functions +// + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name 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. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] 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. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language or DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] 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. + + @param[in] 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. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name 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. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] 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. + + @retval 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. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +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 +/// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName = { + TcpComponentNameGetDriverName, + TcpComponentNameGetControllerName, + "eng" +}; + +/// +/// EFI Component Name 2 Protocol +/// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) TcpComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) TcpComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mTcpDriverNameTable[] = { + { + "eng;en", + L"TCP Network Service Driver" + }, + { + NULL, + NULL + } +}; + +/** + Retrieves a Unicode string that is the user-readable name of the driver. + + This function retrieves the user-readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user-readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name 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. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param[out] 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. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This, and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language or DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +TcpComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mTcpDriverNameTable, + DriverName, + (BOOLEAN) (This == &gTcpComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user-readable name of the controller + that is being managed by a driver. + + This function retrieves the user-readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user-readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param[in] 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. + + @param[in] 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. + + @param[in] Language A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name 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. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param[out] 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. + + @retval 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. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language or ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +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 + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/NetworkPkg/TcpDxe/SockImpl.c b/NetworkPkg/TcpDxe/SockImpl.c new file mode 100644 index 0000000000..7fad042be6 --- /dev/null +++ b/NetworkPkg/TcpDxe/SockImpl.c @@ -0,0 +1,1230 @@ +/** @file + Implementation of the Socket. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "SockImpl.h" + +/** + Get the first buffer block in the specific socket buffer. + + @param[in] 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 + ) +{ + LIST_ENTRY *NetbufList; + + NetbufList = &(Sockbuf->DataQueue->BufList); + + if (IsListEmpty (NetbufList)) { + return NULL; + } + + return NET_LIST_HEAD (NetbufList, NET_BUF, List); +} + +/** + Get the next buffer block in the specific socket buffer. + + @param[in] Sockbuf Pointer to the socket buffer. + @param[in] 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 + ) +{ + 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); +} + +/** + User provided callback function for NetbufFromExt. + + @param[in] Event The Event this notify function registered to, ignored. + +**/ +VOID +EFIAPI +SockFreeFoo ( + IN EFI_EVENT Event + ) +{ + return; +} + +/** + Get the length of the data that can be retrieved from the socket + receive buffer. + + @param[in] SockBuffer Pointer to the socket receive buffer. + @param[out] IsUrg Pointer to a BOOLEAN variable. + If TRUE the data is OOB. + @param[in] BufLen The maximum length of the data buffer to + store the received data in the socket layer. + + @return The length of the data can be retreived. + +**/ +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 != NULL) && (IsUrg != NULL) && (BufLen > 0)); + + // + // Get the first socket receive buffer + // + RcvBufEntry = SockBufFirst (SockBuffer); + ASSERT (RcvBufEntry != NULL); + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + // + // Check whether the receive data is out of bound. If yes, calculate the maximum + // allowed length of the urgent data and output it. + // + *IsUrg = (BOOLEAN) ((TcpRsvData->UrgLen > 0) ? TRUE : FALSE); + + if (*IsUrg && (TcpRsvData->UrgLen < RcvBufEntry->TotalSize)) { + + DataLen = MIN (TcpRsvData->UrgLen, BufLen); + + if (DataLen < TcpRsvData->UrgLen) { + TcpRsvData->UrgLen = TcpRsvData->UrgLen - DataLen; + } else { + TcpRsvData->UrgLen = 0; + } + + return DataLen; + + } + + // + // Process the next socket receive buffer to get the maximum allowed length + // of the received data. + // + DataLen = RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + + while ((BufLen > DataLen) && (RcvBufEntry != NULL)) { + + TcpRsvData = (TCP_RSV_DATA *) RcvBufEntry->ProtoData; + + Urg = (BOOLEAN) ((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 MIN (TcpRsvData->UrgLen + DataLen, BufLen); + + } + + DataLen += RcvBufEntry->TotalSize; + + RcvBufEntry = SockBufNext (SockBuffer, RcvBufEntry); + } + + DataLen = MIN (BufLen, DataLen); + return DataLen; +} + +/** + Copy data from socket buffer to an application provided receive buffer. + + @param[in] Sock Pointer to the socket. + @param[in] TcpRxData Pointer to the application provided receive buffer. + @param[in] RcvdBytes The maximum length of the data can be copied. + @param[in] IsUrg If TRUE the data is Out of Bound, FALSE the data is normal. + +**/ +VOID +SockSetTcpRxData ( + IN SOCKET *Sock, + IN VOID *TcpRxData, + IN UINT32 RcvdBytes, + IN BOOLEAN IsUrg + ) +{ + 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 = IsUrg; + + // + // Copy the CopyBytes data from socket receive buffer to RxData. + // + for (Index = 0; (Index < RxData->FragmentCount) && (RcvdBytes > 0); Index++) { + + Fragment = &RxData->FragmentTable[Index]; + CopyBytes = MIN ((UINT32) (Fragment->FragmentLength), RcvdBytes); + + NetbufQueCopy ( + Sock->RcvBuffer.DataQueue, + OffSet, + CopyBytes, + Fragment->FragmentBuffer + ); + + Fragment->FragmentLength = CopyBytes; + RcvdBytes -= CopyBytes; + OffSet += CopyBytes; + } +} + +/** + Process the send token. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockProcessSndToken ( + IN OUT SOCKET *Sock + ) +{ + UINT32 FreeSpace; + SOCK_TOKEN *SockToken; + UINT32 DataLen; + SOCK_IO_TOKEN *SndToken; + EFI_TCP4_TRANSMIT_DATA *TxData; + EFI_STATUS Status; + + ASSERT ((Sock != NULL) && (SockStream == 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) && !IsListEmpty (&Sock->SndTokenList)) { + + SockToken = NET_LIST_HEAD ( + &(Sock->SndTokenList), + SOCK_TOKEN, + TokenList + ); + + // + // process this token + // + RemoveEntryList (&(SockToken->TokenList)); + InsertTailList ( + &(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: + + RemoveEntryList (&SockToken->TokenList); + SIGNAL_TOKEN (SockToken->Token, Status); + FreePool (SockToken); +} + +/** + Get received data from the socket layer to the receive token. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN OUT SOCKET *Sock, + IN OUT SOCK_IO_TOKEN *RcvToken + ) +{ + UINT32 TokenRcvdBytes; + EFI_TCP4_RECEIVE_DATA *RxData; + BOOLEAN IsUrg; + + ASSERT (Sock != NULL); + + ASSERT (SockStream == 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); + + NetbufQueTrim (Sock->RcvBuffer.DataQueue, 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[in] Sock Pointer to the socket. + @param[in] TcpTxData Pointer to the application provided send buffer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +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) { + DEBUG ( + (EFI_D_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[in] Sock Pointer to the socket. + @param[in, out] PendingTokenList Pointer to the token list to be flushed. + +**/ +VOID +SockFlushPendingToken ( + IN SOCKET *Sock, + IN OUT LIST_ENTRY *PendingTokenList + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *Token; + + ASSERT ((Sock != NULL) && (PendingTokenList != NULL)); + + while (!IsListEmpty (PendingTokenList)) { + SockToken = NET_LIST_HEAD ( + PendingTokenList, + SOCK_TOKEN, + TokenList + ); + + Token = SockToken->Token; + SIGNAL_TOKEN (Token, Sock->SockError); + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + } +} + +/** + Wake up the connection token while the connection is successfully established, + then try to process any pending send token. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeConnToken ( + IN OUT 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); +} + +/** + Wake up the listen token while the connection is established successfully. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeListenToken ( + IN OUT SOCKET *Sock + ) +{ + SOCKET *Parent; + SOCK_TOKEN *SockToken; + EFI_TCP4_LISTEN_TOKEN *ListenToken; + + Parent = Sock->Parent; + + ASSERT ((Parent != NULL) && SOCK_IS_LISTENING (Parent) && SOCK_IS_CONNECTED (Sock)); + + if (!IsListEmpty (&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); + + RemoveEntryList (&SockToken->TokenList); + FreePool (SockToken); + + RemoveEntryList (&Sock->ConnectionList); + + Parent->ConnCnt--; + DEBUG ( + (EFI_D_INFO, + "SockWakeListenToken: accept a socket, now conncnt is %d", + Parent->ConnCnt) + ); + + Sock->Parent = NULL; + } +} + +/** + Wake up the receive token while some data is received. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockWakeRcvToken ( + IN OUT SOCKET *Sock + ) +{ + UINT32 RcvdBytes; + UINT32 TokenRcvdBytes; + SOCK_TOKEN *SockToken; + SOCK_IO_TOKEN *RcvToken; + + ASSERT (Sock->RcvBuffer.DataQueue != NULL); + + RcvdBytes = (Sock->RcvBuffer.DataQueue)->BufSize; + + ASSERT (RcvdBytes > 0); + + while (RcvdBytes > 0 && !IsListEmpty (&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 ; + } + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + RcvdBytes -= TokenRcvdBytes; + } +} + +/** + Create a socket with initial data SockInitData. + + @param[in] SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket, return NULL when an exception occurs. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + SOCKET *Parent; + EFI_STATUS Status; + EFI_GUID *TcpProtocolGuid; + UINTN ProtocolLength; + + ASSERT ((SockInitData != NULL) && (SockInitData->ProtoHandler != NULL)); + ASSERT (SockInitData->Type == SockStream); + ASSERT ((SockInitData->ProtoData != NULL) && (SockInitData->DataSize <= PROTO_RESERVED_LEN)); + + if (SockInitData->IpVersion == IP_VERSION_4) { + TcpProtocolGuid = &gEfiTcp4ProtocolGuid; + ProtocolLength = sizeof (EFI_TCP4_PROTOCOL); + } else { + TcpProtocolGuid = &gEfiTcp6ProtocolGuid; + ProtocolLength = sizeof (EFI_TCP6_PROTOCOL); + } + + + Parent = SockInitData->Parent; + + if ((Parent != NULL) && (Parent->ConnCnt == Parent->BackLog)) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: Socket parent has reached its connection limit with %d ConnCnt and %d BackLog\n", + Parent->ConnCnt, + Parent->BackLog) + ); + + return NULL; + } + + Sock = AllocateZeroPool (sizeof (SOCKET)); + if (NULL == Sock) { + + DEBUG ((EFI_D_ERROR, "SockCreate: No resource to create a new socket\n")); + return NULL; + } + + InitializeListHead (&Sock->Link); + InitializeListHead (&Sock->ConnectionList); + InitializeListHead (&Sock->ListenTokenList); + InitializeListHead (&Sock->RcvTokenList); + InitializeListHead (&Sock->SndTokenList); + InitializeListHead (&Sock->ProcessingSndTokenList); + + EfiInitializeLock (&(Sock->Lock), TPL_CALLBACK); + + Sock->SndBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->SndBuffer.DataQueue) { + DEBUG ( + (EFI_D_ERROR, + "SockCreate: No resource to allocate SndBuffer for new socket\n") + ); + + goto OnError; + } + + Sock->RcvBuffer.DataQueue = NetbufQueAlloc (); + if (NULL == Sock->RcvBuffer.DataQueue) { + DEBUG ( + (EFI_D_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->CreateCallback = SockInitData->CreateCallback; + Sock->DestroyCallback = SockInitData->DestroyCallback; + Sock->Context = SockInitData->Context; + + Sock->SockError = EFI_ABORTED; + Sock->SndBuffer.LowWater = SOCK_BUFF_LOW_WATER; + Sock->RcvBuffer.LowWater = SOCK_BUFF_LOW_WATER; + + Sock->IpVersion = SockInitData->IpVersion; + + // + // Install protocol on Sock->SockHandle + // + CopyMem (&Sock->NetProtocol, SockInitData->Protocol, ProtocolLength); + + // + // copy the protodata into socket + // + CopyMem (Sock->ProtoReserved, SockInitData->ProtoData, SockInitData->DataSize); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Sock->SockHandle, + TcpProtocolGuid, + &Sock->NetProtocol, + NULL + ); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_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++; + + DEBUG ( + (EFI_D_INFO, + "SockCreate: Create a new socket and add to parent, now conncnt is %d\n", + Parent->ConnCnt) + ); + + InsertTailList (&Parent->ConnectionList, &Sock->ConnectionList); + } + + if (Sock->CreateCallback != NULL) { + Status = Sock->CreateCallback (Sock, Sock->Context); + if (EFI_ERROR (Status)) { + goto OnError; + } + } + + return Sock; + +OnError: + + if (Sock->SockHandle != NULL) { + gBS->UninstallMultipleProtocolInterfaces ( + Sock->SockHandle, + TcpProtocolGuid, + &Sock->NetProtocol, + NULL + ); + } + + if (NULL != Sock->SndBuffer.DataQueue) { + NetbufQueFree (Sock->SndBuffer.DataQueue); + } + + if (NULL != Sock->RcvBuffer.DataQueue) { + NetbufQueFree (Sock->RcvBuffer.DataQueue); + } + + FreePool (Sock); + + return NULL; +} + +/** + Destroy a socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockDestroy ( + IN OUT SOCKET *Sock + ) +{ + VOID *SockProtocol; + EFI_GUID *TcpProtocolGuid; + EFI_STATUS Status; + + ASSERT (SockStream == Sock->Type); + + if (Sock->DestroyCallback != NULL) { + Sock->DestroyCallback (Sock, Sock->Context); + } + + // + // 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 != NULL) { + + RemoveEntryList (&(Sock->ConnectionList)); + (Sock->Parent->ConnCnt)--; + + DEBUG ( + (EFI_D_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 + // + if (Sock->IpVersion == IP_VERSION_4) { + TcpProtocolGuid = &gEfiTcp4ProtocolGuid; + } else { + TcpProtocolGuid = &gEfiTcp6ProtocolGuid; + } + + // + // Retrieve the protocol installed on this sock + // + Status = gBS->OpenProtocol ( + Sock->SockHandle, + TcpProtocolGuid, + &SockProtocol, + Sock->DriverBinding, + Sock->SockHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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, + TcpProtocolGuid, + SockProtocol, + NULL + ); + +FreeSock: + + FreePool (Sock); +} + +/** + Flush the sndBuffer and rcvBuffer of socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockConnFlush ( + IN OUT SOCKET *Sock + ) +{ + SOCKET *Child; + + ASSERT (Sock != NULL); + + // + // 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 (!IsListEmpty (&Sock->ConnectionList)) { + Child = NET_LIST_HEAD ( + &Sock->ConnectionList, + SOCKET, + ConnectionList + ); + + SockDestroyChild (Child); + } + + Sock->ConnCnt = 0; + } + +} + +/** + Set the state of the socket. + + @param[in, out] Sock Pointer to the socket. + @param[in] State The new socket state to be set. + +**/ +VOID +SockSetState ( + IN OUT SOCKET *Sock, + IN UINT8 State + ) +{ + Sock->State = State; +} + +/** + Clone a new socket, including its associated protocol control block. + + @param[in] Sock Pointer to the socket to be cloned. + + @return Pointer to the newly cloned socket. If NULL, an 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.IpVersion = Sock->IpVersion; + InitData.Protocol = &(Sock->NetProtocol); + InitData.CreateCallback = Sock->CreateCallback; + InitData.DestroyCallback = Sock->DestroyCallback; + InitData.Context = Sock->Context; + InitData.ProtoData = Sock->ProtoReserved; + InitData.DataSize = sizeof (Sock->ProtoReserved); + + ClonedSock = SockCreate (&InitData); + + if (NULL == ClonedSock) { + DEBUG ((EFI_D_ERROR, "SockClone: no resource to create a cloned sock\n")); + return NULL; + } + + 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[in, out] Sock Pointer to the socket associated with the + established connection. + +**/ +VOID +SockConnEstablished ( + IN OUT SOCKET *Sock + ) +{ + + ASSERT (SO_CONNECTING == Sock->State); + + SockSetState (Sock, SO_CONNECTED); + + if (NULL == Sock->Parent) { + SockWakeConnToken (Sock); + } else { + SockWakeListenToken (Sock); + } + +} + +/** + 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[in, out] Sock Pointer to the socket associated with the closed + connection. + +**/ +VOID +SockConnClosed ( + IN OUT SOCKET *Sock + ) +{ + if (Sock->CloseToken != NULL) { + 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 was sent or processed. + + This function trims the sent data in the socket send buffer, and signals the data + token if proper. + + @param[in, out] Sock Pointer to the socket. + @param[in] Count The length of the data processed or sent, in bytes. + +**/ +VOID +SockDataSent ( + IN OUT SOCKET *Sock, + IN UINT32 Count + ) +{ + SOCK_TOKEN *SockToken; + SOCK_COMPLETION_TOKEN *SndToken; + + ASSERT (!IsListEmpty (&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) { + + RemoveEntryList (&(SockToken->TokenList)); + SIGNAL_TOKEN (SndToken, EFI_SUCCESS); + Count -= SockToken->RemainDataLen; + FreePool (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); +} + +/** + Called by the low layer protocol to copy some data in the socket send + buffer starting from the specific offset to a buffer provided by + the caller. + + @param[in] Sock Pointer to the socket. + @param[in] Offset The start point of the data to be copied. + @param[in] Len The length of the data to be copied. + @param[out] 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, + OUT UINT8 *Dest + ) +{ + ASSERT ((Sock != NULL) && SockStream == 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 the + urgent data length, and then check if any receive token can be signaled. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] NetBuffer Pointer to the buffer that contains the received data. + @param[in] UrgLen The length of the urgent data in the received data. + +**/ +VOID +SockDataRcvd ( + IN OUT SOCKET *Sock, + IN OUT NET_BUF *NetBuffer, + IN UINT32 UrgLen + ) +{ + ASSERT ((Sock != NULL) && (Sock->RcvBuffer.DataQueue != NULL) && + UrgLen <= NetBuffer->TotalSize); + + NET_GET_REF (NetBuffer); + + ((TCP_RSV_DATA *) (NetBuffer->ProtoData))->UrgLen = UrgLen; + + NetbufQueAppend (Sock->RcvBuffer.DataQueue, NetBuffer); + + SockWakeRcvToken (Sock); +} + +/** + Get the length of the free space of the specific socket buffer. + + @param[in] Sock Pointer to the socket. + @param[in] 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 != NULL && ((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; +} + +/** + Called by the low layer protocol to indicate that there will be no more data + from the communication peer. + + This function sets the socket's state to SO_NO_MORE_DATA and signals all queued + IO tokens with the error status EFI_CONNECTION_FIN. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockNoMoreData ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Err; + + SOCK_NO_MORE_DATA (Sock); + + if (!IsListEmpty (&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); + + } +} + diff --git a/NetworkPkg/TcpDxe/SockImpl.h b/NetworkPkg/TcpDxe/SockImpl.h new file mode 100644 index 0000000000..bb4f6c2085 --- /dev/null +++ b/NetworkPkg/TcpDxe/SockImpl.h @@ -0,0 +1,103 @@ +/** @file + The function declaration that provided for Socket Interface. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#ifndef _SOCK_IMPL_H_ +#define _SOCK_IMPL_H_ + +#include "Socket.h" + +/** + Signal a event with the given status. + + @param[in] Token The token's event is to be signaled. + @param[in] TokenStatus The status to be sent with the event. + +**/ +#define SIGNAL_TOKEN(Token, TokenStatus) \ + do { \ + (Token)->Status = (TokenStatus); \ + gBS->SignalEvent ((Token)->Event); \ + } while (0) + +#define SOCK_HEADER_SPACE (60 + 60 + 72) + +/** + Process the TCP send data, buffer the tcp txdata and append + the buffer to socket send buffer, then try to send it. + + @param[in] Sock Pointer to the socket. + @param[in] TcpTxData Pointer to the application provided send buffer. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +SockProcessTcpSndData ( + IN SOCKET *Sock, + IN VOID *TcpTxData + ); + +/** + Get received data from the socket layer to the receive token. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] RcvToken Pointer to the application provided receive token. + + @return The length of data received in this token. + +**/ +UINT32 +SockProcessRcvToken ( + IN OUT SOCKET *Sock, + IN OUT SOCK_IO_TOKEN *RcvToken + ); + +/** + Flush the sndBuffer and rcvBuffer of socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockConnFlush ( + IN OUT SOCKET *Sock + ); + +/** + Create a socket with initial data SockInitData. + + @param[in] SockInitData Pointer to the initial data of the socket. + + @return Pointer to the newly created socket, return NULL when exception occured. + +**/ +SOCKET * +SockCreate ( + IN SOCK_INIT_DATA *SockInitData + ); + +/** + Destroy a socket. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockDestroy ( + IN OUT SOCKET *Sock + ); + +#endif diff --git a/NetworkPkg/TcpDxe/SockInterface.c b/NetworkPkg/TcpDxe/SockInterface.c new file mode 100644 index 0000000000..e36c0e97c8 --- /dev/null +++ b/NetworkPkg/TcpDxe/SockInterface.c @@ -0,0 +1,999 @@ +/** @file + Interface function of the Socket. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "SockImpl.h" + +/** + Check whether the Event is in the List. + + @param[in] List Pointer to the token list to be searched. + @param[in] Event The event to be checked. + + @retval TRUE The specific Event exists in the List. + @retval FALSE The specific Event is not in the List. + +**/ +BOOLEAN +SockTokenExistedInList ( + IN LIST_ENTRY *List, + IN EFI_EVENT Event + ) +{ + 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[in] Sock Pointer to the instance's socket. + @param[in] Event The event to be checked. + + @retval TRUE The Event exists in related socket's lists. + @retval FALSE The Event is not in related socket's lists. + +**/ +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 the socket Sock. + + @param[in] Sock Pointer to the instance's socket. + @param[in] List Pointer to the list to store the token. + @param[in] Token Pointer to the token to be buffered. + @param[in] DataLen The data length of the buffer contained in Token. + + @return Pointer to the token that wraps Token. If NULL, an error condition occurred. + +**/ +SOCK_TOKEN * +SockBufferToken ( + IN SOCKET *Sock, + IN LIST_ENTRY *List, + IN VOID *Token, + IN UINT32 DataLen + ) +{ + SOCK_TOKEN *SockToken; + + SockToken = AllocateZeroPool (sizeof (SOCK_TOKEN)); + if (NULL == SockToken) { + + DEBUG ( + (EFI_D_ERROR, + "SockBufferIOToken: No Memory to allocate SockToken\n") + ); + + return NULL; + } + + SockToken->Sock = Sock; + SockToken->Token = (SOCK_COMPLETION_TOKEN *) Token; + SockToken->RemainDataLen = DataLen; + InsertTailList (List, &SockToken->TokenList); + + return SockToken; +} + +/** + Destory the socket Sock and its associated protocol control block. + + @param[in, out] Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock was destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN OUT SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL)); + + if (Sock->IsDestroyed) { + return EFI_SUCCESS; + } + + Sock->IsDestroyed = TRUE; + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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)) { + + DEBUG ( + (EFI_D_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; + } + + EfiReleaseLock (&(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[in] SockInitData Inital data to setting the socket. + + @return Pointer to the newly created socket. If NULL, an error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData + ) +{ + SOCKET *Sock; + EFI_STATUS Status; + + // + // create a new socket + // + Sock = SockCreate (SockInitData); + if (NULL == Sock) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: No resource to create a new socket\n") + ); + + return NULL; + } + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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)) { + + DEBUG ( + (EFI_D_ERROR, + "SockCreateChild: Protocol failed to attach a socket with %r\n", + Status) + ); + + SockDestroy (Sock); + Sock = NULL; + } + + EfiReleaseLock (&(Sock->Lock)); + return Sock; +} + +/** + Configure the specific socket Sock using configuration data ConfigData. + + @param[in] Sock Pointer to the socket to be configured. + @param[in] ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket 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 = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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: + EfiReleaseLock (&(Sock->Lock)); + + return Status; +} + +/** + Initiate a connection establishment process. + + @param[in] Sock Pointer to the socket to initiate the initate the + connection. + @param[in] Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection 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 = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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: + EfiReleaseLock (&(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[in] Sock Pointer to the socket to accept connections. + @param[in] 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 limits. + +**/ +EFI_STATUS +SockAccept ( + IN SOCKET *Sock, + IN VOID *Token + ) +{ + EFI_TCP4_LISTEN_TOKEN *ListenToken; + LIST_ENTRY *ListEntry; + EFI_STATUS Status; + SOCKET *Socket; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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); + + RemoveEntryList (ListEntry); + + ASSERT (Socket->Parent != NULL); + + Socket->Parent->ConnCnt--; + + DEBUG ( + (EFI_D_INFO, + "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: + EfiReleaseLock (&(Sock->Lock)); + + return Status; +} + +/** + Issue a token with data to the socket to send out. + + @param[in] Sock Pointer to the socket to process the token with + data. + @param[in] Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token 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 limits. + +**/ +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 (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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) { + DEBUG ( + (EFI_D_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)) { + DEBUG ( + (EFI_D_ERROR, + "SockSend: Failed to process Snd Data\n", + Status) + ); + + RemoveEntryList (&(SockToken->TokenList)); + FreePool (SockToken); + } + } + +Exit: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Issue a token to get data from the socket. + + @param[in] Sock Pointer to the socket to get data from. + @param[in] Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token 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 (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Reset the socket and its associated protocol control block. + + @param[in, out] 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 OUT SOCKET *Sock + ) +{ + EFI_STATUS Status; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_ERROR, + "SockFlush: Get the access for socket failed with %r", + Status) + ); + + return EFI_ACCESS_DENIED; + } + + if (!SOCK_IS_CONFIGURED (Sock)) { + + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + Status = Sock->ProtoHandler (Sock, SOCK_FLUSH, NULL); + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Close or abort the socket associated connection. + + @param[in, out] Sock Pointer to the socket of the connection to close + or abort. + @param[in] Token The token for a close operation. + @param[in] OnAbort TRUE for aborting the connection; FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation 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 OUT SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (SockStream == Sock->Type); + + Status = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_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: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Get the mode data of the low layer protocol. + + @param[in] Sock Pointer to the socket to get mode data from. + @param[in, out] Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data was obtained successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN OUT 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[in] Sock Pointer to the socket of the connection to join the + specific multicast group. + @param[in] GroupInfo Pointer to the multicast group info. + + @retval EFI_SUCCESS The configuration completed 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 = EfiAcquireLockOrFail (&(Sock->Lock)); + + if (EFI_ERROR (Status)) { + + DEBUG ( + (EFI_D_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: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + +/** + Add or remove route information in IP route table associated + with this socket. + + @param[in] Sock Pointer to the socket associated with the IP route + table to operate on. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table 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 = EfiAcquireLockOrFail (&(Sock->Lock)); + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_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: + EfiReleaseLock (&(Sock->Lock)); + return Status; +} + diff --git a/NetworkPkg/TcpDxe/Socket.h b/NetworkPkg/TcpDxe/Socket.h new file mode 100644 index 0000000000..a00625244e --- /dev/null +++ b/NetworkPkg/TcpDxe/Socket.h @@ -0,0 +1,924 @@ +/** @file + Common head file for TCP socket. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#ifndef _SOCKET_H_ +#define _SOCKET_H_ + +#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. +// +// +// + +/// +/// Socket state +/// +#define SO_CLOSED 0 +#define SO_LISTENING 1 +#define SO_CONNECTING 2 +#define SO_CONNECTED 3 +#define SO_DISCONNECTING 4 + +/// +/// Socket configure state +/// +#define SO_UNCONFIGURED 0 +#define SO_CONFIGURED_ACTIVE 1 +#define SO_CONFIGURED_PASSIVE 2 +#define SO_NO_MAPPING 3 + +/// +/// The request issued from socket layer to protocol layer. +/// +#define SOCK_ATTACH 0 ///< Attach current socket to a new PCB +#define SOCK_DETACH 1 ///< Detach current socket from the PCB +#define SOCK_CONFIGURE 2 ///< Configure attached PCB +#define SOCK_FLUSH 3 ///< Flush attached PCB +#define SOCK_SND 4 ///< Need protocol to send something +#define SOCK_SNDPUSH 5 ///< Need protocol to send pushed data +#define SOCK_SNDURG 6 ///< Need protocol to send urgent data +#define SOCK_CONSUMED 7 ///< Application has retrieved data from socket +#define SOCK_CONNECT 8 ///< Need to connect to a peer +#define SOCK_CLOSE 9 ///< Need to close the protocol process +#define SOCK_ABORT 10 ///< Need to reset the protocol process +#define SOCK_POLL 11 ///< Need to poll to the protocol layer +#define SOCK_ROUTE 12 ///< Need to add a route information +#define SOCK_MODE 13 ///< Need to get the mode data of the protocol +#define SOCK_GROUP 14 ///< Need to join a mcast group + +/** + Set socket SO_NO_MORE_DATA flag. + + @param[in] Sock Pointer to the socket + +**/ +#define SOCK_NO_MORE_DATA(Sock) ((Sock)->Flag |= SO_NO_MORE_DATA) + +/** + Check whether the socket is unconfigured. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is unconfigued. + @retval FALSE The socket is not unconfigued. + +**/ +#define SOCK_IS_UNCONFIGURED(Sock) ((Sock)->ConfigureState == SO_UNCONFIGURED) + +/** + Check whether the socket is configured. + + @param[in] Sock Pointer to the socket + + @retval TRUE The socket is configued + @retval FALSE The socket is not configued + +**/ +#define SOCK_IS_CONFIGURED(Sock) \ + (((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) || \ + ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE)) + +/** + Check whether the socket is configured to active mode. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is configued to active mode. + @retval FALSE The socket is not configued to active mode. + +**/ +#define SOCK_IS_CONFIGURED_ACTIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_ACTIVE) + +/** + Check whether the socket is configured to passive mode. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is configued to passive mode. + @retval FALSE The socket is not configued to passive mode. + +**/ +#define SOCK_IS_CONNECTED_PASSIVE(Sock) ((Sock)->ConfigureState == SO_CONFIGURED_PASSIVE) + +/** + Check whether the socket is mapped. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is not mapping. + @retval FALSE The socket is mapped. + +**/ +#define SOCK_IS_NO_MAPPING(Sock) ((Sock)->ConfigureState == SO_NO_MAPPING) + +/** + Check whether the socket is closed. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is closed. + @retval FALSE The socket is not closed. + +**/ +#define SOCK_IS_CLOSED(Sock) ((Sock)->State == SO_CLOSED) + +/** + Check whether the socket is listening. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is listening. + @retval FALSE The socket is not listening. + +**/ +#define SOCK_IS_LISTENING(Sock) ((Sock)->State == SO_LISTENING) + +/** + Check whether the socket is connecting. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is connecting. + @retval FALSE The socket is not connecting. + +**/ +#define SOCK_IS_CONNECTING(Sock) ((Sock)->State == SO_CONNECTING) + +/** + Check whether the socket has connected. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket has connected. + @retval FALSE The socket has not connected. + +**/ +#define SOCK_IS_CONNECTED(Sock) ((Sock)->State == SO_CONNECTED) + +/** + Check whether the socket is disconnecting. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is disconnecting. + @retval FALSE The socket is not disconnecting. + +**/ +#define SOCK_IS_DISCONNECTING(Sock) ((Sock)->State == SO_DISCONNECTING) + +/** + Check whether the socket is no more data. + + @param[in] Sock Pointer to the socket. + + @retval TRUE The socket is no more data. + @retval FALSE The socket still has data. + +**/ +#define SOCK_IS_NO_MORE_DATA(Sock) (0 != ((Sock)->Flag & SO_NO_MORE_DATA)) + +/** + Set the size of the receive buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Size The size to set. + +**/ +#define SET_RCV_BUFFSIZE(Sock, Size) ((Sock)->RcvBuffer.HighWater = (Size)) + +/** + Get the size of the receive buffer. + + @param[in] Sock Pointer to the socket. + + @return The receive buffer size. + +**/ +#define GET_RCV_BUFFSIZE(Sock) ((Sock)->RcvBuffer.HighWater) + +/** + Get the size of the receive data. + + @param[in] Sock Pointer to the socket. + + @return The received data size. + +**/ +#define GET_RCV_DATASIZE(Sock) (((Sock)->RcvBuffer.DataQueue)->BufSize) + +/** + Set the size of the send buffer. + + @param[in] Sock Pointer to the socket. + @param[in] Size The size to set. + +**/ +#define SET_SND_BUFFSIZE(Sock, Size) ((Sock)->SndBuffer.HighWater = (Size)) + +/** + Get the size of the send buffer. + + @param[in] Sock Pointer to the socket. + + @return The send buffer size. + +**/ +#define GET_SND_BUFFSIZE(Sock) ((Sock)->SndBuffer.HighWater) + +/** + Get the size of the send data. + + @param[in] Sock Pointer to the socket. + + @return The send data size. + +**/ +#define GET_SND_DATASIZE(Sock) (((Sock)->SndBuffer.DataQueue)->BufSize) + +/** + Set the backlog value of the socket. + + @param[in] Sock Pointer to the socket. + @param[in] Value The value to set. + +**/ +#define SET_BACKLOG(Sock, Value) ((Sock)->BackLog = (Value)) + +/** + Get the backlog value of the socket. + + @param[in] Sock Pointer to the socket. + + @return The backlog value. + +**/ +#define GET_BACKLOG(Sock) ((Sock)->BackLog) + +/** + Set the socket with error state. + + @param[in] Sock Pointer to the socket. + @param[in] Error The error state. + +**/ +#define SOCK_ERROR(Sock, Error) ((Sock)->SockError = (Error)) + +#define SOCK_SIGNATURE SIGNATURE_32 ('S', 'O', 'C', 'K') + +#define SOCK_FROM_THIS(a) CR ((a), SOCKET, NetProtocol, SOCK_SIGNATURE) + +#define SOCK_FROM_TOKEN(Token) (((SOCK_TOKEN *) (Token))->Sock) + +#define PROTO_TOKEN_FORM_SOCK(SockToken, Type) ((Type *) (((SOCK_TOKEN *) (SockToken))->Token)) + +typedef struct _TCP_SOCKET SOCKET; + +/// +/// Socket completion token +/// +typedef struct _SOCK_COMPLETION_TOKEN { + EFI_EVENT Event; ///< The event to be issued + EFI_STATUS Status; ///< The status to be issued +} SOCK_COMPLETION_TOKEN; + +typedef union { + VOID *RxData; + VOID *TxData; +} SOCK_IO_DATA; + +/// +/// The application token with data packet +/// +typedef struct _SOCK_IO_TOKEN { + SOCK_COMPLETION_TOKEN Token; + SOCK_IO_DATA Packet; +} SOCK_IO_TOKEN; + +/// +/// The socket type. +/// +typedef enum { + SockDgram, ///< This socket providing datagram service + SockStream ///< This socket providing stream service +} SOCK_TYPE; + +/// +/// 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 handler of protocol for request from socket. + + @param[in] Socket The socket issuing the request to protocol. + @param[in] Request The request issued by socket. + @param[in] RequestData The request related data. + + @retval EFI_SUCCESS The socket request is completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +typedef +EFI_STATUS +(*SOCK_PROTO_HANDLER) ( + IN SOCKET *Socket, + IN UINT8 Request, + IN VOID *RequestData + ); + +/** + The Callback funtion called after the TCP socket is created. + + @param[in] This Pointer to the socket just created. + @param[in] Context Context of the socket. + + @retval EFI_SUCCESS This protocol installed successfully. + @retval other Some error occured. + +**/ +typedef +EFI_STATUS +(*SOCK_CREATE_CALLBACK) ( + IN SOCKET *This, + IN VOID *Context + ); + +/** + The callback function called before the TCP socket is to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context. + +**/ +typedef +VOID +(*SOCK_DESTROY_CALLBACK) ( + IN SOCKET *This, + IN VOID *Context + ); + +/// +/// The initialize data for create a new socket. +/// +typedef struct _SOCK_INIT_DATA { + SOCK_TYPE Type; + UINT8 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 + UINT8 IpVersion; + VOID *Protocol; ///< The pointer to protocol function template + ///< wanted to install on socket + + // + // Callbacks after socket is created and before socket is to be destroyed. + // + SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created + SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied + VOID *Context; ///< The context of the callback + + // + // Opaque protocol data. + // + VOID *ProtoData; + UINT32 DataSize; + + SOCK_PROTO_HANDLER ProtoHandler; ///< The handler of protocol for socket request + + EFI_HANDLE DriverBinding; ///< The driver binding handle +} SOCK_INIT_DATA; + +/// +/// The union type of TCP and UDP protocol. +/// +typedef union _NET_PROTOCOL { + EFI_TCP4_PROTOCOL Tcp4Protocol; ///< Tcp4 protocol + EFI_TCP6_PROTOCOL Tcp6Protocol; ///< Tcp6 protocol +} NET_PROTOCOL; +/// +/// The socket structure representing a network service access point. +/// +struct _TCP_SOCKET { + // + // Socket description information + // + UINT32 Signature; ///< Signature of the socket + EFI_HANDLE SockHandle; ///< The virtual handle of the socket + EFI_HANDLE DriverBinding; ///< Socket's driver binding protocol + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + LIST_ENTRY Link; + UINT8 ConfigureState; + SOCK_TYPE Type; + UINT8 State; + UINT16 Flag; + EFI_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 + LIST_ENTRY ConnectionList; ///< the connections maintained by this socket + // + // The queue to buffer application's asynchronous token + // + LIST_ENTRY ListenTokenList; + LIST_ENTRY RcvTokenList; + LIST_ENTRY SndTokenList; + 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 + UINT8 IpVersion; + NET_PROTOCOL NetProtocol; ///< TCP or UDP protocol socket used + // + // Callbacks after socket is created and before socket is to be destroyed. + // + SOCK_CREATE_CALLBACK CreateCallback; ///< Callback after created + SOCK_DESTROY_CALLBACK DestroyCallback; ///< Callback before destroied + VOID *Context; ///< The context of the callback +}; + +/// +/// The token structure buffered in socket layer. +/// +typedef struct _SOCK_TOKEN { + 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 TCP driver. +/// +typedef struct _TCP_RSV_DATA { + UINT32 UrgLen; +} TCP_RSV_DATA; + +// +// Socket provided oprerations for low layer protocol implemented in SockImpl.c +// + +/** + Set the state of the socket. + + @param[in, out] Sock Pointer to the socket. + @param[in] State The new socket state to be set. + +**/ +VOID +SockSetState ( + IN OUT SOCKET *Sock, + IN UINT8 State + ); + +/** + Clone a new socket including its associated protocol control block. + + @param[in] Sock Pointer to the socket to be cloned. + + @return Pointer to the newly cloned socket. If NULL, an error condition occurred. + +**/ +SOCKET * +SockClone ( + IN SOCKET *Sock + ); + +/** + 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[in, out] Sock Pointer to the socket associated with the + established connection. + +**/ +VOID +SockConnEstablished ( + IN OUT SOCKET *Sock + ); + +/** + Called by the low layer protocol to indicate that the connection is closed. + + This function flushes the socket, sets the state to SO_CLOSED, and signals + the close token. + + @param[in, out] Sock Pointer to the socket associated with the closed + connection. + +**/ +VOID +SockConnClosed ( + IN OUT SOCKET *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 and signals the data + token, if proper. + + @param[in, out] Sock Pointer to the socket. + @param[in] Count The length of the data processed or sent, in bytes. + +**/ +VOID +SockDataSent ( + IN OUT SOCKET *Sock, + IN UINT32 Count + ); + +/** + 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[in] Sock Pointer to the socket. + @param[in] Offset The start point of the data to be copied. + @param[in] Len The length of the data to be copied. + @param[out] 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, + OUT UINT8 *Dest + ); + +/** + Called by the low layer protocol to deliver received data to socket layer. + + This function appends the data to the socket receive buffer, set the + urgent data length, then checks if any receive token can be signaled. + + @param[in, out] Sock Pointer to the socket. + @param[in, out] NetBuffer Pointer to the buffer that contains the received data. + @param[in] UrgLen The length of the urgent data in the received data. + +**/ +VOID +SockDataRcvd ( + IN OUT SOCKET *Sock, + IN OUT NET_BUF *NetBuffer, + IN UINT32 UrgLen + ); + +/** + Get the length of the free space of the specific socket buffer. + + @param[in] Sock Pointer to the socket. + @param[in] 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 + ); + +/** + Called by the low layer protocol to indicate that there will be no more data + from the communication peer. + + This function sets the socket's state to SO_NO_MORE_DATA and signals all queued + IO tokens with the error status EFI_CONNECTION_FIN. + + @param[in, out] Sock Pointer to the socket. + +**/ +VOID +SockNoMoreData ( + IN OUT SOCKET *Sock + ); + +// +// Socket provided operations for user interface implemented in SockInterface.c +// + +/** + Create a socket and its associated protocol control block + with the intial data SockInitData and protocol specific + data ProtoData. + + @param[in] SockInitData Inital data to setting the socket. + + @return Pointer to the newly created socket. If NULL, an error condition occured. + +**/ +SOCKET * +SockCreateChild ( + IN SOCK_INIT_DATA *SockInitData + ); + +/** + Destory the socket Sock and its associated protocol control block. + + @param[in, out] Sock The socket to be destroyed. + + @retval EFI_SUCCESS The socket Sock was destroyed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockDestroyChild ( + IN OUT SOCKET *Sock + ); + +/** + Configure the specific socket Sock using configuration data ConfigData. + + @param[in] Sock Pointer to the socket to be configured. + @param[in] ConfigData Pointer to the configuration data. + + @retval EFI_SUCCESS The socket 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 + ); + +/** + Initiate a connection establishment process. + + @param[in] Sock Pointer to the socket to initiate the initate the + connection. + @param[in] Token Pointer to the token used for the connection + operation. + + @retval EFI_SUCCESS The connection 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 + ); + +/** + Issue a listen token to get an existed connected network instance, + or wait for a connection if there is none. + + @param[in] Sock Pointer to the socket to accept connections. + @param[in] Token The token to accept a connection. + + @retval EFI_SUCCESS Either a connection is accepted or the Token is + buffered for further acceptance. + @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 + ); + +/** + Issue a token with data to the socket to send out. + + @param[in] Sock Pointer to the socket to process the token with + data. + @param[in] Token The token with data that needs to send out. + + @retval EFI_SUCCESS The token 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 a memory limit. + +**/ +EFI_STATUS +SockSend ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Issue a token to get data from the socket. + + @param[in] Sock Pointer to the socket to get data from. + @param[in] Token The token to store the received data from the + socket. + + @retval EFI_SUCCESS The token 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 a memory limit. + +**/ +EFI_STATUS +SockRcv ( + IN SOCKET *Sock, + IN VOID *Token + ); + +/** + Reset the socket and its associated protocol control block. + + @param[in, out] Sock Pointer to the socket to be flushed. + + @retval EFI_SUCCESS The socket flushed successfully. + @retval EFI_ACCESS_DENIED Failed to get the lock to access the socket. + +**/ +EFI_STATUS +SockFlush ( + IN OUT SOCKET *Sock + ); + +/** + Close or abort the socket associated connection. + + @param[in, out] Sock Pointer to the socket of the connection to close + or abort. + @param[in] Token The token for close operation. + @param[in] OnAbort TRUE for aborting the connection, FALSE to close it. + + @retval EFI_SUCCESS The close or abort operation 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 OUT SOCKET *Sock, + IN VOID *Token, + IN BOOLEAN OnAbort + ); + +/** + Get the mode data of the low layer protocol. + + @param[in] Sock Pointer to the socket to get mode data from. + @param[in, out] Mode Pointer to the data to store the low layer mode + information. + + @retval EFI_SUCCESS The mode data was obtained successfully. + @retval EFI_NOT_STARTED The socket is not configured. + +**/ +EFI_STATUS +SockGetMode ( + IN SOCKET *Sock, + IN OUT VOID *Mode + ); + +/** + Configure the low level protocol to join a multicast group for + this socket's connection. + + @param[in] Sock Pointer to the socket of the connection to join the + specific multicast group. + @param[in] GroupInfo Pointer to the multicast group information. + + @retval EFI_SUCCESS The configuration completed 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 + ); + +/** + Add or remove route information in IP route table associated + with this socket. + + @param[in] Sock Pointer to the socket associated with the IP route + table to operate on. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The route table 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 + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpDispatcher.c b/NetworkPkg/TcpDxe/TcpDispatcher.c new file mode 100644 index 0000000000..eaa75a4ec4 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDispatcher.c @@ -0,0 +1,861 @@ +/** @file + The implementation of a dispatch routine for processing TCP requests. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +/** + Add or remove a route entry in the IP route table associated with this TCP instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] RouteInfo Pointer to the route information to be processed. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_NO_MAPPING When using the default address, configuration(DHCP, + BOOTP, RARP, etc.) is not finished yet. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table. + @retval EFI_NOT_FOUND This route is not in the routing table + (when RouteInfo->DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table + (when RouteInfo->DeleteRoute is FALSE). +**/ +EFI_STATUS +Tcp4Route ( + IN TCP_CB *Tcb, + IN TCP4_ROUTE_INFO *RouteInfo + ) +{ + IP_IO_IP_PROTOCOL Ip; + + Ip = Tcb->IpInfo->Ip; + + ASSERT (Ip.Ip4!= NULL); + + return Ip.Ip4->Routes ( + Ip.Ip4, + RouteInfo->DeleteRoute, + RouteInfo->SubnetAddress, + RouteInfo->SubnetMask, + RouteInfo->GatewayAddress + ); + +} + +/** + Get the operational settings of this TCPv4 instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +EFI_STATUS +Tcp4GetMode ( + IN TCP_CB *Tcb, + IN OUT TCP4_MODE_DATA *Mode + ) +{ + SOCKET *Sock; + EFI_TCP4_CONFIG_DATA *ConfigData; + EFI_TCP4_ACCESS_POINT *AccessPoint; + EFI_TCP4_OPTION *Option; + EFI_IP4_PROTOCOL *Ip; + + Sock = Tcb->Sk; + + if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + if (Mode->Tcp4State != NULL) { + *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State; + } + + if (Mode->Tcp4ConfigData != NULL) { + + ConfigData = Mode->Tcp4ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TypeOfService = Tcb->Tos; + ConfigData->TimeToLive = Tcb->Ttl; + + AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr; + + CopyMem (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + + AccessPoint->SubnetMask = Tcb->SubnetMask; + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + + CopyMem (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + + AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); + AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); + + if (Option != NULL) { + Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk); + Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk); + Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk); + + Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ; + Option->DataRetries = Tcb->MaxRexmit; + Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ; + Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ; + Option->KeepAliveProbes = Tcb->MaxKeepAlive; + Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ; + Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ; + + Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)); + Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)); + Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip.Ip4; + ASSERT (Ip != NULL); + + return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + +/** + Get the operational settings of this TCPv6 instance. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] Mode Pointer to the buffer to store the operational + settings. + + @retval EFI_SUCCESS The mode data was read. + @retval EFI_NOT_STARTED No configuration data is available because this + instance hasn't been started. + +**/ +EFI_STATUS +Tcp6GetMode ( + IN TCP_CB *Tcb, + IN OUT TCP6_MODE_DATA *Mode + ) +{ + SOCKET *Sock; + EFI_TCP6_CONFIG_DATA *ConfigData; + EFI_TCP6_ACCESS_POINT *AccessPoint; + EFI_TCP6_OPTION *Option; + EFI_IP6_PROTOCOL *Ip; + + Sock = Tcb->Sk; + + if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) { + return EFI_NOT_STARTED; + } + + if (Mode->Tcp6State != NULL) { + *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State); + } + + if (Mode->Tcp6ConfigData != NULL) { + + ConfigData = Mode->Tcp6ConfigData; + AccessPoint = &(ConfigData->AccessPoint); + Option = ConfigData->ControlOption; + + ConfigData->TrafficClass = Tcb->Tos; + ConfigData->HopLimit = Tcb->Ttl; + + AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port); + AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port); + AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN); + + IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip); + IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip); + + if (Option != NULL) { + Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk); + Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk); + Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk); + + Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ; + Option->DataRetries = Tcb->MaxRexmit; + Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ; + Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ; + Option->KeepAliveProbes = Tcb->MaxKeepAlive; + Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ; + Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ; + + Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE)); + Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS)); + Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS)); + + Option->EnableSelectiveAck = FALSE; + Option->EnablePathMtuDiscovery = FALSE; + } + } + + Ip = Tcb->IpInfo->Ip.Ip6; + ASSERT (Ip != NULL); + + return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData); +} + +/** + If TcpAp->StationPort isn't zero, check whether the access point + is registered, else generate a random station port for this + access point. + + @param[in] TcpAp Pointer to the access point. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6 + + @retval EFI_SUCCESS The check passed or the port is assigned. + @retval EFI_INVALID_PARAMETER The non-zero station port is already used. + @retval EFI_OUT_OF_RESOURCES No port can be allocated. + +**/ +EFI_STATUS +TcpBind ( + IN TCP_ACCESS_POINT *TcpAp, + IN UINT8 IpVersion + ) +{ + BOOLEAN Cycle; + EFI_IP_ADDRESS Local; + UINT16 *Port; + UINT16 *RandomPort; + + if (IpVersion == IP_VERSION_4) { + CopyMem (&Local, &TcpAp->Tcp4Ap.StationAddress, sizeof (EFI_IPv4_ADDRESS)); + Port = &TcpAp->Tcp4Ap.StationPort; + RandomPort = &mTcp4RandomPort; + } else { + IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress); + Port = &TcpAp->Tcp6Ap.StationPort; + RandomPort = &mTcp6RandomPort; + } + + if (0 != *Port) { + // + // Check if a same endpoing is bound. + // + if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) { + + return EFI_INVALID_PARAMETER; + } + } else { + // + // generate a random port + // + Cycle = FALSE; + + if (TCP_PORT_USER_RESERVED == *RandomPort) { + *RandomPort = TCP_PORT_KNOWN; + } + + (*RandomPort)++; + + while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) { + (*RandomPort)++; + + if (*RandomPort <= TCP_PORT_KNOWN) { + if (Cycle) { + DEBUG ( + (EFI_D_ERROR, + "TcpBind: no port can be allocated for this pcb\n") + ); + return EFI_OUT_OF_RESOURCES; + } + + *RandomPort = TCP_PORT_KNOWN + 1; + + Cycle = TRUE; + } + } + + *Port = *RandomPort; + } + + return EFI_SUCCESS; +} + +/** + Flush the Tcb add its associated protocols. + + @param[in, out] Tcb Pointer to the TCP_CB to be flushed. + +**/ +VOID +TcpFlushPcb ( + IN OUT TCP_CB *Tcb + ) +{ + SOCKET *Sock; + TCP_PROTO_DATA *TcpProto; + + IpIoConfigIp (Tcb->IpInfo, NULL); + + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + + if (SOCK_IS_CONFIGURED (Sock)) { + RemoveEntryList (&Tcb->List); + + if (Sock->DevicePath != NULL) { + // + // Uninstall the device path protocl. + // + gBS->UninstallProtocolInterface ( + Sock->SockHandle, + &gEfiDevicePathProtocolGuid, + Sock->DevicePath + ); + + FreePool (Sock->DevicePath); + Sock->DevicePath = NULL; + } + + TcpSetVariableData (TcpProto->TcpService); + } + + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + Tcb->State = TCP_CLOSED; +} + +/** + Attach a Pcb to the socket. + + @param[in] Sk Pointer to the socket of this TCP instance. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +TcpAttachPcb ( + IN SOCKET *Sk + ) +{ + TCP_CB *Tcb; + TCP_PROTO_DATA *ProtoData; + IP_IO *IpIo; + + Tcb = AllocateZeroPool (sizeof (TCP_CB)); + + if (Tcb == NULL) { + + DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n")); + + return EFI_OUT_OF_RESOURCES; + } + + ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved; + IpIo = ProtoData->TcpService->IpIo; + + // + // Create an IpInfo for this Tcb. + // + Tcb->IpInfo = IpIoAddIp (IpIo); + if (Tcb->IpInfo == NULL) { + + FreePool (Tcb); + return EFI_OUT_OF_RESOURCES; + } + + InitializeListHead (&Tcb->List); + InitializeListHead (&Tcb->SndQue); + InitializeListHead (&Tcb->RcvQue); + + Tcb->State = TCP_CLOSED; + Tcb->Sk = Sk; + ProtoData->TcpPcb = Tcb; + + return EFI_SUCCESS; +} + +/** + Detach the Pcb of the socket. + + @param[in, out] Sk Pointer to the socket of this TCP instance. + +**/ +VOID +TcpDetachPcb ( + IN OUT SOCKET *Sk + ) +{ + TCP_PROTO_DATA *ProtoData; + TCP_CB *Tcb; + + ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + ASSERT (Tcb != NULL); + + TcpFlushPcb (Tcb); + + IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo); + + FreePool (Tcb); + + ProtoData->TcpPcb = NULL; +} + +/** + Configure the Pcb using CfgData. + + @param[in] Sk Pointer to the socket of this TCP instance. + @param[in] CfgData Pointer to the TCP configuration data. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER A same access point has been configured in + another TCP instance. + @retval EFI_OUT_OF_RESOURCES Failed due to resource limits. + +**/ +EFI_STATUS +TcpConfigurePcb ( + IN SOCKET *Sk, + IN TCP_CONFIG_DATA *CfgData + ) +{ + IP_IO_IP_CONFIG_DATA IpCfgData; + EFI_STATUS Status; + EFI_TCP4_OPTION *Option; + TCP_PROTO_DATA *TcpProto; + TCP_CB *Tcb; + TCP_ACCESS_POINT *TcpAp; + + ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL)); + + TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved; + Tcb = TcpProto->TcpPcb; + + ASSERT (Tcb != NULL); + + if (Sk->IpVersion == IP_VERSION_4) { + // + // Add Ip for send pkt to the peer + // + CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA)); + IpCfgData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + IpCfgData.Ip4CfgData.TypeOfService = CfgData->Tcp4CfgData.TypeOfService; + IpCfgData.Ip4CfgData.TimeToLive = CfgData->Tcp4CfgData.TimeToLive; + IpCfgData.Ip4CfgData.UseDefaultAddress = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress; + IpCfgData.Ip4CfgData.SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask; + IpCfgData.Ip4CfgData.ReceiveTimeout = (UINT32) (-1); + CopyMem ( + &IpCfgData.Ip4CfgData.StationAddress, + &CfgData->Tcp4CfgData.AccessPoint.StationAddress, + sizeof (EFI_IPv4_ADDRESS) + ); + + } else { + ASSERT (Sk->IpVersion == IP_VERSION_6); + + CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA)); + IpCfgData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + IpCfgData.Ip6CfgData.TrafficClass = CfgData->Tcp6CfgData.TrafficClass; + IpCfgData.Ip6CfgData.HopLimit = CfgData->Tcp6CfgData.HopLimit; + IpCfgData.Ip6CfgData.ReceiveTimeout = (UINT32) (-1); + IP6_COPY_ADDRESS ( + &IpCfgData.Ip6CfgData.StationAddress, + &CfgData->Tcp6CfgData.AccessPoint.StationAddress + ); + IP6_COPY_ADDRESS ( + &IpCfgData.Ip6CfgData.DestinationAddress, + &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress + ); + } + + // + // Configure the IP instance this Tcb consumes. + // + Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData); + if (EFI_ERROR (Status)) { + goto OnExit; + } + + if (Sk->IpVersion == IP_VERSION_4) { + // + // Get the default address information if the instance is configured to use default address. + // + CfgData->Tcp4CfgData.AccessPoint.StationAddress = IpCfgData.Ip4CfgData.StationAddress; + CfgData->Tcp4CfgData.AccessPoint.SubnetMask = IpCfgData.Ip4CfgData.SubnetMask; + + TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint; + } else { + IP6_COPY_ADDRESS ( + &CfgData->Tcp6CfgData.AccessPoint.StationAddress, + &IpCfgData.Ip6CfgData.StationAddress + ); + + TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint; + } + + // + // check if we can bind this endpoint in CfgData + // + Status = TcpBind (TcpAp, Sk->IpVersion); + + if (EFI_ERROR (Status)) { + DEBUG ( + (EFI_D_ERROR, + "TcpConfigurePcb: Bind endpoint failed with %r\n", + Status) + ); + + goto OnExit; + } + + // + // Initalize the operating information in this Tcb + // + ASSERT (Tcb->State == TCP_CLOSED && + IsListEmpty (&Tcb->SndQue) && + IsListEmpty (&Tcb->RcvQue)); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + Tcb->State = TCP_CLOSED; + + Tcb->SndMss = 536; + Tcb->RcvMss = TcpGetRcvMss (Sk); + + Tcb->SRtt = 0; + Tcb->Rto = 3 * TCP_TICK_HZ; + + Tcb->CWnd = Tcb->SndMss; + Tcb->Ssthresh = 0xffffffff; + + Tcb->CongestState = TCP_CONGEST_OPEN; + + Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN; + Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD; + Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE; + Tcb->MaxRexmit = TCP_MAX_LOSS; + Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME; + Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME; + Tcb->ConnectTimeout = TCP_CONNECT_TIME; + + if (Sk->IpVersion == IP_VERSION_4) { + // + // initialize Tcb in the light of CfgData + // + Tcb->Ttl = CfgData->Tcp4CfgData.TimeToLive; + Tcb->Tos = CfgData->Tcp4CfgData.TypeOfService; + + Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress; + + CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR)); + Tcb->LocalEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort); + Tcb->SubnetMask = CfgData->Tcp4CfgData.AccessPoint.SubnetMask; + + CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); + Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort); + + Option = CfgData->Tcp4CfgData.ControlOption; + } else { + Tcb->Ttl = CfgData->Tcp6CfgData.HopLimit; + Tcb->Tos = CfgData->Tcp6CfgData.TrafficClass; + + IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress); + Tcb->LocalEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort); + + IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress); + Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort); + + // + // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same. + // + Option = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption; + } + + if (Option != NULL) { + SET_RCV_BUFFSIZE ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_RCV_BUF_SIZE_MIN, + TCP_RCV_BUF_SIZE, + TCP_RCV_BUF_SIZE, + Option->ReceiveBufferSize + ) + ) + ); + SET_SND_BUFFSIZE ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_SND_BUF_SIZE_MIN, + TCP_SND_BUF_SIZE, + TCP_SND_BUF_SIZE, + Option->SendBufferSize + ) + ) + ); + + SET_BACKLOG ( + Sk, + (UINT32) (TCP_COMP_VAL ( + TCP_BACKLOG_MIN, + TCP_BACKLOG, + TCP_BACKLOG, + Option->MaxSynBackLog + ) + ) + ); + + Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL ( + TCP_MAX_LOSS_MIN, + TCP_MAX_LOSS, + TCP_MAX_LOSS, + Option->DataRetries + ); + Tcb->FinWait2Timeout = TCP_COMP_VAL ( + TCP_FIN_WAIT2_TIME, + TCP_FIN_WAIT2_TIME_MAX, + TCP_FIN_WAIT2_TIME, + (UINT32) (Option->FinTimeout * TCP_TICK_HZ) + ); + + if (Option->TimeWaitTimeout != 0) { + Tcb->TimeWaitTimeout = TCP_COMP_VAL ( + TCP_TIME_WAIT_TIME, + TCP_TIME_WAIT_TIME_MAX, + TCP_TIME_WAIT_TIME, + (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ) + ); + } else { + Tcb->TimeWaitTimeout = 0; + } + + if (Option->KeepAliveProbes != 0) { + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE); + + Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL ( + TCP_MAX_KEEPALIVE_MIN, + TCP_MAX_KEEPALIVE, + TCP_MAX_KEEPALIVE, + Option->KeepAliveProbes + ); + Tcb->KeepAliveIdle = TCP_COMP_VAL ( + TCP_KEEPALIVE_IDLE_MIN, + TCP_KEEPALIVE_IDLE_MAX, + TCP_KEEPALIVE_IDLE_MIN, + (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ) + ); + Tcb->KeepAlivePeriod = TCP_COMP_VAL ( + TCP_KEEPALIVE_PERIOD_MIN, + TCP_KEEPALIVE_PERIOD, + TCP_KEEPALIVE_PERIOD, + (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ) + ); + } + + Tcb->ConnectTimeout = TCP_COMP_VAL ( + TCP_CONNECT_TIME_MIN, + TCP_CONNECT_TIME, + TCP_CONNECT_TIME, + (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ) + ); + + if (!Option->EnableNagle) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE); + } + + if (!Option->EnableTimeStamp) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS); + } + + if (!Option->EnableWindowScaling) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS); + } + } + + // + // The socket is bound, the is + // determined, construct the IP device path and install it. + // + Status = TcpInstallDevicePath (Sk); + if (EFI_ERROR (Status)) { + goto OnExit; + } + + // + // update state of Tcb and socket + // + if (((Sk->IpVersion == IP_VERSION_4) && !CfgData->Tcp4CfgData.AccessPoint.ActiveFlag) || + ((Sk->IpVersion == IP_VERSION_6) && !CfgData->Tcp6CfgData.AccessPoint.ActiveFlag) + ) { + + TcpSetState (Tcb, TCP_LISTEN); + SockSetState (Sk, SO_LISTENING); + + Sk->ConfigureState = SO_CONFIGURED_PASSIVE; + } else { + + Sk->ConfigureState = SO_CONFIGURED_ACTIVE; + } + + if (Sk->IpVersion == IP_VERSION_6) { + Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK; + } + + TcpInsertTcb (Tcb); + +OnExit: + + return Status; +} + +/** + The procotol handler provided to the socket layer, which is used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param[in] Sock Pointer to the socket of this TCP instance. + @param[in] Request The code of this operation request. + @param[in] Data Pointer to the operation specific data passed in + together with the operation request. This is an + optional parameter that may be NULL. + + @retval EFI_SUCCESS The socket request completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +TcpDispatcher ( + IN SOCKET *Sock, + IN UINT8 Request, + IN VOID *Data OPTIONAL + ) +{ + TCP_CB *Tcb; + TCP_PROTO_DATA *ProtoData; + + ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved; + Tcb = ProtoData->TcpPcb; + + switch (Request) { + case SOCK_POLL: + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + ProtoData->TcpService->IpIo->Ip.Ip4->Poll (ProtoData->TcpService->IpIo->Ip.Ip4); + } else { + ProtoData->TcpService->IpIo->Ip.Ip6->Poll (ProtoData->TcpService->IpIo->Ip.Ip6); + } + + break; + + case SOCK_CONSUMED: + // + // After user received data from socket buffer, socket will + // notify TCP using this message to give it a chance to send out + // window update information + // + ASSERT (Tcb != NULL); + TcpOnAppConsume (Tcb); + break; + + case SOCK_SND: + + ASSERT (Tcb != NULL); + TcpOnAppSend (Tcb); + break; + + case SOCK_CLOSE: + + TcpOnAppClose (Tcb); + + break; + + case SOCK_ABORT: + + TcpOnAppAbort (Tcb); + + break; + + case SOCK_SNDPUSH: + Tcb->SndPsh = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH); + + break; + + case SOCK_SNDURG: + Tcb->SndUp = TcpGetMaxSndNxt (Tcb) + GET_SND_DATASIZE (Tcb->Sk) - 1; + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_URG); + + break; + + case SOCK_CONNECT: + + TcpOnAppConnect (Tcb); + + break; + + case SOCK_ATTACH: + + return TcpAttachPcb (Sock); + + break; + + case SOCK_FLUSH: + + TcpFlushPcb (Tcb); + + break; + + case SOCK_DETACH: + + TcpDetachPcb (Sock); + + break; + + case SOCK_CONFIGURE: + + return TcpConfigurePcb ( + Sock, + (TCP_CONFIG_DATA *) Data + ); + + break; + + case SOCK_MODE: + + ASSERT ((Data != NULL) && (Tcb != NULL)); + + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + + return Tcp4GetMode (Tcb, (TCP4_MODE_DATA *) Data); + } else { + + return Tcp6GetMode (Tcb, (TCP6_MODE_DATA *) Data); + } + + break; + + case SOCK_ROUTE: + + ASSERT ((Data != NULL) && (Tcb != NULL) && (Tcb->Sk->IpVersion == IP_VERSION_4)); + + return Tcp4Route (Tcb, (TCP4_ROUTE_INFO *) Data); + + default: + + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} diff --git a/NetworkPkg/TcpDxe/TcpDriver.c b/NetworkPkg/TcpDxe/TcpDriver.c new file mode 100644 index 0000000000..37e53af4b8 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDriver.c @@ -0,0 +1,891 @@ +/** @file + The driver binding and service binding protocol for the TCP driver. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +UINT16 mTcp4RandomPort; +UINT16 mTcp6RandomPort; + +TCP_HEARTBEAT_TIMER mTcpTimer = { + NULL, + 0 +}; + +EFI_TCP4_PROTOCOL gTcp4ProtocolTemplate = { + Tcp4GetModeData, + Tcp4Configure, + Tcp4Routes, + Tcp4Connect, + Tcp4Accept, + Tcp4Transmit, + Tcp4Receive, + Tcp4Close, + Tcp4Cancel, + Tcp4Poll +}; + +EFI_TCP6_PROTOCOL gTcp6ProtocolTemplate = { + Tcp6GetModeData, + Tcp6Configure, + Tcp6Connect, + Tcp6Accept, + Tcp6Transmit, + Tcp6Receive, + Tcp6Close, + Tcp6Cancel, + Tcp6Poll +}; + +SOCK_INIT_DATA mTcpDefaultSockData = { + SockStream, + SO_CLOSED, + NULL, + TCP_BACKLOG, + TCP_SND_BUF_SIZE, + TCP_RCV_BUF_SIZE, + IP_VERSION_4, + NULL, + TcpCreateSocketCallback, + TcpDestroySocketCallback, + NULL, + NULL, + 0, + TcpDispatcher, + NULL, +}; + +EFI_DRIVER_BINDING_PROTOCOL gTcpDriverBinding = { + TcpDriverBindingSupported, + TcpDriverBindingStart, + TcpDriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gTcpServiceBinding = { + TcpServiceBindingCreateChild, + TcpServiceBindingDestroyChild +}; + + +/** + Create and start the heartbeat timer for the TCP driver. + + @retval EFI_SUCCESS The timer was successfully created and started. + @retval other The timer was not created. + +**/ +EFI_STATUS +TcpCreateTimer ( + VOID + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + if (mTcpTimer.RefCnt == 0) { + + Status = gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + TcpTicking, + NULL, + &mTcpTimer.TimerEvent + ); + if (!EFI_ERROR (Status)) { + + Status = gBS->SetTimer ( + mTcpTimer.TimerEvent, + TimerPeriodic, + (UINT64) (TICKS_PER_SECOND / TCP_TICK_HZ) + ); + } + } + + if (!EFI_ERROR (Status)) { + + mTcpTimer.RefCnt++; + } + + return Status; +} + +/** + Stop and destroy the heartbeat timer for TCP driver. + +**/ +VOID +TcpDestroyTimer ( + VOID + ) +{ + ASSERT (mTcpTimer.RefCnt > 0); + + mTcpTimer.RefCnt--; + + if (mTcpTimer.RefCnt > 0) { + return; + } + + gBS->SetTimer (mTcpTimer.TimerEvent, TimerCancel, 0); + gBS->CloseEvent (mTcpTimer.TimerEvent); + mTcpTimer.TimerEvent = NULL; +} + +/** + The entry point for Tcp driver, which is used to install Tcp driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS The driver loaded. + @retval other The driver did not load. + +**/ +EFI_STATUS +EFIAPI +TcpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINT32 Seed; + + // + // Install the TCP Driver Binding Protocol + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gTcpDriverBinding, + ImageHandle, + &gTcpComponentName, + &gTcpComponentName2 + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize ISS and random port. + // + Seed = NetRandomInitSeed (); + mTcpGlobalIss = NET_RANDOM (Seed) % mTcpGlobalIss; + mTcp4RandomPort = (UINT16) (TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN)); + mTcp6RandomPort = mTcp4RandomPort; + + return EFI_SUCCESS; +} + +/** + Create a new TCP4 or TCP6 driver service binding protocol + + @param[in] Controller Controller handle of device to bind driver to. + @param[in] ImageHandle The TCP driver's image handle. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new IP6 service binding private was created. + +**/ +EFI_STATUS +TcpCreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + IN UINT8 IpVersion + ) +{ + EFI_STATUS Status; + EFI_GUID *IpServiceBindingGuid; + EFI_GUID *TcpServiceBindingGuid; + TCP_SERVICE_DATA *TcpServiceData; + IP_IO_OPEN_DATA OpenData; + + if (IpVersion == IP_VERSION_4) { + IpServiceBindingGuid = &gEfiIp4ServiceBindingProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + IpServiceBindingGuid = &gEfiIp6ServiceBindingProtocolGuid; + TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + Status = gBS->OpenProtocol ( + Controller, + TcpServiceBindingGuid, + NULL, + Image, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + Status = gBS->OpenProtocol ( + Controller, + IpServiceBindingGuid, + NULL, + Image, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Create the TCP service data. + // + TcpServiceData = AllocateZeroPool (sizeof (TCP_SERVICE_DATA)); + if (TcpServiceData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TcpServiceData->Signature = TCP_DRIVER_SIGNATURE; + TcpServiceData->ControllerHandle = Controller; + TcpServiceData->DriverBindingHandle = Image; + TcpServiceData->IpVersion = IpVersion; + CopyMem ( + &TcpServiceData->ServiceBinding, + &gTcpServiceBinding, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + TcpServiceData->IpIo = IpIoCreate (Image, Controller, IpVersion); + if (TcpServiceData->IpIo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + + InitializeListHead (&TcpServiceData->SocketList); + ZeroMem (&OpenData, sizeof (IP_IO_OPEN_DATA)); + + if (IpVersion == IP_VERSION_4) { + CopyMem ( + &OpenData.IpConfigData.Ip4CfgData, + &mIp4IoDefaultIpConfigData, + sizeof (EFI_IP4_CONFIG_DATA) + ); + OpenData.IpConfigData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + } else { + CopyMem ( + &OpenData.IpConfigData.Ip6CfgData, + &mIp6IoDefaultIpConfigData, + sizeof (EFI_IP6_CONFIG_DATA) + ); + OpenData.IpConfigData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP; + } + + OpenData.PktRcvdNotify = TcpRxCallback; + Status = IpIoOpen (TcpServiceData->IpIo, &OpenData); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = TcpCreateTimer (); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + TcpServiceBindingGuid, + &TcpServiceData->ServiceBinding, + NULL + ); + if (EFI_ERROR (Status)) { + TcpDestroyTimer (); + + goto ON_ERROR; + } + + TcpSetVariableData (TcpServiceData); + + return EFI_SUCCESS; + +ON_ERROR: + + if (TcpServiceData->IpIo != NULL) { + IpIoDestroy (TcpServiceData->IpIo); + } + + FreePool (TcpServiceData); + + return Status; +} + +/** + Destroy a TCP6 or TCP4 service binding instance. It will release all + the resources allocated by the instance. + + @param[in] Controller Controller handle of device to bind driver to. + @param[in] ImageHandle The TCP driver's image handle. + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6 + + @retval EFI_SUCCESS The resources used by the instance were cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +TcpDestroyService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + IN UINTN NumberOfChildren, + IN UINT8 IpVersion + ) +{ + EFI_HANDLE NicHandle; + EFI_GUID *IpProtocolGuid; + EFI_GUID *ServiceBindingGuid; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + TCP_SERVICE_DATA *TcpServiceData; + EFI_STATUS Status; + SOCKET *Sock; + + ASSERT ((IpVersion == IP_VERSION_4) || (IpVersion == IP_VERSION_6)); + + if (IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + NicHandle = NetLibGetNicHandle (Controller, IpProtocolGuid); + if (NicHandle == NULL) { + return EFI_NOT_FOUND; + } + + Status = gBS->OpenProtocol ( + NicHandle, + ServiceBindingGuid, + (VOID **) &ServiceBinding, + ImageHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + TcpServiceData = TCP_SERVICE_FROM_THIS (ServiceBinding); + + if (NumberOfChildren == 0) { + // + // Uninstall TCP servicebinding protocol + // + gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + ServiceBindingGuid, + ServiceBinding, + NULL + ); + + // + // Destroy the IpIO consumed by TCP driver + // + IpIoDestroy (TcpServiceData->IpIo); + + // + // Destroy the heartbeat timer. + // + TcpDestroyTimer (); + + // + // Clear the variable. + // + TcpClearVariableData (TcpServiceData); + + // + // Release the TCP service data + // + FreePool (TcpServiceData); + } else { + + while (!IsListEmpty (&TcpServiceData->SocketList)) { + Sock = NET_LIST_HEAD (&TcpServiceData->SocketList, SOCKET, Link); + + ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle); + } + } + + return EFI_SUCCESS; +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] 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 +TcpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + BOOLEAN IsTcp4Started; + + // + // Test for the Tcp4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Test for the Ip4ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + IsTcp4Started = FALSE; + } else { + IsTcp4Started = TRUE; + } + + // + // Check the Tcp6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiTcp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // Test for the Ip6ServiceBinding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + } else if (IsTcp4Started) { + return EFI_ALREADY_STARTED; + } + + return EFI_UNSUPPORTED; +} + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] 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 +TcpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Tcp4Status; + EFI_STATUS Tcp6Status; + + Tcp4Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4); + if ((Tcp4Status == EFI_ALREADY_STARTED) || (Tcp4Status == EFI_UNSUPPORTED)) { + Tcp4Status = EFI_SUCCESS; + } + + Tcp6Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6); + if ((Tcp6Status == EFI_ALREADY_STARTED) || (Tcp6Status == EFI_UNSUPPORTED)) { + Tcp6Status = EFI_SUCCESS; + } + + if (!EFI_ERROR (Tcp4Status) || !EFI_ERROR (Tcp6Status)) { + return EFI_SUCCESS; + } else if (EFI_ERROR (Tcp4Status)) { + return Tcp4Status; + } else { + return Tcp6Status; + } +} + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_STATUS Tcp4Status; + EFI_STATUS Tcp6Status; + + Tcp4Status = TcpDestroyService ( + ControllerHandle, + This->DriverBindingHandle, + NumberOfChildren, + IP_VERSION_4 + ); + + Tcp6Status = TcpDestroyService ( + ControllerHandle, + This->DriverBindingHandle, + NumberOfChildren, + IP_VERSION_6 + ); + + if (EFI_ERROR (Tcp4Status) && EFI_ERROR (Tcp6Status)) { + return EFI_DEVICE_ERROR; + } else { + return EFI_SUCCESS; + } +} + +/** + The Callback funtion called after the TCP socket was created. + + @param[in] This Pointer to the socket just created + @param[in] Context Context of the socket + + @retval EFI_SUCCESS This protocol installed successfully. + @retval other An error occured. + +**/ +EFI_STATUS +TcpCreateSocketCallback ( + IN SOCKET *This, + IN VOID *Context + ) +{ + EFI_STATUS Status; + TCP_SERVICE_DATA *TcpServiceData; + EFI_GUID *IpProtocolGuid; + VOID *Ip; + + if (This->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService; + + // + // Open the default IP protocol of IP_IO BY_DRIVER. + // + Status = gBS->OpenProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + &Ip, + TcpServiceData->DriverBindingHandle, + This->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the device path on the handle where service binding resides on. + // + Status = gBS->OpenProtocol ( + TcpServiceData->ControllerHandle, + &gEfiDevicePathProtocolGuid, + (VOID **) &This->ParentDevicePath, + TcpServiceData->DriverBindingHandle, + This->SockHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); + } else { + // + // Insert this socket into the SocketList. + // + InsertTailList (&TcpServiceData->SocketList, &This->Link); + } + + return Status; +} + +/** + The callback function called before the TCP socket was to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context of the socket. + +**/ +VOID +TcpDestroySocketCallback ( + IN SOCKET *This, + IN VOID *Context + ) +{ + TCP_SERVICE_DATA *TcpServiceData; + EFI_GUID *IpProtocolGuid; + + if (This->IpVersion == IP_VERSION_4) { + IpProtocolGuid = &gEfiIp4ProtocolGuid; + } else { + IpProtocolGuid = &gEfiIp6ProtocolGuid; + } + + TcpServiceData = ((TCP_PROTO_DATA *) This->ProtoReserved)->TcpService; + + // + // Remove this node from the list. + // + RemoveEntryList (&This->Link); + + // + // Close the device path protocol + // + gBS->CloseProtocol ( + TcpServiceData->ControllerHandle, + &gEfiDevicePathProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); + + // + // Close the IP protocol. + // + gBS->CloseProtocol ( + TcpServiceData->IpIo->ChildHandle, + IpProtocolGuid, + TcpServiceData->DriverBindingHandle, + This->SockHandle + ); +} + +/** + Creates a child handle with a set of TCP services. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + SOCKET *Sock; + TCP_SERVICE_DATA *TcpServiceData; + TCP_PROTO_DATA TcpProto; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = EFI_SUCCESS; + TcpServiceData = TCP_SERVICE_FROM_THIS (This); + TcpProto.TcpService = TcpServiceData; + TcpProto.TcpPcb = NULL; + + // + // Create a tcp instance with defualt Tcp default + // sock init data and TcpProto + // + mTcpDefaultSockData.ProtoData = &TcpProto; + mTcpDefaultSockData.DataSize = sizeof (TCP_PROTO_DATA); + mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle; + mTcpDefaultSockData.IpVersion = TcpServiceData->IpVersion; + + if (TcpServiceData->IpVersion == IP_VERSION_4) { + mTcpDefaultSockData.Protocol = &gTcp4ProtocolTemplate; + } else { + mTcpDefaultSockData.Protocol = &gTcp6ProtocolTemplate; + } + + Sock = SockCreateChild (&mTcpDefaultSockData); + if (NULL == Sock) { + DEBUG ( + (EFI_D_ERROR, + "TcpDriverBindingCreateChild: No resource to create a Tcp Child\n") + ); + + Status = EFI_OUT_OF_RESOURCES; + } else { + *ChildHandle = Sock->SockHandle; + } + + mTcpDefaultSockData.ProtoData = NULL; + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Destroys a child handle with a set of TCP services. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to be destroyed. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is not a valid UEFI Handle. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + VOID *Tcp; + SOCKET *Sock; + EFI_TPL OldTpl; + + if (NULL == This || NULL == ChildHandle) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // retrieve the Tcp4 protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp4ProtocolGuid, + &Tcp, + gTcpDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + // + // No Tcp4, try the Tcp6 protocol + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiTcp6ProtocolGuid, + &Tcp, + gTcpDriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + Status = EFI_UNSUPPORTED; + } + } + + if (!EFI_ERROR (Status)) { + // + // destroy this sock and related Tcp protocol control + // block + // + Sock = SOCK_FROM_THIS (Tcp); + + SockDestroyChild (Sock); + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/NetworkPkg/TcpDxe/TcpDriver.h b/NetworkPkg/TcpDxe/TcpDriver.h new file mode 100644 index 0000000000..9de4be617d --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDriver.h @@ -0,0 +1,230 @@ +/** @file + The prototype of driver binding and service binding protocol for TCP driver. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#ifndef _TCP_DRIVER_H_ +#define _TCP_DRIVER_H_ + +#define TCP_DRIVER_SIGNATURE SIGNATURE_32 ('T', 'C', 'P', 'D') + +#define TCP_PORT_KNOWN 1024 +#define TCP_PORT_USER_RESERVED 65535 + +typedef struct _TCP_HEARTBEAT_TIMER { + EFI_EVENT TimerEvent; + INTN RefCnt; +} TCP_HEARTBEAT_TIMER; + +typedef struct _TCP_SERVICE_DATA { + UINT32 Signature; + EFI_HANDLE ControllerHandle; + EFI_HANDLE DriverBindingHandle; + UINT8 IpVersion; + IP_IO *IpIo; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + CHAR16 *MacString; + LIST_ENTRY SocketList; +} TCP_SERVICE_DATA; + +typedef struct _TCP_PROTO_DATA { + TCP_SERVICE_DATA *TcpService; + TCP_CB *TcpPcb; +} TCP_PROTO_DATA; + +#define TCP_SERVICE_FROM_THIS(a) \ + CR ( \ + (a), \ + TCP_SERVICE_DATA, \ + ServiceBinding, \ + TCP_DRIVER_SIGNATURE \ + ) + +// +// Function prototype for the driver's entry point +// + +/** + The entry point for Tcp driver, used to install Tcp driver on the ImageHandle. + + @param[in] ImageHandle The firmware allocated handle for this driver image. + @param[in] SystemTable Pointer to the EFI system table. + + @retval EFI_SUCCESS The driver loaded. + @retval other The driver did not load. + +**/ +EFI_STATUS +EFIAPI +TcpDriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Driver Binding Protocol +// + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of the device to test. + @param[in] 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 +TcpDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS The driver was 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 +TcpDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. + @param[in] ControllerHandle A handle to the device being stopped. The handle must + support a bus specific I/O protocol for the driver + to use to stop the device. + @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. + @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +TcpDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +/** + The Callback funtion called after the TCP socket is created. + + @param[in] This Pointer to the socket just created. + @param[in] Context The context of the socket. + + @retval EFI_SUCCESS This protocol is installed successfully. + @retval other An error occured. + +**/ +EFI_STATUS +TcpCreateSocketCallback ( + IN SOCKET *This, + IN VOID *Context + ); + +/** + The callback function called before the TCP socket is to be destroyed. + + @param[in] This The TCP socket to be destroyed. + @param[in] Context The context of the socket. + +**/ +VOID +TcpDestroySocketCallback ( + IN SOCKET *This, + IN VOID *Context + ); + +// +// Function ptototypes for the ServiceBinding Prococol +// + +/** + Creates a child handle with a set of TCP services. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. + If it is NULL, then a new handle is created. + If it is a pointer to an existing UEFI handle, + then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCES The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to create + the child. + @retval other The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of TCP services. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCES The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER The child handle is not a valid UEFI Handle. + @retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle + because its services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +TcpServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpDxe.inf b/NetworkPkg/TcpDxe/TcpDxe.inf new file mode 100644 index 0000000000..67615388df --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpDxe.inf @@ -0,0 +1,83 @@ +## @file TcpDxe.inf +# Component description file for Tcp module. +# +# Copyright (c) 2009 - 2010, 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = TcpDxe + FILE_GUID = 1A7E4468-2F55-4a56-903C-01265EB7622B + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = TcpDriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources] + TcpDriver.c + SockImpl.c + SockInterface.c + TcpDispatcher.c + TcpOutput.c + TcpMain.c + SockImpl.h + TcpMisc.c + TcpProto.h + TcpOption.c + TcpInput.c + TcpFunc.h + TcpOption.h + TcpTimer.c + TcpMain.h + Socket.h + ComponentName.c + TcpIo.c + TcpDriver.h + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + + +[LibraryClasses] + BaseLib + BaseMemoryLib + DevicePathLib + DebugLib + MemoryAllocationLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DpcLib + NetLib + IpIoLib + + +[Protocols] + gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiIp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTcp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED + diff --git a/NetworkPkg/TcpDxe/TcpFunc.h b/NetworkPkg/TcpDxe/TcpFunc.h new file mode 100644 index 0000000000..c23ba94e44 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpFunc.h @@ -0,0 +1,724 @@ +/** @file + Declaration of external functions shared in TCP driver. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#ifndef _TCP_FUNC_H_ +#define _TCP_FUNC_H_ + +#include "TcpOption.h" + +#define TCP_COMP_VAL(Min, Max, Default, Val) \ + ((((Val) <= (Max)) && ((Val) >= (Min))) ? (Val) : (Default)) + +/** + Timeout handler prototype. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +typedef +VOID +(*TCP_TIMER_HANDLER) ( + IN OUT TCP_CB *Tcb + ); + +// +// Functions in TcpMisc.c +// + +/** + Initialize the Tcb locally related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpInitTcbLocal ( + IN OUT TCP_CB *Tcb + ); + +/** + Initialize the peer related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Pointer to the segment that contains the peer's intial information. + @param[in] Opt Pointer to the options announced by the peer. + +**/ +VOID +TcpInitTcbPeer ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ); + +/** + Try to find one Tcb whose equals to . + + @param[in] Addr Pointer to the IP address needs to match. + @param[in] Port The port number needs to match. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack. + IP_VERSION_6 indicates TCP is running on IP6 stack. + + + @retval TRUE The Tcb which matches the pairs exists. + @retval FALSE Otherwise + +**/ +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IP_ADDRESS *Addr, + IN TCP_PORTNO Port, + IN UINT8 Version + ); + +/** + Locate the TCP_CB related to the socket pair. + + @param[in] LocalPort The local port number. + @param[in] LocalIp The local IP address. + @param[in] RemotePort The remote port number. + @param[in] RemoteIp The remote IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + @param[in] Syn 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 EFI_IP_ADDRESS *LocalIp, + IN TCP_PORTNO RemotePort, + IN EFI_IP_ADDRESS *RemoteIp, + IN UINT8 Version, + IN BOOLEAN Syn + ); + +/** + Insert a Tcb into the proper queue. + + @param[in] Tcb Pointer to the TCP_CB to be inserted. + + @retval 0 The Tcb was inserted successfully. + @retval -1 An error condition occurred. + +**/ +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ); + +/** + Clone a TCP_CB from Tcb. + + @param[in] Tcb Pointer to the TCP_CB to be cloned. + + @return Pointer to the new cloned TCP_CB. If NULL, an error condition occurred. + +**/ +TCP_CB * +TcpCloneTcb ( + IN TCP_CB *Tcb + ); + +/** + Compute an ISS to be used by a new connection. + + @return The result ISS. + +**/ +TCP_SEQNO +TcpGetIss ( + VOID + ); + +/** + Get the local mss. + + @param[in] Sock Pointer to the socket to get mss. + + @return The mss size. + +**/ +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ); + +/** + Set the Tcb's state. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] State The state to be set. + +**/ +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ); + +/** + Compute the TCP segment's checksum. + + @param[in] Nbuf Pointer to the buffer that contains the TCP segment. + @param[in] HeadSum The checksum value of the fixed part of pseudo header. + + @return The checksum value. + +**/ +UINT16 +TcpChecksum ( + IN NET_BUF *Nbuf, + IN UINT16 HeadSum + ); + +/** + Translate the information from the head of the received TCP + segment Nbuf contains, and fill it into a TCP_SEG structure. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] 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 OUT NET_BUF *Nbuf + ); + +/** + Initialize an active connection, + + @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a + connection. + +**/ +VOID +TcpOnAppConnect ( + IN OUT TCP_CB *Tcb + ); + +/** + Application has consumed some data, check whether + to send a window update ack or a delayed ack. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppConsume ( + IN TCP_CB *Tcb + ); + +/** + Initiate the connection close procedure, called when + applications want to close the connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppClose ( + IN OUT TCP_CB *Tcb + ); + +/** + Check whether the application's newly delivered data can be sent out. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The data has been sent out successfully. + @retval -1 The Tcb is not in a state that data is permitted to + be sent out. + +**/ +INTN +TcpOnAppSend ( + IN OUT TCP_CB *Tcb + ); + +/** + Abort the connection by sending a reset segment: called + when the application wants to abort the connection. + + @param[in] Tcb Pointer to the TCP_CB of the TCP instance. + +**/ +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ); + +/** + Reset the connection related with Tcb. + + @param[in] Tcb Pointer to the TCP_CB of the connection to be reset. + +**/ +VOID +TcpResetConnection ( + IN TCP_CB *Tcb + ); + +/** + Set the Tcp variable data. + + @param[in] TcpService Tcp 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 TCP_SERVICE_DATA *TcpService + ); + +/** + Clear the variable and free the resource. + + @param[in] TcpService Tcp service data. + +**/ +VOID +TcpClearVariableData ( + IN TCP_SERVICE_DATA *TcpService + ); + +/** + Install the device path protocol on the TCP instance. + + @param[in] Sock Pointer to the socket representing the TCP instance. + + @retval EFI_SUCCESS The device path protocol installed. + @retval other Failed to install the device path protocol. + +**/ +EFI_STATUS +TcpInstallDevicePath ( + IN SOCKET *Sock + ); + + +// +// Functions in TcpOutput.c +// + +/** + Compute the sequence space left in the old receive window. + + @param[in] 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 + ); + +/** + Compute the current receive window. + + @param[in] 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 + ); + +/** + Get the maximum SndNxt. + + @param[in] 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 + ); + +/** + Compute how much data to send. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The length of the data that can be sent. If 0, no data can be sent. + +**/ +UINT32 +TcpDataToSend ( + IN TCP_CB *Tcb, + IN INTN Force + ); + +/** + Retransmit the segment from sequence Seq. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment to be retransmitted. + + @retval 0 The retransmission succeeded. + @retval -1 An error condition occurred. + +**/ +INTN +TcpRetransmit ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Seq + ); + +/** + Check whether to send data/SYN/FIN and piggyback an ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN OUT TCP_CB *Tcb, + IN INTN Force + ); + +/** + Check whether to send an ACK or delayed ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpToSendAck ( + IN OUT TCP_CB *Tcb + ); + +/** + Send an ACK immediately. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSendAck ( + IN OUT TCP_CB *Tcb + ); + +/** + Send a zero probe segment. It can be used by keepalive and zero window probe. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other An error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN OUT TCP_CB *Tcb + ); + +/** + Send a RESET segment in response to the segment received. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance, may be NULL. + @param[in] Head TCP header of the segment that triggers the reset. + @param[in] Len Length of the segment that triggers the reset. + @param[in] Local Local IP address. + @param[in] Remote Remote peer's IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + @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 EFI_IP_ADDRESS *Local, + IN EFI_IP_ADDRESS *Remote, + IN UINT8 Version + ); + +/** + Verify that the segment is in good shape. + + @param[in] 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 + ); + +// +// Functions from TcpInput.c +// + +/** + Process the received ICMP error messages for TCP. + + @param[in] Nbuf Buffer that contains part of the TCP segment without IP header + truncated from the ICMP error packet. + @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet. + @param[in] Src Source address of the ICMP error message. + @param[in] Dst Destination address of the ICMP error message. + @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates + IP6 stack. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN UINT8 IcmpErr, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ); + +/** + Process the received TCP segments. + + @param[in] Nbuf Buffer that contains received TCP segment without an IP header. + @param[in] Src Source address of the segment, or the peer's IP address. + @param[in] Dst Destination address of the segment, or the local end's IP + address. + @param[in] Version IP_VERSION_4 indicates IP4 stack, IP_VERSION_6 indicates + IP6 stack. + + @retval 0 The segment 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 EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ); + +// +// Functions in TcpTimer.c +// + +/** + Close the TCP connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClose ( + IN OUT TCP_CB *Tcb + ); + +/** + Heart beat timer handler, queues the DPC at TPL_CALLBACK. + + @param[in] Event Timer event signaled, ignored. + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Enable a TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be enabled. + @param[in] TimeOut The timeout value of this timer. + +**/ +VOID +TcpSetTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ); + +/** + Clear one TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be cleared. + +**/ +VOID +TcpClearTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer + ); + +/** + Clear all TCP timers. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClearAllTimer ( + IN OUT TCP_CB *Tcb + ); + +/** + Enable the window prober timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetProbeTimer ( + IN OUT TCP_CB *Tcb + ); + +/** + Enable the keepalive timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN OUT TCP_CB *Tcb + ); + +// +// Functions in TcpIo.c +// + +/** + Packet receive callback function provided to IP_IO. Used to call + the proper function to handle the packet received by IP. + + @param[in] Status Result of the receive request. + @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR. + @param[in] NetSession The IP session for the received packet. + @param[in] Pkt Packet received. + @param[in] Context The data provided by the user for the received packet when + the callback is registered in IP_IO_OPEN_DATA::RcvdContext. + This is an optional parameter that may be NULL. + +**/ +VOID +EFIAPI +TcpRxCallback ( + IN EFI_STATUS Status, + IN UINT8 IcmpErr, + IN EFI_NET_SESSION_DATA *NetSession, + IN NET_BUF *Pkt, + IN VOID *Context OPTIONAL + ); + +/** + Send the segment to IP via IpIo function. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the TCP segment to be sent. + @param[in] Src Source address of the TCP segment. + @param[in] Dest Destination address of the TCP segment. + @param[in] Version IP_VERSION_4 or IP_VERSION_6 + + @retval 0 The segment was sent out successfully. + @retval -1 The segment failed to be sent. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dest, + IN UINT8 Version + ); + +/** + Refresh the remote peer's Neighbor Cache State if already exists. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Neighbor Source address of the TCP segment. + @param[in] Timeout Time in 100-ns units that this entry will remain + in the neighbor cache. A value of zero means that + the entry is permanent. A value of non-zero means + that the entry is dynamic and will be deleted + after Timeout. + + @retval EFI_SUCCESS Successfully updated the neighbor relationship. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_NOT_FOUND This entry is not in the neighbor table. + +**/ +EFI_STATUS +Tcp6RefreshNeighbor ( + IN TCP_CB *Tcb, + IN EFI_IP_ADDRESS *Neighbor, + IN UINT32 Timeout + ); + +// +// Functions in TcpDispatcher.c +// + +/** + The procotol handler provided to the socket layer, used to + dispatch the socket level requests by calling the corresponding + TCP layer functions. + + @param[in] Sock Pointer to the socket of this TCP instance. + @param[in] Request The code of this operation request. + @param[in] Data Pointer to the operation specific data passed in + together with the operation request. This is an + optional parameter that may be NULL. + + @retval EFI_SUCCESS The socket request completed successfully. + @retval other The error status returned by the corresponding TCP + layer function. + +**/ +EFI_STATUS +TcpDispatcher ( + IN SOCKET *Sock, + IN UINT8 Request, + IN VOID *Data OPTIONAL + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpInput.c b/NetworkPkg/TcpDxe/TcpInput.c new file mode 100644 index 0000000000..e63469adb9 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpInput.c @@ -0,0 +1,1592 @@ +/** @file + TCP input process routines. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +/** + Check whether the sequence number of the incoming segment is acceptable. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Pointer to the incoming segment. + + @retval 1 The sequence number is acceptable. + @retval 0 The sequence number is not acceptable. + +**/ +INTN +TcpSeqAcceptable ( + IN TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + return (TCP_SEQ_LEQ (Tcb->RcvWl2, Seg->End) && + TCP_SEQ_LT (Seg->Seq, Tcb->RcvWl2 + Tcb->RcvWnd)); +} + +/** + NewReno fast recovery defined in RFC3782. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Segment that triggers the fast recovery. + +**/ +VOID +TcpFastRecover ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg + ) +{ + UINT32 FlightSize; + UINT32 Acked; + + // + // Step 1: Three duplicate ACKs and not in fast recovery + // + if (Tcb->CongestState != TCP_CONGEST_RECOVER) { + + // + // Step 1A: Invoking fast retransmission. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + + Tcb->Ssthresh = MAX (FlightSize >> 1, (UINT32) (2 * Tcb->SndMss)); + Tcb->Recover = Tcb->SndNxt; + + Tcb->CongestState = TCP_CONGEST_RECOVER; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + + // + // Step 2: Entering fast retransmission + // + TcpRetransmit (Tcb, Tcb->SndUna); + Tcb->CWnd = Tcb->Ssthresh + 3 * Tcb->SndMss; + + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: enter fast retransmission for TCB %p, recover point is %d\n", + Tcb, + Tcb->Recover) + ); + return; + } + + // + // 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; + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received another duplicated ACK (%d) for TCB %p\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 = MIN (Tcb->Ssthresh, FlightSize + Tcb->SndMss); + + Tcb->CongestState = TCP_CONGEST_OPEN; + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received a full ACK(%d) for TCB %p, 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; + + DEBUG ( + (EFI_D_INFO, + "TcpFastRecover: received a partial ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } + } +} + +/** + NewReno fast loss recovery defined in RFC3792. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Segment that triggers the fast loss recovery. + +**/ +VOID +TcpFastLossRecover ( + IN OUT 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; + + DEBUG ( + (EFI_D_INFO, + "TcpFastLossRecover: received a full ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + + } else { + + // + // Partial ACK: + // fast retransmit the first unacknowledge field. + // + TcpRetransmit (Tcb, Seg->Ack); + DEBUG ( + (EFI_D_INFO, + "TcpFastLossRecover: received a partial ACK(%d) for TCB %p\n", + Seg->Ack, + Tcb) + ); + } + } +} + +/** + Compute the RTT as specified in RFC2988. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Measure Currently measured RTT in heartbeats. + +**/ +VOID +TcpComputeRtt ( + IN OUT 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 + 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; + + } + + DEBUG ( + (EFI_D_INFO, + "TcpComputeRtt: new RTT for TCB %p 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[in] Nbuf The buffer that contains a received TCP segment without an IP header. + @param[in] Left The sequence number of the window's left edge. + @param[in] Right The sequence number of the window's right edge. + +**/ +VOID +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; + } + + // + // 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 != 0) { + 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 != 0) { + NetbufTrim (Nbuf, Drop, NET_BUF_TAIL); + } + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); +} + +/** + Trim off the data outside the tcb's receive window. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the NET_BUF containing the received tcp segment. + +**/ +VOID +TcpTrimInWnd ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd); +} + +/** + Process the data and FIN flag, and check whether to deliver + data to the socket layer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 No error occurred to deliver data. + @retval -1 An error condition occurred. The proper response is to reset the + connection. + +**/ +INTN +TcpDeliverData ( + IN OUT TCP_CB *Tcb + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + TCP_SEG *Seg; + UINT32 Urgent; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + // + // make sure there is some data queued, + // and TCP is in a proper state + // + if (IsListEmpty (&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) != 0); + ASSERT (Nbuf->Tcp == NULL); + + if (TCP_SEQ_GT (Seg->Seq, Seq)) { + break; + } + + Entry = Entry->ForwardLink; + Seq = Seg->End; + Tcb->RcvNxt = Seq; + + RemoveEntryList (&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 (!IsListEmpty (&Tcb->RcvQue)) { + DEBUG ( + (EFI_D_ERROR, + "TcpDeliverData: data received after FIN from peer of TCB %p, reset connection\n", + Tcb) + ); + + NetbufFree (Nbuf); + return -1; + } + + DEBUG ( + (EFI_D_INFO, + "TcpDeliverData: processing FIN from peer of TCB %p\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 { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\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; + default: + 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 != 0) { + 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[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer containing the data to be queued. + +**/ +VOID +TcpQueueData ( + IN OUT TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + LIST_ENTRY *Head; + LIST_ENTRY *Prev; + LIST_ENTRY *Cur; + NET_BUF *Node; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (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 (IsListEmpty (Head)) { + + InsertTailList (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); + } + } + + InsertHeadList (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; + + RemoveEntryList (&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)) { + + RemoveEntryList (&Nbuf->List); + NetbufFree (Nbuf); + return; + } + + TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq); + break; + } + + Cur = Cur->ForwardLink; + } +} + + +/** + Adjust the send queue or the retransmit queue. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Ack The acknowledge seuqence number of the received segment. + +**/ +VOID +TcpAdjustSndQue ( + IN TCP_CB *Tcb, + IN TCP_SEQNO Ack + ) +{ + LIST_ENTRY *Head; + 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; + + RemoveEntryList (&Node->List); + NetbufFree (Node); + continue; + } + + TcpTrimSegment (Node, Ack, Seg->End); + break; + } +} + +/** + Process the received TCP segments. + + @param[in] Nbuf Buffer that contains received a TCP segment without an IP header. + @param[in] Src Source address of the segment, or the peer's IP address. + @param[in] Dst Destination address of the segment, or the local end's IP + address. + @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates + IP6 stack. + + @retval 0 Segment processed successfully. It is either accepted or + discarded. However, no connection is reset by the segment. + @retval -1 A connection is reset by the segment. + +**/ +INTN +TcpInput ( + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ) +{ + TCP_CB *Tcb; + TCP_CB *Parent; + TCP_OPTION Option; + TCP_HEAD *Head; + INT32 Len; + TCP_SEG *Seg; + TCP_SEQNO Right; + TCP_SEQNO Urg; + UINT16 Checksum; + + ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6)); + + NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE); + + Parent = NULL; + Tcb = NULL; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != NULL); + Len = Nbuf->TotalSize - (Head->HeadLen << 2); + + if ((Head->HeadLen < 5) || (Len < 0)) { + + DEBUG ((EFI_D_INFO, "TcpInput: received an mal-formated packet\n")); + goto DISCARD; + } + + if (Version == IP_VERSION_4) { + Checksum = NetPseudoHeadChecksum (Src->Addr[0], Dst->Addr[0], 6, 0); + } else { + Checksum = NetIp6PseudoHeadChecksum (&Src->v6, &Dst->v6, 6, 0); + } + + Checksum = TcpChecksum (Nbuf, Checksum); + + if (Checksum != 0) { + DEBUG ((EFI_D_ERROR, "TcpInput: received a checksum error 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, + Version, + (BOOLEAN) TCP_FLG_ON (Head->Flag, TCP_FLG_SYN) + ); + + if ((Tcb == NULL) || (Tcb->State == TCP_CLOSED)) { + DEBUG ((EFI_D_INFO, "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) { + DEBUG ( + (EFI_D_ERROR, + "TcpInput: reset the peer because of mal-format option for Tcb %p\n", + Tcb) + ); + + goto SEND_RESET; + } + + // + // From now on, the segment is headless + // + NetbufTrim (Nbuf, (Head->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + // + // Process the segment in LISTEN state. + // + if (Tcb->State == TCP_LISTEN) { + // + // First step: Check RST + // + if (TCP_FLG_ON (Seg->Flag, TCP_FLG_RST)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard a reset segment for TCB %p 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)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of segment with ACK for TCB %p 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) { + DEBUG ( + (EFI_D_ERROR, + "TcpInput: discard a segment because failed to clone a child for TCB%p\n", + Tcb) + ); + + goto DISCARD; + } + + DEBUG ( + (EFI_D_INFO, + "TcpInput: create a child for TCB %p in listening\n", + Tcb) + ); + + // + // init the TCB structure + // + IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, Dst); + IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, Src); + Tcb->LocalEnd.Port = Head->DstPort; + 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)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of wrong ACK received for TCB %p 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)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset by peer for TCB %p in SYN_SENT\n", + Tcb) + ); + + SOCK_ERROR (Tcb->Sk, EFI_CONNECTION_RESET); + goto DROP_CONNECTION; + } else { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard a reset segment because of no ACK for TCB %p 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); + + DEBUG ( + (EFI_D_INFO, + "TcpInput: connection established for TCB %p 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); + + DEBUG ( + (EFI_D_WARN, + "TcpInput: simultanous open for TCB %p in SYN_SENT\n", + Tcb) + ); + + goto StepSix; + } + } + + goto DISCARD; + } + + // + // Process segment in SYN_RCVD or TCP_CONNECTED states + // + + // + // Clear probe timer since the RecvWindow is opened. + // + if (Tcb->ProbeTimerOn && (Seg->Wnd != 0)) { + TcpClearTimer (Tcb, TCP_TIMER_PROBE); + Tcb->ProbeTimerOn = FALSE; + } + + // + // First step: Check whether SEG.SEQ is acceptable + // + if (TcpSeqAcceptable (Tcb, Seg) == 0) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: sequence acceptance test failed for segment of TCB %p\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)) { + + DEBUG ((EFI_D_WARN, "TcpInput: connection reset for TCB %p\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 { + } + + 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)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because received extra SYN for TCB %p\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)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: segment discard because of no ACK for connected TCB %p\n", + Tcb) + ); + + goto DISCARD; + } else { + if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick == 0) { + Tcp6RefreshNeighbor (Tcb, Src, TCP6_KEEP_NEIGHBOR_TIME * TICKS_PER_SECOND); + Tcb->Tick = TCP6_REFRESH_NEIGHBOR_TICK; + } + } + + 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 = MAX (Tcb->SndWnd, Tcb->SndWndMax); + Tcb->SndWl1 = Seg->Seq; + Tcb->SndWl2 = Seg->Ack; + TcpSetState (Tcb, TCP_ESTABLISHED); + + TcpClearTimer (Tcb, TCP_TIMER_CONNECT); + TcpDeliverData (Tcb); + + DEBUG ( + (EFI_D_INFO, + "TcpInput: connection established for TCB %p in SYN_RCVD\n", + Tcb) + ); + + // + // Continue the process as ESTABLISHED state + // + } else { + DEBUG ( + (EFI_D_WARN, + "TcpInput: send reset because of wrong ACK for TCB %p in SYN_RCVD\n", + Tcb) + ); + + goto SEND_RESET; + } + } + + if (TCP_SEQ_LT (Seg->Ack, Tcb->SndUna)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: ignore the out-of-data ACK for connected TCB %p\n", + Tcb) + ); + + goto StepSix; + + } else if (TCP_SEQ_GT (Seg->Ack, Tcb->SndNxt)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: discard segment for future ACK for connected TCB %p\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 += MAX (Tcb->SndMss * Tcb->SndMss / Tcb->CWnd, 1); + } + + Tcb->CWnd = 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; + } + + DEBUG ( + (EFI_D_WARN, + "TcpInput: peer shrinks the window for connected TCB %p\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 = 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)) { + + DEBUG ( + (EFI_D_INFO, + "TcpInput: local FIN is ACKed by peer for connected TCB %p\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 { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\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 { + + DEBUG ( + (EFI_D_WARN, + "Connection closed immediately because app disables TIME_WAIT timer for %p\n", + Tcb) + ); + + TcpClose (Tcb); + } + break; + + default: + 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_FLG_ON (Seg->Flag, TCP_FLG_URG) && !TCP_FIN_RCVD (Tcb->State)) { + + DEBUG ( + (EFI_D_INFO, + "TcpInput: received urgent data from peer for connected TCB %p\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)) { + + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because data is lost for connected TCB %p\n", + Tcb) + ); + + goto RESET_THEN_DROP; + } + + if (TCP_LOCAL_CLOSED (Tcb->State) && (Nbuf->TotalSize != 0)) { + DEBUG ( + (EFI_D_WARN, + "TcpInput: connection reset because data is lost for connected TCB %p\n", + Tcb) + ); + + goto RESET_THEN_DROP; + } + + TcpQueueData (Tcb, Nbuf); + if (TcpDeliverData (Tcb) == -1) { + goto RESET_THEN_DROP; + } + + if (!IsListEmpty (&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 != NULL) { + Tcb->Parent = Parent; + TcpInsertTcb (Tcb); + } + + if ((Tcb->State != TCP_CLOSED) && + (TcpToSendData (Tcb, 0) == 0) && + (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) || (Nbuf->TotalSize != 0)) + ) { + + TcpToSendAck (Tcb); + } + + NetbufFree (Nbuf); + return 0; + +RESET_THEN_DROP: + TcpSendReset (Tcb, Head, Len, Dst, Src, Version); + +DROP_CONNECTION: + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + NetbufFree (Nbuf); + TcpClose (Tcb); + + return -1; + +SEND_RESET: + + TcpSendReset (Tcb, Head, Len, Dst, Src, Version); + +DISCARD: + + // + // Tcb is a child of Parent, and it doesn't survive + // + DEBUG ((EFI_D_WARN, "TcpInput: Discard a packet\n")); + NetbufFree (Nbuf); + + if ((Parent != NULL) && (Tcb != NULL)) { + + ASSERT (Tcb->Sk != NULL); + TcpClose (Tcb); + } + + return 0; +} + +/** + Process the received ICMP error messages for TCP. + + @param[in] Nbuf The buffer that contains part of the TCP segment without an IP header + truncated from the ICMP error packet. + @param[in] IcmpErr The ICMP error code interpreted from an ICMP error packet. + @param[in] Src Source address of the ICMP error message. + @param[in] Dst Destination address of the ICMP error message. + @param[in] Version IP_VERSION_4 indicates IP4 stack. IP_VERSION_6 indicates + IP6 stack. + +**/ +VOID +TcpIcmpInput ( + IN NET_BUF *Nbuf, + IN UINT8 IcmpErr, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dst, + IN UINT8 Version + ) +{ + TCP_HEAD *Head; + TCP_CB *Tcb; + TCP_SEQNO Seq; + EFI_STATUS IcmpErrStatus; + BOOLEAN IcmpErrIsHard; + BOOLEAN IcmpErrNotify; + + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != NULL); + + Tcb = TcpLocateTcb ( + Head->DstPort, + Dst, + Head->SrcPort, + Src, + Version, + 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, Tcb->Sk->IpVersion, &IcmpErrIsHard, &IcmpErrNotify); + + if (IcmpErrNotify) { + + SOCK_ERROR (Tcb->Sk, IcmpErrStatus); + } + + if (IcmpErrIsHard) { + + TcpClose (Tcb); + } + +CLEAN_EXIT: + + NetbufFree (Nbuf); +} diff --git a/NetworkPkg/TcpDxe/TcpIo.c b/NetworkPkg/TcpDxe/TcpIo.c new file mode 100644 index 0000000000..cecb6d19c5 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpIo.c @@ -0,0 +1,190 @@ +/** @file + Implementation of I/O interfaces between TCP and IpIoLib. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +/** + Packet receive callback function provided to IP_IO, used to call + the proper function to handle the packet received by IP. + + @param[in] Status Result of the receive request. + @param[in] IcmpErr Valid when Status is EFI_ICMP_ERROR. + @param[in] NetSession The IP session for the received packet. + @param[in] Pkt Packet received. + @param[in] Context The data provided by the user for the received packet when + the callback is registered in IP_IO_OPEN_DATA::RcvdContext. + This is an optional parameter that may be NULL. + +**/ +VOID +EFIAPI +TcpRxCallback ( + IN EFI_STATUS Status, + IN UINT8 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, NetSession->IpVersion); + } else { + TcpIcmpInput ( + Pkt, + IcmpErr, + &NetSession->Source, + &NetSession->Dest, + NetSession->IpVersion + ); + } +} + +/** + Send the segment to IP via IpIo function. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the TCP segment to be sent. + @param[in] Src Source address of the TCP segment. + @param[in] Dest Destination address of the TCP segment. + @param[in] Version IP_VERSION_4 or IP_VERSION_6 + + @retval 0 The segment was sent out successfully. + @retval -1 The segment failed to send. + +**/ +INTN +TcpSendIpPacket ( + IN TCP_CB *Tcb, + IN NET_BUF *Nbuf, + IN EFI_IP_ADDRESS *Src, + IN EFI_IP_ADDRESS *Dest, + IN UINT8 Version + ) +{ + EFI_STATUS Status; + IP_IO *IpIo; + IP_IO_OVERRIDE Override; + SOCKET *Sock; + VOID *IpSender; + TCP_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + + IpIo = NULL; + IpSender = IpIoFindSender (&IpIo, Version, Src); + + if (IpSender == NULL) { + DEBUG ((EFI_D_WARN, "TcpSendIpPacket: No appropriate IpSender.\n")); + return -1; + } + + if (Version == IP_VERSION_6) { + // + // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the + // destination address if the dest is already specified through the + // configuration data. Here we get the IpIo we need and use the default IP + // instance in this IpIo to send the packet. The dest address is configured + // to be the unspecified address for the default IP instance. + // + IpSender = NULL; + } + } else { + + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + IpSender = Tcb->IpInfo; + + if (Version == IP_VERSION_6) { + // + // It's IPv6 and this TCP segment belongs to a solid TCB, in such case + // the destination address can't be overridden, so reset the Dest to NULL. + // + Dest = NULL; + } + } + + ASSERT (Version == IpIo->IpVersion); + + if (Version == IP_VERSION_4) { + Override.Ip4OverrideData.TypeOfService = 0; + Override.Ip4OverrideData.TimeToLive = 255; + Override.Ip4OverrideData.DoNotFragment = FALSE; + Override.Ip4OverrideData.Protocol = EFI_IP_PROTO_TCP; + ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS)); + } else { + Override.Ip6OverrideData.Protocol = EFI_IP_PROTO_TCP; + Override.Ip6OverrideData.HopLimit = 255; + Override.Ip6OverrideData.FlowLabel = 0; + } + + Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override); + + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "TcpSendIpPacket: return %r error\n", Status)); + return -1; + } + + return 0; +} + +/** + Refresh the remote peer's Neighbor Cache State if already exists. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Neighbor Source address of the TCP segment. + @param[in] Timeout Time in 100-ns units that this entry will remain + in the neighbor cache. A value of zero means that + the entry is permanent. A value of non-zero means + that the entry is dynamic and will be deleted + after Timeout. + + @retval EFI_SUCCESS Successfully updated the neighbor relationship. + @retval EFI_NOT_STARTED The IpIo is not configured. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_NOT_FOUND This entry is not in the neighbor table. + +**/ +EFI_STATUS +Tcp6RefreshNeighbor ( + IN TCP_CB *Tcb, + IN EFI_IP_ADDRESS *Neighbor, + IN UINT32 Timeout + ) +{ + IP_IO *IpIo; + SOCKET *Sock; + TCP_PROTO_DATA *TcpProto; + + if (NULL == Tcb) { + IpIo = NULL; + IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor); + + if (IpIo == NULL) { + DEBUG ((EFI_D_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n")); + return EFI_NOT_STARTED; + } + + } else { + Sock = Tcb->Sk; + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + IpIo = TcpProto->TcpService->IpIo; + } + + return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout); +} + diff --git a/NetworkPkg/TcpDxe/TcpMain.c b/NetworkPkg/TcpDxe/TcpMain.c new file mode 100644 index 0000000000..55a6d5b7d2 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpMain.c @@ -0,0 +1,1074 @@ +/** @file + Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +/** + Check the integrity of the data buffer. + + @param[in] DataLen The total length of the data buffer. + @param[in] FragmentCount The fragment count of the fragment table. + @param[in] FragmentTable Pointer to the fragment table of the data + buffer. + + @retval EFI_SUCCESS The integrity check passed. + @retval EFI_INVALID_PARAMETER The integrity check failed. + +**/ +EFI_STATUS +TcpChkDataBuf ( + 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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[out] Tcp4State Pointer to the buffer to receive the current TCP + state. Optional parameter that may be NULL. + @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. Optional parameter that may be NULL. + @param[out] Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. Optional parameter that may be NULL. + @param[out] MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + @param[out] SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + + @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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] TcpConfigData Pointer to the configure data to configure the + instance. Optional parameter that may be NULL. + + @retval EFI_SUCCESS The operational settings were 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; + IP4_ADDR Ip; + IP4_ADDR SubnetMask; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != TcpConfigData) { + + CopyMem (&Ip, &TcpConfigData->AccessPoint.RemoteAddress, sizeof (IP4_ADDR)); + if ((Ip != 0) && !NetIp4IsUnicast (NTOHL (Ip), 0)) { + return EFI_INVALID_PARAMETER; + } + + if (TcpConfigData->AccessPoint.ActiveFlag && (0 == TcpConfigData->AccessPoint.RemotePort || (Ip == 0))) { + return EFI_INVALID_PARAMETER; + } + + if (!TcpConfigData->AccessPoint.UseDefaultAddress) { + + CopyMem (&Ip, &TcpConfigData->AccessPoint.StationAddress, sizeof (IP4_ADDR)); + CopyMem (&SubnetMask, &TcpConfigData->AccessPoint.SubnetMask, sizeof (IP4_ADDR)); + if (!NetIp4IsUnicast (NTOHL (Ip), 0) || !IP4_IS_VALID_NETMASK (NTOHL (SubnetMask))) { + 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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param[in] SubnetAddress The destination network. + @param[in] SubnetMask The subnet mask for the destination network. + @param[in] 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 non-blocking TCP connection request for an active TCP instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when + the TCP three way handshake finishes. + + @retval EFI_SUCCESS The connection request 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 resources 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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when + operation finishes. + + @retval EFI_SUCCESS The listen token was 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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] 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 a + 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 = TcpChkDataBuf ( + 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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] 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 = TcpChkDataBuf ( + 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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] 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 resources to finish the + operation. + @retval EFI_DEVICE_ERROR Any unexpected category error not belonging to those + listed above. + +**/ +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[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by the four + functions listed above will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in the 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[in] 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; +} + +/** + Get the current operational status. + + The GetModeData() function copies the current operational settings of this EFI TCPv6 + Protocol instance into user-supplied buffers. This function can also be used to retrieve + the operational setting of underlying drivers such as IPv6, MNP, or SNP. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[out] Tcp6State The buffer in which the current TCP state is + returned. Optional parameter that may be NULL. + @param[out] Tcp6ConfigData The buffer in which the current TCP configuration + is returned. Optional parameter that may be NULL. + @param[out] Ip6ModeData The buffer in which the current IPv6 configuration + data used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] MnpConfigData The buffer in which the current MNP configuration + data indirectly used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] SnpModeData The buffer in which the current SNP mode data + indirectly used by the TCP instance is returned. + Optional parameter that may be NULL. + + @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 +Tcp6GetModeData ( + IN EFI_TCP6_PROTOCOL *This, + OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL, + OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + TCP6_MODE_DATA TcpMode; + SOCKET *Sock; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + Sock = SOCK_FROM_THIS (This); + + TcpMode.Tcp6State = Tcp6State; + TcpMode.Tcp6ConfigData = Tcp6ConfigData; + TcpMode.Ip6ModeData = Ip6ModeData; + TcpMode.MnpConfigData = MnpConfigData; + TcpMode.SnpModeData = SnpModeData; + + return SockGetMode (Sock, &TcpMode); +} + +/** + Initialize or brutally reset the operational parameters for this EFI TCPv6 instance. + + The Configure() function does the following: + - Initialize this TCP instance, i.e., initialize the communication end settings and + specify active open or passive open for an instance. + - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush + transmission and receiving buffer directly without informing the communication peer. + + No other TCPv6 Protocol operation except Poll() can be executed by this instance until + it is configured properly. For an active TCP instance, after a proper configuration it + may call Connect() to initiate a three-way handshake. For a passive TCP instance, + its state transits to Tcp6StateListen after configuration, and Accept() may be + called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL, + the instance is reset. The resetting process will be done brutally, the state machine will + be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed, + and no traffic is allowed through this instance. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance. + If Tcp6ConfigData is set to NULL, the instance is reset. + + @retval EFI_SUCCESS The operational settings were set, changed, or reset + successfully. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for + use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE: + - This is NULL. + - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor + one of the configured IP addresses in the underlying IPv6 driver. + - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast + IPv6 address. + - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or + Tcp6ConfigData->AccessPoint.RemotePort is zero when + Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE. + - A same access point has been configured in other TCP + instance properly. + @retval EFI_ACCESS_DENIED Configuring a TCP instance when it is configured without + calling Configure() with NULL to reset it. + @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 when + executing Configure(). + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Configure ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL + ) +{ + EFI_TCP6_OPTION *Option; + SOCKET *Sock; + EFI_STATUS Status; + EFI_IPv6_ADDRESS *Ip; + + if (NULL == This) { + return EFI_INVALID_PARAMETER; + } + + // + // Tcp protocol related parameter check will be conducted here + // + if (NULL != Tcp6ConfigData) { + + Ip = &Tcp6ConfigData->AccessPoint.RemoteAddress; + if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) { + return EFI_INVALID_PARAMETER; + } + + if (Tcp6ConfigData->AccessPoint.ActiveFlag && + (0 == Tcp6ConfigData->AccessPoint.RemotePort || NetIp6IsUnspecifiedAddr (Ip)) + ) { + return EFI_INVALID_PARAMETER; + } + + Ip = &Tcp6ConfigData->AccessPoint.StationAddress; + if (!NetIp6IsUnspecifiedAddr (Ip) && !NetIp6IsValidUnicast (Ip)) { + return EFI_INVALID_PARAMETER; + } + + Option = Tcp6ConfigData->ControlOption; + if ((NULL != Option) && (Option->EnableSelectiveAck || Option->EnablePathMtuDiscovery)) { + return EFI_UNSUPPORTED; + } + } + + Sock = SOCK_FROM_THIS (This); + + if (NULL == Tcp6ConfigData) { + return SockFlush (Sock); + } + + Status = SockConfigure (Sock, Tcp6ConfigData); + + if (EFI_NO_MAPPING == Status) { + Sock->ConfigureState = SO_NO_MAPPING; + } + + return Status; +} + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + The Connect() function will initiate an active open to the remote peer configured + in a current TCP instance if it is configured active. If the connection succeeds or + fails due to any error, the ConnectionToken->CompletionToken.Event will be signaled + and ConnectionToken->CompletionToken.Status will be updated accordingly. This + function can only be called for the TCP instance in the Tcp6StateClosed state. The + instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS. + If a TCP three-way handshake succeeds, its state will become Tcp6StateEstablished. + Otherwise, the state will return to Tcp6StateClosed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when the TCP three + way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully initiated and the state of + this TCP instance has been changed to Tcp6StateSynSent. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - This instance is not configured as an active one. + - This instance is not in Tcp6StateClosed state. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ConnectionToken is NULL. + - ConnectionToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Connect ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_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. This is a + nonblocking operation. + + The Accept() function initiates an asynchronous accept request to wait for an incoming + connection on the passive TCP instance. If a remote peer successfully establishes a + connection with this instance, a new TCP instance will be created and its handle will + be returned in ListenToken->NewChildHandle. The newly created instance is configured + by inheriting the passive instance's configuration and is ready for use upon return. + The new instance is in the Tcp6StateEstablished state. + + The ListenToken->CompletionToken.Event will be signaled when a new connection is + accepted, when a user aborts the listen or when a connection is reset. + + This function only can be called when a current TCP instance is in Tcp6StateListen state. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when operation finishes. + + + @retval EFI_SUCCESS The listen token queued successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - This instance is not a passive instance. + - This instance is not in Tcp6StateListen state. + - The same listen token has already existed in the listen + token queue of this TCP instance. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ListenToken is NULL. + - ListentToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to a category listed above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Accept ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_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. + + The Transmit() function queues a sending request to this TCP instance along with the + user data. The status of the token is updated and the event in the token will be + signaled once the data is sent out or an error occurs. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] 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 This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a + source address for this instance, but no source address was + available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.TxData is NULL. + - Token->Packet.FragmentCount is zero. + - Token->Packet.DataLength is not equal to the sum of fragment lengths. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - A transmit completion token with the same Token-> + CompletionToken.Event was already in the + transmission queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen 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 +Tcp6Transmit ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_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 = TcpChkDataBuf ( + Token->Packet.TxData->DataLength, + Token->Packet.TxData->FragmentCount, + (EFI_TCP4_FRAGMENT_DATA *) Token->Packet.TxData->FragmentTable + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Sock = SOCK_FROM_THIS (This); + + return SockSend (Sock, Token); +} + +/** + Places an asynchronous receive request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. This + function is always asynchronous. The caller must allocate the Token->CompletionToken.Event + and the FragmentBuffer used to receive data. The caller also must fill the DataLength that + represents the whole length of all FragmentBuffer. When the receive operation completes, the + EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData + fields, and the Token->CompletionToken.Event is signaled. If data obtained, the data and its length + will be copied into the FragmentTable; at the same time the full length of received data will + be recorded in the DataLength fields. Providing a proper notification function and context + for the event enables the user to receive the notification and receiving status. That + notification function is guaranteed to not be re-entered. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] 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 This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.RxData is NULL. + - Token->Packet.RxData->DataLength is 0. + - The Token->Packet.RxData->DataLength is not the + sum of all FragmentBuffer length in FragmentTable. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of + system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI TCPv6 Protocol instance has been reset to startup defaults. + @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 Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - User has called Close() to disconnect this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection and there is no + 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 +Tcp6Receive ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_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 = TcpChkDataBuf ( + Token->Packet.RxData->DataLength, + Token->Packet.RxData->FragmentCount, + (EFI_TCP4_FRAGMENT_DATA *) 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. This function is a + nonblocking operation. + + Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered + transmission data will be sent by the TCP driver, and the current instance will have a graceful close + working flow described as RFC 793 if AbortOnClose is set to FALSE. Otherwise, a rest packet + will be sent by TCP driver to fast disconnect this connection. When the close operation completes + successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous + operations are signaled, and any buffers used for TCP network traffic are flushed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when operation finishes. + + @retval EFI_SUCCESS The Close() was called successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - CloseToken or CloseToken->CompletionToken.Event is already in use. + - Previous Close() call on this instance has not finished. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - CloseToken is NULL. + - CloseToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resource to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to error categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Close ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_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. + + The Cancel() function aborts a pending connection, listen, transmit, or + receive request. + + If Token is not NULL and the token is in the connection, listen, transmission, + or receive queue when it is being cancelled, its Token->Status will be set + to EFI_ABORTED, and then Token->Event will be signaled. + + If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. + + If Token is NULL all asynchronous token issued by Connect(), Accept(), + Transmit(), and Receive() will be aborted. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_TCP6_PROTOCOL.Connect(), + EFI_TCP6_PROTOCOL.Accept(), + EFI_TCP6_PROTOCOL.Transmit() or + EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending + tokens issued by above four functions will be aborted. Type + EFI_TCP6_COMPLETION_TOKEN is defined in + EFI_TCP_PROTOCOL.Connect(). + + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +Tcp6Cancel ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Poll to receive incoming data and transmit outgoing segments. + + The Poll() function increases the rate that data is moved between the network + and application, and can be called when the TCP instance is created successfully. + Its use is optional. + + @param[in] This Pointer to the EFI_TCP6_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 is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Tcp6Poll ( + IN EFI_TCP6_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/NetworkPkg/TcpDxe/TcpMain.h b/NetworkPkg/TcpDxe/TcpMain.h new file mode 100644 index 0000000000..fabffc21d7 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpMain.h @@ -0,0 +1,758 @@ +/** @file + Declaration of protocol interfaces in EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL. + It is the common head file for all Tcp*.c in TCP driver. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#ifndef _TCP_MAIN_H_ +#define _TCP_MAIN_H_ + +#include +#include +#include +#include + +#include "Socket.h" +#include "TcpProto.h" +#include "TcpDriver.h" +#include "TcpFunc.h" + +extern UINT16 mTcp4RandomPort; +extern UINT16 mTcp6RandomPort; +extern CHAR16 *mTcpStateName[]; +extern EFI_COMPONENT_NAME_PROTOCOL gTcpComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gTcpComponentName2; + +extern LIST_ENTRY mTcpRunQue; +extern LIST_ENTRY mTcpListenQue; +extern TCP_SEQNO mTcpGlobalIss; +extern UINT32 mTcpTick; + +/// +/// 30 seconds. +/// +#define TCP6_KEEP_NEIGHBOR_TIME 30 +/// +/// 5 seconds, since 1 tick equals 200ms. +/// +#define TCP6_REFRESH_NEIGHBOR_TICK 25 + +#define TCP_EXPIRE_TIME 65535 + +/// +/// The implementation selects the initial send sequence number and the unit to +/// be added when it is increased. +/// +#define TCP_BASE_ISS 0x4d7e980b +#define TCP_ISS_INCREMENT_1 2048 +#define TCP_ISS_INCREMENT_2 100 + +typedef union { + EFI_TCP4_CONFIG_DATA Tcp4CfgData; + EFI_TCP6_CONFIG_DATA Tcp6CfgData; +} TCP_CONFIG_DATA; + +typedef union { + EFI_TCP4_ACCESS_POINT Tcp4Ap; + EFI_TCP6_ACCESS_POINT Tcp6Ap; +} TCP_ACCESS_POINT; + +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 _TCP6_MODE_DATA { + EFI_TCP6_CONNECTION_STATE *Tcp6State; + EFI_TCP6_CONFIG_DATA *Tcp6ConfigData; + EFI_IP6_MODE_DATA *Ip6ModeData; + EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData; + EFI_SIMPLE_NETWORK_MODE *SnpModeData; +} TCP6_MODE_DATA; + +typedef struct _TCP4_ROUTE_INFO { + BOOLEAN DeleteRoute; + EFI_IPv4_ADDRESS *SubnetAddress; + EFI_IPv4_ADDRESS *SubnetMask; + EFI_IPv4_ADDRESS *GatewayAddress; +} TCP4_ROUTE_INFO; + +// +// EFI_TCP4_PROTOCOL definitions. +// + +/** + Get the current operational status. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[out] Tcp4State Pointer to the buffer to receive the current TCP + state. Optional parameter that may be NULL. + @param[out] Tcp4ConfigData Pointer to the buffer to receive the current TCP + configuration. Optional parameter that may be NULL. + @param[out] Ip4ModeData Pointer to the buffer to receive the current + IPv4 configuration. Optional parameter that may be NULL. + @param[out] MnpConfigData Pointer to the buffer to receive the current MNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + @param[out] SnpModeData Pointer to the buffer to receive the current SNP + configuration data indirectly used by the TCPv4 + Instance. Optional parameter that may be NULL. + + @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 + ); + +/** + Initialize or brutally reset the operational parameters for + this EFI TCPv4 instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] TcpConfigData Pointer to the configure data to configure the + instance. Optional parameter that may be NULL. + + @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 the 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 + ); + +/** + Add or delete routing entries. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] DeleteRoute If TRUE, delete the specified route from routing + table; if FALSE, add the specified route to + routing table. + @param[in] SubnetAddress The destination network. + @param[in] SubnetMask The subnet mask for the destination network. + @param[in] 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 + ); + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] 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 resources 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 + ); + +/** + Listen on the passive instance to accept an incoming connection request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] 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 + ); + +/** + Queues outgoing data into the transmit queue + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance + @param[in] 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 a + 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 + ); + +/** + Place an asynchronous receive request into the receiving queue. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] 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 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 + ); + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] 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 resources to finish the + operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error + categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp4Close ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_CLOSE_TOKEN *CloseToken + ); + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + @param[in] This Pointer to the EFI_TCP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + Connect(), Accept(), Transmit() or Receive(). If + NULL, all pending tokens issued by the above four + functions will be aborted. + + @retval EFI_UNSUPPORTED The operation is not supported in the current + implementation. + +**/ +EFI_STATUS +EFIAPI +Tcp4Cancel ( + IN EFI_TCP4_PROTOCOL *This, + IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Poll to receive incoming data and transmit outgoing segments. + + @param[in] 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 + ); + +// +// EFI_TCP6_PROTOCOL definitions. +// + +/** + Get the current operational status. + + The GetModeData() function copies the current operational settings of this EFI TCPv6 + Protocol instance into user-supplied buffers. This function can also be used to retrieve + the operational setting of underlying drivers such as IPv6, MNP, or SNP. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[out] Tcp6State The buffer in which the current TCP state is + returned. Optional parameter that may be NULL. + @param[out] Tcp6ConfigData The buffer in which the current TCP configuration + is returned. Optional parameter that may be NULL. + @param[out] Ip6ModeData The buffer in which the current IPv6 configuration + data used by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] MnpConfigData The buffer in which the current MNP configuration + data used indirectly by the TCP instance is returned. + Optional parameter that may be NULL. + @param[out] SnpModeData The buffer in which the current SNP mode data + used indirectly by the TCP instance is returned. + Optional parameter that may be NULL. + + @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 +Tcp6GetModeData ( + IN EFI_TCP6_PROTOCOL *This, + OUT EFI_TCP6_CONNECTION_STATE *Tcp6State OPTIONAL, + OUT EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Initialize or brutally reset the operational parameters for this EFI TCPv6 instance. + + The Configure() function does the following: + - Initialize this TCP instance, i.e., initialize the communication end settings and + specify active open or passive open for an instance. + - Reset this TCP instance brutally, i.e., cancel all pending asynchronous tokens, flush + transmission and receiving buffer directly without informing the communication peer. + + No other TCPv6 Protocol operation except Poll() can be executed by this instance until + it is configured properly. For an active TCP instance, after a proper configuration it + may call Connect() to initiates the three-way handshake. For a passive TCP instance, + its state will transit to Tcp6StateListen after configuration, and Accept() may be + called to listen the incoming TCP connection requests. If Tcp6ConfigData is set to NULL, + the instance is reset. Resetting process will be done brutally, the state machine will + be set to Tcp6StateClosed directly, the receive queue and transmit queue will be flushed, + and no traffic is allowed through this instance. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Tcp6ConfigData Pointer to the configure data to configure the instance. + If Tcp6ConfigData is set to NULL, the instance is reset. + + @retval EFI_SUCCESS The operational settings were set, changed, or reset + successfully. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for + use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions are TRUE: + - This is NULL. + - Tcp6ConfigData->AccessPoint.StationAddress is neither zero nor + one of the configured IP addresses in the underlying IPv6 driver. + - Tcp6ConfigData->AccessPoint.RemoteAddress isn't a valid unicast + IPv6 address. + - Tcp6ConfigData->AccessPoint.RemoteAddress is zero or + Tcp6ConfigData->AccessPoint.RemotePort is zero when + Tcp6ConfigData->AccessPoint.ActiveFlag is TRUE. + - A same access point has been configured in other TCP + instance properly. + @retval EFI_ACCESS_DENIED Configuring TCP instance when it is configured without + calling Configure() with NULL to reset it. + @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 when + executing Configure(). + @retval EFI_DEVICE_ERROR An unexpected network or system error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Configure ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData OPTIONAL + ); + +/** + Initiate a nonblocking TCP connection request for an active TCP instance. + + The Connect() function will initiate an active open to the remote peer configured + in current TCP instance if it is configured active. If the connection succeeds or + fails due to an error, the ConnectionToken->CompletionToken.Event will be signaled, + and ConnectionToken->CompletionToken.Status will be updated accordingly. This + function can only be called for the TCP instance in Tcp6StateClosed state. The + instance will transfer into Tcp6StateSynSent if the function returns EFI_SUCCESS. + If TCP three-way handshake succeeds, its state will become Tcp6StateEstablished; + otherwise, the state will return to Tcp6StateClosed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ConnectionToken Pointer to the connection token to return when the TCP + three-way handshake finishes. + + @retval EFI_SUCCESS The connection request successfully initiated and the state of + this TCP instance has been changed to Tcp6StateSynSent. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - This instance is not configured as an active instance. + - This instance is not in Tcp6StateClosed state. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ConnectionToken is NULL. + - ConnectionToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES The driver can't allocate enough resources to initiate the active open. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +Tcp6Connect ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CONNECTION_TOKEN *ConnectionToken + ); + +/** + Listen on the passive instance to accept an incoming connection request. This is a + nonblocking operation. + + The Accept() function initiates an asynchronous accept request to wait for an incoming + connection on the passive TCP instance. If a remote peer successfully establishes a + connection with this instance, a new TCP instance will be created and its handle will + be returned in ListenToken->NewChildHandle. The newly created instance is configured + by inheriting the passive instance's configuration, and is ready for use upon return. + The new instance is in the Tcp6StateEstablished state. + + The ListenToken->CompletionToken.Event will be signaled when a new connection is + accepted, user aborts the listen or connection is reset. + + This function only can be called when the current TCP instance is in Tcp6StateListen state. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] ListenToken Pointer to the listen token to return when the operation finishes. + + + @retval EFI_SUCCESS The listen token was been queued successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - This instance is not a passive instance. + - This instance is not in Tcp6StateListen state. + - The same listen token has already existed in the listen + token queue of this TCP instance. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - ListenToken is NULL. + - ListentToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error + categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Accept ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_LISTEN_TOKEN *ListenToken + ); + +/** + Queues outgoing data into the transmit queue. + + The Transmit() function queues a sending request to this TCP instance along with the + user data. The status of the token is updated and the event in the token will be + signaled once the data is sent out or some error occurs. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] 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 This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a + source address for this instance, but no source address was + available for use. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.TxData is NULL. + - Token->Packet.FragmentCount is zero. + - Token->Packet.DataLength is not equal to the sum of fragment lengths. + @retval EFI_ACCESS_DENIED One or more of the following conditions are TRUE: + - A transmit completion token with the same Token-> + CompletionToken.Event was already in the + transmission queue. + - The current instance is in Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen 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 a resource + shortage. + @retval EFI_NETWORK_UNREACHABLE There is no route to the destination network or address. + +**/ +EFI_STATUS +EFIAPI +Tcp6Transmit ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ); + +/** + Places an asynchronous receive request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. This + function is always asynchronous. The caller must allocate the Token->CompletionToken.Event + and the FragmentBuffer used to receive data. The caller also must fill the DataLength, which + represents the whole length of all FragmentBuffer. When the receive operation completes, the + EFI TCPv6 Protocol driver updates the Token->CompletionToken.Status and Token->Packet.RxData + fields, and the Token->CompletionToken.Event is signaled. If data is obtained, the data and its length + will be copied into the FragmentTable. At the same time the full length of received data will + be recorded in the DataLength fields. Providing a proper notification function and context + for the event enables the user to receive the notification and receiving status. That + notification function is guaranteed to not be re-entered. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] 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 This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token->CompletionToken.Event is NULL. + - Token->Packet.RxData is NULL. + - Token->Packet.RxData->DataLength is 0. + - The Token->Packet.RxData->DataLength is not the + sum of all FragmentBuffer length in FragmentTable. + @retval EFI_OUT_OF_RESOURCES The receive completion token could not be queued due to a lack of + system resources (usually memory). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + The EFI TCPv6 Protocol instance has been reset to startup defaults. + @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 Tcp6StateClosed state. + - The current instance is a passive one and it is in + Tcp6StateListen state. + - The user has called Close() to disconnect this connection. + @retval EFI_CONNECTION_FIN The communication peer has closed the connection, and there is no + 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 +Tcp6Receive ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_IO_TOKEN *Token + ); + +/** + Disconnecting a TCP connection gracefully or reset a TCP connection. This function is a + nonblocking operation. + + Initiate an asynchronous close token to the TCP driver. After Close() is called, any buffered + transmission data will be sent by the TCP driver, and the current instance will have a graceful close + working flow described as RFC 793 if AbortOnClose is set to FALSE, otherwise, a rest packet + will be sent by TCP driver to fast disconnect this connection. When the close operation completes + successfully the TCP instance is in Tcp6StateClosed state, all pending asynchronous + operations are signaled, and any buffers used for TCP network traffic are flushed. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] CloseToken Pointer to the close token to return when operation finishes. + + @retval EFI_SUCCESS The Close() was called successfully. + @retval EFI_NOT_STARTED This EFI TCPv6 Protocol instance has not been configured. + @retval EFI_ACCESS_DENIED One or more of the following are TRUE: + - CloseToken or CloseToken->CompletionToken.Event is already in use. + - Previous Close() call on this instance has not finished. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - CloseToken is NULL. + - CloseToken->CompletionToken.Event is NULL. + @retval EFI_OUT_OF_RESOURCES Could not allocate enough resources to finish the operation. + @retval EFI_DEVICE_ERROR Any unexpected error not belonging to the error categories given above. + +**/ +EFI_STATUS +EFIAPI +Tcp6Close ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_CLOSE_TOKEN *CloseToken + ); + +/** + Abort an asynchronous connection, listen, transmission or receive request. + + The Cancel() function aborts a pending connection, listen, transmit or + receive request. + + If Token is not NULL and the token is in the connection, listen, transmission + or receive queue when it is being cancelled, its Token->Status will be set + to EFI_ABORTED and then Token->Event will be signaled. + + If the token is not in one of the queues, which usually means that the + asynchronous operation has completed, EFI_NOT_FOUND is returned. + + If Token is NULL all asynchronous token issued by Connect(), Accept(), + Transmit() and Receive() will be aborted. + + @param[in] This Pointer to the EFI_TCP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_TCP6_PROTOCOL.Connect(), + EFI_TCP6_PROTOCOL.Accept(), + EFI_TCP6_PROTOCOL.Transmit() or + EFI_TCP6_PROTOCOL.Receive(). If NULL, all pending + tokens issued by above four functions will be aborted. Type + EFI_TCP6_COMPLETION_TOKEN is defined in + EFI_TCP_PROTOCOL.Connect(). + + @retval EFI_UNSUPPORTED The implementation does not support this function. + +**/ +EFI_STATUS +EFIAPI +Tcp6Cancel ( + IN EFI_TCP6_PROTOCOL *This, + IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Poll to receive incoming data and transmit outgoing segments. + + The Poll() function increases the rate that data is moved between the network + and application and can be called when the TCP instance is created successfully. + Its use is optional. + + @param[in] This Pointer to the EFI_TCP6_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 is processed. + @retval EFI_TIMEOUT Data was dropped out of the transmission or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +Tcp6Poll ( + IN EFI_TCP6_PROTOCOL *This + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpMisc.c b/NetworkPkg/TcpDxe/TcpMisc.c new file mode 100644 index 0000000000..492ec35fb8 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpMisc.c @@ -0,0 +1,1281 @@ +/** @file + Misc support routines for TCP driver. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +LIST_ENTRY mTcpRunQue = { + &mTcpRunQue, + &mTcpRunQue +}; + +LIST_ENTRY mTcpListenQue = { + &mTcpListenQue, + &mTcpListenQue +}; + +TCP_SEQNO mTcpGlobalIss = TCP_BASE_ISS; + +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[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpInitTcbLocal ( + IN OUT TCP_CB *Tcb + ) +{ + // + // Compute the checksum of the fixed parts of pseudo header + // + if (Tcb->Sk->IpVersion == IP_VERSION_4) { + Tcb->HeadSum = NetPseudoHeadChecksum ( + Tcb->LocalEnd.Ip.Addr[0], + Tcb->RemoteEnd.Ip.Addr[0], + 0x06, + 0 + ); + } else { + Tcb->HeadSum = NetIp6PseudoHeadChecksum ( + &Tcb->LocalEnd.Ip.v6, + &Tcb->RemoteEnd.Ip.v6, + 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); + + // + // First window size is never scaled + // + Tcb->RcvWndScale = 0; + + Tcb->ProbeTimerOn = FALSE; +} + +/** + Initialize the peer related members. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seg Pointer to the segment that contains the peer's intial info. + @param[in] Opt Pointer to the options announced by the peer. + +**/ +VOID +TcpInitTcbPeer ( + IN OUT TCP_CB *Tcb, + IN TCP_SEG *Seg, + IN TCP_OPTION *Opt + ) +{ + UINT16 RcvMss; + + ASSERT ((Tcb != NULL) && (Seg != NULL) && (Opt != NULL)); + 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 = (UINT16) 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; + } +} + +/** + Check whether one IP address equals the other. + + @param[in] Ip1 Pointer to IP address to be checked. + @param[in] Ip2 Pointer to IP address to be checked. + @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address, + IP_VERSION_6 indicates the IP address is an IPv6 address. + + @retval TRUE Ip1 equals Ip2. + @retval FALSE Ip1 does not equal Ip2. + +**/ +BOOLEAN +TcpIsIpEqual ( + IN EFI_IP_ADDRESS *Ip1, + IN EFI_IP_ADDRESS *Ip2, + IN UINT8 Version + ) +{ + ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6)); + + if (Version == IP_VERSION_4) { + return (BOOLEAN) (Ip1->Addr[0] == Ip2->Addr[0]); + } else { + return (BOOLEAN) EFI_IP6_EQUAL (&Ip1->v6, &Ip2->v6); + } +} + +/** + Check whether one IP address is filled with ZERO. + + @param[in] Ip Pointer to the IP address to be checked. + @param[in] Version IP_VERSION_4 indicates the IP address is an IPv4 address, + IP_VERSION_6 indicates the IP address is an IPv6 address. + + @retval TRUE Ip is all zero address. + @retval FALSE Ip is not all zero address. + +**/ +BOOLEAN +TcpIsIpZero ( + IN EFI_IP_ADDRESS *Ip, + IN UINT8 Version + ) +{ + ASSERT ((Version == IP_VERSION_4) || (Version == IP_VERSION_6)); + + if (Version == IP_VERSION_4) { + return (BOOLEAN) (Ip->Addr[0] == 0); + } else { + return (BOOLEAN) ((Ip->Addr[0] == 0) && (Ip->Addr[1] == 0) && + (Ip->Addr[2] == 0) && (Ip->Addr[3] == 0)); + } +} + +/** + Locate a listen TCB that matchs the Local and Remote. + + @param[in] Local Pointer to the local (IP, Port). + @param[in] Remote Pointer to the remote (IP, Port). + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + @return Pointer to the TCP_CB with the least number of wildcards, + if NULL no match is found. + +**/ +TCP_CB * +TcpLocateListenTcb ( + IN TCP_PEER *Local, + IN TCP_PEER *Remote, + IN UINT8 Version + ) +{ + 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 ((Version != Node->Sk->IpVersion) || + (Local->Port != Node->LocalEnd.Port) || + !TCP_PEER_MATCH (Remote, &Node->RemoteEnd, Version) || + !TCP_PEER_MATCH (Local, &Node->LocalEnd, Version) + ) { + + continue; + } + + // + // Compute the number of wildcard + // + Cur = 0; + if (TcpIsIpZero (&Node->RemoteEnd.Ip, Version)) { + Cur++; + } + + if (Node->RemoteEnd.Port == 0) { + Cur++; + } + + if (TcpIsIpZero (&Node->LocalEnd.Ip, Version)) { + Cur++; + } + + if (Cur < Last) { + if (Cur == 0) { + return Node; + } + + Last = Cur; + Match = Node; + } + } + + return Match; +} + +/** + Try to find one Tcb whose equals to . + + @param[in] Addr Pointer to the IP address needs to match. + @param[in] Port The port number needs to match. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + + @retval TRUE The Tcb which matches the pair exists. + @retval FALSE Otherwise + +**/ +BOOLEAN +TcpFindTcbByPeer ( + IN EFI_IP_ADDRESS *Addr, + IN TCP_PORTNO Port, + IN UINT8 Version + ) +{ + TCP_PORTNO LocalPort; + 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 ((Version == Tcb->Sk->IpVersion) && + TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) && + (LocalPort == Tcb->LocalEnd.Port) + ) { + + return TRUE; + } + } + + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Version == Tcb->Sk->IpVersion) && + TcpIsIpEqual (Addr, &Tcb->LocalEnd.Ip, Version) && + (LocalPort == Tcb->LocalEnd.Port) + ) { + + return TRUE; + } + } + + return FALSE; +} + +/** + Locate the TCP_CB related to the socket pair. + + @param[in] LocalPort The local port number. + @param[in] LocalIp The local IP address. + @param[in] RemotePort The remote port number. + @param[in] RemoteIp The remote IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + @param[in] Syn 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 EFI_IP_ADDRESS *LocalIp, + IN TCP_PORTNO RemotePort, + IN EFI_IP_ADDRESS *RemoteIp, + IN UINT8 Version, + IN BOOLEAN Syn + ) +{ + TCP_PEER Local; + TCP_PEER Remote; + LIST_ENTRY *Entry; + TCP_CB *Tcb; + + Local.Port = LocalPort; + Remote.Port = RemotePort; + + CopyMem (&Local.Ip, LocalIp, sizeof (EFI_IP_ADDRESS)); + CopyMem (&Remote.Ip, RemoteIp, sizeof (EFI_IP_ADDRESS)); + + // + // First check for exact match. + // + NET_LIST_FOR_EACH (Entry, &mTcpRunQue) { + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if ((Version == Tcb->Sk->IpVersion) && + TCP_PEER_EQUAL (&Remote, &Tcb->RemoteEnd, Version) && + TCP_PEER_EQUAL (&Local, &Tcb->LocalEnd, Version) + ) { + + RemoveEntryList (&Tcb->List); + InsertHeadList (&mTcpRunQue, &Tcb->List); + + return Tcb; + } + } + + // + // Only check the listen queue when the SYN flag is on. + // + if (Syn) { + return TcpLocateListenTcb (&Local, &Remote, Version); + } + + return NULL; +} + +/** + Insert a Tcb into the proper queue. + + @param[in] Tcb Pointer to the TCP_CB to be inserted. + + @retval 0 The Tcb was inserted successfully. + @retval -1 Error condition occurred. + +**/ +INTN +TcpInsertTcb ( + IN TCP_CB *Tcb + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Head; + TCP_CB *Node; + TCP_PROTO_DATA *TcpProto; + + ASSERT ( + (Tcb != NULL) && + ( + (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 the 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, Tcb->Sk->IpVersion) && + TCP_PEER_EQUAL (&Tcb->RemoteEnd, &Node->RemoteEnd, Tcb->Sk->IpVersion) + ) { + + return -1; + } + } + + InsertHeadList (Head, &Tcb->List); + + TcpProto = (TCP_PROTO_DATA *) Tcb->Sk->ProtoReserved; + TcpSetVariableData (TcpProto->TcpService); + + return 0; +} + +/** + Clone a TCP_CB from Tcb. + + @param[in] 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 = AllocateZeroPool (sizeof (TCP_CB)); + + if (Clone == NULL) { + return NULL; + } + + CopyMem (Clone, Tcb, sizeof (TCP_CB)); + + // + // Increase the reference count of the shared IpInfo. + // + NET_GET_REF (Tcb->IpInfo); + + InitializeListHead (&Clone->List); + InitializeListHead (&Clone->SndQue); + InitializeListHead (&Clone->RcvQue); + + Clone->Sk = SockClone (Tcb->Sk); + if (Clone->Sk == NULL) { + DEBUG ((EFI_D_ERROR, "TcpCloneTcb: failed to clone a sock\n")); + FreePool (Clone); + return NULL; + } + + ((TCP_PROTO_DATA *) (Clone->Sk->ProtoReserved))->TcpPcb = Clone; + + return Clone; +} + +/** + Compute an ISS to be used by a new connection. + + @return The resulting ISS. + +**/ +TCP_SEQNO +TcpGetIss ( + VOID + ) +{ + mTcpGlobalIss += TCP_ISS_INCREMENT_1; + return mTcpGlobalIss; +} + +/** + Get the local mss. + + @param[in] Sock Pointer to the socket to get mss. + + @return The mss size. + +**/ +UINT16 +TcpGetRcvMss ( + IN SOCKET *Sock + ) +{ + EFI_IP4_MODE_DATA Ip4Mode; + EFI_IP6_MODE_DATA Ip6Mode; + EFI_IP4_PROTOCOL *Ip4; + EFI_IP6_PROTOCOL *Ip6; + TCP_PROTO_DATA *TcpProto; + + ASSERT (Sock != NULL); + + ZeroMem (&Ip4Mode, sizeof (EFI_IP4_MODE_DATA)); + ZeroMem (&Ip6Mode, sizeof (EFI_IP6_MODE_DATA)); + + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + + if (Sock->IpVersion == IP_VERSION_4) { + Ip4 = TcpProto->TcpService->IpIo->Ip.Ip4; + ASSERT (Ip4 != NULL); + Ip4->GetModeData (Ip4, &Ip4Mode, NULL, NULL); + + return (UINT16) (Ip4Mode.MaxPacketSize - sizeof (TCP_HEAD)); + } else { + Ip6 = TcpProto->TcpService->IpIo->Ip.Ip6; + ASSERT (Ip6 != NULL); + Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL); + + return (UINT16) (Ip6Mode.MaxPacketSize - sizeof (TCP_HEAD)); + } +} + +/** + Set the Tcb's state. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] State The state to be set. + +**/ +VOID +TcpSetState ( + IN TCP_CB *Tcb, + IN UINT8 State + ) +{ + ASSERT (Tcb->State < (sizeof (mTcpStateName) / sizeof (CHAR16 *))); + ASSERT (State < (sizeof (mTcpStateName) / sizeof (CHAR16 *))); + + DEBUG ( + (EFI_D_INFO, + "Tcb (%p) state %s --> %s\n", + Tcb, + mTcpStateName[Tcb->State], + mTcpStateName[State]) + ); + + Tcb->State = State; + + switch (State) { + case TCP_ESTABLISHED: + + SockConnEstablished (Tcb->Sk); + + if (Tcb->Parent != NULL) { + // + // A new connection is accepted by a listening socket. Install + // the device path. + // + TcpInstallDevicePath (Tcb->Sk); + } + + break; + + case TCP_CLOSED: + + SockConnClosed (Tcb->Sk); + + break; + default: + break; + } +} + +/** + Compute the TCP segment's checksum. + + @param[in] Nbuf Pointer to the buffer that contains the TCP segment. + @param[in] 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 (UINT16) (~Checksum); +} + +/** + Translate the information from the head of the received TCP + segment Nbuf contents and fill it into a TCP_SEG structure. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in, out] 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 OUT NET_BUF *Nbuf + ) +{ + TCP_SEG *Seg; + TCP_HEAD *Head; + + Seg = TCPSEG_NETBUF (Nbuf); + Head = (TCP_HEAD *) NetbufGetByte (Nbuf, 0, NULL); + ASSERT (Head != 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 the 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; +} + +/** + Initialize an active connection. + + @param[in, out] Tcb Pointer to the TCP_CB that wants to initiate a + connection. + +**/ +VOID +TcpOnAppConnect ( + IN OUT 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[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppClose ( + IN OUT TCP_CB *Tcb + ) +{ + ASSERT (Tcb != NULL); + + if (!IsListEmpty (&Tcb->RcvQue) || GET_RCV_DATASIZE (Tcb->Sk) != 0) { + + DEBUG ( + (EFI_D_WARN, + "TcpOnAppClose: connection reset because data is lost for TCB %p\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; + default: + break; + } + + TcpToSendData (Tcb, 1); +} + +/** + Check whether the application's newly delivered data can be sent out. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The data has been sent out successfully. + @retval -1 The Tcb is not in a state that data is permitted to + be sent out. + +**/ +INTN +TcpOnAppSend ( + IN OUT TCP_CB *Tcb + ) +{ + + switch (Tcb->State) { + case TCP_CLOSED: + return -1; + + case TCP_LISTEN: + return -1; + + case TCP_SYN_SENT: + case TCP_SYN_RCVD: + return 0; + + case TCP_ESTABLISHED: + case TCP_CLOSE_WAIT: + TcpToSendData (Tcb, 0); + return 0; + + case TCP_FIN_WAIT_1: + case TCP_FIN_WAIT_2: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_TIME_WAIT: + return -1; + + default: + break; + } + + return 0; +} + +/** + Application has consumed some data. Check whether + to send a window update ack or a delayed ack. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpOnAppConsume ( + IN TCP_CB *Tcb + ) +{ + UINT32 TcpOld; + + switch (Tcb->State) { + case TCP_ESTABLISHED: + TcpOld = TcpRcvWinOld (Tcb); + if (TcpRcvWinNow (Tcb) > TcpOld) { + + if (TcpOld < Tcb->RcvMss) { + + DEBUG ( + (EFI_D_INFO, + "TcpOnAppConsume: send a window update for a window closed Tcb %p\n", + Tcb) + ); + + TcpSendAck (Tcb); + } else if (Tcb->DelayedAck == 0) { + + DEBUG ( + (EFI_D_INFO, + "TcpOnAppConsume: scheduled a delayed ACK to update window for Tcb %p\n", + Tcb) + ); + + Tcb->DelayedAck = 1; + } + } + + break; + + default: + break; + } +} + +/** + Abort the connection by sending a reset segment. Called + when the application wants to abort the connection. + + @param[in] Tcb Pointer to the TCP_CB of the TCP instance. + +**/ +VOID +TcpOnAppAbort ( + IN TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "TcpOnAppAbort: connection reset issued by application for TCB %p\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; + default: + break; + } + + TcpSetState (Tcb, TCP_CLOSED); +} + +/** + Reset the connection related with Tcb. + + @param[in] Tcb Pointer to the TCP_CB of the connection to be reset. + +**/ +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 = (UINT8) (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, Tcb->Sk->IpVersion); + + NetbufFree (Nbuf); +} + +/** + Set the Tcp variable data. + + @param[in] TcpService Tcp 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 TCP_SERVICE_DATA *TcpService + ) +{ + EFI_GUID *ServiceBindingGuid; + UINT32 NumConfiguredInstance; + LIST_ENTRY *Entry; + TCP_CB *TcpPcb; + TCP_PROTO_DATA *TcpProto; + UINTN VariableDataSize; + EFI_TCP4_VARIABLE_DATA *Tcp4VariableData; + EFI_TCP4_SERVICE_POINT *Tcp4ServicePoint; + EFI_TCP6_VARIABLE_DATA *Tcp6VariableData; + EFI_TCP6_SERVICE_POINT *Tcp6ServicePoint; + VOID *VariableData; + CHAR16 *NewMacString; + EFI_STATUS Status; + + if (TcpService->IpVersion == IP_VERSION_4) { + ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + NumConfiguredInstance = 0; + Tcp4VariableData = NULL; + Tcp6VariableData = NULL; + + // + // 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 = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == TcpService) { + // + // This tcp instance belongs to the TcpService. + // + 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 = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == TcpService) { + // + // This tcp instance belongs to the TcpService. + // + NumConfiguredInstance++; + } + } + + Tcp4ServicePoint = NULL; + Tcp6ServicePoint = NULL; + + // + // Calculate the size of the Tcp4VariableData. As there may be no Tcp4 child, + // we should add extra buffers for the service points only if the number of configured + // children is more than one. + // + if (TcpService->IpVersion == IP_VERSION_4) { + VariableDataSize = sizeof (EFI_TCP4_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_TCP4_SERVICE_POINT) * (NumConfiguredInstance - 1); + } + + Tcp4VariableData = AllocateZeroPool (VariableDataSize); + if (Tcp4VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcp4VariableData->DriverHandle = TcpService->DriverBindingHandle; + Tcp4VariableData->ServiceCount = NumConfiguredInstance; + + Tcp4ServicePoint = &Tcp4VariableData->Services[0]; + VariableData = Tcp4VariableData; + } else { + VariableDataSize = sizeof (EFI_TCP6_VARIABLE_DATA); + + if (NumConfiguredInstance > 1) { + VariableDataSize += sizeof (EFI_TCP6_SERVICE_POINT) * (NumConfiguredInstance - 1); + } + + Tcp6VariableData = AllocateZeroPool (VariableDataSize); + if (Tcp6VariableData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Tcp6VariableData->DriverHandle = TcpService->DriverBindingHandle; + Tcp6VariableData->ServiceCount = NumConfiguredInstance; + + Tcp6ServicePoint = &Tcp6VariableData->Services[0]; + VariableData = Tcp6VariableData; + } + + // + // 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 = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == TcpService) { + // + // This tcp instance belongs to the TcpService. + // + if (TcpService->IpVersion == IP_VERSION_4) { + Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp4ServicePoint++; + } else { + Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip); + Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip); + Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp6ServicePoint++; + } + } + } + + // + // 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 = (TCP_PROTO_DATA *) TcpPcb->Sk->ProtoReserved; + + if (TcpProto->TcpService == TcpService) { + // + // This tcp instance belongs to the TcpService. + // + if (TcpService->IpVersion == IP_VERSION_4) { + Tcp4ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + CopyMem (&Tcp4ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + Tcp4ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + CopyMem (&Tcp4ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip, sizeof (EFI_IPv4_ADDRESS)); + Tcp4ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp4ServicePoint++; + } else { + Tcp6ServicePoint->InstanceHandle = TcpPcb->Sk->SockHandle; + IP6_COPY_ADDRESS (&Tcp6ServicePoint->LocalAddress, &TcpPcb->LocalEnd.Ip); + Tcp6ServicePoint->LocalPort = NTOHS (TcpPcb->LocalEnd.Port); + IP6_COPY_ADDRESS (&Tcp6ServicePoint->RemoteAddress, &TcpPcb->RemoteEnd.Ip); + Tcp6ServicePoint->RemotePort = NTOHS (TcpPcb->RemoteEnd.Port); + + Tcp6ServicePoint++; + } + } + } + + // + // Get the mac string. + // + Status = NetLibGetMacString ( + TcpService->ControllerHandle, + TcpService->DriverBindingHandle, + &NewMacString + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + if (TcpService->MacString != NULL) { + // + // The variable is set already. We're going to update it. + // + if (StrCmp (TcpService->MacString, NewMacString) != 0) { + // + // The mac address is changed. Delete the previous variable first. + // + gRT->SetVariable ( + TcpService->MacString, + ServiceBindingGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + } + + FreePool (TcpService->MacString); + } + + TcpService->MacString = NewMacString; + + Status = gRT->SetVariable ( + TcpService->MacString, + ServiceBindingGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + VariableDataSize, + VariableData + ); + +ON_ERROR: + + FreePool (VariableData); + + return Status; +} + +/** + Clear the variable and free the resource. + + @param[in] TcpService Tcp service data. + +**/ +VOID +TcpClearVariableData ( + IN TCP_SERVICE_DATA *TcpService + ) +{ + EFI_GUID *ServiceBindingGuid; + + ASSERT (TcpService->MacString != NULL); + + if (TcpService->IpVersion == IP_VERSION_4) { + ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid; + } else { + ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid; + } + + gRT->SetVariable ( + TcpService->MacString, + ServiceBindingGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + NULL + ); + + FreePool (TcpService->MacString); + TcpService->MacString = NULL; +} + +/** + Install the device path protocol on the TCP instance. + + @param[in] Sock Pointer to the socket representing the TCP instance. + + @retval EFI_SUCCESS The device path protocol was installed. + @retval other Failed to install the device path protocol. + +**/ +EFI_STATUS +TcpInstallDevicePath ( + IN SOCKET *Sock + ) +{ + TCP_PROTO_DATA *TcpProto; + TCP_SERVICE_DATA *TcpService; + TCP_CB *Tcb; + IPv4_DEVICE_PATH Ip4DPathNode; + IPv6_DEVICE_PATH Ip6DPathNode; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS Status; + TCP_PORTNO LocalPort; + TCP_PORTNO RemotePort; + + TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved; + TcpService = TcpProto->TcpService; + Tcb = TcpProto->TcpPcb; + + LocalPort = NTOHS (Tcb->LocalEnd.Port); + RemotePort = NTOHS (Tcb->RemoteEnd.Port); + if (Sock->IpVersion == IP_VERSION_4) { + NetLibCreateIPv4DPathNode ( + &Ip4DPathNode, + TcpService->ControllerHandle, + Tcb->LocalEnd.Ip.Addr[0], + LocalPort, + Tcb->RemoteEnd.Ip.Addr[0], + RemotePort, + EFI_IP_PROTO_TCP, + Tcb->UseDefaultAddr + ); + + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip4DPathNode; + } else { + NetLibCreateIPv6DPathNode ( + &Ip6DPathNode, + TcpService->ControllerHandle, + &Tcb->LocalEnd.Ip.v6, + LocalPort, + &Tcb->RemoteEnd.Ip.v6, + RemotePort, + EFI_IP_PROTO_TCP + ); + + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) &Ip6DPathNode; + } + + Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath); + if (Sock->DevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->InstallProtocolInterface ( + &Sock->SockHandle, + &gEfiDevicePathProtocolGuid, + EFI_NATIVE_INTERFACE, + Sock->DevicePath + ); + if (EFI_ERROR (Status)) { + FreePool (Sock->DevicePath); + Sock->DevicePath = NULL; + } + + return Status; +} + diff --git a/NetworkPkg/TcpDxe/TcpOption.c b/NetworkPkg/TcpDxe/TcpOption.c new file mode 100644 index 0000000000..bacce1070d --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpOption.c @@ -0,0 +1,374 @@ +/** @file + Routines to process TCP option. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +/** + Get a UINT16 value from buffer. + + @param[in] Buf Pointer to input buffer. + + @return The UINT16 value obtained from the buffer. + +**/ +UINT16 +TcpGetUint16 ( + IN UINT8 *Buf + ) +{ + UINT16 Value; + CopyMem (&Value, Buf, sizeof (UINT16)); + return NTOHS (Value); +} + +/** + Get a UINT32 value from buffer. + + @param[in] Buf Pointer to input buffer. + + @return The UINT32 value obtained from the buffer. + +**/ +UINT32 +TcpGetUint32 ( + IN UINT8 *Buf + ) +{ + UINT32 Value; + CopyMem (&Value, Buf, sizeof (UINT32)); + return NTOHL (Value); +} + +/** + Put a UINT32 value in buffer. + + @param[out] Buf Pointer to the buffer. + @param[in] Data The UINT32 Date to put in the buffer. + +**/ +VOID +TcpPutUint32 ( + OUT UINT8 *Buf, + IN UINT32 Data + ) +{ + Data = HTONL (Data); + CopyMem (Buf, &Data, sizeof (UINT32)); +} + +/** + Compute the window scale value according to the given buffer size. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ) +{ + UINT8 Scale; + UINT32 BufSize; + + ASSERT ((Tcb != NULL) && (Tcb->Sk != NULL)); + + 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[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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 + ) +{ + UINT8 *Data; + UINT16 Len; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + + Len = 0; + + // + // Add a timestamp option if not disabled by the 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 != NULL); + 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 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 != NULL); + + Len += TCP_OPTION_WS_ALIGNED_LEN; + TcpPutUint32 (Data, TCP_OPTION_WS_FAST | TcpComputeScale (Tcb)); + } + + // + // Build the MSS option. + // + Data = NetbufAllocSpace (Nbuf, TCP_OPTION_MSS_LEN, 1); + ASSERT (Data != NULL); + + Len += TCP_OPTION_MSS_LEN; + TcpPutUint32 (Data, TCP_OPTION_MSS_FAST | Tcb->RcvMss); + + return Len; +} + +/** + Build the TCP option in synchronized states. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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 + ) +{ + UINT8 *Data; + UINT16 Len; + + ASSERT ((Tcb != NULL) && (Nbuf != NULL) && (Nbuf->Tcp == NULL)); + Len = 0; + + // + // Build the 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 != NULL); + 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[in] Tcp Pointer to the TCP_CB of this TCP instance. + @param[in, out] 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 OUT TCP_OPTION *Option + ) +{ + UINT8 *Head; + UINT8 TotalLen; + UINT8 Cur; + UINT8 Type; + UINT8 Len; + + ASSERT ((Tcp != NULL) && (Option != NULL)); + + Option->Flag = 0; + + TotalLen = (UINT8) ((Tcp->HeadLen << 2) - sizeof (TCP_HEAD)); + if (TotalLen <= 0) { + return 0; + } + + Head = (UINT8 *) (Tcp + 1); + + // + // Fast process of the 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 = (UINT8) 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 = (UINT8) (Cur + Len); + break; + } + + } + + return 0; +} + +/** + Check the segment against PAWS. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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/NetworkPkg/TcpDxe/TcpOption.h b/NetworkPkg/TcpDxe/TcpOption.h new file mode 100644 index 0000000000..0ccadb9536 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpOption.h @@ -0,0 +1,145 @@ +/** @file + Tcp option's routine header file. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#ifndef _TCP_OPTION_H_ +#define _TCP_OPTION_H_ + +// +// Supported TCP option types and their length. +// +#define TCP_OPTION_EOP 0 ///< End Of oPtion +#define TCP_OPTION_NOP 1 ///< No-Option. +#define TCP_OPTION_MSS 2 ///< Maximum Segment Size +#define TCP_OPTION_WS 3 ///< Window scale +#define TCP_OPTION_TS 8 ///< Timestamp +#define TCP_OPTION_MSS_LEN 4 ///< Length of MSS option +#define TCP_OPTION_WS_LEN 3 ///< Length of window scale option +#define TCP_OPTION_TS_LEN 10 ///< Length of timestamp option +#define TCP_OPTION_WS_ALIGNED_LEN 4 ///< Length of window scale option, aligned +#define TCP_OPTION_TS_ALIGNED_LEN 12 ///< Length of timestamp option, aligned + +// +// recommend format of timestamp window scale +// option for fast process. +// +#define TCP_OPTION_TS_FAST ((TCP_OPTION_NOP << 24) | \ + (TCP_OPTION_NOP << 16) | \ + (TCP_OPTION_TS << 8) | \ + (TCP_OPTION_TS_LEN)) + +#define TCP_OPTION_WS_FAST ((TCP_OPTION_NOP << 24) | \ + (TCP_OPTION_WS << 16) | \ + (TCP_OPTION_WS_LEN << 8)) + +#define TCP_OPTION_MSS_FAST ((TCP_OPTION_MSS << 24) | (TCP_OPTION_MSS_LEN << 16)) + +// +// Other misc definations +// +#define TCP_OPTION_RCVD_MSS 0x01 +#define TCP_OPTION_RCVD_WS 0x02 +#define TCP_OPTION_RCVD_TS 0x04 +#define TCP_OPTION_MAX_WS 14 ///< Maxium window scale value +#define TCP_OPTION_MAX_WIN 0xffff ///< Max window size in TCP header + +/// +/// The structure to store the parse option value. +/// ParseOption only parses the options, doesn't process them. +/// +typedef struct _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; + +/** + Compute the window scale value according to the given buffer size. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + + @return The scale value. + +**/ +UINT8 +TcpComputeScale ( + IN TCP_CB *Tcb + ); + +/** + Build the TCP option in three-way handshake. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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 + ); + +/** + Build the TCP option in synchronized states. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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 + ); + +/** + Parse the supported options. + + @param[in] Tcp Pointer to the TCP_CB of this TCP instance. + @param[in, out] Option Pointer to the TCP_OPTION used to store the + successfully pasrsed options. + + @retval 0 The options successfully pasrsed. + @retval -1 Ilegal option was found. + +**/ +INTN +TcpParseOption ( + IN TCP_HEAD *Tcp, + IN OUT TCP_OPTION *Option + ); + +/** + Check the segment against PAWS. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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 + ); + +#endif diff --git a/NetworkPkg/TcpDxe/TcpOutput.c b/NetworkPkg/TcpDxe/TcpOutput.c new file mode 100644 index 0000000000..c038213484 --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpOutput.c @@ -0,0 +1,1219 @@ +/** @file + TCP output process routines. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +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[in] 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[in] 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 != NULL); + + 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[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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 outgoing segment. + +**/ +UINT16 +TcpComputeWnd ( + IN OUT 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 = MIN (Wnd >> Tcb->RcvWndScale, 0xffff); + return NTOHS ((UINT16) Wnd); +} + +/** + Get the maximum SndNxt. + + @param[in] 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 + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + + if (IsListEmpty (&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[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, 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 != NULL); + + // + // 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 = MIN (Win, Left); + + if (Len > Tcb->SndMss) { + Len = Tcb->SndMss; + } + + if ((Force != 0)|| (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 combines it with a probe timer. + // +SetPersistTimer: + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) { + + DEBUG ( + (EFI_D_WARN, + "TcpDataToSend: enter persistent state for TCB %p\n", + Tcb) + ); + + if (!Tcb->ProbeTimerOn) { + TcpSetProbeTimer (Tcb); + } + } + + return 0; +} + +/** + Build the TCP header of the TCP segment and transmit the segment by IP. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Nbuf Pointer to the buffer containing the segment to be + sent out. + + @retval 0 The segment was sent out successfully. + @retval -1 An error condition occurred. + +**/ +INTN +TcpTransmitSegment ( + IN OUT TCP_CB *Tcb, + IN NET_BUF *Nbuf + ) +{ + UINT16 Len; + TCP_HEAD *Head; + TCP_SEG *Seg; + BOOLEAN Syn; + UINT32 DataLen; + + ASSERT ((Nbuf != NULL) && (Nbuf->Tcp == NULL) && (TcpVerifySegment (Nbuf) != 0)); + + 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) 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 the delayedack flag. + // + Tcb->DelayedAck = 0; + + return TcpSendIpPacket (Tcb, Nbuf, &Tcb->LocalEnd.Ip, &Tcb->RemoteEnd.Ip, Tcb->Sk->IpVersion); +} + +/** + Get a segment from the Tcb's SndQue. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] 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 + ) +{ + LIST_ENTRY *Head; + 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 != NULL) && 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; + } + } + + if ((Cur == Head) || (Seg == NULL) || (Node == NULL)) { + return NULL; + } + + // + // 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 != 0) { + Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL); + ASSERT (Data != NULL); + + if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) { + goto OnError; + } + } + + CopyMem (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[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] 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 != NULL) && (Tcb->Sk != NULL)); + + Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD); + + if (Nbuf == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpGetSegmentSock: failed to allocate a netbuf for TCB %p\n", + Tcb) + ); + + return NULL; + } + + NetbufReserve (Nbuf, TCP_MAX_HEAD); + + DataGet = 0; + + if (Len != 0) { + // + // copy data to the segment. + // + Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL); + ASSERT (Data != NULL); + + DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data); + } + + NET_GET_REF (Nbuf); + + TCPSEG_NETBUF (Nbuf)->Seq = Seq; + TCPSEG_NETBUF (Nbuf)->End = Seq + Len; + + InsertTailList (&(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[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Seq The sequence number of the segment. + @param[in] 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 != NULL); + + // + // 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) != 0); + return Nbuf; +} + +/** + Retransmit the segment from sequence Seq. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] 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)) { + DEBUG ( + (EFI_D_WARN, + "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n", + Tcb) + ); + + return 0; + } + + Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq); + Len = MIN (Len, Tcb->SndMss); + + Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len); + if (Nbuf == NULL) { + return -1; + } + + ASSERT (TcpVerifySegment (Nbuf) != 0); + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + goto OnError; + } + + // + // The retransmitted buffer may be on the SndQue, + // trim TCP head because all the buffers on SndQue + // are headless. + // + ASSERT (Nbuf->Tcp != NULL); + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + return 0; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return -1; +} + +/** + Verify that all the segments in SndQue are in good shape. + + @param[in] 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 LIST_ENTRY *Head + ) +{ + LIST_ENTRY *Entry; + NET_BUF *Nbuf; + TCP_SEQNO Seq; + + if (IsListEmpty (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; +} + +/** + Check whether to send data/SYN/FIN and piggyback an ACK. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Force If TRUE, ignore the sender's SWS avoidance algorithm + and send out data by force. + + @return The number of bytes sent. + +**/ +INTN +TcpToSendData ( + IN OUT 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 != NULL) && (Tcb->Sk != NULL) && (Tcb->State != TCP_LISTEN)); + + Sent = 0; + + if ((Tcb->State == TCP_CLOSED) || TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) { + + return 0; + } + + do { + // + // Compute how much data can be sent + // + Len = TcpDataToSend (Tcb, Force); + Seq = Tcb->SndNxt; + + ASSERT ((Tcb->State) < (sizeof (mTcpOutFlag) / sizeof (mTcpOutFlag[0]))); + Flag = mTcpOutFlag[Tcb->State]; + + if ((Flag & TCP_FLG_SYN) != 0) { + + 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)) == 0)) { + return Sent; + } + + Nbuf = TcpGetSegment (Tcb, Seq, Len); + + if (Nbuf == NULL) { + DEBUG ( + (EFI_D_ERROR, + "TcpToSendData: failed to get a segment for TCB %p\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) != 0) { + // + // 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) + ) { + DEBUG ( + (EFI_D_INFO, + "TcpToSendData: send FIN to peer for TCB %p in state %s\n", + Tcb, + mTcpStateName[Tcb->State]) + ); + + End++; + } else { + TCP_CLEAR_FLG (Flag, TCP_FLG_FIN); + } + } + + Seg->Seq = Seq; + Seg->End = End; + Seg->Flag = Flag; + + ASSERT (TcpVerifySegment (Nbuf) != 0); + ASSERT (TcpCheckSndQue (&Tcb->SndQue) != 0); + + // + // Don't send an empty segment here. + // + if (Seg->End == Seg->Seq) { + DEBUG ( + (EFI_D_WARN, + "TcpToSendData: created a empty segment for TCB %p, free it now\n", + Tcb) + ); + + NetbufFree (Nbuf); + return Sent; + } + + if (TcpTransmitSegment (Tcb, Nbuf) != 0) { + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + if ((Flag & TCP_FLG_FIN) != 0) { + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT); + } + + goto OnError; + } + + Sent += TCP_SUB_SEQ (End, Seq); + + // + // All the buffers in the SndQue are headless. + // + ASSERT (Nbuf->Tcp != NULL); + + NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD); + Nbuf->Tcp = NULL; + + NetbufFree (Nbuf); + + // + // Update the status in TCB. + // + Tcb->DelayedAck = 0; + + if ((Flag & TCP_FLG_FIN) != 0) { + 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 requires not to update RTT when in loss. + // + if ((Tcb->CongestState == TCP_CONGEST_OPEN) && !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + + DEBUG ( + (EFI_D_INFO, + "TcpToSendData: set RTT measure sequence %d for TCB %p\n", + Seq, + Tcb) + ); + + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); + Tcb->RttSeq = Seq; + Tcb->RttMeasure = 0; + } + + } while (Len == Tcb->SndMss); + + return Sent; + +OnError: + if (Nbuf != NULL) { + NetbufFree (Nbuf); + } + + return Sent; +} + +/** + Send an ACK immediately. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSendAck ( + IN OUT 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[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + + @retval 0 The zero probe segment was sent out successfully. + @retval other An error condition occurred. + +**/ +INTN +TcpSendZeroProbe ( + IN OUT 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[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpToSendAck ( + IN OUT TCP_CB *Tcb + ) +{ + UINT32 TcpNow; + + // + // 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)) { + TcpSendAck (Tcb); + return; + } + + TcpNow = TcpRcvWinNow (Tcb); + + if (TcpNow > TcpRcvWinOld (Tcb)) { + TcpSendAck (Tcb); + return; + } + + DEBUG ( + (EFI_D_INFO, + "TcpToSendAck: scheduled a delayed ACK for TCB %p\n", + Tcb) + ); + + // + // Schedule a delayed ACK. + // + Tcb->DelayedAck++; +} + +/** + Send a RESET segment in response to the segment received. + + @param[in] Tcb Pointer to the TCP_CB of this TCP instance. May be NULL. + @param[in] Head TCP header of the segment that triggers the reset. + @param[in] Len Length of the segment that triggers the reset. + @param[in] Local Local IP address. + @param[in] Remote Remote peer's IP address. + @param[in] Version IP_VERSION_4 indicates TCP is running on IP4 stack, + IP_VERSION_6 indicates TCP is running on IP6 stack. + + @retval 0 A reset was sent or there is 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 EFI_IP_ADDRESS *Local, + IN EFI_IP_ADDRESS *Remote, + IN UINT8 Version + ) +{ + NET_BUF *Nbuf; + TCP_HEAD *Nhead; + UINT16 HeadSum; + + // + // Don't respond to a Reset with reset. + // + if ((Head->Flag & TCP_FLG_RST) != 0) { + 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 + // is associated with it, otherwise derive 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 = (UINT8) (sizeof (TCP_HEAD) >> 2); + Nhead->Res = 0; + Nhead->Wnd = HTONS (0xFFFF); + Nhead->Checksum = 0; + Nhead->Urg = 0; + + if (Version == IP_VERSION_4) { + HeadSum = NetPseudoHeadChecksum (Local->Addr[0], Remote->Addr[0], 6, 0); + } else { + HeadSum = NetIp6PseudoHeadChecksum (&Local->v6, &Remote->v6, 6, 0); + } + + Nhead->Checksum = TcpChecksum (Nbuf, HeadSum); + + TcpSendIpPacket (Tcb, Nbuf, Local, Remote, Version); + + NetbufFree (Nbuf); + + return 0; +} + +/** + Verify that the segment is in good shape. + + @param[in] Nbuf The 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; +} + diff --git a/NetworkPkg/TcpDxe/TcpProto.h b/NetworkPkg/TcpDxe/TcpProto.h new file mode 100644 index 0000000000..88dfbb9d5c --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpProto.h @@ -0,0 +1,342 @@ +/** @file + TCP protocol header file. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#ifndef _TCP_PROTO_H_ +#define _TCP_PROTO_H_ + +/// +/// Tcp states don't change their order. It is used as an +/// index to mTcpOutFlag and other macros. +/// +#define TCP_CLOSED 0 +#define TCP_LISTEN 1 +#define TCP_SYN_SENT 2 +#define TCP_SYN_RCVD 3 +#define TCP_ESTABLISHED 4 +#define TCP_FIN_WAIT_1 5 +#define TCP_FIN_WAIT_2 6 +#define TCP_CLOSING 7 +#define TCP_TIME_WAIT 8 +#define TCP_CLOSE_WAIT 9 +#define TCP_LAST_ACK 10 + + +/// +/// Flags in the TCP header +/// +#define TCP_FLG_FIN 0x01 +#define TCP_FLG_SYN 0x02 +#define TCP_FLG_RST 0x04 +#define TCP_FLG_PSH 0x08 +#define TCP_FLG_ACK 0x10 +#define TCP_FLG_URG 0x20 + + // + // mask for all the flags + // +#define TCP_FLG_FLAG 0x3F + + +#define TCP_CONNECT_REFUSED (-1) ///< TCP error status +#define TCP_CONNECT_RESET (-2) ///< TCP error status +#define TCP_CONNECT_CLOSED (-3) ///< TCP error status + +// +// Current congestion status as suggested by RFC3782. +// +#define TCP_CONGEST_RECOVER 1 ///< During the NewReno fast recovery. +#define TCP_CONGEST_LOSS 2 ///< Retxmit because of retxmit time out. +#define TCP_CONGEST_OPEN 3 ///< TCP is opening its congestion window. + +// +// TCP control flags +// +#define TCP_CTRL_NO_NAGLE 0x0001 ///< Disable Nagle algorithm +#define TCP_CTRL_NO_KEEPALIVE 0x0002 ///< Disable keepalive timer. +#define TCP_CTRL_NO_WS 0x0004 ///< Disable window scale option. +#define TCP_CTRL_RCVD_WS 0x0008 ///< Received a wnd scale option in syn. +#define TCP_CTRL_NO_TS 0x0010 ///< Disable Timestamp option. +#define TCP_CTRL_RCVD_TS 0x0020 ///< Received a Timestamp option in syn. +#define TCP_CTRL_SND_TS 0x0040 ///< Send Timestamp option to remote. +#define TCP_CTRL_SND_URG 0x0080 ///< In urgent send mode. +#define TCP_CTRL_RCVD_URG 0x0100 ///< In urgent receive mode. +#define TCP_CTRL_SND_PSH 0x0200 ///< In PUSH send mode. +#define TCP_CTRL_FIN_SENT 0x0400 ///< FIN is sent. +#define TCP_CTRL_FIN_ACKED 0x0800 ///< FIN is ACKed. +#define TCP_CTRL_TIMER_ON 0x1000 ///< At least one of the timer is on. +#define TCP_CTRL_RTT_ON 0x2000 ///< The RTT measurement is on. +#define TCP_CTRL_ACK_NOW 0x4000 ///< Send the ACK now, don't delay. + +// +// Timer related values +// +#define TCP_TIMER_CONNECT 0 ///< Connection establishment timer. +#define TCP_TIMER_REXMIT 1 ///< Retransmit timer. +#define TCP_TIMER_PROBE 2 ///< Window probe timer. +#define TCP_TIMER_KEEPALIVE 3 ///< Keepalive timer. +#define TCP_TIMER_FINWAIT2 4 ///< FIN_WAIT_2 timer. +#define TCP_TIMER_2MSL 5 ///< TIME_WAIT timer. +#define TCP_TIMER_NUMBER 6 ///< The total number of the TCP timer. +#define TCP_TICK 200 ///< Every TCP tick is 200ms. +#define TCP_TICK_HZ 5 ///< The frequence of TCP tick. +#define TCP_RTT_SHIFT 3 ///< SRTT & RTTVAR scaled by 8. +#define TCP_RTO_MIN TCP_TICK_HZ ///< The minium value of RTO. +#define TCP_RTO_MAX (TCP_TICK_HZ * 60) ///< The maxium value of RTO. +#define TCP_FOLD_RTT 4 ///< Timeout threshod to fold RTT. + +// +// Default values for some timers +// +#define TCP_MAX_LOSS 12 ///< Default max times to retxmit. +#define TCP_KEEPALIVE_IDLE_MIN (TCP_TICK_HZ * 60 * 60 * 2) ///< First keepalive. +#define TCP_KEEPALIVE_PERIOD (TCP_TICK_HZ * 60) +#define TCP_MAX_KEEPALIVE 8 +#define TCP_FIN_WAIT2_TIME (2 * TCP_TICK_HZ) +#define TCP_TIME_WAIT_TIME (2 * TCP_TICK_HZ) +#define TCP_PAWS_24DAY (24 * 24 * 60 * 60 * TCP_TICK_HZ) +#define 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 +// +#define TCP_MAX_HEAD 192 + +// +// Value ranges for some control option +// +#define TCP_RCV_BUF_SIZE (2 * 1024 * 1024) +#define TCP_RCV_BUF_SIZE_MIN (8 * 1024) +#define TCP_SND_BUF_SIZE (2 * 1024 * 1024) +#define TCP_SND_BUF_SIZE_MIN (8 * 1024) +#define TCP_BACKLOG 10 +#define TCP_BACKLOG_MIN 5 +#define TCP_MAX_LOSS_MIN 6 +#define TCP_CONNECT_TIME_MIN (60 * TCP_TICK_HZ) +#define TCP_MAX_KEEPALIVE_MIN 4 +#define TCP_KEEPALIVE_IDLE_MAX (TCP_TICK_HZ * 60 * 60 * 4) +#define TCP_KEEPALIVE_PERIOD_MIN (TCP_TICK_HZ * 30) +#define TCP_FIN_WAIT2_TIME_MAX (4 * TCP_TICK_HZ) +#define TCP_TIME_WAIT_TIME_MAX (60 * TCP_TICK_HZ) + +/// +/// 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))) + +// +// Check whether Flag is on +// +#define TCP_FLG_ON(Value, Flag) ((BOOLEAN) (((Value) & (Flag)) != 0)) +// +// Set and Clear operation on a Flag +// +#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, Ver) \ + (((Pa)->Port == (Pb)->Port) && TcpIsIpEqual(&((Pa)->Ip), &((Pb)->Ip), Ver)) + +// +// Test whether Pa matches Pb, or Pa is more specific +// than pb. Zero means wildcard. +// +#define TCP_PEER_MATCH(Pa, Pb, Ver) \ + ( \ + (((Pb)->Port == 0) || ((Pb)->Port == (Pa)->Port)) && \ + (TcpIsIpZero (&((Pb)->Ip), Ver) || TcpIsIpEqual (&((Pb)->Ip), &((Pa)->Ip), Ver)) \ + ) + +#define TCP_TIMER_ON(Flag, Timer) ((Flag) & (1 << (Timer))) +#define TCP_SET_TIMER(Flag, Timer) ((Flag) = (UINT16) ((Flag) | (1 << (Timer)))) +#define TCP_CLEAR_TIMER(Flag, Timer) ((Flag) = (UINT16) ((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 + +/// +/// TCP segmentation data. +/// +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 field in the segment. + UINT8 Flag; ///< TCP header flags. + UINT16 Urg; ///< Valid if URG flag is set. + UINT32 Wnd; ///< TCP window size field. +} TCP_SEG; + +/// +/// Network endpoint, IP plus Port structure. +/// +typedef struct _TCP_PEER { + EFI_IP_ADDRESS Ip; ///< IP address, in network byte order. + TCP_PORTNO Port; ///< Port number, in network byte order. +} TCP_PEER; + +typedef struct _TCP_CONTROL_BLOCK TCP_CB; + +/// +/// TCP control block: it includes various states. +/// +struct _TCP_CONTROL_BLOCK { + LIST_ENTRY List; ///< Back and forward link entry + TCP_CB *Parent; ///< The parent TCP_CB structure + + SOCKET *Sk; ///< The socket it controled. + TCP_PEER LocalEnd; ///< Local endpoint. + TCP_PEER RemoteEnd;///< Remote endpoint. + + LIST_ENTRY SndQue; ///< Retxmission queue. + LIST_ENTRY RcvQue; ///< Reassemble queue. + UINT32 CtrlFlag; ///< Control flags, such as NO_NAGLE. + INT32 Error; ///< Soft error status, such as 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, + ///< do 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; ///< Countdown offset for the nearest timer. + UINT32 Idle; ///< How long the connection is in idle. + UINT32 ProbeTime; ///< The time out value for current window prober. + BOOLEAN ProbeTimerOn;///< If TRUE, the probe time is on. + + // + // 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. + + // + // RFC2988 defined variables. about RTT measurement + // + TCP_SEQNO RttSeq; ///< The seq of measured segment now. + UINT32 RttMeasure; ///< Currently measured RTT in heartbeats. + 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 timeout. + UINT32 TimeWaitTimeout; ///< The TIME_WAIT timeout. + UINT32 ConnectTimeout; ///< The connect establishment timeout. + + // + // configuration for tcp provided by user + // + BOOLEAN UseDefaultAddr; + UINT8 Tos; + UINT8 Ttl; + EFI_IPv4_ADDRESS SubnetMask; + + IP_IO_IP_INFO *IpInfo; ///< Pointer reference to Ip used to send pkt + UINT32 Tick; ///< 1 tick = 200ms +}; + +#endif diff --git a/NetworkPkg/TcpDxe/TcpTimer.c b/NetworkPkg/TcpDxe/TcpTimer.c new file mode 100644 index 0000000000..cc52ad924c --- /dev/null +++ b/NetworkPkg/TcpDxe/TcpTimer.c @@ -0,0 +1,593 @@ +/** @file + TCP timer related functions. + + Copyright (c) 2009 - 2010, 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. + +**/ + +#include "TcpMain.h" + +UINT32 mTcpTick = 1000; + +/** + Connect timeout handler. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpConnectTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for TCP retransmission timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpRexmitTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for window probe timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpProbeTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for keepalive timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpKeepaliveTimeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for FIN_WAIT_2 timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpFinwait2Timeout ( + IN OUT TCP_CB *Tcb + ); + +/** + Timeout handler for 2MSL timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +Tcp2MSLTimeout ( + IN OUT TCP_CB *Tcb + ); + +TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = { + TcpConnectTimeout, + TcpRexmitTimeout, + TcpProbeTimeout, + TcpKeepaliveTimeout, + TcpFinwait2Timeout, + Tcp2MSLTimeout, +}; + +/** + Close the TCP connection. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClose ( + IN OUT TCP_CB *Tcb + ) +{ + NetbufFreeList (&Tcb->SndQue); + NetbufFreeList (&Tcb->RcvQue); + + TcpSetState (Tcb, TCP_CLOSED); +} + +/** + Backoff the RTO. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpBackoffRto ( + IN OUT TCP_CB *Tcb + ) +{ + // + // Fold the RTT estimate if too many times, the estimate + // may be wrong, fold it. So the next time a valid + // measurement is sampled, we can start fresh. + // + if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) { + Tcb->RttVar += Tcb->SRtt >> 2; + Tcb->SRtt = 0; + } + + Tcb->Rto <<= 1; + + if (Tcb->Rto < TCP_RTO_MIN) { + + Tcb->Rto = TCP_RTO_MIN; + } else if (Tcb->Rto > TCP_RTO_MAX) { + + Tcb->Rto = TCP_RTO_MAX; + } +} + +/** + Connect timeout handler. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpConnectTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + if (!TCP_CONNECTED (Tcb->State)) { + DEBUG ( + (EFI_D_ERROR, + "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n", + Tcb) + ); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + if (TCP_SYN_RCVD == Tcb->State) { + DEBUG ( + (EFI_D_WARN, + "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n", + Tcb) + ); + + TcpResetConnection (Tcb); + + } + + TcpClose (Tcb); + } +} + + +/** + Timeout handler for TCP retransmission timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpRexmitTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + UINT32 FlightSize; + + DEBUG ( + (EFI_D_WARN, + "TcpRexmitTimeout: transmission timeout for TCB %p\n", + Tcb) + ); + + // + // Set the congestion window. FlightSize is the + // amount of data that has been sent but not + // yet ACKed. + // + FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); + Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2); + + Tcb->CWnd = Tcb->SndMss; + Tcb->LossRecover = Tcb->SndNxt; + + Tcb->LossTimes++; + if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) { + + DEBUG ( + (EFI_D_ERROR, + "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n", + Tcb) + ); + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpBackoffRto (Tcb); + TcpRetransmit (Tcb, Tcb->SndUna); + TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); + + Tcb->CongestState = TCP_CONGEST_LOSS; + + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); +} + +/** + Timeout handler for window probe timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpProbeTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + // + // This is the timer for sender's SWSA. RFC1122 requires + // a timer set for sender's SWSA, and suggest combine it + // with window probe timer. If data is sent, don't set + // the probe timer, since retransmit timer is on. + // + if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) { + + ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0); + Tcb->ProbeTimerOn = FALSE; + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetProbeTimer (Tcb); +} + +/** + Timeout handler for keepalive timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpKeepaliveTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + Tcb->KeepAliveProbes++; + + // + // Too many Keep-alive probes, drop the connection + // + if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) { + + if (EFI_ABORTED == Tcb->Sk->SockError) { + SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); + } + + TcpClose (Tcb); + return ; + } + + TcpSendZeroProbe (Tcb); + TcpSetKeepaliveTimer (Tcb); +} + +/** + Timeout handler for FIN_WAIT_2 timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpFinwait2Timeout ( + IN OUT TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n", + Tcb) + ); + + TcpClose (Tcb); +} + +/** + Timeout handler for 2MSL timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +Tcp2MSLTimeout ( + IN OUT TCP_CB *Tcb + ) +{ + DEBUG ( + (EFI_D_WARN, + "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n", + Tcb) + ); + + TcpClose (Tcb); +} + +/** + Update the timer status and the next expire time according to the timers + to expire in a specific future time slot. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpUpdateTimer ( + IN OUT TCP_CB *Tcb + ) +{ + UINT16 Index; + + // + // Don't use a too large value to init NextExpire + // since mTcpTick wraps around as sequence no does. + // + Tcb->NextExpire = TCP_EXPIRE_TIME; + TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && + TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire) + ) { + + Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick); + TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); + } + } +} + +/** + Enable a TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be enabled. + @param[in] TimeOut The timeout value of this timer. + +**/ +VOID +TcpSetTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer, + IN UINT32 TimeOut + ) +{ + TCP_SET_TIMER (Tcb->EnabledTimer, Timer); + Tcb->Timer[Timer] = mTcpTick + TimeOut; + + TcpUpdateTimer (Tcb); +} + +/** + Clear one TCP timer. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + @param[in] Timer The index of the timer to be cleared. + +**/ +VOID +TcpClearTimer ( + IN OUT TCP_CB *Tcb, + IN UINT16 Timer + ) +{ + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer); + TcpUpdateTimer (Tcb); +} + +/** + Clear all TCP timers. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpClearAllTimer ( + IN OUT TCP_CB *Tcb + ) +{ + Tcb->EnabledTimer = 0; + TcpUpdateTimer (Tcb); +} + +/** + Enable the window prober timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetProbeTimer ( + IN OUT TCP_CB *Tcb + ) +{ + if (!Tcb->ProbeTimerOn) { + Tcb->ProbeTime = Tcb->Rto; + Tcb->ProbeTimerOn = TRUE; + + } else { + Tcb->ProbeTime <<= 1; + } + + if (Tcb->ProbeTime < TCP_RTO_MIN) { + + Tcb->ProbeTime = TCP_RTO_MIN; + } else if (Tcb->ProbeTime > TCP_RTO_MAX) { + + Tcb->ProbeTime = TCP_RTO_MAX; + } + + TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime); +} + +/** + Enable the keepalive timer and set the timeout value. + + @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. + +**/ +VOID +TcpSetKeepaliveTimer ( + IN OUT TCP_CB *Tcb + ) +{ + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) { + return ; + + } + + // + // Set the timer to KeepAliveIdle if either + // 1. the keepalive timer is off + // 2. The keepalive timer is on, but the idle + // is less than KeepAliveIdle, that means the + // connection is alive since our last probe. + // + if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) || + (Tcb->Idle < Tcb->KeepAliveIdle) + ) { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle); + Tcb->KeepAliveProbes = 0; + + } else { + + TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod); + } +} + +/** + Heart beat timer handler. + + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTickingDpc ( + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + TCP_CB *Tcb; + INT16 Index; + + mTcpTick++; + mTcpGlobalIss += TCP_ISS_INCREMENT_2; + + // + // Don't use LIST_FOR_EACH, which isn't delete safe. + // + for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) { + + Next = Entry->ForwardLink; + + Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); + + if (Tcb->State == TCP_CLOSED) { + continue; + } + // + // The connection is doing RTT measurement. + // + if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { + Tcb->RttMeasure++; + } + + Tcb->Idle++; + + if (Tcb->DelayedAck != 0) { + TcpSendAck (Tcb); + } + + if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) { + Tcb->Tick--; + } + + // + // No timer is active or no timer expired + // + if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) { + + continue; + } + + // + // Call the timeout handler for each expired timer. + // + for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { + + if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) { + // + // disable the timer before calling the handler + // in case the handler enables it again. + // + TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index); + mTcpTimerHandler[Index](Tcb); + + // + // The Tcb may have been deleted by the timer, or + // no other timer is set. + // + if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) { + break; + } + } + } + + // + // If the Tcb still exist or some timer is set, update the timer + // + if (Index == TCP_TIMER_NUMBER) { + TcpUpdateTimer (Tcb); + } + } +} + +/** + Heart beat timer handler, queues the DPC at TPL_CALLBACK. + + @param[in] Event Timer event signaled, ignored. + @param[in] Context Context of the timer event, ignored. + +**/ +VOID +EFIAPI +TcpTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context); +} + -- cgit v1.2.3