summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/ComponentName.c522
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.c1233
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.h115
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockInterface.c1126
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/Socket.h917
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDispatcher.c891
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.c995
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.h290
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.inf86
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.uni18
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxeExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpFunc.h693
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpInput.c1707
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpIo.c186
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.c1106
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.h777
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMisc.c1042
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.c338
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.h123
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOutput.c1251
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpProto.h344
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpTimer.c587
22 files changed, 14361 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/ComponentName.c
new file mode 100644
index 00000000..60adabd3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/ComponentName.c
@@ -0,0 +1,522 @@
+/** @file
+ Implementation of protocols EFI_COMPONENT_NAME_PROTOCOL and
+ EFI_COMPONENT_NAME2_PROTOCOL.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 NULL.
+
+ @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
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gTcpControllerNameTable = 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)
+ );
+}
+
+/**
+ Update the component name for the Tcp4 child handle.
+
+ @param Tcp4[in] A pointer to the EFI_TCP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateTcp4Name (
+ IN EFI_TCP4_PROTOCOL *Tcp4
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[80];
+ EFI_TCP4_CONFIG_DATA Tcp4ConfigData;
+
+ if (Tcp4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer as:
+ // TCPv4 (SrcPort=59, DestPort=60, ActiveFlag=TRUE)
+ //
+ ZeroMem (&Tcp4ConfigData, sizeof (Tcp4ConfigData));
+ Status = Tcp4->GetModeData (Tcp4, NULL, &Tcp4ConfigData, NULL, NULL, NULL);
+ if (!EFI_ERROR (Status)) {
+ UnicodeSPrint (HandleName, sizeof (HandleName),
+ L"TCPv4 (SrcPort=%d, DestPort=%d, ActiveFlag=%s)",
+ Tcp4ConfigData.AccessPoint.StationPort,
+ Tcp4ConfigData.AccessPoint.RemotePort,
+ (Tcp4ConfigData.AccessPoint.ActiveFlag ? L"TRUE" : L"FALSE")
+ );
+ } else if (Status == EFI_NOT_STARTED) {
+ UnicodeSPrint (
+ HandleName,
+ sizeof (HandleName),
+ L"TCPv4 (Not started)"
+ );
+ } else {
+ return Status;
+ }
+
+ if (gTcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gTcpControllerNameTable);
+ gTcpControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gTcpComponentName.SupportedLanguages,
+ &gTcpControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gTcpComponentName2.SupportedLanguages,
+ &gTcpControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ Update the component name for the Tcp6 child handle.
+
+ @param Tcp6[in] A pointer to the EFI_TCP6_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateTcp6Name (
+ IN EFI_TCP6_PROTOCOL *Tcp6
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[80];
+ EFI_TCP6_CONFIG_DATA Tcp6ConfigData;
+
+ if (Tcp6 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer.
+ //
+ ZeroMem (&Tcp6ConfigData, sizeof (Tcp6ConfigData));
+ Status = Tcp6->GetModeData (Tcp6, NULL, &Tcp6ConfigData, NULL, NULL, NULL);
+ if (!EFI_ERROR (Status)) {
+ UnicodeSPrint (HandleName, sizeof (HandleName),
+ L"TCPv6(SrcPort=%d, DestPort=%d, ActiveFlag=%d)",
+ Tcp6ConfigData.AccessPoint.StationPort,
+ Tcp6ConfigData.AccessPoint.RemotePort,
+ Tcp6ConfigData.AccessPoint.ActiveFlag
+ );
+ } else if (Status == EFI_NOT_STARTED) {
+ UnicodeSPrint (HandleName, sizeof (HandleName), L"TCPv6(Not started)");
+ } else {
+ return Status;
+ }
+
+
+ if (gTcpControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gTcpControllerNameTable);
+ gTcpControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gTcpComponentName.SupportedLanguages,
+ &gTcpControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gTcpComponentName2.SupportedLanguages,
+ &gTcpControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ 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 NULL.
+
+ @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_STATUS Status;
+ EFI_TCP4_PROTOCOL *Tcp4;
+ EFI_TCP6_PROTOCOL *Tcp6;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiIp6ProtocolGuid
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ (VOID **)&Tcp6,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateTcp6Name (Tcp6);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Make sure this driver is currently managing ControllHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiIp4ProtocolGuid
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ (VOID **)&Tcp4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateTcp4Name (Tcp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gTcpControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gTcpComponentName)
+ );
+}
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.c
new file mode 100644
index 00000000..72ad1cdb
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.c
@@ -0,0 +1,1233 @@
+/** @file
+ Implementation of the Socket.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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] Arg The Arg parameter forwarded by NetbufFromExt(). Ignored.
+
+**/
+VOID
+EFIAPI
+SockFreeFoo (
+ IN VOID *Arg
+ )
+{
+ 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 retrieved.
+
+**/
+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)
+ );
+
+ //
+ // Process 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_NET,
+ "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;
+ }
+}
+
+/**
+ Cancel the tokens in the specific token list.
+
+ @param[in] Token Pointer to the Token. If NULL, all tokens
+ in SpecifiedTokenList will be canceled.
+ @param[in, out] SpecifiedTokenList Pointer to the token list to be checked.
+
+ @retval EFI_SUCCESS Cancel the tokens in the specific token listsuccessfully.
+ @retval EFI_NOT_FOUND The Token is not found in SpecifiedTokenList.
+
+**/
+EFI_STATUS
+SockCancelToken (
+ IN SOCK_COMPLETION_TOKEN *Token,
+ IN OUT LIST_ENTRY *SpecifiedTokenList
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Entry;
+ SOCK_TOKEN *SockToken;
+
+ Status = EFI_SUCCESS;
+ Entry = NULL;
+ SockToken = NULL;
+
+ if (IsListEmpty (SpecifiedTokenList) && Token != NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Iterate through the SpecifiedTokenList.
+ //
+ Entry = SpecifiedTokenList->ForwardLink;
+ while (Entry != SpecifiedTokenList) {
+ SockToken = NET_LIST_USER_STRUCT (Entry, SOCK_TOKEN, TokenList);
+
+ if (Token == NULL) {
+ SIGNAL_TOKEN (SockToken->Token, EFI_ABORTED);
+ RemoveEntryList (&SockToken->TokenList);
+ FreePool (SockToken);
+
+ Entry = SpecifiedTokenList->ForwardLink;
+ Status = EFI_SUCCESS;
+ } else {
+ if (Token == (VOID *) SockToken->Token) {
+ SIGNAL_TOKEN (Token, EFI_ABORTED);
+ RemoveEntryList (&(SockToken->TokenList));
+ FreePool (SockToken);
+
+ return EFI_SUCCESS;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ Entry = Entry->ForwardLink;
+ }
+ }
+
+ ASSERT (IsListEmpty (SpecifiedTokenList) || Token != NULL);
+
+ return Status;
+}
+
+/**
+ 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_NET,
+ "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
+ )
+{
+ ASSERT (SockStream == Sock->Type);
+
+ //
+ // Flush the completion token buffered
+ // by sock and rcv, snd buffer
+ //
+ if (!SOCK_IS_UNCONFIGURED (Sock)) {
+
+ SockConnFlush (Sock);
+ SockSetState (Sock, SO_CLOSED);
+ Sock->ConfigureState = SO_UNCONFIGURED;
+
+ }
+ //
+ // Destroy 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,
+ "SockDestroy: Delete a unaccepted socket from parent now conncnt is %d\n",
+ Sock->Parent->ConnCnt)
+ );
+
+ Sock->Parent = NULL;
+ }
+
+ 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.h
new file mode 100644
index 00000000..4ee47e4c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockImpl.h
@@ -0,0 +1,115 @@
+/** @file
+ The function declaration that provided for Socket Interface.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _SOCK_IMPL_H_
+#define _SOCK_IMPL_H_
+
+#include "Socket.h"
+#include "TcpMain.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
+ );
+
+/**
+ Cancel the tokens in the specific token list.
+
+ @param[in] Token Pointer to the Token. If NULL, all tokens
+ in SpecifiedTokenList will be canceled.
+ @param[in, out] SpecifiedTokenList Pointer to the token list to be checked.
+
+ @retval EFI_SUCCESS Cancel the tokens in the specific token listsuccessfully.
+ @retval EFI_NOT_FOUND The Token is not found in SpecifiedTokenList.
+
+**/
+EFI_STATUS
+SockCancelToken (
+ IN SOCK_COMPLETION_TOKEN *Token,
+ IN OUT LIST_ENTRY *SpecifiedTokenList
+ );
+
+/**
+ 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 occurred.
+
+**/
+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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockInterface.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockInterface.c
new file mode 100644
index 00000000..42cf0f10
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/SockInterface.c
@@ -0,0 +1,1126 @@
+/** @file
+ Interface function of the Socket.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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;
+}
+
+/**
+ Destroy 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;
+ TCP_PROTO_DATA *ProtoData;
+ TCP_CB *Tcb;
+ EFI_GUID *IpProtocolGuid;
+ EFI_GUID *TcpProtocolGuid;
+ VOID *SockProtocol;
+
+ ASSERT ((Sock != NULL) && (Sock->ProtoHandler != NULL));
+
+ if (Sock->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ Sock->InDestroy = TRUE;
+
+ if (Sock->IpVersion == IP_VERSION_4) {
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+ ProtoData = (TCP_PROTO_DATA *) Sock->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ //
+ // Close the IP protocol.
+ //
+ gBS->CloseProtocol (
+ Tcb->IpInfo->ChildHandle,
+ IpProtocolGuid,
+ ProtoData->TcpService->IpIo->Image,
+ Sock->SockHandle
+ );
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ //
+ // 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,
+ "SockDestroyChild: Open protocol installed on socket failed with %r\n",
+ Status)
+ );
+ }
+
+ //
+ // Uninstall the protocol installed on this sock
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+
+
+ 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->InDestroy = 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 initial data SockInitData and protocol specific
+ data ProtoData.
+
+ @param[in] SockInitData Initial data to setting the socket.
+
+ @return Pointer to the newly created socket. If NULL, an error condition occurred.
+
+**/
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData
+ )
+{
+ SOCKET *Sock;
+ EFI_STATUS Status;
+ VOID *SockProtocol;
+ EFI_GUID *TcpProtocolGuid;
+
+ //
+ // 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)
+ );
+ goto ERROR;
+ }
+ //
+ // inform the protocol layer to attach the socket
+ // with a new protocol control block
+ //
+ Status = Sock->ProtoHandler (Sock, SOCK_ATTACH, NULL);
+ EfiReleaseLock (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCreateChild: Protocol failed to attach a socket with %r\n",
+ Status)
+ );
+ goto ERROR;
+ }
+
+ return Sock;
+
+ERROR:
+
+ if (Sock->DestroyCallback != NULL) {
+ Sock->DestroyCallback (Sock, Sock->Context);
+ }
+
+ if (Sock->IpVersion == IP_VERSION_4) {
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ gBS->OpenProtocol (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ &SockProtocol,
+ Sock->DriverBinding,
+ Sock->SockHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ //
+ // Uninstall the protocol installed on this sock
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ Sock->SockHandle,
+ TcpProtocolGuid,
+ SockProtocol,
+ NULL
+ );
+ SockDestroy (Sock);
+ return NULL;
+}
+
+/**
+ 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
+ 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 accepted 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_NET,
+ "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) {
+ SockProcessRcvToken (Sock, RcvToken);
+
+ 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;
+}
+
+/**
+ Abort the socket associated connection, listen, transmission or receive request.
+
+ @param[in, out] Sock Pointer to the socket to abort.
+ @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
+SockCancel (
+ IN OUT SOCKET *Sock,
+ IN VOID *Token
+ )
+{
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ ASSERT (SockStream == Sock->Type);
+
+ Status = EfiAcquireLockOrFail (&(Sock->Lock));
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "SockCancel: Get the access for socket failed with %r",
+ Status)
+ );
+
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (SOCK_IS_UNCONFIGURED (Sock)) {
+ Status = EFI_NOT_STARTED;
+ goto Exit;
+ }
+
+ //
+ // 1. Check ConnectionToken.
+ //
+ if (Token == NULL || (SOCK_COMPLETION_TOKEN *) Token == Sock->ConnectionToken) {
+ if (Sock->ConnectionToken != NULL) {
+ SIGNAL_TOKEN (Sock->ConnectionToken, EFI_ABORTED);
+ Sock->ConnectionToken = NULL;
+ }
+
+ if (Token != NULL) {
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+ }
+
+ //
+ // 2. Check ListenTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->ListenTokenList);
+ if (Token != NULL && !EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // 3. Check RcvTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->RcvTokenList);
+ if (Token != NULL && !EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // 4. Check SndTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->SndTokenList);
+ if (Token != NULL && !EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // 5. Check ProcessingSndTokenList.
+ //
+ Status = SockCancelToken (Token, &Sock->ProcessingSndTokenList);
+
+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);
+}
+
+/**
+ 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/Socket.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/Socket.h
new file mode 100644
index 00000000..f059802b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/Socket.h
@@ -0,0 +1,917 @@
+/** @file
+ Common head file for TCP socket.
+
+ Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 unconfigured.
+ @retval FALSE The socket is not unconfigured.
+
+**/
+#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 configured
+ @retval FALSE The socket is not configured
+
+**/
+#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 configured to active mode.
+ @retval FALSE The socket is not configured 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 configured to passive mode.
+ @retval FALSE The socket is not configured 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 water 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 function 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 occurred.
+
+**/
+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 water mark of send buffer
+ UINT32 RcvBufferSize; ///< The high water 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 destroyed
+ 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 TCP4 and TCP6 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 InDestroy;
+
+ //
+ // 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; ///< TCP4 or TCP6 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 destroyed
+ 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 pointer 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 operations 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 initial data SockInitData and protocol specific
+ data ProtoData.
+
+ @param[in] SockInitData Initial data to setting the socket.
+
+ @return Pointer to the newly created socket. If NULL, an error condition occurred.
+
+**/
+SOCKET *
+SockCreateChild (
+ IN SOCK_INIT_DATA *SockInitData
+ );
+
+/**
+ Destroy 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
+ 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
+ );
+
+/**
+ Abort the socket associated connection, listen, transmission or receive request.
+
+ @param[in, out] Sock Pointer to the socket to abort.
+ @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
+SockCancel (
+ IN OUT SOCKET *Sock,
+ IN VOID *Token
+ );
+
+/**
+ 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
+ );
+
+/**
+ 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDispatcher.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDispatcher.c
new file mode 100644
index 00000000..2d20fe6a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDispatcher.c
@@ -0,0 +1,891 @@
+/** @file
+ The implementation of a dispatch routine for processing TCP requests.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "TcpMain.h"
+
+/**
+ Add or remove a route entry in the IP route table associated with this TCP instance.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in] RouteInfo Pointer to the route information to be processed.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_NOT_STARTED The driver instance has not been started.
+ @retval EFI_NO_MAPPING When using the default address, configuration(DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_OUT_OF_RESOURCES Could not add the entry to the routing table.
+ @retval EFI_NOT_FOUND This route is not in the routing table
+ (when RouteInfo->DeleteRoute is TRUE).
+ @retval EFI_ACCESS_DENIED The route is already defined in the routing table
+ (when RouteInfo->DeleteRoute is FALSE).
+**/
+EFI_STATUS
+Tcp4Route (
+ IN TCP_CB *Tcb,
+ IN TCP4_ROUTE_INFO *RouteInfo
+ )
+{
+ IP_IO_IP_PROTOCOL Ip;
+
+ Ip = Tcb->IpInfo->Ip;
+
+ ASSERT (Ip.Ip4!= NULL);
+
+ return Ip.Ip4->Routes (
+ Ip.Ip4,
+ RouteInfo->DeleteRoute,
+ RouteInfo->SubnetAddress,
+ RouteInfo->SubnetMask,
+ RouteInfo->GatewayAddress
+ );
+
+}
+
+/**
+ Get the operational settings of this TCPv4 instance.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Mode Pointer to the buffer to store the operational
+ settings.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+
+**/
+EFI_STATUS
+Tcp4GetMode (
+ IN TCP_CB *Tcb,
+ IN OUT TCP4_MODE_DATA *Mode
+ )
+{
+ SOCKET *Sock;
+ EFI_TCP4_CONFIG_DATA *ConfigData;
+ EFI_TCP4_ACCESS_POINT *AccessPoint;
+ EFI_TCP4_OPTION *Option;
+ EFI_IP4_PROTOCOL *Ip;
+
+ Sock = Tcb->Sk;
+
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp4ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->Tcp4State != NULL) {
+ *(Mode->Tcp4State) = (EFI_TCP4_CONNECTION_STATE) Tcb->State;
+ }
+
+ if (Mode->Tcp4ConfigData != NULL) {
+
+ ConfigData = Mode->Tcp4ConfigData;
+ AccessPoint = &(ConfigData->AccessPoint);
+ Option = ConfigData->ControlOption;
+
+ ConfigData->TypeOfService = Tcb->Tos;
+ ConfigData->TimeToLive = Tcb->Ttl;
+
+ AccessPoint->UseDefaultAddress = Tcb->UseDefaultAddr;
+
+ IP4_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);
+
+ IP4_COPY_ADDRESS (&AccessPoint->SubnetMask, &Tcb->SubnetMask);
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
+
+ IP4_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);
+
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
+
+ if (Option != NULL) {
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
+
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
+ Option->DataRetries = Tcb->MaxRexmit;
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
+
+ Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));
+ Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));
+ Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));
+
+ Option->EnableSelectiveAck = FALSE;
+ Option->EnablePathMtuDiscovery = FALSE;
+ }
+ }
+
+ Ip = Tcb->IpInfo->Ip.Ip4;
+ ASSERT (Ip != NULL);
+
+ return Ip->GetModeData (Ip, Mode->Ip4ModeData, Mode->MnpConfigData, Mode->SnpModeData);
+}
+
+/**
+ Get the operational settings of this TCPv6 instance.
+
+ @param[in] Tcb Pointer to the TCP_CB of this TCP instance.
+ @param[in, out] Mode Pointer to the buffer to store the operational
+ settings.
+
+ @retval EFI_SUCCESS The mode data was read.
+ @retval EFI_NOT_STARTED No configuration data is available because this
+ instance hasn't been started.
+
+**/
+EFI_STATUS
+Tcp6GetMode (
+ IN TCP_CB *Tcb,
+ IN OUT TCP6_MODE_DATA *Mode
+ )
+{
+ SOCKET *Sock;
+ EFI_TCP6_CONFIG_DATA *ConfigData;
+ EFI_TCP6_ACCESS_POINT *AccessPoint;
+ EFI_TCP6_OPTION *Option;
+ EFI_IP6_PROTOCOL *Ip;
+
+ Sock = Tcb->Sk;
+
+ if (!SOCK_IS_CONFIGURED (Sock) && (Mode->Tcp6ConfigData != NULL)) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Mode->Tcp6State != NULL) {
+ *(Mode->Tcp6State) = (EFI_TCP6_CONNECTION_STATE) (Tcb->State);
+ }
+
+ if (Mode->Tcp6ConfigData != NULL) {
+
+ ConfigData = Mode->Tcp6ConfigData;
+ AccessPoint = &(ConfigData->AccessPoint);
+ Option = ConfigData->ControlOption;
+
+ ConfigData->TrafficClass = Tcb->Tos;
+ ConfigData->HopLimit = Tcb->Ttl;
+
+ AccessPoint->StationPort = NTOHS (Tcb->LocalEnd.Port);
+ AccessPoint->RemotePort = NTOHS (Tcb->RemoteEnd.Port);
+ AccessPoint->ActiveFlag = (BOOLEAN) (Tcb->State != TCP_LISTEN);
+
+ IP6_COPY_ADDRESS (&AccessPoint->StationAddress, &Tcb->LocalEnd.Ip);
+ IP6_COPY_ADDRESS (&AccessPoint->RemoteAddress, &Tcb->RemoteEnd.Ip);
+
+ if (Option != NULL) {
+ Option->ReceiveBufferSize = GET_RCV_BUFFSIZE (Tcb->Sk);
+ Option->SendBufferSize = GET_SND_BUFFSIZE (Tcb->Sk);
+ Option->MaxSynBackLog = GET_BACKLOG (Tcb->Sk);
+
+ Option->ConnectionTimeout = Tcb->ConnectTimeout / TCP_TICK_HZ;
+ Option->DataRetries = Tcb->MaxRexmit;
+ Option->FinTimeout = Tcb->FinWait2Timeout / TCP_TICK_HZ;
+ Option->TimeWaitTimeout = Tcb->TimeWaitTimeout / TCP_TICK_HZ;
+ Option->KeepAliveProbes = Tcb->MaxKeepAlive;
+ Option->KeepAliveTime = Tcb->KeepAliveIdle / TCP_TICK_HZ;
+ Option->KeepAliveInterval = Tcb->KeepAlivePeriod / TCP_TICK_HZ;
+
+ Option->EnableNagle = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE));
+ Option->EnableTimeStamp = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_TS));
+ Option->EnableWindowScaling = (BOOLEAN) (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_WS));
+
+ Option->EnableSelectiveAck = FALSE;
+ Option->EnablePathMtuDiscovery = FALSE;
+ }
+ }
+
+ Ip = Tcb->IpInfo->Ip.Ip6;
+ ASSERT (Ip != NULL);
+
+ return Ip->GetModeData (Ip, Mode->Ip6ModeData, Mode->MnpConfigData, Mode->SnpModeData);
+}
+
+/**
+ If TcpAp->StationPort isn't zero, check whether the access point
+ is registered, else generate a random station port for this
+ access point.
+
+ @param[in] TcpAp Pointer to the access point.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6
+
+ @retval EFI_SUCCESS The check passed or the port is assigned.
+ @retval EFI_INVALID_PARAMETER The non-zero station port is already used.
+ @retval EFI_OUT_OF_RESOURCES No port can be allocated.
+
+**/
+EFI_STATUS
+TcpBind (
+ IN TCP_ACCESS_POINT *TcpAp,
+ IN UINT8 IpVersion
+ )
+{
+ BOOLEAN Cycle;
+ EFI_IP_ADDRESS Local;
+ UINT16 *Port;
+ UINT16 *RandomPort;
+
+ if (IpVersion == IP_VERSION_4) {
+ IP4_COPY_ADDRESS (&Local, &TcpAp->Tcp4Ap.StationAddress);
+ Port = &TcpAp->Tcp4Ap.StationPort;
+ RandomPort = &mTcp4RandomPort;
+ } else {
+ IP6_COPY_ADDRESS (&Local, &TcpAp->Tcp6Ap.StationAddress);
+ Port = &TcpAp->Tcp6Ap.StationPort;
+ RandomPort = &mTcp6RandomPort;
+ }
+
+ if (0 != *Port) {
+ //
+ // Check if a same endpoing is bound.
+ //
+ if (TcpFindTcbByPeer (&Local, *Port, IpVersion)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+ } else {
+ //
+ // generate a random port
+ //
+ Cycle = FALSE;
+
+ if (TCP_PORT_USER_RESERVED == *RandomPort) {
+ *RandomPort = TCP_PORT_KNOWN;
+ }
+
+ (*RandomPort)++;
+
+ while (TcpFindTcbByPeer (&Local, *RandomPort, IpVersion)) {
+ (*RandomPort)++;
+
+ if (*RandomPort <= TCP_PORT_KNOWN) {
+ if (Cycle) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpBind: no port can be allocated for this pcb\n")
+ );
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *RandomPort = TCP_PORT_KNOWN + 1;
+
+ Cycle = TRUE;
+ }
+ }
+
+ *Port = *RandomPort;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Flush the Tcb add its associated protocols.
+
+ @param[in, out] Tcb Pointer to the TCP_CB to be flushed.
+
+**/
+VOID
+TcpFlushPcb (
+ IN OUT TCP_CB *Tcb
+ )
+{
+ SOCKET *Sock;
+
+ IpIoConfigIp (Tcb->IpInfo, NULL);
+
+ Sock = Tcb->Sk;
+
+ if (SOCK_IS_CONFIGURED (Sock)) {
+ RemoveEntryList (&Tcb->List);
+
+ if (Sock->DevicePath != NULL) {
+ //
+ // Uninstall the device path protocol.
+ //
+ gBS->UninstallProtocolInterface (
+ Sock->SockHandle,
+ &gEfiDevicePathProtocolGuid,
+ Sock->DevicePath
+ );
+
+ FreePool (Sock->DevicePath);
+ Sock->DevicePath = NULL;
+ }
+ }
+
+ NetbufFreeList (&Tcb->SndQue);
+ NetbufFreeList (&Tcb->RcvQue);
+ Tcb->State = TCP_CLOSED;
+ Tcb->RemoteIpZero = FALSE;
+}
+
+/**
+ Attach a Pcb to the socket.
+
+ @param[in] Sk Pointer to the socket of this TCP instance.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
+
+**/
+EFI_STATUS
+TcpAttachPcb (
+ IN SOCKET *Sk
+ )
+{
+ TCP_CB *Tcb;
+ TCP_PROTO_DATA *ProtoData;
+ IP_IO *IpIo;
+ EFI_STATUS Status;
+ VOID *Ip;
+ EFI_GUID *IpProtocolGuid;
+
+ if (Sk->IpVersion == IP_VERSION_4) {
+ IpProtocolGuid = &gEfiIp4ProtocolGuid;
+ } else {
+ IpProtocolGuid = &gEfiIp6ProtocolGuid;
+ }
+
+ Tcb = AllocateZeroPool (sizeof (TCP_CB));
+
+ if (Tcb == NULL) {
+
+ DEBUG ((EFI_D_ERROR, "TcpConfigurePcb: failed to allocate a TCB\n"));
+
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;
+ IpIo = ProtoData->TcpService->IpIo;
+
+ //
+ // Create an IpInfo for this Tcb.
+ //
+ Tcb->IpInfo = IpIoAddIp (IpIo);
+ if (Tcb->IpInfo == NULL) {
+
+ FreePool (Tcb);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Open the new created IP instance BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ Tcb->IpInfo->ChildHandle,
+ IpProtocolGuid,
+ &Ip,
+ IpIo->Image,
+ Sk->SockHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ IpIoRemoveIp (IpIo, Tcb->IpInfo);
+ FreePool (Tcb);
+ return Status;
+ }
+
+ InitializeListHead (&Tcb->List);
+ InitializeListHead (&Tcb->SndQue);
+ InitializeListHead (&Tcb->RcvQue);
+
+ Tcb->State = TCP_CLOSED;
+ Tcb->Sk = Sk;
+ ProtoData->TcpPcb = Tcb;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detach the Pcb of the socket.
+
+ @param[in, out] Sk Pointer to the socket of this TCP instance.
+
+**/
+VOID
+TcpDetachPcb (
+ IN OUT SOCKET *Sk
+ )
+{
+ TCP_PROTO_DATA *ProtoData;
+ TCP_CB *Tcb;
+
+ ProtoData = (TCP_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = ProtoData->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ TcpFlushPcb (Tcb);
+
+ IpIoRemoveIp (ProtoData->TcpService->IpIo, Tcb->IpInfo);
+
+ FreePool (Tcb);
+
+ ProtoData->TcpPcb = NULL;
+}
+
+/**
+ Configure the Pcb using CfgData.
+
+ @param[in] Sk Pointer to the socket of this TCP instance.
+ @param[in] CfgData Pointer to the TCP configuration data.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_INVALID_PARAMETER A same access point has been configured in
+ another TCP instance.
+ @retval EFI_OUT_OF_RESOURCES Failed due to resource limits.
+
+**/
+EFI_STATUS
+TcpConfigurePcb (
+ IN SOCKET *Sk,
+ IN TCP_CONFIG_DATA *CfgData
+ )
+{
+ IP_IO_IP_CONFIG_DATA IpCfgData;
+ EFI_STATUS Status;
+ EFI_TCP4_OPTION *Option;
+ TCP_PROTO_DATA *TcpProto;
+ TCP_CB *Tcb;
+ TCP_ACCESS_POINT *TcpAp;
+
+ ASSERT ((CfgData != NULL) && (Sk != NULL) && (Sk->SockHandle != NULL));
+
+ TcpProto = (TCP_PROTO_DATA *) Sk->ProtoReserved;
+ Tcb = TcpProto->TcpPcb;
+
+ ASSERT (Tcb != NULL);
+
+ if (Sk->IpVersion == IP_VERSION_4) {
+ //
+ // Add Ip for send pkt to the peer
+ //
+ CopyMem (&IpCfgData.Ip4CfgData, &mIp4IoDefaultIpConfigData, sizeof (EFI_IP4_CONFIG_DATA));
+ IpCfgData.Ip4CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ IpCfgData.Ip4CfgData.TypeOfService = CfgData->Tcp4CfgData.TypeOfService;
+ IpCfgData.Ip4CfgData.TimeToLive = CfgData->Tcp4CfgData.TimeToLive;
+ IpCfgData.Ip4CfgData.UseDefaultAddress = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;
+ IP4_COPY_ADDRESS (
+ &IpCfgData.Ip4CfgData.SubnetMask,
+ &CfgData->Tcp4CfgData.AccessPoint.SubnetMask
+ );
+ IpCfgData.Ip4CfgData.ReceiveTimeout = (UINT32) (-1);
+ IP4_COPY_ADDRESS (
+ &IpCfgData.Ip4CfgData.StationAddress,
+ &CfgData->Tcp4CfgData.AccessPoint.StationAddress
+ );
+
+ } else {
+ ASSERT (Sk->IpVersion == IP_VERSION_6);
+
+ CopyMem (&IpCfgData.Ip6CfgData, &mIp6IoDefaultIpConfigData, sizeof (EFI_IP6_CONFIG_DATA));
+ IpCfgData.Ip6CfgData.DefaultProtocol = EFI_IP_PROTO_TCP;
+ IpCfgData.Ip6CfgData.TrafficClass = CfgData->Tcp6CfgData.TrafficClass;
+ IpCfgData.Ip6CfgData.HopLimit = CfgData->Tcp6CfgData.HopLimit;
+ IpCfgData.Ip6CfgData.ReceiveTimeout = (UINT32) (-1);
+ IP6_COPY_ADDRESS (
+ &IpCfgData.Ip6CfgData.StationAddress,
+ &CfgData->Tcp6CfgData.AccessPoint.StationAddress
+ );
+ IP6_COPY_ADDRESS (
+ &IpCfgData.Ip6CfgData.DestinationAddress,
+ &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress
+ );
+ }
+
+ //
+ // Configure the IP instance this Tcb consumes.
+ //
+ Status = IpIoConfigIp (Tcb->IpInfo, &IpCfgData);
+ if (EFI_ERROR (Status)) {
+ goto OnExit;
+ }
+
+ if (Sk->IpVersion == IP_VERSION_4) {
+ //
+ // Get the default address information if the instance is configured to use default address.
+ //
+ IP4_COPY_ADDRESS (
+ &CfgData->Tcp4CfgData.AccessPoint.StationAddress,
+ &IpCfgData.Ip4CfgData.StationAddress
+ );
+ IP4_COPY_ADDRESS (
+ &CfgData->Tcp4CfgData.AccessPoint.SubnetMask,
+ &IpCfgData.Ip4CfgData.SubnetMask
+ );
+
+ TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp4CfgData.AccessPoint;
+ } else {
+ IP6_COPY_ADDRESS (
+ &CfgData->Tcp6CfgData.AccessPoint.StationAddress,
+ &IpCfgData.Ip6CfgData.StationAddress
+ );
+
+ TcpAp = (TCP_ACCESS_POINT *) &CfgData->Tcp6CfgData.AccessPoint;
+ }
+
+ //
+ // check if we can bind this endpoint in CfgData
+ //
+ Status = TcpBind (TcpAp, Sk->IpVersion);
+
+ if (EFI_ERROR (Status)) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpConfigurePcb: Bind endpoint failed with %r\n",
+ Status)
+ );
+
+ goto OnExit;
+ }
+
+ //
+ // Initialize the operating information in this Tcb
+ //
+ ASSERT (Tcb->State == TCP_CLOSED &&
+ IsListEmpty (&Tcb->SndQue) &&
+ IsListEmpty (&Tcb->RcvQue));
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+ Tcb->State = TCP_CLOSED;
+
+ Tcb->SndMss = 536;
+ Tcb->RcvMss = TcpGetRcvMss (Sk);
+
+ Tcb->SRtt = 0;
+ Tcb->Rto = 3 * TCP_TICK_HZ;
+
+ Tcb->CWnd = Tcb->SndMss;
+ Tcb->Ssthresh = 0xffffffff;
+
+ Tcb->CongestState = TCP_CONGEST_OPEN;
+
+ Tcb->KeepAliveIdle = TCP_KEEPALIVE_IDLE_MIN;
+ Tcb->KeepAlivePeriod = TCP_KEEPALIVE_PERIOD;
+ Tcb->MaxKeepAlive = TCP_MAX_KEEPALIVE;
+ Tcb->MaxRexmit = TCP_MAX_LOSS;
+ Tcb->FinWait2Timeout = TCP_FIN_WAIT2_TIME;
+ Tcb->TimeWaitTimeout = TCP_TIME_WAIT_TIME;
+ Tcb->ConnectTimeout = TCP_CONNECT_TIME;
+
+ if (Sk->IpVersion == IP_VERSION_4) {
+ //
+ // initialize Tcb in the light of CfgData
+ //
+ Tcb->Ttl = CfgData->Tcp4CfgData.TimeToLive;
+ Tcb->Tos = CfgData->Tcp4CfgData.TypeOfService;
+
+ Tcb->UseDefaultAddr = CfgData->Tcp4CfgData.AccessPoint.UseDefaultAddress;
+
+ CopyMem (&Tcb->LocalEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.StationAddress, sizeof (IP4_ADDR));
+ Tcb->LocalEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.StationPort);
+ IP4_COPY_ADDRESS (&Tcb->SubnetMask, &CfgData->Tcp4CfgData.AccessPoint.SubnetMask);
+
+ CopyMem (&Tcb->RemoteEnd.Ip, &CfgData->Tcp4CfgData.AccessPoint.RemoteAddress, sizeof (IP4_ADDR));
+ Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp4CfgData.AccessPoint.RemotePort);
+
+ Option = CfgData->Tcp4CfgData.ControlOption;
+ } else {
+ Tcb->Ttl = CfgData->Tcp6CfgData.HopLimit;
+ Tcb->Tos = CfgData->Tcp6CfgData.TrafficClass;
+
+ IP6_COPY_ADDRESS (&Tcb->LocalEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.StationAddress);
+ Tcb->LocalEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.StationPort);
+
+ IP6_COPY_ADDRESS (&Tcb->RemoteEnd.Ip, &CfgData->Tcp6CfgData.AccessPoint.RemoteAddress);
+ Tcb->RemoteEnd.Port = HTONS (CfgData->Tcp6CfgData.AccessPoint.RemotePort);
+
+ //
+ // Type EFI_TCP4_OPTION and EFI_TCP6_OPTION are the same.
+ //
+ Option = (EFI_TCP4_OPTION *) CfgData->Tcp6CfgData.ControlOption;
+ }
+
+ if (Option != NULL) {
+ SET_RCV_BUFFSIZE (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_RCV_BUF_SIZE_MIN,
+ TCP_RCV_BUF_SIZE,
+ TCP_RCV_BUF_SIZE,
+ Option->ReceiveBufferSize
+ )
+ )
+ );
+ SET_SND_BUFFSIZE (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_SND_BUF_SIZE_MIN,
+ TCP_SND_BUF_SIZE,
+ TCP_SND_BUF_SIZE,
+ Option->SendBufferSize
+ )
+ )
+ );
+
+ SET_BACKLOG (
+ Sk,
+ (UINT32) (TCP_COMP_VAL (
+ TCP_BACKLOG_MIN,
+ TCP_BACKLOG,
+ TCP_BACKLOG,
+ Option->MaxSynBackLog
+ )
+ )
+ );
+
+ Tcb->MaxRexmit = (UINT16) TCP_COMP_VAL (
+ TCP_MAX_LOSS_MIN,
+ TCP_MAX_LOSS,
+ TCP_MAX_LOSS,
+ Option->DataRetries
+ );
+ Tcb->FinWait2Timeout = TCP_COMP_VAL (
+ TCP_FIN_WAIT2_TIME,
+ TCP_FIN_WAIT2_TIME_MAX,
+ TCP_FIN_WAIT2_TIME,
+ (UINT32) (Option->FinTimeout * TCP_TICK_HZ)
+ );
+
+ if (Option->TimeWaitTimeout != 0) {
+ Tcb->TimeWaitTimeout = TCP_COMP_VAL (
+ TCP_TIME_WAIT_TIME,
+ TCP_TIME_WAIT_TIME_MAX,
+ TCP_TIME_WAIT_TIME,
+ (UINT32) (Option->TimeWaitTimeout * TCP_TICK_HZ)
+ );
+ } else {
+ Tcb->TimeWaitTimeout = 0;
+ }
+
+ if (Option->KeepAliveProbes != 0) {
+ TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE);
+
+ Tcb->MaxKeepAlive = (UINT8) TCP_COMP_VAL (
+ TCP_MAX_KEEPALIVE_MIN,
+ TCP_MAX_KEEPALIVE,
+ TCP_MAX_KEEPALIVE,
+ Option->KeepAliveProbes
+ );
+ Tcb->KeepAliveIdle = TCP_COMP_VAL (
+ TCP_KEEPALIVE_IDLE_MIN,
+ TCP_KEEPALIVE_IDLE_MAX,
+ TCP_KEEPALIVE_IDLE_MIN,
+ (UINT32) (Option->KeepAliveTime * TCP_TICK_HZ)
+ );
+ Tcb->KeepAlivePeriod = TCP_COMP_VAL (
+ TCP_KEEPALIVE_PERIOD_MIN,
+ TCP_KEEPALIVE_PERIOD,
+ TCP_KEEPALIVE_PERIOD,
+ (UINT32) (Option->KeepAliveInterval * TCP_TICK_HZ)
+ );
+ }
+
+ Tcb->ConnectTimeout = TCP_COMP_VAL (
+ TCP_CONNECT_TIME_MIN,
+ TCP_CONNECT_TIME,
+ TCP_CONNECT_TIME,
+ (UINT32) (Option->ConnectionTimeout * TCP_TICK_HZ)
+ );
+
+ if (!Option->EnableNagle) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE);
+ }
+
+ if (!Option->EnableTimeStamp) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_TS);
+ }
+
+ if (!Option->EnableWindowScaling) {
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_NO_WS);
+ }
+ }
+
+ //
+ // The socket is bound, the <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;
+
+ if (NetIp6IsUnspecifiedAddr (&Tcb->RemoteEnd.Ip.v6)) {
+ Tcb->RemoteIpZero = TRUE;
+ }
+ }
+
+ TcpInsertTcb (Tcb);
+
+OnExit:
+
+ return Status;
+}
+
+/**
+ The protocol 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.c
new file mode 100644
index 00000000..0f74fc30
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.c
@@ -0,0 +1,995 @@
+/** @file
+ The driver binding and service binding protocol for the TCP driver.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 gTcp4DriverBinding = {
+ Tcp4DriverBindingSupported,
+ Tcp4DriverBindingStart,
+ Tcp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gTcp6DriverBinding = {
+ Tcp6DriverBindingSupported,
+ Tcp6DriverBindingStart,
+ Tcp6DriverBindingStop,
+ 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,
+ &gTcp4DriverBinding,
+ ImageHandle,
+ &gTcpComponentName,
+ &gTcpComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Install the TCP Driver Binding Protocol
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gTcp6DriverBinding,
+ NULL,
+ &gTcpComponentName,
+ &gTcpComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ EfiLibUninstallDriverBindingComponentName2 (
+ &gTcp4DriverBinding,
+ &gTcpComponentName,
+ &gTcpComponentName2
+ );
+ 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] Image 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;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (TcpServiceData->IpIo != NULL) {
+ IpIoDestroy (TcpServiceData->IpIo);
+ TcpServiceData->IpIo = NULL;
+ }
+
+ FreePool (TcpServiceData);
+
+ return Status;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+TcpDestroyChildEntryInHandleBuffer (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ SOCKET *Sock;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = NET_LIST_USER_STRUCT_S (Entry, SOCKET, Link, SOCK_SIGNATURE);
+ ServiceBinding = ((TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding;
+ NumberOfChildren = ((TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren;
+ ChildHandleBuffer = ((TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer;
+
+ if (!NetIsInHandleBuffer (Sock->SockHandle, NumberOfChildren, ChildHandleBuffer)) {
+ return EFI_SUCCESS;
+ }
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Sock->SockHandle);
+}
+
+/**
+ 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] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+ @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 EFI_HANDLE *ChildHandleBuffer, OPTIONAL
+ IN UINT8 IpVersion
+ )
+{
+ EFI_HANDLE NicHandle;
+ EFI_GUID *IpProtocolGuid;
+ EFI_GUID *ServiceBindingGuid;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ TCP_SERVICE_DATA *TcpServiceData;
+ EFI_STATUS Status;
+ LIST_ENTRY *List;
+ TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context;
+
+ 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_SUCCESS;
+ }
+
+ 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) {
+ List = &TcpServiceData->SocketList;
+ Context.ServiceBinding = ServiceBinding;
+ Context.NumberOfChildren = NumberOfChildren;
+ Context.ChildHandleBuffer = ChildHandleBuffer;
+ Status = NetDestroyLinkList (
+ List,
+ TcpDestroyChildEntryInHandleBuffer,
+ &Context,
+ NULL
+ );
+ } else if (IsListEmpty (&TcpServiceData->SocketList)) {
+ //
+ // Uninstall TCP servicebinding protocol
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ NicHandle,
+ ServiceBindingGuid,
+ ServiceBinding,
+ NULL
+ );
+
+ //
+ // Destroy the IpIO consumed by TCP driver
+ //
+ IpIoDestroy (TcpServiceData->IpIo);
+ TcpServiceData->IpIo = NULL;
+
+ //
+ // Destroy the heartbeat timer.
+ //
+ TcpDestroyTimer ();
+
+ //
+ // Release the TCP service data
+ //
+ FreePool (TcpServiceData);
+
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ 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
+Tcp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the Tcp4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test for the Ip4ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ return Status;
+}
+
+/**
+ 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
+Tcp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_4);
+ if ((Status == EFI_ALREADY_STARTED) || (Status == EFI_UNSUPPORTED)) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ 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
+Tcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ return TcpDestroyService (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ NumberOfChildren,
+ ChildHandleBuffer,
+ IP_VERSION_4
+ );
+}
+
+/**
+ 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
+Tcp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Test for the Tcp6ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ //
+ // Test for the Ip6ServiceBinding Protocol
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ return Status;
+}
+
+/**
+ 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
+Tcp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = TcpCreateService (ControllerHandle, This->DriverBindingHandle, IP_VERSION_6);
+ if ((Status == EFI_ALREADY_STARTED) || (Status == EFI_UNSUPPORTED)) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ 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
+Tcp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ return TcpDestroyService (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ NumberOfChildren,
+ ChildHandleBuffer,
+ IP_VERSION_6
+ );
+}
+
+/**
+ The Callback function 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 occurred.
+
+**/
+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_GET_PROTOCOL
+ );
+ 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 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_SUCCESS 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
+ )
+{
+ 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 default 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_SUCCESS 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 NULL.
+ @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;
+
+ if (NULL == This || NULL == ChildHandle) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // retrieve the Tcp4 protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp4ProtocolGuid,
+ &Tcp,
+ gTcp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // No Tcp4, try the Tcp6 protocol
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiTcp6ProtocolGuid,
+ &Tcp,
+ gTcp6DriverBinding.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);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.h
new file mode 100644
index 00000000..56447432
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDriver.h
@@ -0,0 +1,290 @@
+/** @file
+ The prototype of driver binding and service binding protocol for TCP driver.
+
+ Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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;
+ 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
+Tcp4DriverBindingSupported (
+ 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
+Tcp4DriverBindingStart (
+ 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
+Tcp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ 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
+Tcp6DriverBindingSupported (
+ 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
+Tcp6DriverBindingStart (
+ 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
+Tcp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ The Callback function 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 occurred.
+
+**/
+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 prototypes for the ServiceBinding Protocol
+//
+
+/**
+ 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_SUCCESS 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_SUCCESS 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 NULL.
+ @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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.inf
new file mode 100644
index 00000000..76de2621
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.inf
@@ -0,0 +1,86 @@
+## @file
+# TCPv4 I/O and TCPv6 I/O services.
+#
+# This module provides EFI TCPv4 Protocol and EFI TCPv6 Protocol to send and receive data stream.
+# It might provide TCPv4 Protocol or TCPv6 Protocol or both of them that depends on which network
+# stack has been loaded in system. This driver supports both IPv4 and IPv6 network stack.
+#
+# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[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
+ MODULE_UNI_FILE = TcpDxe.uni
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 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
+ NetworkPkg/NetworkPkg.dec
+
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DevicePathLib
+ DebugLib
+ MemoryAllocationLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ DpcLib
+ NetLib
+ IpIoLib
+
+
+[Protocols]
+ ## SOMETIMES_CONSUMES
+ ## SOMETIMES_PRODUCES
+ gEfiDevicePathProtocolGuid
+ gEfiIp4ProtocolGuid ## TO_START
+ gEfiIp4ServiceBindingProtocolGuid ## TO_START
+ gEfiTcp4ProtocolGuid ## BY_START
+ gEfiTcp4ServiceBindingProtocolGuid ## BY_START
+ gEfiIp6ProtocolGuid ## TO_START
+ gEfiIp6ServiceBindingProtocolGuid ## TO_START
+ gEfiTcp6ProtocolGuid ## BY_START
+ gEfiTcp6ServiceBindingProtocolGuid ## BY_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ TcpDxeExtra.uni
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.uni
new file mode 100644
index 00000000..9b9bf029
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxe.uni
@@ -0,0 +1,18 @@
+// /** @file
+// TCPv4 I/O and TCPv6 I/O services.
+//
+// This module provides EFI TCPv4 Protocol and EFI TCPv6 Protocol to send and receive data stream.
+// It might provide TCPv4 Protocol or TCPv6 Protocol or both of them that depends on
+// which network stack has been loaded in system.
+//
+// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "TCPv4 I/O and TCPv6 I/O services"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provides EFI TCPv4 Protocol and EFI TCPv6 Protocol to send and receive data stream. It might provide TCPv4 Protocol or TCPv6 Protocol or both of them that depends on which network stack has been loaded in system."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxeExtra.uni
new file mode 100644
index 00000000..65a03c59
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpDxeExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// TcpDxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"TCP DXE"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpFunc.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpFunc.h
new file mode 100644
index 00000000..10e12481
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpFunc.h
@@ -0,0 +1,693 @@
+/** @file
+ Declaration of external functions shared in TCP driver.
+
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 initial 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
+ );
+
+/**
+ 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 protocol 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpInput.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpInput.c
new file mode 100644
index 00000000..e01b5a48
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpInput.c
@@ -0,0 +1,1707 @@
+/** @file
+ TCP input process routines.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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->RcvNxt, 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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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_NET,
+ "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 maximum 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_NET,
+ "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.
+
+ @retval 0 The segment is broken.
+ @retval 1 The segment is in good shape.
+
+**/
+INTN
+TcpTrimSegment (
+ IN NET_BUF *Nbuf,
+ IN TCP_SEQNO Left,
+ IN TCP_SEQNO Right
+ )
+{
+ TCP_SEG *Seg;
+ TCP_SEQNO Urg;
+ UINT32 Drop;
+
+ Seg = TCPSEG_NETBUF (Nbuf);
+
+ //
+ // If the segment is completely out of window,
+ // truncate every thing, include SYN and FIN.
+ //
+ if (TCP_SEQ_LEQ (Seg->End, Left) || TCP_SEQ_LEQ (Right, Seg->Seq)) {
+
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_SYN);
+ TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_FIN);
+
+ Seg->Seq = Seg->End;
+ NetbufTrim (Nbuf, Nbuf->TotalSize, NET_BUF_HEAD);
+ return 1;
+ }
+
+ //
+ // 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);
+ }
+ }
+
+ return TcpVerifySegment (Nbuf);
+}
+
+/**
+ 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.
+
+ @retval 0 The segment is broken.
+ @retval 1 The segment is in good shape.
+
+**/
+INTN
+TcpTrimInWnd (
+ IN TCP_CB *Tcb,
+ IN NET_BUF *Nbuf
+ )
+{
+ return TcpTrimSegment (Nbuf, Tcb->RcvNxt, Tcb->RcvWl2 + Tcb->RcvWnd);
+}
+
+/**
+ Process the data and FIN flag, 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);
+
+ if (TcpVerifySegment (Nbuf) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpToSendData: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+ NetbufFree (Nbuf);
+ return -1;
+ }
+
+ 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_NET,
+ "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.
+
+ @retval 0 An error condition occurred.
+ @retval 1 No error occurred to queue data.
+
+**/
+INTN
+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 1;
+ }
+
+ //
+ // 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)) {
+ return 1;
+ }
+
+ if (TcpTrimSegment (Nbuf, TCPSEG_NETBUF (Node)->End, Seg->End) == 0) {
+ return 0;
+ }
+ }
+ }
+
+ 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);
+ return 1;
+ }
+
+ if (TcpTrimSegment (Nbuf, Seg->Seq, TCPSEG_NETBUF (Node)->Seq) == 0) {
+ RemoveEntryList (&Nbuf->List);
+ return 0;
+ }
+ break;
+ }
+
+ Cur = Cur->ForwardLink;
+ }
+
+ return 1;
+}
+
+
+/**
+ 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 sequence number of the received segment.
+
+ @retval 0 An error condition occurred.
+ @retval 1 No error occurred.
+
+**/
+INTN
+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;
+ }
+
+ return TcpTrimSegment (Node, Ack, Seg->End);
+ }
+
+ return 1;
+}
+
+/**
+ 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;
+ INT32 Usable;
+
+ 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);
+
+ if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {
+ DEBUG ((EFI_D_NET, "TcpInput: received a malformed packet\n"));
+ goto DISCARD;
+ }
+
+ Len = Nbuf->TotalSize - (Head->HeadLen << 2);
+
+ if ((Head->HeadLen < 5) || (Len < 0)) {
+
+ DEBUG ((EFI_D_NET, "TcpInput: received a malformed 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_NET, "TcpInput: send reset because no TCB found\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 malformed 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_NET,
+ "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);
+ if (TcpTrimInWnd (Tcb, Nbuf) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ 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 simultaneous 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);
+ }
+
+ if (TcpTrimInWnd (Tcb, Nbuf) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
+
+ DEBUG (
+ (EFI_D_NET,
+ "TcpInput: connection established for TCB %p in SYN_SENT\n",
+ Tcb)
+ );
+
+ goto StepSix;
+ } else {
+ //
+ // Received a SYN segment without ACK, simultaneous open.
+ //
+ TcpSetState (Tcb, TCP_SYN_RCVD);
+
+ ASSERT (Tcb->SndNxt == Tcb->Iss + 1);
+
+ if (TcpAdjustSndQue (Tcb, Tcb->SndNxt) == 0 || TcpTrimInWnd (Tcb, Nbuf) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: simultaneous 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 simultaneous 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.
+ //
+ if (TcpTrimInWnd (Tcb, Nbuf) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ //
+ // 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_NET,
+ "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)) {
+
+ if (TcpAdjustSndQue (Tcb, Seg->Ack) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ 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)) {
+ //
+ // Check for Window Retraction in RFC7923 section 2.4.
+ // The lower n bits of the peer's actual receive window is wiped out if TCP
+ // window scale is enabled, it will look like the peer is shrinking the window.
+ // Check whether the SndNxt is out of the advertised receive window by more than
+ // 2^Rcv.Wind.Shift before moving the SndNxt to the left.
+ //
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: peer advise negative useable window for connected TCB %p\n",
+ Tcb)
+ );
+ Usable = TCP_SUB_SEQ (Tcb->SndNxt, Right);
+ if ((Usable >> Tcb->SndWndScale) > 0) {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpInput: SndNxt is out of window by more than window scale for TCB %p\n",
+ Tcb)
+ );
+ 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_NET,
+ "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_NET,
+ "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;
+ }
+
+ if (TcpQueueData (Tcb, Nbuf) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpInput: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+
+ goto DISCARD;
+ }
+
+ 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;
+
+ if (Nbuf->TotalSize < sizeof (TCP_HEAD)) {
+ goto CLEAN_EXIT;
+ }
+
+ 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpIo.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpIo.c
new file mode 100644
index 00000000..1519b1be
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpIo.c
@@ -0,0 +1,186 @@
+/** @file
+ Implementation of I/O interfaces between TCP and IpIoLib.
+
+ Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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.
+ //
+ if (!Tcb->RemoteIpZero) {
+ 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.c
new file mode 100644
index 00000000..2543ff6e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.c
@@ -0,0 +1,1106 @@
+/** @file
+ Implementation of EFI_TCP4_PROTOCOL and EFI_TCP6_PROTOCOL.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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++) {
+ if (FragmentTable[Index].FragmentBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ 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 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 (IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) {
+ 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 (!IP4_IS_VALID_NETMASK (NTOHL (SubnetMask)) ||
+ (SubnetMask != 0 && !NetIp4IsUnicast (NTOHL (Ip), 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 instance 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 This The pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token The pointer to a token that has been issued by
+ EFI_TCP4_PROTOCOL.Connect(),
+ EFI_TCP4_PROTOCOL.Accept(),
+ EFI_TCP4_PROTOCOL.Transmit() or
+ EFI_TCP4_PROTOCOL.Receive(). If NULL, all pending
+ tokens issued by above four functions will be aborted. Type
+ EFI_TCP4_COMPLETION_TOKEN is defined in
+ EFI_TCP4_PROTOCOL.Connect().
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn't been configured.
+ @retval EFI_NO_MAPPING When using the default address, configuration
+ (DHCP, BOOTP,RARP, etc.) hasn't finished yet.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn't found in the
+ transmission or receive queue. It has either
+ completed or wasn't issued by Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp4Cancel (
+ IN EFI_TCP4_PROTOCOL *This,
+ IN EFI_TCP4_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockCancel (Sock, Token);
+}
+
+/**
+ 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.
+ - ListenToken->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_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn't been configured.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn't found in the transmission or
+ receive queue. It has either completed or wasn't issued by
+ Transmit() and Receive().
+
+**/
+EFI_STATUS
+EFIAPI
+Tcp6Cancel (
+ IN EFI_TCP6_PROTOCOL *This,
+ IN EFI_TCP6_COMPLETION_TOKEN *Token OPTIONAL
+ )
+{
+ SOCKET *Sock;
+
+ if (NULL == This) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Sock = SOCK_FROM_THIS (This);
+
+ return SockCancel (Sock, Token);
+}
+
+/**
+ 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.h
new file mode 100644
index 00000000..3508503f
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMain.h
@@ -0,0 +1,777 @@
+/** @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 - 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 <Library/PrintLib.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 EFI_UNICODE_STRING_TABLE *gTcpControllerNameTable;
+
+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;
+
+typedef struct {
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+} TCP_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT;
+
+//
+// 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 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 instance 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 This The pointer to the EFI_TCP4_PROTOCOL instance.
+ @param Token The pointer to a token that has been issued by
+ EFI_TCP4_PROTOCOL.Connect(),
+ EFI_TCP4_PROTOCOL.Accept(),
+ EFI_TCP4_PROTOCOL.Transmit() or
+ EFI_TCP4_PROTOCOL.Receive(). If NULL, all pending
+ tokens issued by above four functions will be aborted. Type
+ EFI_TCP4_COMPLETION_TOKEN is defined in
+ EFI_TCP4_PROTOCOL.Connect().
+
+ @retval EFI_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn't been configured.
+ @retval EFI_NO_MAPPING When using the default address, configuration
+ (DHCP, BOOTP,RARP, etc.) hasn't finished yet.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn't found in the
+ transmission or receive queue. It has either
+ completed or wasn't issued by Transmit() and Receive().
+
+**/
+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.
+ - ListenToken->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_SUCCESS The asynchronous I/O request is aborted and Token->Event
+ is signaled.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_NOT_STARTED This instance hasn't been configured.
+ @retval EFI_NOT_FOUND The asynchronous I/O request isn't found in the transmission or
+ receive queue. It has either completed or wasn't issued by
+ Transmit() and Receive().
+
+**/
+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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMisc.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMisc.c
new file mode 100644
index 00000000..c315c4f3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpMisc.c
@@ -0,0 +1,1042 @@
+/** @file
+ Misc support routines for TCP driver.
+
+ (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+ Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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->RetxmitSeqMax = 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 initial 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);
+
+ Tcb->TsRecent = Opt->TSVal;
+
+ //
+ // 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;
+
+ 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);
+
+
+ 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);
+ if (!EFI_ERROR (Ip6->GetModeData (Ip6, &Ip6Mode, NULL, NULL))) {
+ if (Ip6Mode.AddressList != NULL) {
+ FreePool (Ip6Mode.AddressList);
+ }
+
+ if (Ip6Mode.GroupTable != NULL) {
+ FreePool (Ip6Mode.GroupTable);
+ }
+
+ if (Ip6Mode.RouteTable != NULL) {
+ FreePool (Ip6Mode.RouteTable);
+ }
+
+ if (Ip6Mode.NeighborCache != NULL) {
+ FreePool (Ip6Mode.NeighborCache);
+ }
+
+ if (Ip6Mode.PrefixTable != NULL) {
+ FreePool (Ip6Mode.PrefixTable);
+ }
+
+ if (Ip6Mode.IcmpTypeList != NULL) {
+ FreePool (Ip6Mode.IcmpTypeList);
+ }
+ }
+
+ 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_NET,
+ "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_NET,
+ "TcpOnAppConsume: send a window update for a window closed Tcb %p\n",
+ Tcb)
+ );
+
+ TcpSendAck (Tcb);
+ } else if (Tcb->DelayedAck == 0) {
+
+ DEBUG (
+ (EFI_D_NET,
+ "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);
+}
+
+/**
+ 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
+ );
+
+ IP4_COPY_ADDRESS (&Ip4DPathNode.SubnetMask, &Tcb->SubnetMask);
+
+ 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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.c
new file mode 100644
index 00000000..c96fd4f8
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.c
@@ -0,0 +1,338 @@
+/** @file
+ Routines to process TCP option.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 Illegal 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;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.h
new file mode 100644
index 00000000..6285c493
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOption.h
@@ -0,0 +1,123 @@
+/** @file
+ Tcp option's routine header file.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 definitions
+//
+#define TCP_OPTION_RCVD_MSS 0x01
+#define TCP_OPTION_RCVD_WS 0x02
+#define TCP_OPTION_RCVD_TS 0x04
+#define TCP_OPTION_MAX_WS 14 ///< Maximum 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 Illegal option was found.
+
+**/
+INTN
+TcpParseOption (
+ IN TCP_HEAD *Tcp,
+ IN OUT TCP_OPTION *Option
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOutput.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOutput.c
new file mode 100644
index 00000000..4df9f3d0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpOutput.c
@@ -0,0 +1,1251 @@
+/** @file
+ TCP output process routines.
+
+ Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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));
+
+ if (TcpVerifySegment (Nbuf) == 0) {
+ return -1;
+ }
+
+ 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.
+ // Because 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);
+ }
+
+ if (TcpVerifySegment (Nbuf) == 0) {
+ NetbufFree (Nbuf);
+ return NULL;
+ }
+
+ 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 maximum 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.
+ //
+
+ //
+ // Handle the Window Retraction if TCP window scale is enabled according to RFC7323:
+ // On first retransmission, or if the sequence number is out of
+ // window by less than 2^Rcv.Wind.Shift, then do normal
+ // retransmission(s) without regard to the receiver window as long
+ // as the original segment was in window when it was sent.
+ //
+ if ((Tcb->SndWndScale != 0) &&
+ (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax) || TCP_SEQ_BETWEEN (Tcb->SndWl2 + Tcb->SndWnd, Seq, Tcb->SndWl2 + Tcb->SndWnd + (1 << Tcb->SndWndScale)))) {
+ Len = TCP_SUB_SEQ (Tcb->SndNxt, Seq);
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpRetransmit: retransmission without regard to the receiver window for TCB %p\n",
+ Tcb)
+ );
+
+ } else if (TCP_SEQ_GEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {
+ Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);
+
+ } else {
+ DEBUG (
+ (EFI_D_WARN,
+ "TcpRetransmit: retransmission cancelled because send window too small for TCB %p\n",
+ Tcb)
+ );
+
+ return 0;
+ }
+
+ Len = MIN (Len, Tcb->SndMss);
+
+ Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
+ if (Nbuf == NULL) {
+ return -1;
+ }
+
+ if (TcpVerifySegment (Nbuf) == 0) {
+ goto OnError;
+ }
+
+ if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
+ goto OnError;
+ }
+
+ if (TCP_SEQ_GT (Seq, Tcb->RetxmitSeqMax)) {
+ Tcb->RetxmitSeqMax = Seq;
+ }
+
+ //
+ // 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) < (ARRAY_SIZE (mTcpOutFlag)));
+ 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_NET,
+ "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;
+
+ if (TcpVerifySegment (Nbuf) == 0 || TcpCheckSndQue (&Tcb->SndQue) == 0) {
+ DEBUG (
+ (EFI_D_ERROR,
+ "TcpToSendData: discard a broken segment for TCB %p\n",
+ Tcb)
+ );
+ goto OnError;
+ }
+
+ //
+ // 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)
+ );
+
+ goto OnError;
+ }
+
+ 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_NET,
+ "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_NET,
+ "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/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpProto.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpProto.h
new file mode 100644
index 00000000..9add5de4
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpProto.h
@@ -0,0 +1,344 @@
+/** @file
+ TCP protocol header file.
+
+ Copyright (c) 2009 - 2017, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 minimum value of RTO.
+#define TCP_RTO_MAX (TCP_TICK_HZ * 60) ///< The maximum value of RTO.
+#define TCP_FOLD_RTT 4 ///< Timeout threshold 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 accommodate:
+// 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 controlled.
+ 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.
+
+ //
+ // RFC7323
+ // Addressing Window Retraction for TCP Window Scale Option.
+ //
+ TCP_SEQNO RetxmitSeqMax; ///< Max Seq number in previous retransmission.
+
+ //
+ // 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; ///< Maximum keep alive probe times.
+ UINT8 KeepAliveProbes; ///< The number of keep alive probe.
+ UINT16 MaxRexmit; ///< The maximum 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;
+
+
+ BOOLEAN RemoteIpZero; ///< RemoteEnd.Ip is ZERO when configured.
+ IP_IO_IP_INFO *IpInfo; ///< Pointer reference to Ip used to send pkt
+ UINT32 Tick; ///< 1 tick = 200ms
+};
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpTimer.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpTimer.c
new file mode 100644
index 00000000..f7454eb3
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/TcpDxe/TcpTimer.c
@@ -0,0 +1,587 @@
+/** @file
+ TCP timer related functions.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#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 connection 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);
+}
+