summaryrefslogtreecommitdiff
path: root/NetworkPkg/TcpDxe
diff options
context:
space:
mode:
authorhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>2010-11-01 06:13:54 +0000
committerhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>2010-11-01 06:13:54 +0000
commita3bcde70e6dc69000f85cc5deee98101d2ae200a (patch)
tree693709a5293f80b320931693b34b2d6983cfcf4b /NetworkPkg/TcpDxe
parent12873d57666d0beff41959a1fb8f9062016f0983 (diff)
downloadedk2-platforms-a3bcde70e6dc69000f85cc5deee98101d2ae200a.tar.xz
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
Diffstat (limited to 'NetworkPkg/TcpDxe')
-rw-r--r--NetworkPkg/TcpDxe/ComponentName.c304
-rw-r--r--NetworkPkg/TcpDxe/SockImpl.c1230
-rw-r--r--NetworkPkg/TcpDxe/SockImpl.h103
-rw-r--r--NetworkPkg/TcpDxe/SockInterface.c999
-rw-r--r--NetworkPkg/TcpDxe/Socket.h924
-rw-r--r--NetworkPkg/TcpDxe/TcpDispatcher.c861
-rw-r--r--NetworkPkg/TcpDxe/TcpDriver.c891
-rw-r--r--NetworkPkg/TcpDxe/TcpDriver.h230
-rw-r--r--NetworkPkg/TcpDxe/TcpDxe.inf83
-rw-r--r--NetworkPkg/TcpDxe/TcpFunc.h724
-rw-r--r--NetworkPkg/TcpDxe/TcpInput.c1592
-rw-r--r--NetworkPkg/TcpDxe/TcpIo.c190
-rw-r--r--NetworkPkg/TcpDxe/TcpMain.c1074
-rw-r--r--NetworkPkg/TcpDxe/TcpMain.h758
-rw-r--r--NetworkPkg/TcpDxe/TcpMisc.c1281
-rw-r--r--NetworkPkg/TcpDxe/TcpOption.c374
-rw-r--r--NetworkPkg/TcpDxe/TcpOption.h145
-rw-r--r--NetworkPkg/TcpDxe/TcpOutput.c1219
-rw-r--r--NetworkPkg/TcpDxe/TcpProto.h342
-rw-r--r--NetworkPkg/TcpDxe/TcpTimer.c593
20 files changed, 13917 insertions, 0 deletions
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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Uefi.h>
+
+#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
+
+#include <Library/NetLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DpcLib.h>
+
+#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.<BR>
+
+ 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 <SrcIp, SrcPort, DstIp, DstPort> is
+ // determined, construct the IP device path and install it.
+ //
+ Status = TcpInstallDevicePath (Sk);
+ if (EFI_ERROR (Status)) {
+ goto OnExit;
+ }
+
+ //
+ // update state of Tcb and socket
+ //
+ if (((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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+#
+# 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.<BR>
+
+ 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 <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @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 <Addr Port> 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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 <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+#include <Library/IpIoLib.h>
+#include <Library/DevicePathLib.h>
+
+#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.<BR>
+
+ 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 <Ip, Port> equals to <IN Addr, IN Port>.
+
+ @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 <Addr Port> 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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.<BR>
+
+ 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);
+}
+