diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe')
31 files changed, 15820 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/ComponentName.c new file mode 100644 index 00000000..0be9d7e1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/ComponentName.c @@ -0,0 +1,428 @@ +/** @file + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.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 is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip4ComponentNameGetDriverName ( + 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 is NULL. + + @retval EFI_INVALID_PARAMETER 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 +Ip4ComponentNameGetControllerName ( + 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 gIp4ComponentName = { + Ip4ComponentNameGetDriverName, + Ip4ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIp4ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip4ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip4ComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIp4DriverNameTable[] = { + { + "eng;en", + L"IP4 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gIp4ControllerNameTable = 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 is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +Ip4ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIp4DriverNameTable, + DriverName, + (BOOLEAN)(This == &gIp4ComponentName) + ); + +} + +/** + Update the component name for the IP4 child handle. + + @param Ip4[in] A pointer to the EFI_IP4_PROTOCOL. + + + @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully. + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + +**/ +EFI_STATUS +UpdateName ( + IN EFI_IP4_PROTOCOL *Ip4 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[80]; + EFI_IP4_MODE_DATA Ip4ModeData; + + if (Ip4 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer as: + // IPv4 (SrcIP=127.0.0.1, DestIP=127.0.0.1) + // + Status = Ip4->GetModeData (Ip4, &Ip4ModeData, NULL, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (!Ip4ModeData.IsStarted || !Ip4ModeData.IsConfigured) { + UnicodeSPrint (HandleName, sizeof (HandleName), L"IPv4 (Not started)"); + } else { + UnicodeSPrint (HandleName, sizeof (HandleName), + L"IPv4 (SrcIP=%d.%d.%d.%d)", + Ip4ModeData.ConfigData.StationAddress.Addr[0], + Ip4ModeData.ConfigData.StationAddress.Addr[1], + Ip4ModeData.ConfigData.StationAddress.Addr[2], + Ip4ModeData.ConfigData.StationAddress.Addr[3] + ); + } + + if (gIp4ControllerNameTable != NULL) { + FreeUnicodeStringTable (gIp4ControllerNameTable); + gIp4ControllerNameTable = NULL; + } + Status = AddUnicodeString2 ( + "eng", + gIp4ComponentName.SupportedLanguages, + &gIp4ControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gIp4ComponentName2.SupportedLanguages, + &gIp4ControllerNameTable, + 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 is NULL. + + @retval EFI_INVALID_PARAMETER 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 +Ip4ComponentNameGetControllerName ( + 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_IP4_PROTOCOL *Ip4; + + // + // Only provide names for child handles. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiManagedNetworkProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiIp4ProtocolGuid, + (VOID **)&Ip4, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (Ip4); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gIp4ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gIp4ComponentName) + ); +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Common.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Common.c new file mode 100644 index 00000000..50d7658c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Common.c @@ -0,0 +1,306 @@ +/** @file + +Copyright (c) 2005 - 2017, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + + +/** + Return the cast type (Unicast/Broadcast) specific to an + interface. All the addresses are host byte ordered. + + @param[in] IpAddr The IP address to classify in host byte order + @param[in] IpIf The interface that IpAddr received from + + @return The cast type of this IP address specific to the interface. + @retval IP4_LOCAL_HOST The IpAddr equals to the interface's address + @retval IP4_SUBNET_BROADCAST The IpAddr is a directed subnet broadcast to the + interface + @retval IP4_NET_BROADCAST The IpAddr is a network broadcast to the interface + @retval 0 Otherwise. + +**/ +INTN +Ip4GetNetCast ( + IN IP4_ADDR IpAddr, + IN IP4_INTERFACE *IpIf + ) +{ + if (IpAddr == IpIf->Ip) { + return IP4_LOCAL_HOST; + + } else if (IpAddr == IpIf->SubnetBrdcast) { + return IP4_SUBNET_BROADCAST; + + } else if (IpAddr == IpIf->NetBrdcast) { + return IP4_NET_BROADCAST; + + } + + return 0; +} + + +/** + Find the cast type of the packet related to the local host. + This isn't the same as link layer cast type. For example, DHCP + server may send local broadcast to the local unicast MAC. + + @param[in] IpSb The IP4 service binding instance that received the + packet + @param[in] Dst The destination address in the packet (host byte + order) + @param[in] Src The source address in the packet (host byte order) + + @return The cast type for the Dst, it will return on the first non-promiscuous + cast type to a configured interface. If the packet doesn't match any of + the interface, multicast address and local broadcast address are checked. + +**/ +INTN +Ip4GetHostCast ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN IP4_ADDR Src + ) +{ + LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + INTN Type; + INTN Class; + + Type = 0; + + if (IpSb->MnpConfigData.EnablePromiscuousReceive) { + Type = IP4_PROMISCUOUS; + } + + // + // Go through the interface list of the IP service, most likely. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + // + // Skip the unconfigured interface and invalid source address: + // source address can't be broadcast. + // + if (!IpIf->Configured || IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) { + continue; + } + + if ((Class = Ip4GetNetCast (Dst, IpIf)) > Type) { + return Class; + } + } + + // + // If it is local broadcast address. The source address must + // be a unicast address on one of the direct connected network. + // If it is a multicast address, accept it only if we are in + // the group. + // + if (Dst == IP4_ALLONE_ADDRESS) { + IpIf = Ip4FindNet (IpSb, Src); + + if (IpIf != NULL && !IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) { + return IP4_LOCAL_BROADCAST; + } + + } else if (IP4_IS_MULTICAST (Dst) && Ip4FindGroup (&IpSb->IgmpCtrl, Dst) != NULL) { + return IP4_MULTICAST; + } + + return Type; +} + + +/** + Find an interface whose configured IP address is Ip. + + @param[in] IpSb The IP4 service binding instance + @param[in] Ip The Ip address (host byte order) to find + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindInterface ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip + ) +{ + LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && (IpIf->Ip == Ip)) { + return IpIf; + } + } + + return NULL; +} + + +/** + Find an interface that Ip is on that connected network. + + @param[in] IpSb The IP4 service binding instance + @param[in] Ip The Ip address (host byte order) to find + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindNet ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip + ) +{ + LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && IP4_NET_EQUAL (Ip, IpIf->Ip, IpIf->SubnetMask)) { + return IpIf; + } + } + + return NULL; +} + + +/** + Find an interface of the service with the same Ip/Netmask pair. + + @param[in] IpSb Ip4 service binding instance + @param[in] Ip The Ip address to find (host byte order) + @param[in] Netmask The network to find (host byte order) + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindStationAddress ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip, + IN IP4_ADDR Netmask + ) +{ + LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && (IpIf->Ip == Ip) && (IpIf->SubnetMask == Netmask)) { + return IpIf; + } + } + + return NULL; +} + + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address in stead of + hard code the NIC to be Ethernet. + + @param[in] Mnp The Mnp instance to get the MAC address. + @param[in] Multicast The multicast IP address to translate. + @param[out] Mac The buffer to hold the translated address. + + @retval EFI_SUCCESS if the multicast IP is successfully translated to a + multicast MAC address. + @retval other Otherwise some error. + +**/ +EFI_STATUS +Ip4GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN IP4_ADDR Multicast, + OUT EFI_MAC_ADDRESS *Mac + ) +{ + EFI_IP_ADDRESS EfiIp; + + EFI_IP4 (EfiIp.v4) = HTONL (Multicast); + return Mnp->McastIpToMac (Mnp, FALSE, &EfiIp, Mac); +} + + +/** + Convert the multibyte field in IP header's byter order. + In spite of its name, it can also be used to convert from + host to network byte order. + + @param[in] Head The IP head to convert + + @return Point to the converted IP head + +**/ +IP4_HEAD * +Ip4NtohHead ( + IN IP4_HEAD *Head + ) +{ + Head->TotalLen = NTOHS (Head->TotalLen); + Head->Id = NTOHS (Head->Id); + Head->Fragment = NTOHS (Head->Fragment); + Head->Src = NTOHL (Head->Src); + Head->Dst = NTOHL (Head->Dst); + + return Head; +} + + +/** + Validate that Ip/Netmask pair is OK to be used as station + address. Only continuous netmasks are supported. and check + that StationAddress is a unicast address on the network. + + @param[in] Ip The IP address to validate. + @param[in] Netmask The netmask of the IP. + + @retval TRUE The Ip/Netmask pair is valid. + @retval FALSE The Ip/Netmask pair is invalid. + +**/ +BOOLEAN +Ip4StationAddressValid ( + IN IP4_ADDR Ip, + IN IP4_ADDR Netmask + ) +{ + // + // Only support the station address with 0.0.0.0/0 to enable DHCP client. + // + if (Netmask == IP4_ALLZERO_ADDRESS) { + return (BOOLEAN) (Ip == IP4_ALLZERO_ADDRESS); + } + + // + // Only support the continuous net masks + // + if (NetGetMaskLength (Netmask) == (IP4_MASK_MAX + 1)) { + return FALSE; + } + + // + // Station address can't be class D or class E address + // + if (NetGetIpClass (Ip) > IP4_ADDR_CLASSC) { + return FALSE; + } + + return NetIp4IsUnicast (Ip, Netmask); +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Common.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Common.h new file mode 100644 index 00000000..c3dfe7de --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Common.h @@ -0,0 +1,217 @@ +/** @file + Common definition for IP4. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_COMMON_H__ +#define __EFI_IP4_COMMON_H__ + +typedef struct _IP4_INTERFACE IP4_INTERFACE; +typedef struct _IP4_PROTOCOL IP4_PROTOCOL; +typedef struct _IP4_SERVICE IP4_SERVICE; + +#define IP4_ETHER_PROTO 0x0800 + +// +// The packet is received as link level broadcast/multicast/promiscuous. +// +#define IP4_LINK_BROADCAST 0x00000001 +#define IP4_LINK_MULTICAST 0x00000002 +#define IP4_LINK_PROMISC 0x00000004 + +// +// IP4 address cast type classification. Keep it true that any +// type bigger than or equal to LOCAL_BROADCAST is broadcast. +// +#define IP4_PROMISCUOUS 1 +#define IP4_LOCAL_HOST 2 +#define IP4_MULTICAST 3 +#define IP4_LOCAL_BROADCAST 4 // Destination is 255.255.255.255 +#define IP4_SUBNET_BROADCAST 5 +#define IP4_NET_BROADCAST 6 + +// +// IP4 header flags +// +#define IP4_HEAD_DF_MASK 0x4000 +#define IP4_HEAD_MF_MASK 0x2000 +#define IP4_HEAD_OFFSET_MASK 0x1fff + +#define IP4_ALLZERO_ADDRESS 0x00000000u +#define IP4_ALLONE_ADDRESS 0xFFFFFFFFu +#define IP4_ALLSYSTEM_ADDRESS 0xE0000001u +#define IP4_ALLROUTER_ADDRESS 0xE0000002u + +/// +/// Compose the fragment field to be used in the IP4 header. +/// +#define IP4_HEAD_FRAGMENT_FIELD(Df, Mf, Offset) \ + ((UINT16)(((Df) ? IP4_HEAD_DF_MASK : 0) | ((Mf) ? IP4_HEAD_MF_MASK : 0) | (((Offset) >> 3) & IP4_HEAD_OFFSET_MASK))) + +#define IP4_LAST_FRAGMENT(FragmentField) \ + (((FragmentField) & IP4_HEAD_MF_MASK) == 0) + +#define IP4_FIRST_FRAGMENT(FragmentField) \ + ((BOOLEAN)(((FragmentField) & IP4_HEAD_OFFSET_MASK) == 0)) + +#define IP4_DO_NOT_FRAGMENT(FragmentField) \ + ((BOOLEAN)(((FragmentField) & IP4_HEAD_DF_MASK) == IP4_HEAD_DF_MASK)) + +#define IP4_IS_BROADCAST(CastType) ((CastType) >= IP4_LOCAL_BROADCAST) + +/// +/// Convert the Microsecond to second. IP transmit/receive time is +/// in the unit of microsecond. IP ticks once per second. +/// +#define IP4_US_TO_SEC(Us) (((Us) + 999999) / 1000000) + +/** + Return the cast type (Unicast/Broadcast) specific to an + interface. All the addresses are host byte ordered. + + @param[in] IpAddr The IP address to classify in host byte order + @param[in] IpIf The interface that IpAddr received from + + @return The cast type of this IP address specific to the interface. + @retval IP4_LOCAL_HOST The IpAddr equals to the interface's address + @retval IP4_SUBNET_BROADCAST The IpAddr is a directed subnet broadcast to the + interface + @retval IP4_NET_BROADCAST The IpAddr is a network broadcast to the interface + @retval 0 Otherwise. + +**/ +INTN +Ip4GetNetCast ( + IN IP4_ADDR IpAddr, + IN IP4_INTERFACE *IpIf + ); + +/** + Find the cast type of the packet related to the local host. + This isn't the same as link layer cast type. For example, DHCP + server may send local broadcast to the local unicast MAC. + + @param[in] IpSb The IP4 service binding instance that received the + packet + @param[in] Dst The destination address in the packet (host byte + order) + @param[in] Src The source address in the packet (host byte order) + + @return The cast type for the Dst, it will return on the first non-promiscuous + cast type to a configured interface. If the packet doesn't match any of + the interface, multicast address and local broadcast address are checked. + +**/ +INTN +Ip4GetHostCast ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN IP4_ADDR Src + ); + +/** + Find an interface whose configured IP address is Ip. + + @param[in] IpSb The IP4 service binding instance + @param[in] Ip The Ip address (host byte order) to find + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindInterface ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip + ); + +/** + Find an interface that Ip is on that connected network. + + @param[in] IpSb The IP4 service binding instance + @param[in] Ip The Ip address (host byte order) to find + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindNet ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip + ); + +/** + Find an interface of the service with the same Ip/Netmask pair. + + @param[in] IpSb Ip4 service binding instance + @param[in] Ip The Ip address to find (host byte order) + @param[in] Netmask The network to find (host byte order) + + @return The IP4_INTERFACE point if found, otherwise NULL + +**/ +IP4_INTERFACE * +Ip4FindStationAddress ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Ip, + IN IP4_ADDR Netmask + ); + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address in stead of + hard code the NIC to be Ethernet. + + @param[in] Mnp The Mnp instance to get the MAC address. + @param[in] Multicast The multicast IP address to translate. + @param[out] Mac The buffer to hold the translated address. + + @retval EFI_SUCCESS if the multicast IP is successfully translated to a + multicast MAC address. + @retval other Otherwise some error. + +**/ +EFI_STATUS +Ip4GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN IP4_ADDR Multicast, + OUT EFI_MAC_ADDRESS *Mac + ); + +/** + Convert the multibyte field in IP header's byter order. + In spite of its name, it can also be used to convert from + host to network byte order. + + @param[in] Head The IP head to convert + + @return Point to the converted IP head + +**/ +IP4_HEAD * +Ip4NtohHead ( + IN IP4_HEAD *Head + ); + + +/** + Validate that Ip/Netmask pair is OK to be used as station + address. Only continuous netmasks are supported. and check + that StationAddress is a unicast address on the network. + + @param[in] Ip The IP address to validate. + @param[in] Netmask The netmask of the IP. + + @retval TRUE The Ip/Netmask pair is valid. + @retval FALSE The Ip/Netmask pair is invalid. + +**/ +BOOLEAN +Ip4StationAddressValid ( + IN IP4_ADDR Ip, + IN IP4_ADDR Netmask + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2.vfr b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2.vfr new file mode 100644 index 00000000..cac3b7f9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2.vfr @@ -0,0 +1,94 @@ +/** @file + Vfr file for IP4Dxe. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include "Ip4NvData.h" + +#define EFI_NETWORK_DEVICE_CLASS 0x04 + +formset + guid = IP4_CONFIG2_NVDATA_GUID, + title = STRING_TOKEN(STR_IP4_CONFIG2_FORM_TITLE), + help = STRING_TOKEN(STR_IP4_CONFIG2_FORM_HELP), + class = EFI_NETWORK_DEVICE_CLASS, + subclass = 0x03, + + varstore IP4_CONFIG2_IFR_NVDATA, + name = IP4_CONFIG2_IFR_NVDATA, + guid = IP4_CONFIG2_NVDATA_GUID; + + form formid = FORMID_MAIN_FORM, + title = STRING_TOKEN(STR_IP4_DEVICE_FORM_TITLE); + + checkbox varid = IP4_CONFIG2_IFR_NVDATA.Configure, + prompt = STRING_TOKEN(STR_IP4_CONFIGURE), + help = STRING_TOKEN(STR_IP4_CONFIGURE_HELP), + flags = INTERACTIVE, + key = KEY_ENABLE, + endcheckbox; + + suppressif ideqval IP4_CONFIG2_IFR_NVDATA.Configure == 0x00; + + checkbox varid = IP4_CONFIG2_IFR_NVDATA.DhcpEnable, + prompt = STRING_TOKEN(STR_IP4_ENABLE_DHCP), + help = STRING_TOKEN(STR_IP4_ENABLE_DHCP), + flags = INTERACTIVE, + key = KEY_DHCP_ENABLE, + endcheckbox; + endif; + + suppressif ideqval IP4_CONFIG2_IFR_NVDATA.DhcpEnable == 0x01 OR ideqval IP4_CONFIG2_IFR_NVDATA.Configure == 0x00; + + string varid = IP4_CONFIG2_IFR_NVDATA.StationAddress, + prompt = STRING_TOKEN(STR_IP4_LOCAL_IP_ADDRESS), + help = STRING_TOKEN(STR_IP4_IP_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_LOCAL_IP, + minsize = IP_MIN_SIZE, + maxsize = IP_MAX_SIZE, + endstring; + + string varid = IP4_CONFIG2_IFR_NVDATA.SubnetMask, + prompt = STRING_TOKEN(STR_IP4_LOCAL_MASK), + help = STRING_TOKEN(STR_IP4_MASK_HELP), + flags = INTERACTIVE, + key = KEY_SUBNET_MASK, + minsize = IP_MIN_SIZE, + maxsize = IP_MAX_SIZE, + endstring; + + string varid = IP4_CONFIG2_IFR_NVDATA.GatewayAddress, + prompt = STRING_TOKEN(STR_IP4_LOCAL_GATEWAY), + help = STRING_TOKEN(STR_IP4_GATEWAY_HELP), + flags = INTERACTIVE, + key = KEY_GATE_WAY, + minsize = IP_MIN_SIZE, + maxsize = IP_MAX_SIZE, + endstring; + + string varid = IP4_CONFIG2_IFR_NVDATA.DnsAddress, + prompt = STRING_TOKEN(STR_IP4_LOCAL_DNS), + help = STRING_TOKEN(STR_IP4_DNS_HELP), + flags = INTERACTIVE, + key = KEY_DNS, + minsize = IP_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + endif; + + subtitle text = STRING_TOKEN(STR_NULL); + + text + help = STRING_TOKEN(STR_SAVE_CHANGES), + text = STRING_TOKEN(STR_SAVE_CHANGES), + flags = INTERACTIVE, + key = KEY_SAVE_CHANGES; + + endform; + +endformset; + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Impl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Impl.c new file mode 100644 index 00000000..d615c1ad --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Impl.c @@ -0,0 +1,2168 @@ +/** @file + The implementation of EFI IPv4 Configuration II Protocol. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +LIST_ENTRY mIp4Config2InstanceList = {&mIp4Config2InstanceList, &mIp4Config2InstanceList}; + +/** + The event process routine when the DHCPv4 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context Pointer to the IP4 config2 instance data. + +**/ +VOID +EFIAPI +Ip4Config2OnDhcp4SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Destroy the Dhcp4 child in IP4_CONFIG2_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP4 config2 instance to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destroy the child. + +**/ +EFI_STATUS +Ip4Config2DestroyDhcp4 ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ) +{ + IP4_SERVICE *IpSb; + EFI_STATUS Status; + EFI_DHCP4_PROTOCOL *Dhcp4; + + Dhcp4 = Instance->Dhcp4; + ASSERT (Dhcp4 != NULL); + + Dhcp4->Stop (Dhcp4); + Dhcp4->Configure (Dhcp4, NULL); + Instance->Dhcp4 = NULL; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + // + // Close DHCPv4 protocol and destroy the child. + // + Status = gBS->CloseProtocol ( + Instance->Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + Instance->Dhcp4Handle + ); + + Instance->Dhcp4Handle = NULL; + + return Status; +} + +/** + Update the current policy to NewPolicy. During the transition + period, the default router list + and address list in all interfaces will be released. + + @param[in] IpSb The IP4 service binding instance. + @param[in] NewPolicy The new policy to be updated to. + +**/ +VOID +Ip4Config2OnPolicyChanged ( + IN IP4_SERVICE *IpSb, + IN EFI_IP4_CONFIG2_POLICY NewPolicy + ) +{ + IP4_INTERFACE *IpIf; + IP4_ROUTE_TABLE *RouteTable; + + // + // Currently there are only two policies: static and dhcp. Regardless of + // what transition is going on, i.e., static -> dhcp and dhcp -> + // static, we have to free default router table and all addresses. + // + + if (IpSb->DefaultInterface != NULL) { + if (IpSb->DefaultRouteTable != NULL) { + Ip4FreeRouteTable (IpSb->DefaultRouteTable); + IpSb->DefaultRouteTable = NULL; + } + + Ip4CancelReceive (IpSb->DefaultInterface); + + Ip4FreeInterface (IpSb->DefaultInterface, NULL); + IpSb->DefaultInterface = NULL; + } + + Ip4CleanAssembleTable (&IpSb->Assemble); + + // + // Create new default interface and route table. + // + IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image); + if (IpIf == NULL) { + return ; + } + + RouteTable = Ip4CreateRouteTable (); + if (RouteTable == NULL) { + Ip4FreeInterface (IpIf, NULL); + return ; + } + + IpSb->DefaultInterface = IpIf; + InsertHeadList (&IpSb->Interfaces, &IpIf->Link); + IpSb->DefaultRouteTable = RouteTable; + Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb); + + if (IpSb->State == IP4_SERVICE_CONFIGED || IpSb->State == IP4_SERVICE_STARTED) { + IpSb->State = IP4_SERVICE_UNSTARTED; + } + + // + // Start the dhcp configuration. + // + if (NewPolicy == Ip4Config2PolicyDhcp) { + Ip4StartAutoConfig (&IpSb->Ip4Config2Instance); + } + +} + +/** + Signal the registered event. It is the callback routine for NetMapIterate. + + @param[in] Map Points to the list of registered event. + @param[in] Item The registered event. + @param[in] Arg Not used. + + @retval EFI_SUCCESS The event was signaled successfully. +**/ +EFI_STATUS +EFIAPI +Ip4Config2SignalEvent ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Arg + ) +{ + gBS->SignalEvent ((EFI_EVENT) Item->Key); + + return EFI_SUCCESS; +} + +/** + Read the configuration data from variable storage according to the VarName and + gEfiIp4Config2ProtocolGuid. It checks the integrity of variable data. If the + data is corrupted, it clears the variable data to ZERO. Otherwise, it outputs the + configuration data to IP4_CONFIG2_INSTANCE. + + @param[in] VarName The pointer to the variable name + @param[in, out] Instance The pointer to the IP4 config2 instance data. + + @retval EFI_NOT_FOUND The variable can not be found or already corrupted. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_SUCCESS The configuration data was retrieved successfully. + +**/ +EFI_STATUS +Ip4Config2ReadConfigData ( + IN CHAR16 *VarName, + IN OUT IP4_CONFIG2_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + UINTN VarSize; + IP4_CONFIG2_VARIABLE *Variable; + IP4_CONFIG2_DATA_ITEM *DataItem; + UINTN Index; + IP4_CONFIG2_DATA_RECORD DataRecord; + CHAR8 *Data; + + // + // Try to read the configuration variable. + // + VarSize = 0; + Status = gRT->GetVariable ( + VarName, + &gEfiIp4Config2ProtocolGuid, + NULL, + &VarSize, + NULL + ); + + if (Status == EFI_BUFFER_TOO_SMALL) { + // + // Allocate buffer and read the config variable. + // + Variable = AllocatePool (VarSize); + if (Variable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gRT->GetVariable ( + VarName, + &gEfiIp4Config2ProtocolGuid, + NULL, + &VarSize, + Variable + ); + if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) { + // + // GetVariable still error or the variable is corrupted. + // Fall back to the default value. + // + FreePool (Variable); + + // + // Remove the problematic variable and return EFI_NOT_FOUND, a new + // variable will be set again. + // + gRT->SetVariable ( + VarName, + &gEfiIp4Config2ProtocolGuid, + IP4_CONFIG2_VARIABLE_ATTRIBUTE, + 0, + NULL + ); + + return EFI_NOT_FOUND; + } + + + for (Index = 0; Index < Variable->DataRecordCount; Index++) { + + CopyMem (&DataRecord, &Variable->DataRecord[Index], sizeof (DataRecord)); + + DataItem = &Instance->DataItem[DataRecord.DataType]; + if (DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED) && + (DataItem->DataSize != DataRecord.DataSize) + ) { + // + // Perhaps a corrupted data record... + // + continue; + } + + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { + // + // This data item has variable length data. + // + DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize); + if (DataItem->Data.Ptr == NULL) { + // + // no memory resource + // + continue; + } + } + + Data = (CHAR8 *) Variable + DataRecord.Offset; + CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize); + + DataItem->DataSize = DataRecord.DataSize; + DataItem->Status = EFI_SUCCESS; + } + + FreePool (Variable); + return EFI_SUCCESS; + } + + return Status; +} + +/** + Write the configuration data from IP4_CONFIG2_INSTANCE to variable storage. + + @param[in] VarName The pointer to the variable name. + @param[in] Instance The pointer to the IP4 config2 instance data. + + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_SUCCESS The configuration data is written successfully. + +**/ +EFI_STATUS +Ip4Config2WriteConfigData ( + IN CHAR16 *VarName, + IN IP4_CONFIG2_INSTANCE *Instance + ) +{ + UINTN Index; + UINTN VarSize; + IP4_CONFIG2_DATA_ITEM *DataItem; + IP4_CONFIG2_VARIABLE *Variable; + IP4_CONFIG2_DATA_RECORD *DataRecord; + CHAR8 *Heap; + EFI_STATUS Status; + + VarSize = sizeof (IP4_CONFIG2_VARIABLE) - sizeof (IP4_CONFIG2_DATA_RECORD); + + for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { + + VarSize += sizeof (IP4_CONFIG2_DATA_RECORD) + DataItem->DataSize; + } + } + + Variable = AllocatePool (VarSize); + if (Variable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Heap = (CHAR8 *) Variable + VarSize; + Variable->DataRecordCount = 0; + + for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { + + Heap -= DataItem->DataSize; + CopyMem (Heap, DataItem->Data.Ptr, DataItem->DataSize); + + DataRecord = &Variable->DataRecord[Variable->DataRecordCount]; + DataRecord->DataType = (EFI_IP4_CONFIG2_DATA_TYPE) Index; + DataRecord->DataSize = (UINT32) DataItem->DataSize; + DataRecord->Offset = (UINT16) (Heap - (CHAR8 *) Variable); + + Variable->DataRecordCount++; + } + } + + Variable->Checksum = 0; + Variable->Checksum = (UINT16) ~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize); + + Status = gRT->SetVariable ( + VarName, + &gEfiIp4Config2ProtocolGuid, + IP4_CONFIG2_VARIABLE_ATTRIBUTE, + VarSize, + Variable + ); + + FreePool (Variable); + + return Status; +} + + +/** + Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of GetModeData. + The EFI_IP4_ROUTE_TABLE is clumsy to use in the internal operation of the + IP4 driver. + + @param[in] IpSb The IP4 service binding instance. + @param[out] Table The built IP4 route table. + + @retval EFI_SUCCESS The route table is successfully build + @retval EFI_NOT_FOUND Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip4Config2BuildDefaultRouteTable ( + IN IP4_SERVICE *IpSb, + OUT EFI_IP4_ROUTE_TABLE *Table + ) +{ + LIST_ENTRY *Entry; + IP4_ROUTE_ENTRY *RtEntry; + UINT32 Count; + INT32 Index; + + if (IpSb->DefaultRouteTable == NULL) { + return EFI_NOT_FOUND; + } + + Count = IpSb->DefaultRouteTable->TotalNum; + + if (Count == 0) { + return EFI_NOT_FOUND; + } + + // + // Copy the route entry to EFI route table. Keep the order of + // route entry copied from most specific to default route. That + // is, interlevel the route entry from the instance's route area + // and those from the default route table's route area. + // + Count = 0; + + for (Index = IP4_MASK_MAX; Index >= 0; Index--) { + + NET_LIST_FOR_EACH (Entry, &(IpSb->DefaultRouteTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + EFI_IP4 (Table[Count].SubnetAddress) = HTONL (RtEntry->Dest & RtEntry->Netmask); + EFI_IP4 (Table[Count].SubnetMask) = HTONL (RtEntry->Netmask); + EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop); + + Count++; + } + + } + + return EFI_SUCCESS; +} + +/** + The event process routine when the DHCPv4 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP4 config2 instance data. + +**/ +VOID +EFIAPI +Ip4Config2OnDhcp4SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_CONFIG2_INSTANCE *Instance; + + Instance = (IP4_CONFIG2_INSTANCE *) Context; + + if ((Instance->Dhcp4Handle != NULL) || (Instance->Policy != Ip4Config2PolicyDhcp)) { + // + // The DHCP4 child is already created or the policy is no longer DHCP. + // + return ; + } + + Ip4StartAutoConfig (Instance); +} + +/** + Set the station address and subnetmask for the default interface. + + @param[in] IpSb The pointer to the IP4 service binding instance. + @param[in] StationAddress Ip address to be set. + @param[in] SubnetMask Subnet to be set. + + @retval EFI_SUCCESS Set default address successful. + @retval Others Some errors occur in setting. + +**/ +EFI_STATUS +Ip4Config2SetDefaultAddr ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR StationAddress, + IN IP4_ADDR SubnetMask + ) +{ + EFI_STATUS Status; + IP4_INTERFACE *IpIf; + IP4_PROTOCOL *Ip4Instance; + EFI_ARP_PROTOCOL *Arp; + LIST_ENTRY *Entry; + IP4_ADDR Subnet; + IP4_ROUTE_TABLE *RouteTable; + + IpIf = IpSb->DefaultInterface; + ASSERT (IpIf != NULL); + + if ((IpIf->Ip == StationAddress) && (IpIf->SubnetMask == SubnetMask)) { + IpSb->State = IP4_SERVICE_CONFIGED; + return EFI_SUCCESS; + } + + if (IpSb->Reconfig) { + // + // The default address is changed, free the previous interface first. + // + if (IpSb->DefaultRouteTable != NULL) { + Ip4FreeRouteTable (IpSb->DefaultRouteTable); + IpSb->DefaultRouteTable = NULL; + } + + Ip4CancelReceive (IpSb->DefaultInterface); + Ip4FreeInterface (IpSb->DefaultInterface, NULL); + IpSb->DefaultInterface = NULL; + // + // Create new default interface and route table. + // + IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image); + if (IpIf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RouteTable = Ip4CreateRouteTable (); + if (RouteTable == NULL) { + Ip4FreeInterface (IpIf, NULL); + return EFI_OUT_OF_RESOURCES; + } + + IpSb->DefaultInterface = IpIf; + InsertHeadList (&IpSb->Interfaces, &IpIf->Link); + IpSb->DefaultRouteTable = RouteTable; + Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb); + } + + if (IpSb->State == IP4_SERVICE_CONFIGED) { + IpSb->State = IP4_SERVICE_UNSTARTED; + } + + Status = Ip4SetAddress (IpIf, StationAddress, SubnetMask); + if (EFI_ERROR (Status)) { + return Status; + } + + if (IpIf->Arp != NULL) { + // + // A non-NULL IpIf->Arp here means a new ARP child is created when setting default address, + // but some IP children may have referenced the default interface before it is configured, + // these IP instances also consume this ARP protocol so they need to open it BY_CHILD_CONTROLLER. + // + Arp = NULL; + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + Ip4Instance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, AddrLink, IP4_PROTOCOL_SIGNATURE); + Status = gBS->OpenProtocol ( + IpIf->ArpHandle, + &gEfiArpProtocolGuid, + (VOID **) &Arp, + gIp4DriverBinding.DriverBindingHandle, + Ip4Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + + // + // Add a route for the connected network. + // + Subnet = StationAddress & SubnetMask; + + Ip4AddRoute ( + IpSb->DefaultRouteTable, + Subnet, + SubnetMask, + IP4_ALLZERO_ADDRESS + ); + + IpSb->State = IP4_SERVICE_CONFIGED; + IpSb->Reconfig = FALSE; + + return EFI_SUCCESS; +} + +/** + Set the station address, subnetmask and gateway address for the default interface. + + @param[in] Instance The pointer to the IP4 config2 instance data. + @param[in] StationAddress Ip address to be set. + @param[in] SubnetMask Subnet to be set. + @param[in] GatewayAddress Gateway to be set. + + @retval EFI_SUCCESS Set default If successful. + @retval Others Errors occur as indicated. + +**/ +EFI_STATUS +Ip4Config2SetDefaultIf ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN IP4_ADDR StationAddress, + IN IP4_ADDR SubnetMask, + IN IP4_ADDR GatewayAddress + ) +{ + EFI_STATUS Status; + IP4_SERVICE *IpSb; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + // + // Check whether the StationAddress/SubnetMask pair is valid. + // + if (!Ip4StationAddressValid (StationAddress, SubnetMask)) { + return EFI_INVALID_PARAMETER; + } + + Status = Ip4Config2SetDefaultAddr (IpSb, StationAddress, SubnetMask); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create a route if there is a default router. + // + if (GatewayAddress != IP4_ALLZERO_ADDRESS) { + Ip4AddRoute ( + IpSb->DefaultRouteTable, + IP4_ALLZERO_ADDRESS, + IP4_ALLZERO_ADDRESS, + GatewayAddress + ); + } + + return EFI_SUCCESS; +} + + +/** + Release all the DHCP related resources. + + @param Instance The IP4 config2 instance. + + @return None + +**/ +VOID +Ip4Config2CleanDhcp4 ( + IN IP4_CONFIG2_INSTANCE *Instance + ) +{ + IP4_SERVICE *IpSb; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + if (Instance->Dhcp4 != NULL) { + Instance->Dhcp4->Stop (Instance->Dhcp4); + + gBS->CloseProtocol ( + Instance->Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + Instance->Dhcp4 = NULL; + } + + if (Instance->Dhcp4Handle != NULL) { + NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + Instance->Dhcp4Handle + ); + + Instance->Dhcp4Handle = NULL; + } + + if (Instance->Dhcp4Event != NULL) { + gBS->CloseEvent (Instance->Dhcp4Event); + Instance->Dhcp4Event = NULL; + } +} + +/** + This worker function sets the DNS server list for the EFI IPv4 network + stack running on the communication device that this EFI_IP4_CONFIG2_PROTOCOL + manages. The DNS server addresses must be unicast IPv4 addresses. + + @param[in] Instance The pointer to the IP4 config2 instance data. + @param[in] DataSize The size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set, points to an array of + EFI_IPv4_ADDRESS instances. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_ABORTED The DNS server addresses to be set equal the current + configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv4 + network stack was set. + +**/ +EFI_STATUS +Ip4Config2SetDnsServerWorker ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + UINTN OldIndex; + UINTN NewIndex; + EFI_IPv4_ADDRESS *OldDns; + EFI_IPv4_ADDRESS *NewDns; + UINTN OldDnsCount; + UINTN NewDnsCount; + IP4_CONFIG2_DATA_ITEM *Item; + BOOLEAN OneAdded; + VOID *Tmp; + IP4_ADDR DnsAddress; + + if ((DataSize % sizeof (EFI_IPv4_ADDRESS) != 0) || (DataSize == 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + Item = &Instance->DataItem[Ip4Config2DataTypeDnsServer]; + NewDns = (EFI_IPv4_ADDRESS *) Data; + OldDns = Item->Data.DnsServers; + NewDnsCount = DataSize / sizeof (EFI_IPv4_ADDRESS); + OldDnsCount = Item->DataSize / sizeof (EFI_IPv4_ADDRESS); + OneAdded = FALSE; + + if (NewDnsCount != OldDnsCount) { + Tmp = AllocatePool (DataSize); + if (Tmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Tmp = NULL; + } + + for (NewIndex = 0; NewIndex < NewDnsCount; NewIndex++) { + CopyMem (&DnsAddress, NewDns + NewIndex, sizeof (IP4_ADDR)); + if (IP4_IS_UNSPECIFIED (NTOHL (DnsAddress)) || IP4_IS_LOCAL_BROADCAST (NTOHL (DnsAddress))) { + // + // The dns server address must be unicast. + // + if (Tmp != NULL) { + FreePool (Tmp); + } + return EFI_INVALID_PARAMETER; + } + + if (OneAdded) { + // + // If any address in the new setting is not in the old settings, skip the + // comparision below. + // + continue; + } + + for (OldIndex = 0; OldIndex < OldDnsCount; OldIndex++) { + if (EFI_IP4_EQUAL (NewDns + NewIndex, OldDns + OldIndex)) { + // + // If found break out. + // + break; + } + } + + if (OldIndex == OldDnsCount) { + OneAdded = TRUE; + } + } + + if (!OneAdded && (DataSize == Item->DataSize)) { + // + // No new item is added and the size is the same. + // + Item->Status = EFI_SUCCESS; + return EFI_ABORTED; + } else { + if (Tmp != NULL) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + Item->Data.Ptr = Tmp; + } + + CopyMem (Item->Data.Ptr, Data, DataSize); + Item->DataSize = DataSize; + Item->Status = EFI_SUCCESS; + return EFI_SUCCESS; + } +} + + + +/** + Callback function when DHCP process finished. It will save the + retrieved IP configure parameter from DHCP to the NVRam. + + @param Event The callback event + @param Context Opaque context to the callback + + @return None + +**/ +VOID +EFIAPI +Ip4Config2OnDhcp4Complete ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_CONFIG2_INSTANCE *Instance; + EFI_DHCP4_MODE_DATA Dhcp4Mode; + EFI_STATUS Status; + IP4_ADDR StationAddress; + IP4_ADDR SubnetMask; + IP4_ADDR GatewayAddress; + UINT32 Index; + UINT32 OptionCount; + EFI_DHCP4_PACKET_OPTION **OptionList; + + Instance = (IP4_CONFIG2_INSTANCE *) Context; + ASSERT (Instance->Dhcp4 != NULL); + + // + // Get the DHCP retrieved parameters + // + Status = Instance->Dhcp4->GetModeData (Instance->Dhcp4, &Dhcp4Mode); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (Dhcp4Mode.State == Dhcp4Bound) { + StationAddress = EFI_NTOHL (Dhcp4Mode.ClientAddress); + SubnetMask = EFI_NTOHL (Dhcp4Mode.SubnetMask); + GatewayAddress = EFI_NTOHL (Dhcp4Mode.RouterAddress); + + Status = Ip4Config2SetDefaultIf (Instance, StationAddress, SubnetMask, GatewayAddress); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Parse the ACK to get required DNS server information. + // + OptionCount = 0; + OptionList = NULL; + + Status = Instance->Dhcp4->Parse (Instance->Dhcp4, Dhcp4Mode.ReplyPacket, &OptionCount, OptionList); + if (Status != EFI_BUFFER_TOO_SMALL) { + goto Exit; + } + + OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); + if (OptionList == NULL) { + goto Exit; + } + + Status = Instance->Dhcp4->Parse (Instance->Dhcp4, Dhcp4Mode.ReplyPacket, &OptionCount, OptionList); + if (EFI_ERROR (Status)) { + FreePool (OptionList); + goto Exit; + } + + for (Index = 0; Index < OptionCount; Index++) { + // + // Look for DNS Server opcode (6). + // + if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) { + if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) { + break; + } + + Ip4Config2SetDnsServerWorker (Instance, OptionList[Index]->Length, &OptionList[Index]->Data[0]); + break; + } + } + + FreePool (OptionList); + + Instance->DhcpSuccess = TRUE; + } + +Exit: + Ip4Config2CleanDhcp4 (Instance); + DispatchDpc (); +} + + +/** + Start the DHCP configuration for this IP service instance. + It will locates the EFI_IP4_CONFIG2_PROTOCOL, then start the + DHCP configuration. + + @param[in] Instance The IP4 config2 instance to configure + + @retval EFI_SUCCESS The auto configuration is successfully started + @retval Others Failed to start auto configuration. + +**/ +EFI_STATUS +Ip4StartAutoConfig ( + IN IP4_CONFIG2_INSTANCE *Instance + ) +{ + IP4_SERVICE *IpSb; + EFI_DHCP4_PROTOCOL *Dhcp4; + EFI_DHCP4_MODE_DATA Dhcp4Mode; + EFI_DHCP4_PACKET_OPTION *OptionList[1]; + IP4_CONFIG2_DHCP4_OPTION ParaList; + EFI_STATUS Status; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + if (IpSb->State > IP4_SERVICE_UNSTARTED) { + return EFI_SUCCESS; + } + + // + // A host must not invoke DHCP configuration if it is already + // participating in the DHCP configuration process. + // + if (Instance->Dhcp4Handle != NULL) { + return EFI_SUCCESS; + } + + Status = NetLibCreateServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Instance->Dhcp4Handle + ); + + if (Status == EFI_UNSUPPORTED) { + // + // No DHCPv4 Service Binding protocol, register a notify. + // + if (Instance->Dhcp4SbNotifyEvent == NULL) { + Instance->Dhcp4SbNotifyEvent = EfiCreateProtocolNotifyEvent ( + &gEfiDhcp4ServiceBindingProtocolGuid, + TPL_CALLBACK, + Ip4Config2OnDhcp4SbInstalled, + (VOID *) Instance, + &Instance->Registration + ); + } + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Instance->Dhcp4SbNotifyEvent != NULL) { + gBS->CloseEvent (Instance->Dhcp4SbNotifyEvent); + } + + Status = gBS->OpenProtocol ( + Instance->Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Instance->Dhcp4, + IpSb->Image, + IpSb->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status)) { + NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + Instance->Dhcp4Handle + ); + + Instance->Dhcp4Handle = NULL; + + return Status; + } + + // + // Check the current DHCP status, if the DHCP process has + // already finished, return now. + // + Dhcp4 = Instance->Dhcp4; + Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode); + if (Dhcp4Mode.State == Dhcp4Bound) { + Ip4Config2OnDhcp4Complete (NULL, Instance); + + return EFI_SUCCESS; + } + + // + // Try to start the DHCP process. Use most of the current + // DHCP configuration to avoid problems if some DHCP client + // yields the control of this DHCP service to us. + // + ParaList.Head.OpCode = DHCP4_TAG_PARA_LIST; + ParaList.Head.Length = 3; + ParaList.Head.Data[0] = DHCP4_TAG_NETMASK; + ParaList.Route = DHCP4_TAG_ROUTER; + ParaList.Dns = DHCP4_TAG_DNS_SERVER; + OptionList[0] = &ParaList.Head; + Dhcp4Mode.ConfigData.OptionCount = 1; + Dhcp4Mode.ConfigData.OptionList = OptionList; + + Status = Dhcp4->Configure (Dhcp4, &Dhcp4Mode.ConfigData); + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Instance->Dhcp4Handle, + &gEfiDhcp4ProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp4ServiceBindingProtocolGuid, + Instance->Dhcp4Handle + ); + + Instance->Dhcp4 = NULL; + + Instance->Dhcp4Handle = NULL; + + return Status; + } + + // + // Start the DHCP process + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip4Config2OnDhcp4Complete, + Instance, + &Instance->Dhcp4Event + ); + if (EFI_ERROR (Status)) { + Ip4Config2DestroyDhcp4 (Instance); + return Status; + } + + Status = Dhcp4->Start (Dhcp4, Instance->Dhcp4Event); + if (EFI_ERROR (Status)) { + Ip4Config2DestroyDhcp4 (Instance); + gBS->CloseEvent (Instance->Dhcp4Event); + Instance->Dhcp4Event = NULL; + + return Status; + } + + IpSb->State = IP4_SERVICE_STARTED; + DispatchDpc (); + + return EFI_SUCCESS; +} + + + +/** + The work function is to get the interface information of the communication + device this IP4_CONFIG2_INSTANCE manages. + + @param[in] Instance Pointer to the IP4 config2 instance data. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in + bytes, the size of buffer required to store the specified + configuration data. + @param[in] Data The data buffer in which the configuration data is returned. + Ignored if DataSize is ZERO. + + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified + configuration data, and the required size is + returned in DataSize. + @retval EFI_SUCCESS The specified configuration data was obtained. + +**/ +EFI_STATUS +Ip4Config2GetIfInfo ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + IP4_SERVICE *IpSb; + UINTN Length; + IP4_CONFIG2_DATA_ITEM *Item; + EFI_IP4_CONFIG2_INTERFACE_INFO *IfInfo; + IP4_ADDR Address; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + Length = sizeof (EFI_IP4_CONFIG2_INTERFACE_INFO); + + if (IpSb->DefaultRouteTable != NULL) { + Length += IpSb->DefaultRouteTable->TotalNum * sizeof (EFI_IP4_ROUTE_TABLE); + } + + if (*DataSize < Length) { + *DataSize = Length; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy the fixed size part of the interface info. + // + Item = &Instance->DataItem[Ip4Config2DataTypeInterfaceInfo]; + IfInfo = (EFI_IP4_CONFIG2_INTERFACE_INFO *) Data; + CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP4_CONFIG2_INTERFACE_INFO)); + + // + // Update the address info. + // + if (IpSb->DefaultInterface != NULL) { + Address = HTONL (IpSb->DefaultInterface->Ip); + CopyMem (&IfInfo->StationAddress, &Address, sizeof (EFI_IPv4_ADDRESS)); + Address = HTONL (IpSb->DefaultInterface->SubnetMask); + CopyMem (&IfInfo->SubnetMask, &Address, sizeof (EFI_IPv4_ADDRESS)); + } + + if (IpSb->DefaultRouteTable != NULL) { + IfInfo->RouteTableSize = IpSb->DefaultRouteTable->TotalNum; + IfInfo->RouteTable = (EFI_IP4_ROUTE_TABLE *) ((UINT8 *) Data + sizeof (EFI_IP4_CONFIG2_INTERFACE_INFO)); + + Ip4Config2BuildDefaultRouteTable (IpSb, IfInfo->RouteTable); + } + + return EFI_SUCCESS; +} + +/** + The work function is to set the general configuration policy for the EFI IPv4 network + stack that is running on the communication device managed by this IP4_CONFIG2_INSTANCE. + The policy will affect other configuration settings. + + @param[in] Instance Pointer to the IP4 config2 instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_INVALID_PARAMETER The to be set policy is invalid. + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_ABORTED The new policy equals the current policy. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip4Config2SetPolicy ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP4_CONFIG2_POLICY NewPolicy; + IP4_CONFIG2_DATA_ITEM *DataItem; + IP4_SERVICE *IpSb; + + if (DataSize != sizeof (EFI_IP4_CONFIG2_POLICY)) { + return EFI_BAD_BUFFER_SIZE; + } + + NewPolicy = *((EFI_IP4_CONFIG2_POLICY *) Data); + + if (NewPolicy >= Ip4Config2PolicyMax) { + return EFI_INVALID_PARAMETER; + } + + if (NewPolicy == Instance->Policy) { + if (NewPolicy != Ip4Config2PolicyDhcp || Instance->DhcpSuccess) { + return EFI_ABORTED; + } + } else { + // + // The policy is changed. Clean the ManualAddress, Gateway and DnsServers, + // shrink the variable data size, and fire up all the related events. + // + DataItem = &Instance->DataItem[Ip4Config2DataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip4Config2SignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip4Config2DataTypeGateway]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip4Config2SignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip4Config2DataTypeDnsServer]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip4Config2SignalEvent, NULL); + + if (NewPolicy == Ip4Config2PolicyDhcp) { + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_VOLATILE); + } else { + // + // The policy is changed from dhcp to static. Stop the DHCPv4 process + // and destroy the DHCPv4 child. + // + if (Instance->Dhcp4Handle != NULL) { + Ip4Config2DestroyDhcp4 (Instance); + } + + // + // Close the event. + // + if (Instance->Dhcp4Event != NULL) { + gBS->CloseEvent (Instance->Dhcp4Event); + Instance->Dhcp4Event = NULL; + } + } + } + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + Ip4Config2OnPolicyChanged (IpSb, NewPolicy); + + Instance->Policy = NewPolicy; + + return EFI_SUCCESS; +} + +/** + The work function is to set the station addresses manually for the EFI IPv4 + network stack. It is only configurable when the policy is Ip4Config2PolicyStatic. + + @param[in] Instance Pointer to the IP4 config2 instance data. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_NOT_READY An asynchronous process is invoked to set the specified + configuration data, and the process is not finished. + @retval EFI_ABORTED The manual addresses to be set equal current + configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip4Config2SetManualAddress ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP4_CONFIG2_MANUAL_ADDRESS NewAddress; + IP4_CONFIG2_DATA_ITEM *DataItem; + EFI_STATUS Status; + IP4_ADDR StationAddress; + IP4_ADDR SubnetMask; + VOID *Ptr; + IP4_SERVICE *IpSb; + IP4_INTERFACE *IpIf; + IP4_ROUTE_TABLE *RouteTable; + + DataItem = NULL; + Status = EFI_SUCCESS; + Ptr = NULL; + IpIf = NULL; + RouteTable = NULL; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + ASSERT (Instance->DataItem[Ip4Config2DataTypeManualAddress].Status != EFI_NOT_READY); + + if ((DataSize != 0) && ((DataSize % sizeof (EFI_IP4_CONFIG2_MANUAL_ADDRESS)) != 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip4Config2PolicyStatic) { + return EFI_WRITE_PROTECTED; + } + + DataItem = &Instance->DataItem[Ip4Config2DataTypeManualAddress]; + + if (Data != NULL && DataSize != 0) { + NewAddress = *((EFI_IP4_CONFIG2_MANUAL_ADDRESS *) Data); + + StationAddress = EFI_NTOHL (NewAddress.Address); + SubnetMask = EFI_NTOHL (NewAddress.SubnetMask); + + // + // Check whether the StationAddress/SubnetMask pair is valid. + // + if (!Ip4StationAddressValid (StationAddress, SubnetMask)) { + return EFI_INVALID_PARAMETER; + } + + // + // Store the new data, and init the DataItem status to EFI_NOT_READY because + // we may have an asynchronous configuration process. + // + Ptr = AllocateCopyPool (DataSize, Data); + if (Ptr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + + DataItem->Data.Ptr = Ptr; + DataItem->DataSize = DataSize; + DataItem->Status = EFI_NOT_READY; + + IpSb->Reconfig = TRUE; + Status = Ip4Config2SetDefaultAddr (IpSb, StationAddress, SubnetMask); + + DataItem->Status = Status; + + if (EFI_ERROR (DataItem->Status) && DataItem->Status != EFI_NOT_READY) { + if (Ptr != NULL) { + FreePool (Ptr); + } + DataItem->Data.Ptr = NULL; + } + } else { + // + // DataSize is 0 and Data is NULL, clean up the manual address. + // + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + + // + // Free the default router table and Interface, clean up the assemble table. + // + if (IpSb->DefaultInterface != NULL) { + if (IpSb->DefaultRouteTable != NULL) { + Ip4FreeRouteTable (IpSb->DefaultRouteTable); + IpSb->DefaultRouteTable = NULL; + } + + Ip4CancelReceive (IpSb->DefaultInterface); + + Ip4FreeInterface (IpSb->DefaultInterface, NULL); + IpSb->DefaultInterface = NULL; + } + + Ip4CleanAssembleTable (&IpSb->Assemble); + + // + // Create new default interface and route table. + // + IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image); + if (IpIf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RouteTable = Ip4CreateRouteTable (); + if (RouteTable == NULL) { + Ip4FreeInterface (IpIf, NULL); + return EFI_OUT_OF_RESOURCES; + } + + IpSb->DefaultInterface = IpIf; + InsertHeadList (&IpSb->Interfaces, &IpIf->Link); + IpSb->DefaultRouteTable = RouteTable; + Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb); + + // + // Reset the State to unstarted. + // + if (IpSb->State == IP4_SERVICE_CONFIGED || IpSb->State == IP4_SERVICE_STARTED) { + IpSb->State = IP4_SERVICE_UNSTARTED; + } + } + + return Status; +} + +/** + The work function is to set the gateway addresses manually for the EFI IPv4 + network stack that is running on the communication device that this EFI IPv4 + Configuration Protocol manages. It is not configurable when the policy is + Ip4Config2PolicyDhcp. The gateway addresses must be unicast IPv4 addresses. + + @param[in] Instance The pointer to the IP4 config2 instance data. + @param[in] DataSize The size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. This points to an array of + EFI_IPv6_ADDRESS instances. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to complete the operation. + @retval EFI_ABORTED The manual gateway addresses to be set equal the + current configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip4Config2SetGateway ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + IP4_SERVICE *IpSb; + IP4_CONFIG2_DATA_ITEM *DataItem; + IP4_ADDR Gateway; + + UINTN Index1; + UINTN Index2; + EFI_IPv4_ADDRESS *OldGateway; + EFI_IPv4_ADDRESS *NewGateway; + UINTN OldGatewayCount; + UINTN NewGatewayCount; + BOOLEAN OneRemoved; + BOOLEAN OneAdded; + VOID *Tmp; + + OldGateway = NULL; + NewGateway = NULL; + OneRemoved = FALSE; + OneAdded = FALSE; + Tmp = NULL; + + if ((DataSize != 0) && (DataSize % sizeof (EFI_IPv4_ADDRESS) != 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip4Config2PolicyStatic) { + return EFI_WRITE_PROTECTED; + } + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + DataItem = &Instance->DataItem[Ip4Config2DataTypeGateway]; + OldGateway = DataItem->Data.Gateway; + OldGatewayCount = DataItem->DataSize / sizeof (EFI_IPv4_ADDRESS); + + for (Index1 = 0; Index1 < OldGatewayCount; Index1++) { + // + // Remove the old route entry. + // + CopyMem (&Gateway, OldGateway + Index1, sizeof (IP4_ADDR)); + Ip4DelRoute ( + IpSb->DefaultRouteTable, + IP4_ALLZERO_ADDRESS, + IP4_ALLZERO_ADDRESS, + NTOHL (Gateway) + ); + OneRemoved = TRUE; + } + + if (Data != NULL && DataSize != 0) { + NewGateway = (EFI_IPv4_ADDRESS *) Data; + NewGatewayCount = DataSize / sizeof (EFI_IPv4_ADDRESS); + for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { + CopyMem (&Gateway, NewGateway + Index1, sizeof (IP4_ADDR)); + + if ((IpSb->DefaultInterface->SubnetMask != 0) && + !NetIp4IsUnicast (NTOHL (Gateway), IpSb->DefaultInterface->SubnetMask)) { + return EFI_INVALID_PARAMETER; + } + + for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) { + if (EFI_IP4_EQUAL (NewGateway + Index1, NewGateway + Index2)) { + return EFI_INVALID_PARAMETER; + } + } + } + + if (NewGatewayCount != OldGatewayCount) { + Tmp = AllocatePool (DataSize); + if (Tmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } else { + Tmp = NULL; + } + + for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { + // + // Add the new route entry. + // + CopyMem (&Gateway, NewGateway + Index1, sizeof (IP4_ADDR)); + Ip4AddRoute ( + IpSb->DefaultRouteTable, + IP4_ALLZERO_ADDRESS, + IP4_ALLZERO_ADDRESS, + NTOHL (Gateway) + ); + + OneAdded = TRUE; + } + + if (!OneRemoved && !OneAdded) { + DataItem->Status = EFI_SUCCESS; + return EFI_ABORTED; + } else { + if (Tmp != NULL) { + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = Tmp; + } + + CopyMem (DataItem->Data.Ptr, Data, DataSize); + DataItem->DataSize = DataSize; + DataItem->Status = EFI_SUCCESS; + } + } else { + // + // DataSize is 0 and Data is NULL, clean up the Gateway address. + // + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + The work function is to set the DNS server list for the EFI IPv4 network + stack running on the communication device that this EFI_IP4_CONFIG2_PROTOCOL + manages. It is not configurable when the policy is Ip4Config2PolicyDhcp. + The DNS server addresses must be unicast IPv4 addresses. + + @param[in] Instance The pointer to the IP4 config2 instance data. + @param[in] DataSize The size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set, points to an array of + EFI_IPv4_ADDRESS instances. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type. + @retval EFI_WRITE_PROTECTED The specified configuration data cannot be set + under the current policy. + @retval EFI_INVALID_PARAMETER One or more fields in Data is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_ABORTED The DNS server addresses to be set equal the current + configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv4 + network stack was set. + +**/ +EFI_STATUS +Ip4Config2SetDnsServer ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_STATUS Status; + IP4_CONFIG2_DATA_ITEM *Item; + + Status = EFI_SUCCESS; + Item = NULL; + + if (Instance->Policy != Ip4Config2PolicyStatic) { + return EFI_WRITE_PROTECTED; + } + + Item = &Instance->DataItem[Ip4Config2DataTypeDnsServer]; + + if (DATA_ATTRIB_SET (Item->Attribute, DATA_ATTRIB_VOLATILE)) { + REMOVE_DATA_ATTRIB (Item->Attribute, DATA_ATTRIB_VOLATILE); + } + + if (Data != NULL && DataSize != 0) { + Status = Ip4Config2SetDnsServerWorker (Instance, DataSize, Data); + } else { + // + // DataSize is 0 and Data is NULL, clean up the DnsServer address. + // + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + Item->Data.Ptr = NULL; + Item->DataSize = 0; + Item->Status = EFI_NOT_FOUND; + } + + return Status; +} + +/** + Generate the operational state of the interface this IP4 config2 instance manages + and output in EFI_IP4_CONFIG2_INTERFACE_INFO. + + @param[in] IpSb The pointer to the IP4 service binding instance. + @param[out] IfInfo The pointer to the IP4 config2 interface information structure. + +**/ +VOID +Ip4Config2InitIfInfo ( + IN IP4_SERVICE *IpSb, + OUT EFI_IP4_CONFIG2_INTERFACE_INFO *IfInfo + ) +{ + UnicodeSPrint ( + IfInfo->Name, + EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE, + L"eth%d", + IpSb->Ip4Config2Instance.IfIndex + ); + + IfInfo->IfType = IpSb->SnpMode.IfType; + IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize; + CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize); +} + + + +/** + Set the configuration for the EFI IPv4 network stack running on the communication + device this EFI_IP4_CONFIG2_PROTOCOL instance manages. + + This function is used to set the configuration data of type DataType for the EFI + IPv4 network stack that is running on the communication device that this EFI IPv4 + Configuration Protocol instance manages. + + DataSize is used to calculate the count of structure instances in the Data for + a DataType in which multiple structure instances are allowed. + + This function is always non-blocking. When setting some type of configuration data, + an asynchronous process is invoked to check the correctness of the data, such as + performing Duplicate Address Detection on the manually set local IPv4 addresses. + EFI_NOT_READY is returned immediately to indicate that such an asynchronous process + is invoked, and the process is not finished yet. The caller wanting to get the result + of the asynchronous process is required to call RegisterDataNotify() to register an + event on the specified configuration data. Once the event is signaled, the caller + can call GetData() to obtain the configuration data and know the result. + For other types of configuration data that do not require an asynchronous configuration + process, the result of the operation is immediately returned. + + @param[in] This The pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param[in] DataType The type of data to set. + @param[in] DataSize Size of the buffer pointed to by Data in bytes. + @param[in] Data The data buffer to set. The type of the data buffer is + associated with the DataType. + + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set successfully. + @retval EFI_INVALID_PARAMETER One or more of the following are TRUE: + - This is NULL. + - One or more fields in Data and DataSize do not match the + requirement of the data type indicated by DataType. + @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified + configuration data cannot be set under the current policy. + @retval EFI_ACCESS_DENIED Another set operation on the specified configuration + data is already in process. + @retval EFI_NOT_READY An asynchronous process was invoked to set the specified + configuration data, and the process is not finished yet. + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type + indicated by DataType. + @retval EFI_UNSUPPORTED This DataType is not supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Config2SetData ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP4_CONFIG2_INSTANCE *Instance; + IP4_SERVICE *IpSb; + + if ((This == NULL) || (Data == NULL && DataSize != 0) || (Data != NULL && DataSize == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip4Config2DataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This); + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE); + + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = Instance->DataItem[DataType].Status; + if (Status != EFI_NOT_READY) { + + if (Instance->DataItem[DataType].SetData == NULL) { + // + // This type of data is readonly. + // + Status = EFI_WRITE_PROTECTED; + } else { + + Status = Instance->DataItem[DataType].SetData (Instance, DataSize, Data); + if (!EFI_ERROR (Status)) { + // + // Fire up the events registered with this type of data. + // + NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip4Config2SignalEvent, NULL); + Ip4Config2WriteConfigData (IpSb->MacString, Instance); + } else if (Status == EFI_ABORTED) { + // + // The SetData is aborted because the data to set is the same with + // the one maintained. + // + Status = EFI_SUCCESS; + NetMapIterate (&Instance->DataItem[DataType].EventMap, Ip4Config2SignalEvent, NULL); + } + } + } else { + // + // Another asynchronous process is on the way. + // + Status = EFI_ACCESS_DENIED; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Get the configuration data for the EFI IPv4 network stack running on the communication + device that this EFI_IP4_CONFIG2_PROTOCOL instance manages. + + This function returns the configuration data of type DataType for the EFI IPv4 network + stack running on the communication device that this EFI IPv4 Configuration Protocol instance + manages. + + The caller is responsible for allocating the buffer used to return the specified + configuration data. The required size will be returned to the caller if the size of + the buffer is too small. + + EFI_NOT_READY is returned if the specified configuration data is not ready due to an + asynchronous configuration process already in progress. The caller can call RegisterDataNotify() + to register an event on the specified configuration data. Once the asynchronous configuration + process is finished, the event will be signaled, and a subsequent GetData() call will return + the specified configuration data. + + @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param[in] DataType The type of data to get. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in bytes, the + size of buffer required to store the specified configuration data. + @param[in] Data The data buffer in which the configuration data is returned. The + type of the data buffer is associated with the DataType. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - This is NULL. + - DataSize is NULL. + - Data is NULL if *DataSize is not zero. + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified configuration data, + and the required size is returned in DataSize. + @retval EFI_NOT_READY The specified configuration data is not ready due to an + asynchronous configuration process already in progress. + @retval EFI_NOT_FOUND The specified configuration data is not found. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Config2GetData ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP4_CONFIG2_INSTANCE *Instance; + IP4_CONFIG2_DATA_ITEM *DataItem; + + if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip4Config2DataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This); + DataItem = &Instance->DataItem[DataType]; + + Status = Instance->DataItem[DataType].Status; + if (!EFI_ERROR (Status)) { + + if (DataItem->GetData != NULL) { + + Status = DataItem->GetData (Instance, DataSize, Data); + } else if (*DataSize < Instance->DataItem[DataType].DataSize) { + // + // Update the buffer length. + // + *DataSize = Instance->DataItem[DataType].DataSize; + Status = EFI_BUFFER_TOO_SMALL; + } else { + + *DataSize = Instance->DataItem[DataType].DataSize; + CopyMem (Data, Instance->DataItem[DataType].Data.Ptr, *DataSize); + } + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Register an event that is signaled whenever a configuration process on the specified + configuration data is done. + + This function registers an event that is to be signaled whenever a configuration + process on the specified configuration data is performed. An event can be registered + for a different DataType simultaneously. The caller is responsible for determining + which type of configuration data causes the signaling of the event in such an event. + + @param[in] This Pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param[in] DataType The type of data to unregister the event for. + @param[in] Event The event to register. + + @retval EFI_SUCCESS The notification event for the specified configuration data is + registered. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_UNSUPPORTED The configuration data type specified by DataType is not + supported. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_ACCESS_DENIED The Event is already registered for the DataType. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Config2RegisterDataNotify ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP4_CONFIG2_INSTANCE *Instance; + NET_MAP *EventMap; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip4Config2DataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This); + EventMap = &Instance->DataItem[DataType].EventMap; + + // + // Check whether this event is already registered for this DataType. + // + Item = NetMapFindKey (EventMap, Event); + if (Item == NULL) { + + Status = NetMapInsertTail (EventMap, Event, NULL); + + if (EFI_ERROR (Status)) { + + Status = EFI_OUT_OF_RESOURCES; + } + + } else { + + Status = EFI_ACCESS_DENIED; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Remove a previously registered event for the specified configuration data. + + @param This The pointer to the EFI_IP4_CONFIG2_PROTOCOL instance. + @param DataType The type of data to remove from the previously + registered event. + @param Event The event to be unregistered. + + @retval EFI_SUCCESS The event registered for the specified + configuration data was removed. + @retval EFI_INVALID_PARAMETER This is NULL or Event is NULL. + @retval EFI_NOT_FOUND The Event has not been registered for the + specified DataType. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Config2UnregisterDataNotify ( + IN EFI_IP4_CONFIG2_PROTOCOL *This, + IN EFI_IP4_CONFIG2_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP4_CONFIG2_INSTANCE *Instance; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip4Config2DataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP4_CONFIG2_INSTANCE_FROM_PROTOCOL (This); + + Item = NetMapFindKey (&Instance->DataItem[DataType].EventMap, Event); + if (Item != NULL) { + + NetMapRemoveItem (&Instance->DataItem[DataType].EventMap, Item, NULL); + Status = EFI_SUCCESS; + } else { + + Status = EFI_NOT_FOUND; + } + + gBS->RestoreTPL (OldTpl); + + return Status; +} + +/** + Initialize an IP4_CONFIG2_INSTANCE. + + @param[out] Instance The buffer of IP4_CONFIG2_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP4_CONFIG2_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip4Config2InitInstance ( + OUT IP4_CONFIG2_INSTANCE *Instance + ) +{ + IP4_SERVICE *IpSb; + IP4_CONFIG2_INSTANCE *TmpInstance; + LIST_ENTRY *Entry; + EFI_STATUS Status; + UINTN Index; + UINT16 IfIndex; + IP4_CONFIG2_DATA_ITEM *DataItem; + + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + Instance->Signature = IP4_CONFIG2_INSTANCE_SIGNATURE; + + + // + // Determine the index of this interface. + // + IfIndex = 0; + NET_LIST_FOR_EACH (Entry, &mIp4Config2InstanceList) { + TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_CONFIG2_INSTANCE, Link, IP4_CONFIG2_INSTANCE_SIGNATURE); + + if (TmpInstance->IfIndex > IfIndex) { + // + // There is a sequence hole because some interface is down. + // + break; + } + + IfIndex++; + } + + Instance->IfIndex = IfIndex; + NetListInsertBefore (Entry, &Instance->Link); + + for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) { + // + // Initialize the event map for each data item. + // + NetMapInit (&Instance->DataItem[Index].EventMap); + } + + + // + // Initialize each data type: associate storage and set data size for the + // fixed size data types, hook the SetData function, set the data attribute. + // + DataItem = &Instance->DataItem[Ip4Config2DataTypeInterfaceInfo]; + DataItem->GetData = Ip4Config2GetIfInfo; + DataItem->Data.Ptr = &Instance->InterfaceInfo; + DataItem->DataSize = sizeof (Instance->InterfaceInfo); + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE); + Ip4Config2InitIfInfo (IpSb, &Instance->InterfaceInfo); + + DataItem = &Instance->DataItem[Ip4Config2DataTypePolicy]; + DataItem->SetData = Ip4Config2SetPolicy; + DataItem->Data.Ptr = &Instance->Policy; + DataItem->DataSize = sizeof (Instance->Policy); + Instance->Policy = Ip4Config2PolicyStatic; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip4Config2DataTypeManualAddress]; + DataItem->SetData = Ip4Config2SetManualAddress; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip4Config2DataTypeGateway]; + DataItem->SetData = Ip4Config2SetGateway; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip4Config2DataTypeDnsServer]; + DataItem->SetData = Ip4Config2SetDnsServer; + DataItem->Status = EFI_NOT_FOUND; + + Instance->Configured = TRUE; + + // + // Try to read the config data from NV variable. + // If not found, write initialized config data into NV variable + // as a default config data. + // + Status = Ip4Config2ReadConfigData (IpSb->MacString, Instance); + if (Status == EFI_NOT_FOUND) { + Status = Ip4Config2WriteConfigData (IpSb->MacString, Instance); + } + + if (EFI_ERROR (Status)) { + return Status; + } + + Instance->Ip4Config2.SetData = EfiIp4Config2SetData; + Instance->Ip4Config2.GetData = EfiIp4Config2GetData; + Instance->Ip4Config2.RegisterDataNotify = EfiIp4Config2RegisterDataNotify; + Instance->Ip4Config2.UnregisterDataNotify = EfiIp4Config2UnregisterDataNotify; + + // + // Publish the IP4 configuration form + // + return Ip4Config2FormInit (Instance); +} + + +/** + Release an IP4_CONFIG2_INSTANCE. + + @param[in, out] Instance The buffer of IP4_CONFIG2_INSTANCE to be freed. + +**/ +VOID +Ip4Config2CleanInstance ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ) +{ + UINTN Index; + IP4_CONFIG2_DATA_ITEM *DataItem; + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + + if (!Instance->Configured) { + return ; + } + + if (Instance->Dhcp4Handle != NULL) { + + Ip4Config2DestroyDhcp4 (Instance); + } + + // + // Close the event. + // + if (Instance->Dhcp4Event != NULL) { + gBS->CloseEvent (Instance->Dhcp4Event); + Instance->Dhcp4Event = NULL; + } + + for (Index = 0; Index < Ip4Config2DataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED)) { + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + } + + NetMapClean (&Instance->DataItem[Index].EventMap); + } + + Ip4Config2FormUnload (Instance); + + RemoveEntryList (&Instance->Link); +} + +/** + The event handle for IP4 auto reconfiguration. The original default + interface and route table will be removed as the default. + + @param[in] Context The IP4 service binding instance. + +**/ +VOID +EFIAPI +Ip4AutoReconfigCallBackDpc ( + IN VOID *Context + ) +{ + IP4_SERVICE *IpSb; + + IpSb = (IP4_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE); + + if (IpSb->State > IP4_SERVICE_UNSTARTED) { + IpSb->State = IP4_SERVICE_UNSTARTED; + } + + IpSb->Reconfig = TRUE; + + Ip4StartAutoConfig (&IpSb->Ip4Config2Instance); + + return ; +} + + +/** + Request Ip4AutoReconfigCallBackDpc as a DPC at TPL_CALLBACK. + + @param Event The event that is signalled. + @param Context The IP4 service binding instance. + +**/ +VOID +EFIAPI +Ip4AutoReconfigCallBack ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip4AutoReconfigCallBackDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip4AutoReconfigCallBackDpc, Context); +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Impl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Impl.h new file mode 100644 index 00000000..13ec3674 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Impl.h @@ -0,0 +1,294 @@ +/** @file + Definitions for EFI IPv4 Configuration II Protocol implementation. + + Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __IP4_CONFIG2_IMPL_H__ +#define __IP4_CONFIG2_IMPL_H__ + +#define IP4_CONFIG2_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'P', 'C', '2') +#define IP4_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I') + +#define IP4_CONFIG2_VARIABLE_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) + +#define DATA_ATTRIB_SIZE_FIXED 0x1 +#define DATA_ATTRIB_VOLATILE 0x2 + +#define DATA_ATTRIB_SET(Attrib, Bits) (BOOLEAN)((Attrib) & (Bits)) +#define SET_DATA_ATTRIB(Attrib, Bits) ((Attrib) |= (Bits)) +#define REMOVE_DATA_ATTRIB(Attrib, Bits) ((Attrib) &= (~Bits)) + +typedef struct _IP4_CONFIG2_INSTANCE IP4_CONFIG2_INSTANCE; + +#define IP4_CONFIG2_INSTANCE_FROM_PROTOCOL(Proto) \ + CR ((Proto), \ + IP4_CONFIG2_INSTANCE, \ + Ip4Config2, \ + IP4_CONFIG2_INSTANCE_SIGNATURE \ + ) + +#define IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE(Instance) \ + CR ((Instance), \ + IP4_SERVICE, \ + Ip4Config2Instance, \ + IP4_SERVICE_SIGNATURE \ + ) + +#define IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Callback) \ + CR ((Callback), \ + IP4_CONFIG2_INSTANCE, \ + CallbackInfo, \ + IP4_CONFIG2_INSTANCE_SIGNATURE \ + ) + +#define IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \ + CR ((ConfigAccess), \ + IP4_FORM_CALLBACK_INFO, \ + HiiConfigAccessProtocol, \ + IP4_FORM_CALLBACK_INFO_SIGNATURE \ + ) + +/** + The prototype of work function for EfiIp4Config2SetData(). + + @param[in] Instance The pointer to the IP4 config2 instance data. + @param[in] DataSize In bytes, the size of the buffer pointed to by Data. + @param[in] Data The data buffer to set. + + @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type, + 8 bytes. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv4 + network stack was set successfully. + +**/ +typedef +EFI_STATUS +(*IP4_CONFIG2_SET_DATA) ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + The prototype of work function for EfiIp4Config2GetData(). + + @param[in] Instance The pointer to the IP4 config2 instance data. + @param[in, out] DataSize On input, in bytes, the size of Data. On output, in + bytes, the size of buffer required to store the specified + configuration data. + @param[in] Data The data buffer in which the configuration data is returned. + Ignored if DataSize is ZERO. + + @retval EFI_BUFFER_TOO_SMALL The size of Data is too small for the specified + configuration data, and the required size is + returned in DataSize. + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + +**/ +typedef +EFI_STATUS +(*IP4_CONFIG2_GET_DATA) ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ); + +typedef union { + VOID *Ptr; + EFI_IP4_CONFIG2_INTERFACE_INFO *IfInfo; + EFI_IP4_CONFIG2_POLICY *Policy; + EFI_IP4_CONFIG2_MANUAL_ADDRESS *ManualAddress; + EFI_IPv4_ADDRESS *Gateway; + EFI_IPv4_ADDRESS *DnsServers; +} IP4_CONFIG2_DATA; + +typedef struct { + IP4_CONFIG2_SET_DATA SetData; + IP4_CONFIG2_GET_DATA GetData; + EFI_STATUS Status; + UINT8 Attribute; + NET_MAP EventMap; + IP4_CONFIG2_DATA Data; + UINTN DataSize; +} IP4_CONFIG2_DATA_ITEM; + +typedef struct { + UINT16 Offset; + UINT32 DataSize; + EFI_IP4_CONFIG2_DATA_TYPE DataType; +} IP4_CONFIG2_DATA_RECORD; + +#pragma pack(1) + +// +// heap data that contains the data for each data record. +// +// EFI_IP4_CONFIG2_POLICY Policy; +// UINT32 ManualaddressCount; +// UINT32 GatewayCount; +// UINT32 DnsServersCount; +// EFI_IP4_CONFIG2_MANUAL_ADDRESS ManualAddress[]; +// EFI_IPv4_ADDRESS Gateway[]; +// EFI_IPv4_ADDRESS DnsServers[]; +// +typedef struct { + UINT16 Checksum; + UINT16 DataRecordCount; + IP4_CONFIG2_DATA_RECORD DataRecord[1]; +} IP4_CONFIG2_VARIABLE; + +#pragma pack() + +typedef struct { + EFI_IP4_CONFIG2_POLICY Policy; ///< manual or automatic + EFI_IP4_CONFIG2_MANUAL_ADDRESS *ManualAddress; ///< IP addresses + UINT32 ManualAddressCount; ///< IP addresses count + EFI_IPv4_ADDRESS *GatewayAddress; ///< Gateway address + UINT32 GatewayAddressCount; ///< Gateway address count + EFI_IPv4_ADDRESS *DnsAddress; ///< DNS server address + UINT32 DnsAddressCount; ///< DNS server address count +} IP4_CONFIG2_NVDATA; + +typedef struct _IP4_FORM_CALLBACK_INFO { + UINT32 Signature; + EFI_HANDLE ChildHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL HiiConfigAccessProtocol; + EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath; + EFI_HII_HANDLE RegisteredHandle; +} IP4_FORM_CALLBACK_INFO; + +struct _IP4_CONFIG2_INSTANCE { + UINT32 Signature; + BOOLEAN Configured; + LIST_ENTRY Link; + UINT16 IfIndex; + + EFI_IP4_CONFIG2_PROTOCOL Ip4Config2; + + EFI_IP4_CONFIG2_INTERFACE_INFO InterfaceInfo; + EFI_IP4_CONFIG2_POLICY Policy; + IP4_CONFIG2_DATA_ITEM DataItem[Ip4Config2DataTypeMaximum]; + + EFI_EVENT Dhcp4SbNotifyEvent; + VOID *Registration; + EFI_HANDLE Dhcp4Handle; + EFI_DHCP4_PROTOCOL *Dhcp4; + BOOLEAN DhcpSuccess; + BOOLEAN OtherInfoOnly; + EFI_EVENT Dhcp4Event; + UINT32 FailedIaAddressCount; + EFI_IPv4_ADDRESS *DeclineAddress; + UINT32 DeclineAddressCount; + + IP4_FORM_CALLBACK_INFO CallbackInfo; + + IP4_CONFIG2_NVDATA Ip4NvData; +}; + +// +// Configure the DHCP to request the routers and netmask +// from server. The DHCP4_TAG_NETMASK is included in Head. +// +#pragma pack(1) +typedef struct { + EFI_DHCP4_PACKET_OPTION Head; + UINT8 Route; + UINT8 Dns; +} IP4_CONFIG2_DHCP4_OPTION; +#pragma pack() + +/** + Read the configuration data from variable storage according to the VarName and + gEfiIp4Config2ProtocolGuid. It checks the integrity of variable data. If the + data is corrupted, it clears the variable data to ZERO. Otherwise, it outputs the + configuration data to IP4_CONFIG2_INSTANCE. + + @param[in] VarName The pointer to the variable name + @param[in, out] Instance The pointer to the IP4 config2 instance data. + + @retval EFI_NOT_FOUND The variable can not be found or already corrupted. + @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete the operation. + @retval EFI_SUCCESS The configuration data was retrieved successfully. + +**/ +EFI_STATUS +Ip4Config2ReadConfigData ( + IN CHAR16 *VarName, + IN OUT IP4_CONFIG2_INSTANCE *Instance + ); + +/** + Start the DHCP configuration for this IP service instance. + It will locates the EFI_IP4_CONFIG2_PROTOCOL, then start the + DHCP configuration. + + @param[in] Instance The IP4 config2 instance to configure. + + @retval EFI_SUCCESS The auto configuration is successfully started. + @retval Others Failed to start auto configuration. + +**/ +EFI_STATUS +Ip4StartAutoConfig ( + IN IP4_CONFIG2_INSTANCE *Instance + ); + +/** + Initialize an IP4_CONFIG2_INSTANCE. + + @param[out] Instance The buffer of IP4_CONFIG2_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP4_CONFIG2_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip4Config2InitInstance ( + OUT IP4_CONFIG2_INSTANCE *Instance + ); + +/** + Release an IP4_CONFIG2_INSTANCE. + + @param[in, out] Instance The buffer of IP4_CONFIG2_INSTANCE to be freed. + +**/ +VOID +Ip4Config2CleanInstance ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ); + +/** + Request Ip4AutoReconfigCallBackDpc as a DPC at TPL_CALLBACK. + + @param Event The event that is signalled. + @param Context The IP4 service binding instance. + +**/ +VOID +EFIAPI +Ip4AutoReconfigCallBack ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Destroy the Dhcp4 child in IP4_CONFIG2_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP4 config2 instance to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destroy the child. + +**/ +EFI_STATUS +Ip4Config2DestroyDhcp4 ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Nv.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Nv.c new file mode 100644 index 00000000..5b29261e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Nv.c @@ -0,0 +1,1444 @@ +/** @file + Helper functions for configuring or getting the parameters relating to Ip4. + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +CHAR16 mIp4Config2StorageName[] = L"IP4_CONFIG2_IFR_NVDATA"; + +/** + Calculate the prefix length of the IPv4 subnet mask. + + @param[in] SubnetMask The IPv4 subnet mask. + + @return The prefix length of the subnet mask. + @retval 0 Other errors as indicated. + +**/ +UINT8 +GetSubnetMaskPrefixLength ( + IN EFI_IPv4_ADDRESS *SubnetMask + ) +{ + UINT8 Len; + UINT32 ReverseMask; + + // + // The SubnetMask is in network byte order. + // + ReverseMask = SwapBytes32 (*(UINT32 *)&SubnetMask[0]); + + // + // Reverse it. + // + ReverseMask = ~ReverseMask; + + if ((ReverseMask & (ReverseMask + 1)) != 0) { + return 0; + } + + Len = 0; + + while (ReverseMask != 0) { + ReverseMask = ReverseMask >> 1; + Len++; + } + + return (UINT8) (32 - Len); +} + +/** + Convert the decimal dotted IPv4 address into the binary IPv4 address. + + @param[in] Str The UNICODE string. + @param[out] Ip The storage to return the IPv4 address. + + @retval EFI_SUCCESS The binary IP address is returned in Ip. + @retval EFI_INVALID_PARAMETER The IP string is malformatted. + +**/ +EFI_STATUS +Ip4Config2StrToIp ( + IN CHAR16 *Str, + OUT EFI_IPv4_ADDRESS *Ip + ) +{ + UINTN Index; + UINTN Number; + + Index = 0; + + while (*Str != L'\0') { + + if (Index > 3) { + return EFI_INVALID_PARAMETER; + } + + Number = 0; + while ((*Str >= L'0') && (*Str <= L'9')) { + Number = Number * 10 + (*Str - L'0'); + Str++; + } + + if (Number > 0xFF) { + return EFI_INVALID_PARAMETER; + } + + Ip->Addr[Index] = (UINT8) Number; + + if ((*Str != L'\0') && (*Str != L'.')) { + // + // The current character should be either the NULL terminator or + // the dot delimiter. + // + return EFI_INVALID_PARAMETER; + } + + if (*Str == L'.') { + // + // Skip the delimiter. + // + Str++; + } + + Index++; + } + + if (Index != 4) { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + Convert the decimal dotted IPv4 addresses separated by space into the binary IPv4 address list. + + @param[in] Str The UNICODE string contains IPv4 addresses. + @param[out] PtrIpList The storage to return the IPv4 address list. + @param[out] IpCount The size of the IPv4 address list. + + @retval EFI_SUCCESS The binary IP address list is returned in PtrIpList. + @retval EFI_OUT_OF_RESOURCES Error occurs in allocating memory. + @retval EFI_INVALID_PARAMETER The IP string is malformatted. + +**/ +EFI_STATUS +Ip4Config2StrToIpList ( + IN CHAR16 *Str, + OUT EFI_IPv4_ADDRESS **PtrIpList, + OUT UINTN *IpCount + ) +{ + UINTN BeginIndex; + UINTN EndIndex; + UINTN Index; + UINTN IpIndex; + CHAR16 *StrTemp; + BOOLEAN SpaceTag; + + BeginIndex = 0; + EndIndex = BeginIndex; + Index = 0; + IpIndex = 0; + StrTemp = NULL; + SpaceTag = TRUE; + + *PtrIpList = NULL; + *IpCount = 0; + + if (Str == NULL) { + return EFI_SUCCESS; + } + + // + // Get the number of Ip. + // + while (*(Str + Index) != L'\0') { + if (*(Str + Index) == L' ') { + SpaceTag = TRUE; + } else { + if (SpaceTag) { + (*IpCount)++; + SpaceTag = FALSE; + } + } + + Index++; + } + + if (*IpCount == 0) { + return EFI_SUCCESS; + } + + // + // Allocate buffer for IpList. + // + *PtrIpList = AllocateZeroPool(*IpCount * sizeof(EFI_IPv4_ADDRESS)); + if (*PtrIpList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get IpList from Str. + // + Index = 0; + while (*(Str + Index) != L'\0') { + if (*(Str + Index) == L' ') { + if(!SpaceTag) { + StrTemp = AllocateZeroPool((EndIndex - BeginIndex + 1) * sizeof(CHAR16)); + if (StrTemp == NULL) { + FreePool(*PtrIpList); + *PtrIpList = NULL; + *IpCount = 0; + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (StrTemp, Str + BeginIndex, (EndIndex - BeginIndex) * sizeof(CHAR16)); + *(StrTemp + (EndIndex - BeginIndex)) = L'\0'; + + if (Ip4Config2StrToIp (StrTemp, &((*PtrIpList)[IpIndex])) != EFI_SUCCESS) { + FreePool(StrTemp); + FreePool(*PtrIpList); + *PtrIpList = NULL; + *IpCount = 0; + return EFI_INVALID_PARAMETER; + } + + BeginIndex = EndIndex; + IpIndex++; + + FreePool(StrTemp); + } + + BeginIndex++; + EndIndex++; + SpaceTag = TRUE; + } else { + EndIndex++; + SpaceTag = FALSE; + } + + Index++; + + if (*(Str + Index) == L'\0') { + if (!SpaceTag) { + StrTemp = AllocateZeroPool((EndIndex - BeginIndex + 1) * sizeof(CHAR16)); + if (StrTemp == NULL) { + FreePool(*PtrIpList); + *PtrIpList = NULL; + *IpCount = 0; + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (StrTemp, Str + BeginIndex, (EndIndex - BeginIndex) * sizeof(CHAR16)); + *(StrTemp + (EndIndex - BeginIndex)) = L'\0'; + + if (Ip4Config2StrToIp (StrTemp, &((*PtrIpList)[IpIndex])) != EFI_SUCCESS) { + FreePool(StrTemp); + FreePool(*PtrIpList); + *PtrIpList = NULL; + *IpCount = 0; + return EFI_INVALID_PARAMETER; + } + + FreePool(StrTemp); + } + } + } + + return EFI_SUCCESS; +} + +/** + Convert the IPv4 address into a dotted string. + + @param[in] Ip The IPv4 address. + @param[out] Str The dotted IP string. + +**/ +VOID +Ip4Config2IpToStr ( + IN EFI_IPv4_ADDRESS *Ip, + OUT CHAR16 *Str + ) +{ + UnicodeSPrint ( + Str, + 2 * IP4_STR_MAX_SIZE, + L"%d.%d.%d.%d", + Ip->Addr[0], + Ip->Addr[1], + Ip->Addr[2], + Ip->Addr[3] + ); +} + + +/** + Convert the IPv4 address list into string consists of several decimal + dotted IPv4 addresses separated by space. + + @param[in] Ip The IPv4 address list. + @param[in] IpCount The size of IPv4 address list. + @param[out] Str The string contains several decimal dotted + IPv4 addresses separated by space. + + @retval EFI_SUCCESS Operation is success. + @retval EFI_OUT_OF_RESOURCES Error occurs in allocating memory. + +**/ +EFI_STATUS +Ip4Config2IpListToStr ( + IN EFI_IPv4_ADDRESS *Ip, + IN UINTN IpCount, + OUT CHAR16 *Str + ) +{ + UINTN Index; + UINTN TemIndex; + UINTN StrIndex; + CHAR16 *TempStr; + EFI_IPv4_ADDRESS *TempIp; + + Index = 0; + TemIndex = 0; + StrIndex = 0; + TempStr = NULL; + TempIp = NULL; + + for (Index = 0; Index < IpCount; Index ++) { + TempIp = Ip + Index; + if (TempStr == NULL) { + TempStr = AllocateZeroPool(2 * IP4_STR_MAX_SIZE); + if (TempStr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + UnicodeSPrint ( + TempStr, + 2 * IP4_STR_MAX_SIZE, + L"%d.%d.%d.%d", + TempIp->Addr[0], + TempIp->Addr[1], + TempIp->Addr[2], + TempIp->Addr[3] + ); + + for (TemIndex = 0; TemIndex < IP4_STR_MAX_SIZE; TemIndex ++) { + if (*(TempStr + TemIndex) == L'\0') { + if (Index == IpCount - 1) { + Str[StrIndex++] = L'\0'; + } else { + Str[StrIndex++] = L' '; + } + break; + } else { + Str[StrIndex++] = *(TempStr + TemIndex); + } + } + } + + if (TempStr != NULL) { + FreePool(TempStr); + } + + return EFI_SUCCESS; +} + +/** + The notify function of create event when performing a manual configuration. + + @param[in] Event The pointer of Event. + @param[in] Context The pointer of Context. + +**/ +VOID +EFIAPI +Ip4Config2ManualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Convert the network configuration data into the IFR data. + + @param[in] Instance The IP4 config2 instance. + @param[in, out] IfrNvData The IFR nv data. + + @retval EFI_SUCCESS The configure parameter to IFR data was + set successfully. + @retval EFI_INVALID_PARAMETER Source instance or target IFR data is not available. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip4Config2ConvertConfigNvDataToIfrNvData ( + IN IP4_CONFIG2_INSTANCE *Instance, + IN OUT IP4_CONFIG2_IFR_NVDATA *IfrNvData + ) +{ + IP4_SERVICE *IpSb; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_IP4_CONFIG2_INTERFACE_INFO *Ip4Info; + EFI_IP4_CONFIG2_POLICY Policy; + UINTN DataSize; + UINTN GatewaySize; + EFI_IPv4_ADDRESS GatewayAddress; + EFI_STATUS Status; + UINTN DnsSize; + UINTN DnsCount; + EFI_IPv4_ADDRESS *DnsAddress; + + Status = EFI_SUCCESS; + Ip4Config2 = &Instance->Ip4Config2; + Ip4Info = NULL; + DnsAddress = NULL; + GatewaySize = sizeof (EFI_IPv4_ADDRESS); + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP4_CONFIG2_INSTANCE_SIGNATURE); + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + + if (IpSb->DefaultInterface->Configured) { + IfrNvData->Configure = 1; + } else { + IfrNvData->Configure = 0; + goto Exit; + } + + // + // Get the Policy info. + // + DataSize = sizeof (EFI_IP4_CONFIG2_POLICY); + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + &DataSize, + &Policy + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (Policy == Ip4Config2PolicyStatic) { + IfrNvData->DhcpEnable = FALSE; + } else if (Policy == Ip4Config2PolicyDhcp) { + IfrNvData->DhcpEnable = TRUE; + goto Exit; + } + + // + // Get the interface info. + // + DataSize = 0; + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypeInterfaceInfo, + &DataSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + Ip4Info = AllocateZeroPool (DataSize); + if (Ip4Info == NULL) { + Status = EFI_OUT_OF_RESOURCES; + return Status; + } + + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypeInterfaceInfo, + &DataSize, + Ip4Info + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get the Gateway info. + // + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypeGateway, + &GatewaySize, + &GatewayAddress + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get the Dns info. + // + DnsSize = 0; + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypeDnsServer, + &DnsSize, + NULL + ); + if ((Status != EFI_BUFFER_TOO_SMALL) && (Status != EFI_NOT_FOUND)) { + goto Exit; + } + + DnsCount = (UINT32) (DnsSize / sizeof (EFI_IPv4_ADDRESS)); + + if (DnsSize > 0) { + DnsAddress = AllocateZeroPool(DnsSize); + if (DnsAddress == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + Status = Ip4Config2->GetData ( + Ip4Config2, + Ip4Config2DataTypeDnsServer, + &DnsSize, + DnsAddress + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Ip4Config2IpToStr (&Ip4Info->StationAddress, IfrNvData->StationAddress); + Ip4Config2IpToStr (&Ip4Info->SubnetMask, IfrNvData->SubnetMask); + Ip4Config2IpToStr (&GatewayAddress, IfrNvData->GatewayAddress); + Status = Ip4Config2IpListToStr (DnsAddress, DnsCount, IfrNvData->DnsAddress); + +Exit: + + if (DnsAddress != NULL) { + FreePool(DnsAddress); + } + + if (Ip4Info != NULL) { + FreePool(Ip4Info); + } + + return Status; +} + +/** + Convert the IFR data into the network configuration data and set the IP + configure parameters for the NIC. + + @param[in] IfrFormNvData The IFR NV data. + @param[in, out] Instance The IP4 config2 instance. + + @retval EFI_SUCCESS The configure parameter for this NIC was + set successfully. + @retval EFI_INVALID_PARAMETER The address information for setting is invalid. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip4Config2ConvertIfrNvDataToConfigNvData ( + IN IP4_CONFIG2_IFR_NVDATA *IfrFormNvData, + IN OUT IP4_CONFIG2_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Cfg2; + IP4_CONFIG2_NVDATA *Ip4NvData; + + EFI_IP_ADDRESS StationAddress; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS Gateway; + IP4_ADDR Ip; + EFI_IPv4_ADDRESS *DnsAddress; + UINTN DnsCount; + UINTN Index; + + EFI_EVENT TimeoutEvent; + EFI_EVENT SetAddressEvent; + BOOLEAN IsAddressOk; + UINTN DataSize; + EFI_INPUT_KEY Key; + + Status = EFI_SUCCESS; + Ip4Cfg2 = &Instance->Ip4Config2; + Ip4NvData = &Instance->Ip4NvData; + + DnsCount = 0; + DnsAddress = NULL; + + TimeoutEvent = NULL; + SetAddressEvent = NULL; + + + + if (Instance == NULL || IfrFormNvData == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IfrFormNvData->Configure != TRUE) { + return EFI_SUCCESS; + } + + if (IfrFormNvData->DhcpEnable == TRUE) { + Ip4NvData->Policy = Ip4Config2PolicyDhcp; + + Status = Ip4Cfg2->SetData ( + Ip4Cfg2, + Ip4Config2DataTypePolicy, + sizeof (EFI_IP4_CONFIG2_POLICY), + &Ip4NvData->Policy + ); + if (EFI_ERROR(Status)) { + return Status; + } + } else { + // + // Get Ip4NvData from IfrFormNvData if it is valid. + // + Ip4NvData->Policy = Ip4Config2PolicyStatic; + + Status = Ip4Config2StrToIp (IfrFormNvData->SubnetMask, &SubnetMask.v4); + if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (GetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL); + return EFI_INVALID_PARAMETER; + } + + Status = Ip4Config2StrToIp (IfrFormNvData->StationAddress, &StationAddress.v4); + if (EFI_ERROR (Status) || + (SubnetMask.Addr[0] != 0 && !NetIp4IsUnicast (NTOHL (StationAddress.Addr[0]), NTOHL (SubnetMask.Addr[0]))) || + !Ip4StationAddressValid (NTOHL (StationAddress.Addr[0]), NTOHL (SubnetMask.Addr[0]))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL); + return EFI_INVALID_PARAMETER; + } + + Status = Ip4Config2StrToIp (IfrFormNvData->GatewayAddress, &Gateway.v4); + if (EFI_ERROR (Status) || + (Gateway.Addr[0] != 0 && SubnetMask.Addr[0] != 0 && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), NTOHL (SubnetMask.Addr[0])))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL); + return EFI_INVALID_PARAMETER; + } + + Status = Ip4Config2StrToIpList (IfrFormNvData->DnsAddress, &DnsAddress, &DnsCount); + if (!EFI_ERROR (Status) && DnsCount > 0) { + for (Index = 0; Index < DnsCount; Index ++) { + CopyMem (&Ip, &DnsAddress[Index], sizeof (IP4_ADDR)); + if (IP4_IS_UNSPECIFIED (NTOHL (Ip)) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL); + FreePool(DnsAddress); + return EFI_INVALID_PARAMETER; + } + } + } else { + if (EFI_ERROR (Status)) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL); + } + } + + if (Ip4NvData->ManualAddress != NULL) { + FreePool(Ip4NvData->ManualAddress); + } + Ip4NvData->ManualAddressCount = 1; + Ip4NvData->ManualAddress = AllocateZeroPool(sizeof(EFI_IP4_CONFIG2_MANUAL_ADDRESS)); + if (Ip4NvData->ManualAddress == NULL) { + if (DnsAddress != NULL) { + FreePool(DnsAddress); + } + + return EFI_OUT_OF_RESOURCES; + } + CopyMem(&Ip4NvData->ManualAddress->Address, &StationAddress.v4, sizeof(EFI_IPv4_ADDRESS)); + CopyMem(&Ip4NvData->ManualAddress->SubnetMask, &SubnetMask.v4, sizeof(EFI_IPv4_ADDRESS)); + + if (Ip4NvData->GatewayAddress != NULL) { + FreePool(Ip4NvData->GatewayAddress); + } + Ip4NvData->GatewayAddressCount = 1; + Ip4NvData->GatewayAddress = AllocateZeroPool(sizeof(EFI_IPv4_ADDRESS)); + if (Ip4NvData->GatewayAddress == NULL) { + if (DnsAddress != NULL) { + FreePool(DnsAddress); + } + return EFI_OUT_OF_RESOURCES; + } + CopyMem(Ip4NvData->GatewayAddress, &Gateway.v4, sizeof(EFI_IPv4_ADDRESS)); + + if (Ip4NvData->DnsAddress != NULL) { + FreePool(Ip4NvData->DnsAddress); + } + Ip4NvData->DnsAddressCount = (UINT32) DnsCount; + Ip4NvData->DnsAddress = DnsAddress; + + // + // Setting Ip4NvData. + // + Status = Ip4Cfg2->SetData ( + Ip4Cfg2, + Ip4Config2DataTypePolicy, + sizeof (EFI_IP4_CONFIG2_POLICY), + &Ip4NvData->Policy + ); + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Create events & timers for asynchronous settings. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + if (EFI_ERROR (Status)) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip4Config2ManualAddressNotify, + &IsAddressOk, + &SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + IsAddressOk = FALSE; + + Status = Ip4Cfg2->RegisterDataNotify ( + Ip4Cfg2, + Ip4Config2DataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Set ManualAddress. + // + DataSize = Ip4NvData->ManualAddressCount * sizeof (EFI_IP4_CONFIG2_MANUAL_ADDRESS); + Status = Ip4Cfg2->SetData ( + Ip4Cfg2, + Ip4Config2DataTypeManualAddress, + DataSize, + (VOID *) Ip4NvData->ManualAddress + ); + + if (Status == EFI_NOT_READY) { + gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000); + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + if (IsAddressOk) { + Status = EFI_SUCCESS; + break; + } + } + } + + Ip4Cfg2->UnregisterDataNotify ( + Ip4Cfg2, + Ip4Config2DataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Set gateway. + // + DataSize = Ip4NvData->GatewayAddressCount * sizeof (EFI_IPv4_ADDRESS); + Status = Ip4Cfg2->SetData ( + Ip4Cfg2, + Ip4Config2DataTypeGateway, + DataSize, + Ip4NvData->GatewayAddress + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Set DNS addresses. + // + if (Ip4NvData->DnsAddressCount > 0 && Ip4NvData->DnsAddress != NULL) { + DataSize = Ip4NvData->DnsAddressCount * sizeof (EFI_IPv4_ADDRESS); + Status = Ip4Cfg2->SetData ( + Ip4Cfg2, + Ip4Config2DataTypeDnsServer, + DataSize, + Ip4NvData->DnsAddress + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + } + } + +Exit: + if (SetAddressEvent != NULL) { + gBS->CloseEvent (SetAddressEvent); + } + + if (TimeoutEvent != NULL) { + gBS->CloseEvent (TimeoutEvent); + } + + return Status; +} + +/** + This function allows the caller to request the current + configuration for one or more named elements. The resulting + string is in <ConfigAltResp> format. Any and all alternative + configuration strings shall also be appended to the end of the + current configuration string. If they are, they must appear + after the current configuration. They must contain the same + routing (GUID, NAME, PATH) as the current configuration string. + They must have an additional description indicating the type of + alternative configuration the string represents, + "ALTCFG=<StringToken>". That <StringToken> (when + converted from Hex UNICODE to binary) is a reference to a + string in the associated string pack. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Request A null-terminated Unicode string in + <ConfigRequest> format. Note that this + includes the routing information as well as + the configurable name / value pairs. It is + invalid for this string to be in + <MultiConfigRequest> format. + @param[out] Progress On return, points to a character in the + Request string. Points to the string's null + terminator if request was successful. Points + to the most recent "&" before the first + failing name / value pair (or the beginning + of the string if the failure is in the first + name / value pair) if the request was not + successful. + @param[out] Results A null-terminated Unicode string in + <ConfigAltResp> format which has all values + filled in for the names in the Request string. + String to be allocated by the called function. + + @retval EFI_SUCCESS The Results string is filled with the + values corresponding to all requested + names. + @retval EFI_OUT_OF_RESOURCES Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + @retval EFI_NOT_FOUND Routing data doesn't match any + known driver. Progress set to the + first character in the routing header. + Note: There is no requirement that the + driver validate the routing data. It + must skip the <ConfigHdr> in order to + process the names. + @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set + to most recent & before the + error or the beginning of the + string. + @retval EFI_INVALID_PARAMETER Unknown name. Progress points + to the & before the name in + question.Currently not implemented. +**/ +EFI_STATUS +EFIAPI +Ip4FormExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + EFI_STATUS Status; + IP4_CONFIG2_INSTANCE *Ip4Config2Instance; + IP4_FORM_CALLBACK_INFO *Private; + IP4_CONFIG2_IFR_NVDATA *IfrFormNvData; + EFI_STRING ConfigRequestHdr; + EFI_STRING ConfigRequest; + BOOLEAN AllocatedRequest; + EFI_STRING FormResult; + UINTN Size; + UINTN BufferSize; + + if (Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + IfrFormNvData = NULL; + ConfigRequest = NULL; + FormResult = NULL; + Size = 0; + AllocatedRequest = FALSE; + ConfigRequest = Request; + Private = IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(This); + Ip4Config2Instance = IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Private); + BufferSize = sizeof (IP4_CONFIG2_IFR_NVDATA); + *Progress = Request; + + // + // Check Request data in <ConfigHdr>. + // + if ((Request == NULL) || HiiIsConfigHdrMatch (Request, &gIp4Config2NvDataGuid, mIp4Config2StorageName)) { + IfrFormNvData = AllocateZeroPool (sizeof (IP4_CONFIG2_IFR_NVDATA)); + if (IfrFormNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip4Config2ConvertConfigNvDataToIfrNvData (Ip4Config2Instance, IfrFormNvData); + + if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { + // + // Request has no request element, construct full request string. + // Allocate and fill a buffer large enough to hold the <ConfigHdr> template + // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator + // + ConfigRequestHdr = HiiConstructConfigHdr (&gIp4Config2NvDataGuid, mIp4Config2StorageName, Private->ChildHandle); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + if (ConfigRequest == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Failure; + } + AllocatedRequest = TRUE; + + UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); + FreePool (ConfigRequestHdr); + } + + // + // Convert buffer data to <ConfigResp> by helper function BlockToConfig() + // + Status = gHiiConfigRouting->BlockToConfig ( + gHiiConfigRouting, + ConfigRequest, + (UINT8 *) IfrFormNvData, + BufferSize, + &FormResult, + Progress + ); + + FreePool (IfrFormNvData); + + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + + if (EFI_ERROR (Status)) { + goto Failure; + } + } + + if (Request == NULL || HiiIsConfigHdrMatch (Request, &gIp4Config2NvDataGuid, mIp4Config2StorageName)) { + *Results = FormResult; + } else { + return EFI_NOT_FOUND; + } + +Failure: + // + // Set Progress string to the original request string. + // + if (Request == NULL) { + *Progress = NULL; + } else if (StrStr (Request, L"OFFSET") == NULL) { + *Progress = Request + StrLen (Request); + } + + return Status; +} + +/** + This function applies changes in a driver's configuration. + Input is a Configuration, which has the routing data for this + driver followed by name / value configuration pairs. The driver + must apply those pairs to its configurable storage. If the + driver's configuration is stored in a linear block of data + and the driver's name / value pairs are in <BlockConfig> + format, it may use the ConfigToBlock helper function (above) to + simplify the job. Currently not implemented. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Configuration A null-terminated Unicode string in + <ConfigString> format. + @param[out] Progress A pointer to a string filled in with the + offset of the most recent '&' before the + first failing name / value pair (or the + beginning of the string if the failure + is in the first name / value pair) or + the terminating NULL if all was + successful. + + @retval EFI_SUCCESS The results have been distributed or are + awaiting distribution. + @retval EFI_OUT_OF_MEMORY Not enough memory to store the + parts of the results that must be + stored awaiting possible future + protocols. + @retval EFI_INVALID_PARAMETERS Passing in a NULL for the + Results parameter would result + in this type of error. + @retval EFI_NOT_FOUND Target for the specified routing data + was not found. +**/ +EFI_STATUS +EFIAPI +Ip4FormRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + IP4_CONFIG2_IFR_NVDATA *IfrFormNvData; + IP4_CONFIG2_INSTANCE *Ip4Config2Instance; + IP4_FORM_CALLBACK_INFO *Private; + + Status = EFI_SUCCESS; + IfrFormNvData = NULL; + + if (Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Configuration; + + Private = IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(This); + Ip4Config2Instance = IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Private); + + // + // Check Routing data in <ConfigHdr>. + // + if (HiiIsConfigHdrMatch (Configuration, &gIp4Config2NvDataGuid, mIp4Config2StorageName)) { + // + // Convert buffer data to <ConfigResp> by helper function BlockToConfig() + // + IfrFormNvData = AllocateZeroPool (sizeof (IP4_CONFIG2_IFR_NVDATA)); + if (IfrFormNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + BufferSize = 0; + + Status = gHiiConfigRouting->ConfigToBlock ( + gHiiConfigRouting, + Configuration, + (UINT8 *) IfrFormNvData, + &BufferSize, + Progress + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + Status = gHiiConfigRouting->ConfigToBlock ( + gHiiConfigRouting, + Configuration, + (UINT8 *) IfrFormNvData, + &BufferSize, + Progress + ); + if (!EFI_ERROR (Status)) { + Status = Ip4Config2ConvertIfrNvDataToConfigNvData (IfrFormNvData, Ip4Config2Instance); + } + + FreePool (IfrFormNvData); + } else { + return EFI_NOT_FOUND; + } + + return Status; + +} + +/** + This function is called to provide results data to the driver. + This data consists of a unique key that is used to identify + which data is either being passed back or being asked for. + + @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. + @param[in] Action Specifies the type of action taken by the browser. + @param[in] QuestionId A unique value which is sent to the original + exporting driver so that it can identify the type + of data to expect. The format of the data tends to + vary based on the opcode that enerated the callback. + @param[in] Type The type of value for the question. + @param[in] Value A pointer to the data being sent to the original + exporting driver. + @param[out] ActionRequest On return, points to the action requested by the + callback function. + + @retval EFI_SUCCESS The callback successfully handled the action. + @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the + variable and its data. + @retval EFI_DEVICE_ERROR The variable could not be saved. + @retval EFI_UNSUPPORTED The specified Action is not supported by the + callback. Currently not implemented. + @retval EFI_INVALID_PARAMETERS Passing in wrong parameter. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +Ip4FormCallback ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN EFI_BROWSER_ACTION Action, + IN EFI_QUESTION_ID QuestionId, + IN UINT8 Type, + IN EFI_IFR_TYPE_VALUE *Value, + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest + ) +{ + EFI_STATUS Status; + IP4_CONFIG2_INSTANCE *Instance; + IP4_CONFIG2_IFR_NVDATA *IfrFormNvData; + IP4_FORM_CALLBACK_INFO *Private; + + EFI_IP_ADDRESS StationAddress; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS Gateway; + IP4_ADDR Ip; + EFI_IPv4_ADDRESS *DnsAddress; + UINTN DnsCount; + UINTN Index; + EFI_INPUT_KEY Key; + + IfrFormNvData = NULL; + DnsCount = 0; + DnsAddress = NULL; + + if (Action == EFI_BROWSER_ACTION_CHANGED) { + Private = IP4_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(This); + Instance = IP4_CONFIG2_INSTANCE_FROM_FORM_CALLBACK(Private); + + IfrFormNvData = AllocateZeroPool (sizeof (IP4_CONFIG2_IFR_NVDATA)); + if (IfrFormNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Retrieve uncommitted data from Browser + // + if (!HiiGetBrowserData (&gIp4Config2NvDataGuid, mIp4Config2StorageName, sizeof (IP4_CONFIG2_IFR_NVDATA), (UINT8 *) IfrFormNvData)) { + FreePool (IfrFormNvData); + return EFI_NOT_FOUND; + } + + Status = EFI_SUCCESS; + + switch (QuestionId) { + case KEY_LOCAL_IP: + Status = Ip4Config2StrToIp (IfrFormNvData->StationAddress, &StationAddress.v4); + if (EFI_ERROR (Status) || IP4_IS_UNSPECIFIED (NTOHL (StationAddress.Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (StationAddress.Addr[0]))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL); + Status = EFI_INVALID_PARAMETER; + } + break; + + case KEY_SUBNET_MASK: + Status = Ip4Config2StrToIp (IfrFormNvData->SubnetMask, &SubnetMask.v4); + if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (GetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL); + Status = EFI_INVALID_PARAMETER; + } + break; + + case KEY_GATE_WAY: + Status = Ip4Config2StrToIp (IfrFormNvData->GatewayAddress, &Gateway.v4); + if (EFI_ERROR (Status) || IP4_IS_LOCAL_BROADCAST(NTOHL(Gateway.Addr[0]))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL); + Status = EFI_INVALID_PARAMETER; + } + break; + + case KEY_DNS: + Status = Ip4Config2StrToIpList (IfrFormNvData->DnsAddress, &DnsAddress, &DnsCount); + if (!EFI_ERROR (Status) && DnsCount > 0) { + for (Index = 0; Index < DnsCount; Index ++) { + CopyMem (&Ip, &DnsAddress[Index], sizeof (IP4_ADDR)); + if (IP4_IS_UNSPECIFIED (NTOHL (Ip)) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL); + Status = EFI_INVALID_PARAMETER; + break; + } + } + } else { + if (EFI_ERROR (Status)) { + CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Dns Server!", NULL); + } + } + + if(DnsAddress != NULL) { + FreePool(DnsAddress); + } + break; + + case KEY_SAVE_CHANGES: + Status = Ip4Config2ConvertIfrNvDataToConfigNvData (IfrFormNvData, Instance); + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; + break; + + default: + break; + } + + FreePool (IfrFormNvData); + + return Status; + } + + // + // All other action return unsupported. + // + return EFI_UNSUPPORTED; +} + +/** + Install HII Config Access protocol for network device and allocate resource. + + @param[in, out] Instance The IP4 config2 Instance. + + @retval EFI_SUCCESS The HII Config Access protocol is installed. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip4Config2FormInit ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + IP4_SERVICE *IpSb; + IP4_FORM_CALLBACK_INFO *CallbackInfo; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; + VENDOR_DEVICE_PATH VendorDeviceNode; + EFI_SERVICE_BINDING_PROTOCOL *MnpSb; + CHAR16 *MacString; + CHAR16 MenuString[128]; + CHAR16 PortString[128]; + CHAR16 *OldMenuString; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + ASSERT (IpSb != NULL); + + CallbackInfo = &Instance->CallbackInfo; + + CallbackInfo->Signature = IP4_FORM_CALLBACK_INFO_SIGNATURE; + + Status = gBS->HandleProtocol ( + IpSb->Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Construct device path node for EFI HII Config Access protocol, + // which consists of controller physical device path and one hardware + // vendor guid node. + // + ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH)); + VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH; + VendorDeviceNode.Header.SubType = HW_VENDOR_DP; + + CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid); + + SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH)); + CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode ( + ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode + ); + if (CallbackInfo->HiiVendorDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + ConfigAccess = &CallbackInfo->HiiConfigAccessProtocol; + ConfigAccess->ExtractConfig = Ip4FormExtractConfig; + ConfigAccess->RouteConfig = Ip4FormRouteConfig; + ConfigAccess->Callback = Ip4FormCallback; + + // + // Install Device Path Protocol and Config Access protocol on new handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + ConfigAccess, + NULL + ); + + if (!EFI_ERROR (Status)) { + // + // Open the Parent Handle for the child + // + Status = gBS->OpenProtocol ( + IpSb->Controller, + &gEfiManagedNetworkServiceBindingProtocolGuid, + (VOID **) &MnpSb, + IpSb->Image, + CallbackInfo->ChildHandle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + } + + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Publish our HII data + // + CallbackInfo->RegisteredHandle = HiiAddPackages ( + &gIp4Config2NvDataGuid, + CallbackInfo->ChildHandle, + Ip4DxeStrings, + Ip4Config2Bin, + NULL + ); + if (CallbackInfo->RegisteredHandle == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Append MAC string in the menu help string and tile help string + // + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString); + if (!EFI_ERROR (Status)) { + OldMenuString = HiiGetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP4_CONFIG2_FORM_HELP), + NULL + ); + UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP4_CONFIG2_FORM_HELP), + MenuString, + NULL + ); + + UnicodeSPrint (PortString, 128, L"MAC:%s", MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP4_DEVICE_FORM_HELP), + PortString, + NULL + ); + + FreePool (MacString); + FreePool (OldMenuString); + + return EFI_SUCCESS; + } + +Error: + Ip4Config2FormUnload (Instance); + return Status; +} + +/** + Uninstall the HII Config Access protocol for network devices and free up the resources. + + @param[in, out] Instance The IP4 config2 instance to unload a form. + +**/ +VOID +Ip4Config2FormUnload ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ) +{ + IP4_SERVICE *IpSb; + IP4_FORM_CALLBACK_INFO *CallbackInfo; + IP4_CONFIG2_NVDATA *Ip4NvData; + + IpSb = IP4_SERVICE_FROM_IP4_CONFIG2_INSTANCE (Instance); + ASSERT (IpSb != NULL); + + CallbackInfo = &Instance->CallbackInfo; + + if (CallbackInfo->ChildHandle != NULL) { + // + // Close the child handle + // + gBS->CloseProtocol ( + IpSb->Controller, + &gEfiManagedNetworkServiceBindingProtocolGuid, + IpSb->Image, + CallbackInfo->ChildHandle + ); + + // + // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL + // + gBS->UninstallMultipleProtocolInterfaces ( + CallbackInfo->ChildHandle, + &gEfiDevicePathProtocolGuid, + CallbackInfo->HiiVendorDevicePath, + &gEfiHiiConfigAccessProtocolGuid, + &CallbackInfo->HiiConfigAccessProtocol, + NULL + ); + } + + if (CallbackInfo->HiiVendorDevicePath != NULL) { + FreePool (CallbackInfo->HiiVendorDevicePath); + } + + if (CallbackInfo->RegisteredHandle != NULL) { + // + // Remove HII package list + // + HiiRemovePackages (CallbackInfo->RegisteredHandle); + } + + Ip4NvData = &Instance->Ip4NvData; + + if(Ip4NvData->ManualAddress != NULL) { + FreePool(Ip4NvData->ManualAddress); + } + + if(Ip4NvData->GatewayAddress != NULL) { + FreePool(Ip4NvData->GatewayAddress); + } + + if(Ip4NvData->DnsAddress != NULL) { + FreePool(Ip4NvData->DnsAddress); + } + + Ip4NvData->ManualAddressCount = 0; + Ip4NvData->GatewayAddressCount = 0; + Ip4NvData->DnsAddressCount = 0; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Nv.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Nv.h new file mode 100644 index 00000000..806c10dd --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Config2Nv.h @@ -0,0 +1,45 @@ +/** @file + The header file of IP4Config2Nv.c + +Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _IP4_CONFIG2NV_H_ +#define _IP4_CONFIG2NV_H_ + +#include "Ip4Impl.h" + +extern UINT8 Ip4Config2Bin[]; +extern UINT8 Ip4DxeStrings[]; + +#define NIC_ITEM_CONFIG_SIZE (sizeof (IP4_CONFIG2_INSTANCE) + (sizeof (EFI_IPv4_ADDRESS) * MAX_IP4_CONFIG_DNS)) + +/** + Install HII Config Access protocol for network device and allocate resource. + + @param[in, out] Instance The IP4 config2 Instance. + + @retval EFI_SUCCESS The HII Config Access protocol is installed. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip4Config2FormInit ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ); + +/** + Uninstall the HII Config Access protocol for network devices and free up the resources. + + @param[in, out] Instance The IP4 config2 instance to unload a form. + +**/ +VOID +Ip4Config2FormUnload ( + IN OUT IP4_CONFIG2_INSTANCE *Instance + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Driver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Driver.c new file mode 100644 index 00000000..feb49b9b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Driver.c @@ -0,0 +1,1069 @@ +/** @file + The driver binding and service binding protocol for IP4 driver. + +Copyright (c) 2005 - 2019, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding = { + Ip4DriverBindingSupported, + Ip4DriverBindingStart, + Ip4DriverBindingStop, + 0xa, + NULL, + NULL +}; + +BOOLEAN mIpSec2Installed = FALSE; + +/** + Callback function for IpSec2 Protocol install. + + @param[in] Event Event whose notification function is being invoked + @param[in] Context Pointer to the notification function's context + +**/ +VOID +EFIAPI +IpSec2InstalledCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + // + // Test if protocol was even found. + // Notification function will be called at least once. + // + Status = gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **)&mIpSec); + if (Status == EFI_SUCCESS && mIpSec != NULL) { + // + // Close the event so it does not get called again. + // + gBS->CloseEvent (Event); + + mIpSec2Installed = TRUE; + } +} + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + The entry point for IP4 driver which install the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Ip4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *Registration; + + EfiCreateProtocolNotifyEvent ( + &gEfiIpSec2ProtocolGuid, + TPL_CALLBACK, + IpSec2InstalledCallback, + NULL, + &Registration + ); + + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIp4DriverBinding, + ImageHandle, + &gIp4ComponentName, + &gIp4ComponentName2 + ); +} + +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @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 +Ip4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // Test for the MNP service binding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Test for the Arp service binding Protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiArpServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + return Status; +} + +/** + Clean up a IP4 service binding instance. It will release all + the resource allocated by the instance. The instance may be + partly initialized, or partly destroyed. If a resource is + destroyed, it is marked as that in case the destroy failed and + being called again later. + + @param[in] IpSb The IP4 service binding instance to clean up + + @retval EFI_SUCCESS The resource used by the instance are cleaned up + @retval other Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip4CleanService ( + IN IP4_SERVICE *IpSb + ); + + +/** + Create a new IP4 driver service binding private instance. + + @param Controller The controller that has MNP service binding + installed + @param ImageHandle The IP4 driver's image handle + @param Service The variable to receive the newly created IP4 + service. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource + @retval EFI_SUCCESS A new IP4 service binding private is created. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip4CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT IP4_SERVICE **Service + ) +{ + IP4_SERVICE *IpSb; + EFI_STATUS Status; + + ASSERT (Service != NULL); + + *Service = NULL; + + // + // allocate a service private data then initialize all the filed to + // empty resources, so if any thing goes wrong when allocating + // resources, Ip4CleanService can be called to clean it up. + // + IpSb = AllocateZeroPool (sizeof (IP4_SERVICE)); + + if (IpSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IpSb->Signature = IP4_SERVICE_SIGNATURE; + IpSb->ServiceBinding.CreateChild = Ip4ServiceBindingCreateChild; + IpSb->ServiceBinding.DestroyChild = Ip4ServiceBindingDestroyChild; + IpSb->State = IP4_SERVICE_UNSTARTED; + + IpSb->NumChildren = 0; + InitializeListHead (&IpSb->Children); + + InitializeListHead (&IpSb->Interfaces); + IpSb->DefaultInterface = NULL; + IpSb->DefaultRouteTable = NULL; + + Ip4InitAssembleTable (&IpSb->Assemble); + + IpSb->IgmpCtrl.Igmpv1QuerySeen = 0; + InitializeListHead (&IpSb->IgmpCtrl.Groups); + + IpSb->Image = ImageHandle; + IpSb->Controller = Controller; + + IpSb->MnpChildHandle = NULL; + IpSb->Mnp = NULL; + + IpSb->MnpConfigData.ReceivedQueueTimeoutValue = 0; + IpSb->MnpConfigData.TransmitQueueTimeoutValue = 0; + IpSb->MnpConfigData.ProtocolTypeFilter = IP4_ETHER_PROTO; + IpSb->MnpConfigData.EnableUnicastReceive = TRUE; + IpSb->MnpConfigData.EnableMulticastReceive = TRUE; + IpSb->MnpConfigData.EnableBroadcastReceive = TRUE; + IpSb->MnpConfigData.EnablePromiscuousReceive = FALSE; + IpSb->MnpConfigData.FlushQueuesOnReset = TRUE; + IpSb->MnpConfigData.EnableReceiveTimestamps = FALSE; + IpSb->MnpConfigData.DisableBackgroundPolling = FALSE; + + ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE)); + + IpSb->Timer = NULL; + IpSb->ReconfigCheckTimer = NULL; + + IpSb->ReconfigEvent = NULL; + + IpSb->Reconfig = FALSE; + + IpSb->MediaPresent = TRUE; + + // + // Create various resources. First create the route table, timer + // event, ReconfigEvent and MNP child. IGMP, interface's initialization depend + // on the MNP child. + // + IpSb->DefaultRouteTable = Ip4CreateRouteTable (); + + if (IpSb->DefaultRouteTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip4TimerTicking, + IpSb, + &IpSb->Timer + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip4TimerReconfigChecking, + IpSb, + &IpSb->ReconfigCheckTimer + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip4AutoReconfigCallBack, + IpSb, + &IpSb->ReconfigEvent + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = NetLibCreateServiceChild ( + Controller, + ImageHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + &IpSb->MnpChildHandle + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &IpSb->Mnp, + ImageHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip4ServiceConfigMnp (IpSb, TRUE); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip4InitIgmp (IpSb); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->MacString = NULL; + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->DefaultInterface = Ip4CreateInterface (IpSb->Mnp, Controller, ImageHandle); + + if (IpSb->DefaultInterface == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link); + + ZeroMem (&IpSb->Ip4Config2Instance, sizeof (IP4_CONFIG2_INSTANCE)); + + Status = Ip4Config2InitInstance (&IpSb->Ip4Config2Instance); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->MaxPacketSize = IpSb->SnpMode.MaxPacketSize - sizeof (IP4_HEAD); + if (NetLibGetVlanId (IpSb->Controller) != 0) { + // + // This is a VLAN device, reduce MTU by VLAN tag length + // + IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN; + } + IpSb->OldMaxPacketSize = IpSb->MaxPacketSize; + *Service = IpSb; + + return EFI_SUCCESS; + +ON_ERROR: + Ip4CleanService (IpSb); + FreePool (IpSb); + + return Status; +} + + +/** + Clean up a IP4 service binding instance. It will release all + the resource allocated by the instance. The instance may be + partly initialized, or partly destroyed. If a resource is + destroyed, it is marked as that in case the destroy failed and + being called again later. + + @param[in] IpSb The IP4 service binding instance to clean up + + @retval EFI_SUCCESS The resource used by the instance are cleaned up + @retval other Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip4CleanService ( + IN IP4_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + + IpSb->State = IP4_SERVICE_DESTROY; + + if (IpSb->Timer != NULL) { + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->CloseEvent (IpSb->Timer); + + IpSb->Timer = NULL; + } + + if (IpSb->ReconfigCheckTimer != NULL) { + gBS->SetTimer (IpSb->ReconfigCheckTimer, TimerCancel, 0); + gBS->CloseEvent (IpSb->ReconfigCheckTimer); + + IpSb->ReconfigCheckTimer = NULL; + } + + if (IpSb->DefaultInterface != NULL) { + Status = Ip4FreeInterface (IpSb->DefaultInterface, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + IpSb->DefaultInterface = NULL; + } + + if (IpSb->DefaultRouteTable != NULL) { + Ip4FreeRouteTable (IpSb->DefaultRouteTable); + IpSb->DefaultRouteTable = NULL; + } + + Ip4CleanAssembleTable (&IpSb->Assemble); + + if (IpSb->MnpChildHandle != NULL) { + if (IpSb->Mnp != NULL) { + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + + IpSb->Mnp = NULL; + } + + NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiManagedNetworkServiceBindingProtocolGuid, + IpSb->MnpChildHandle + ); + + IpSb->MnpChildHandle = NULL; + } + + if (IpSb->ReconfigEvent != NULL) { + gBS->CloseEvent (IpSb->ReconfigEvent); + + IpSb->ReconfigEvent = NULL; + } + + IpSb->Reconfig = FALSE; + + if (IpSb->MacString != NULL) { + FreePool (IpSb->MacString); + } + + Ip4Config2CleanInstance (&IpSb->Ip4Config2Instance); + + return EFI_SUCCESS; +} + +/** + 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 +Ip4DestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; + + if (Entry == NULL || Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP4_PROTOCOL, Link, IP4_PROTOCOL_SIGNATURE); + ServiceBinding = ((IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (IpInstance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle); +} + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @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 This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +Ip4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + EFI_STATUS Status; + IP4_SERVICE *IpSb; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Cfg2; + UINTN Index; + IP4_CONFIG2_DATA_ITEM *DataItem; + + IpSb = NULL; + Ip4Cfg2 = NULL; + DataItem = NULL; + + // + // Test for the Ip4 service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = Ip4CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (IpSb != NULL); + + Ip4Cfg2 = &IpSb->Ip4Config2Instance.Ip4Config2; + + // + // Install the Ip4ServiceBinding Protocol onto ControllerHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + &IpSb->ServiceBinding, + &gEfiIp4Config2ProtocolGuid, + Ip4Cfg2, + NULL + ); + + if (EFI_ERROR (Status)) { + goto FREE_SERVICE; + } + + // + // Read the config data from NV variable again. + // The default data can be changed by other drivers. + // + Status = Ip4Config2ReadConfigData (IpSb->MacString, &IpSb->Ip4Config2Instance); + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + // + // Consume the installed EFI_IP4_CONFIG2_PROTOCOL to set the default data items. + // + for (Index = Ip4Config2DataTypePolicy; Index < Ip4Config2DataTypeMaximum; Index++) { + DataItem = &IpSb->Ip4Config2Instance.DataItem[Index]; + if (DataItem->Data.Ptr != NULL) { + Status = Ip4Cfg2->SetData ( + Ip4Cfg2, + Index, + DataItem->DataSize, + DataItem->Data.Ptr + ); + if (EFI_ERROR(Status)) { + goto UNINSTALL_PROTOCOL; + } + + if (Index == Ip4Config2DataTypePolicy && (*(DataItem->Data.Policy) == Ip4Config2PolicyDhcp)) { + break; + } + } + } + + // + // Ready to go: start the receiving and timer. + // Ip4Config2SetPolicy maybe call Ip4ReceiveFrame() to set the default interface's RecvRequest first after + // Ip4Config2 instance is initialized. So, EFI_ALREADY_STARTED is the allowed return status. + // + Status = Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb); + + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + goto UNINSTALL_PROTOCOL; + } + + Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_SECOND); + + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + Status = gBS->SetTimer (IpSb->ReconfigCheckTimer, TimerPeriodic, 500 * TICKS_PER_MS); + + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + // + // Initialize the IP4 ID + // + mIp4Id = (UINT16)NET_RANDOM (NetRandomInitSeed ()); + + return Status; + +UNINSTALL_PROTOCOL: + gBS->UninstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiIp4ServiceBindingProtocolGuid, + &IpSb->ServiceBinding, + &gEfiIp4Config2ProtocolGuid, + Ip4Cfg2, + NULL + ); + +FREE_SERVICE: + Ip4CleanService (IpSb); + FreePool (IpSb); + return Status; +} + + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +Ip4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + IP4_SERVICE *IpSb; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + INTN State; + LIST_ENTRY *List; + IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + IP4_INTERFACE *IpIf; + IP4_ROUTE_TABLE *RouteTable; + + BOOLEAN IsDhcp4; + + IsDhcp4 = FALSE; + + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid); + if (NicHandle != NULL) { + IsDhcp4 = TRUE; + } else { + return EFI_SUCCESS; + } + } + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiIp4ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + IpSb = IP4_SERVICE_FROM_PROTOCOL (ServiceBinding); + + if (IsDhcp4) { + Status = Ip4Config2DestroyDhcp4 (&IpSb->Ip4Config2Instance); + gBS->CloseEvent (IpSb->Ip4Config2Instance.Dhcp4Event); + IpSb->Ip4Config2Instance.Dhcp4Event = NULL; + } else if (NumberOfChildren != 0) { + List = &IpSb->Children; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + Ip4DestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else if (IpSb->DefaultInterface->ArpHandle == ControllerHandle) { + + // + // The ARP protocol for the default interface is being uninstalled and all + // its IP child handles should have been destroyed before. So, release the + // default interface and route table, create a new one and mark it as not started. + // + Ip4CancelReceive (IpSb->DefaultInterface); + Ip4FreeInterface (IpSb->DefaultInterface, NULL); + Ip4FreeRouteTable (IpSb->DefaultRouteTable); + + IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image); + if (IpIf == NULL) { + goto ON_ERROR; + } + RouteTable = Ip4CreateRouteTable (); + if (RouteTable == NULL) { + Ip4FreeInterface (IpIf, NULL); + goto ON_ERROR;; + } + + IpSb->DefaultInterface = IpIf; + InsertHeadList (&IpSb->Interfaces, &IpIf->Link); + IpSb->DefaultRouteTable = RouteTable; + Ip4ReceiveFrame (IpIf, NULL, Ip4AccpetFrame, IpSb); + + IpSb->State = IP4_SERVICE_UNSTARTED; + + } else if (IsListEmpty (&IpSb->Children)) { + State = IpSb->State; + // + // OK, clean other resources then uninstall the service binding protocol. + // + Status = Ip4CleanService (IpSb); + if (EFI_ERROR (Status)) { + IpSb->State = State; + goto ON_ERROR; + } + + gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiIp4ServiceBindingProtocolGuid, + ServiceBinding, + &gEfiIp4Config2ProtocolGuid, + &IpSb->Ip4Config2Instance.Ip4Config2, + NULL + ); + + if (gIp4ControllerNameTable != NULL) { + FreeUnicodeStringTable (gIp4ControllerNameTable); + gIp4ControllerNameTable = NULL; + } + FreePool (IpSb); + } + +ON_ERROR: + return Status; +} + + +/** + Creates a child handle and installs a protocol. + + 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 This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param 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 +Ip4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + IP4_SERVICE *IpSb; + IP4_PROTOCOL *IpInstance; + EFI_TPL OldTpl; + EFI_STATUS Status; + VOID *Mnp; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpSb = IP4_SERVICE_FROM_PROTOCOL (This); + IpInstance = AllocatePool (sizeof (IP4_PROTOCOL)); + + if (IpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip4InitProtocol (IpSb, IpInstance); + + // + // Install Ip4 onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp4ProtocolGuid, + &IpInstance->Ip4Proto, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpInstance->Handle = *ChildHandle; + + // + // Open the Managed Network protocol BY_CHILD. + // + Status = gBS->OpenProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + (VOID **) &Mnp, + gIp4DriverBinding.DriverBindingHandle, + IpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + *ChildHandle, + &gEfiIp4ProtocolGuid, + &IpInstance->Ip4Proto, + NULL + ); + + goto ON_ERROR; + } + + // + // Insert it into the service binding instance. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&IpSb->Children, &IpInstance->Link); + IpSb->NumChildren++; + + gBS->RestoreTPL (OldTpl); + +ON_ERROR: + + if (EFI_ERROR (Status)) { + + Ip4CleanProtocol (IpInstance); + + FreePool (IpInstance); + } + + return Status; +} + + +/** + Destroys a child handle with a protocol installed on it. + + 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 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 +Ip4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + IP4_SERVICE *IpSb; + IP4_PROTOCOL *IpInstance; + EFI_IP4_PROTOCOL *Ip4; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + IpSb = IP4_SERVICE_FROM_PROTOCOL (This); + + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiIp4ProtocolGuid, + (VOID **) &Ip4, + gIp4DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (Ip4); + + if (IpInstance->Service != IpSb) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // A child can be destroyed more than once. For example, + // Ip4DriverBindingStop will destroy all of its children. + // when UDP driver is being stopped, it will destroy all + // the IP child it opens. + // + if (IpInstance->InDestroy) { + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + } + + IpInstance->InDestroy = TRUE; + + // + // Close the Managed Network protocol. + // + gBS->CloseProtocol ( + IpSb->MnpChildHandle, + &gEfiManagedNetworkProtocolGuid, + gIp4DriverBinding.DriverBindingHandle, + ChildHandle + ); + + if (IpInstance->Interface != NULL && IpInstance->Interface->Arp != NULL) { + gBS->CloseProtocol ( + IpInstance->Interface->ArpHandle, + &gEfiArpProtocolGuid, + gIp4DriverBinding.DriverBindingHandle, + ChildHandle + ); + } + + // + // Uninstall the IP4 protocol first. Many thing happens during + // this: + // 1. The consumer of the IP4 protocol will be stopped if it + // opens the protocol BY_DRIVER. For example, if MNP driver is + // stopped, IP driver's stop function will be called, and uninstall + // EFI_IP4_PROTOCOL will trigger the UDP's stop function. This + // makes it possible to create the network stack bottom up, and + // stop it top down. + // 2. the upper layer will recycle the received packet. The recycle + // event's TPL is higher than this function. The recycle events + // will be called back before preceding. If any packets not recycled, + // that means there is a resource leak. + // + gBS->RestoreTPL (OldTpl); + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiIp4ProtocolGuid, + &IpInstance->Ip4Proto + ); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (EFI_ERROR (Status)) { + IpInstance->InDestroy = FALSE; + goto ON_ERROR; + } + + Status = Ip4CleanProtocol (IpInstance); + if (EFI_ERROR (Status)) { + gBS->InstallMultipleProtocolInterfaces ( + &ChildHandle, + &gEfiIp4ProtocolGuid, + Ip4, + NULL + ); + + goto ON_ERROR; + } + + RemoveEntryList (&IpInstance->Link); + IpSb->NumChildren--; + + gBS->RestoreTPL (OldTpl); + + FreePool (IpInstance); + return EFI_SUCCESS; + +ON_ERROR: + gBS->RestoreTPL (OldTpl); + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Driver.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Driver.h new file mode 100644 index 00000000..8d6c7dde --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Driver.h @@ -0,0 +1,184 @@ +/** @file + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_DRIVER_H__ +#define __EFI_IP4_DRIVER_H__ + +#include <Protocol/ServiceBinding.h> + +extern EFI_DRIVER_BINDING_PROTOCOL gIp4DriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gIp4ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gIp4ComponentName2; +extern EFI_UNICODE_STRING_TABLE *gIp4ControllerNameTable; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} IP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +// +// Function prototype for the driver's entry point +// +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including + both device drivers and bus drivers. + + The entry point for IP4 driver which install the driver + binding and component name protocol on its image. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Ip4DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Driver Binding Protocol +// +/** + Test to see if this driver supports ControllerHandle. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported() it must also follow these calling restrictions. + + @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 +Ip4DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are a few calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @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 This driver is added to ControllerHandle + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle + @retval other This driver does not support this device + +**/ +EFI_STATUS +EFIAPI +Ip4DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL * This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are a few calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop() it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number + of children is zero stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle + @retval other This driver was not removed from this device + +**/ +EFI_STATUS +EFIAPI +Ip4DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +// +// Function prototypes for the ServiceBinding Protocol +// +/** + Creates a child handle and installs a protocol. + + 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 This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param 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 +Ip4ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + 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 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 +Ip4ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Dxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Dxe.inf new file mode 100644 index 00000000..668f45fb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Dxe.inf @@ -0,0 +1,110 @@ +## @file +# This module produces EFI IPv4 Protocol and EFI IPv4 Service Binding Protocol. +# +# This module produces EFI IPv4 Protocol upon EFI MNP Protocol and EFI ARP Protocol, +# to provide basic network IPv4 packet I/O services, which includes support for a +# subset of the Internet Control Message Protocol (ICMP) and may include support for +# the Internet Group Management Protocol (IGMP). +# +# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Ip4Dxe + MODULE_UNI_FILE = Ip4Dxe.uni + FILE_GUID = 9FB1A1F3-3B71-4324-B39A-745CBB015FFF + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Ip4DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gIp4DriverBinding +# COMPONENT_NAME = gIp4ComponentName +# COMPONENT_NAME2 = gIp4ComponentName2 +# + +[Sources] + Ip4Driver.c + Ip4Option.h + Ip4Route.h + Ip4If.c + Ip4Igmp.h + Ip4Output.c + Ip4Icmp.c + Ip4Igmp.c + Ip4Impl.c + Ip4Common.h + Ip4Impl.h + Ip4Driver.h + Ip4Common.c + Ip4If.h + Ip4Option.c + Ip4Output.h + ComponentName.c + Ip4Input.h + Ip4Route.c + Ip4Icmp.h + Ip4Input.c + Ip4Config2Impl.c + Ip4Config2Impl.h + Ip4Config2.vfr + Ip4DxeStrings.uni + Ip4NvData.h + Ip4Config2Nv.h + Ip4Config2Nv.c + + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + DebugLib + NetLib + DpcLib + HiiLib + PrintLib + DevicePathLib + UefiHiiServicesLib + +[Protocols] + ## BY_START + ## UNDEFINED # variable + gEfiIp4ServiceBindingProtocolGuid + gEfiIp4ProtocolGuid ## BY_START + gEfiManagedNetworkServiceBindingProtocolGuid ## TO_START + gEfiManagedNetworkProtocolGuid ## TO_START + gEfiArpServiceBindingProtocolGuid ## TO_START + gEfiIp4Config2ProtocolGuid ## BY_START + gEfiArpProtocolGuid ## TO_START + gEfiDhcp4ServiceBindingProtocolGuid ## TO_START + gEfiDhcp4ProtocolGuid ## TO_START + gEfiIpSec2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiHiiConfigAccessProtocolGuid ## BY_START + gEfiDevicePathProtocolGuid ## TO_START + +[Guids] + ## SOMETIMES_CONSUMES ## GUID # HiiIsConfigHdrMatch EFI_NIC_IP4_CONFIG_VARIABLE + ## SOMETIMES_PRODUCES ## GUID # HiiConstructConfigHdr EFI_NIC_IP4_CONFIG_VARIABLE + ## SOMETIMES_PRODUCES ## GUID # HiiGetBrowserData EFI_NIC_IP4_CONFIG_VARIABLE + ## SOMETIMES_CONSUMES ## HII + gIp4Config2NvDataGuid + +[UserExtensions.TianoCore."ExtraFiles"] + Ip4DxeExtra.uni + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Dxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Dxe.uni new file mode 100644 index 00000000..380d07da --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Dxe.uni @@ -0,0 +1,19 @@ +// /** @file
+// This module produces EFI IPv4 Protocol and EFI IPv4 Service Binding Protocol.
+//
+// This module produces EFI IPv4 Protocol upon EFI MNP Protocol and EFI ARP Protocol,
+// to provide basic network IPv4 packet I/O services, which includes support for a
+// subset of the Internet Control Message Protocol (ICMP) and may include support for
+// the Internet Group Management Protocol (IGMP).
+//
+// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI IPv4 Protocol and EFI IPv4 Service Binding Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI IPv4 Protocol upon EFI MNP Protocol and EFI ARP Protocol to provide basic network IPv4 packet I/O services, which includes support for a subset of the Internet Control Message Protocol (ICMP), and may include support for the Internet Group Management Protocol (IGMP)."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4DxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4DxeExtra.uni new file mode 100644 index 00000000..dbbf9d5f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4DxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// Ip4Dxe 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
+"IP v4 DXE Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4DxeStrings.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4DxeStrings.uni new file mode 100644 index 00000000..c262298f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4DxeStrings.uni @@ -0,0 +1,30 @@ +// /** @file
+// String definitions for Ip4Config2 formset
+
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+//**/
+
+
+/=#
+
+#langdef en-US "English"
+
+#string STR_IP4_CONFIG2_FORM_TITLE #language en-US "IPv4 Network Configuration"
+#string STR_IP4_CONFIG2_FORM_HELP #language en-US "Configure network parameters."
+#string STR_IP4_DEVICE_FORM_TITLE #language en-US ""
+#string STR_IP4_DEVICE_FORM_HELP #language en-US ""
+#string STR_IP4_CONFIGURE #language en-US "Configured"
+#string STR_IP4_CONFIGURE_HELP #language en-US "Indicate whether network address configured successfully or not."
+#string STR_IP4_ENABLE_DHCP #language en-US "Enable DHCP"
+#string STR_IP4_LOCAL_IP_ADDRESS #language en-US "Local IP Address"
+#string STR_IP4_IP_ADDRESS_HELP #language en-US "Enter IP address in dotted-decimal notation. Example: 192.168.10.12\r\n"
+#string STR_IP4_LOCAL_MASK #language en-US "Local NetMask"
+#string STR_IP4_MASK_HELP #language en-US "Enter NetMask in dotted-decimal notation. Example: 255.255.255.0\r\n"
+#string STR_IP4_LOCAL_GATEWAY #language en-US "Local Gateway"
+#string STR_IP4_GATEWAY_HELP #language en-US "Enter Gateway in dotted-decimal notation. Example: 192.168.10.1\r\n"
+#string STR_IP4_LOCAL_DNS #language en-US "Local DNS Servers"
+#string STR_IP4_DNS_HELP #language en-US "Enter DNS Servers in dotted-decimal notation. Example: 192.168.10.8 192.168.10.9\r\n"
+#string STR_SAVE_CHANGES #language en-US "Save Changes and Exit"
+#string STR_NULL #language en-US ""
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Icmp.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Icmp.c new file mode 100644 index 00000000..964d4b51 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Icmp.c @@ -0,0 +1,363 @@ +/** @file + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +IP4_ICMP_CLASS +mIcmpClass[] = { + {ICMP_ECHO_REPLY, ICMP_QUERY_MESSAGE }, + {1, ICMP_INVALID_MESSAGE}, + {2, ICMP_INVALID_MESSAGE}, + {ICMP_DEST_UNREACHABLE, ICMP_ERROR_MESSAGE }, + {ICMP_SOURCE_QUENCH, ICMP_ERROR_MESSAGE }, + {ICMP_REDIRECT, ICMP_ERROR_MESSAGE }, + {6, ICMP_INVALID_MESSAGE}, + {7, ICMP_INVALID_MESSAGE}, + {ICMP_ECHO_REQUEST, ICMP_QUERY_MESSAGE }, + {9, ICMP_INVALID_MESSAGE}, + {10, ICMP_INVALID_MESSAGE}, + {ICMP_TIME_EXCEEDED, ICMP_ERROR_MESSAGE }, + {ICMP_PARAMETER_PROBLEM, ICMP_ERROR_MESSAGE }, + {ICMP_TIMESTAMP , ICMP_QUERY_MESSAGE }, + {14, ICMP_INVALID_MESSAGE}, + {ICMP_INFO_REQUEST , ICMP_QUERY_MESSAGE }, + {ICMP_INFO_REPLY , ICMP_QUERY_MESSAGE }, +}; + +EFI_IP4_ICMP_TYPE +mIp4SupportedIcmp[23] = { + {ICMP_ECHO_REPLY, ICMP_DEFAULT_CODE }, + + {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE }, + {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE }, + {ICMP_DEST_UNREACHABLE, ICMP_PROTO_UNREACHABLE }, + {ICMP_DEST_UNREACHABLE, ICMP_PORT_UNREACHABLE }, + {ICMP_DEST_UNREACHABLE, ICMP_FRAGMENT_FAILED }, + {ICMP_DEST_UNREACHABLE, ICMP_SOURCEROUTE_FAILED }, + {ICMP_DEST_UNREACHABLE, ICMP_NET_UNKNOWN }, + {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNKNOWN }, + {ICMP_DEST_UNREACHABLE, ICMP_SOURCE_ISOLATED }, + {ICMP_DEST_UNREACHABLE, ICMP_NET_PROHIBITED }, + {ICMP_DEST_UNREACHABLE, ICMP_HOST_PROHIBITED }, + {ICMP_DEST_UNREACHABLE, ICMP_NET_UNREACHABLE_TOS }, + {ICMP_DEST_UNREACHABLE, ICMP_HOST_UNREACHABLE_TOS}, + + {ICMP_SOURCE_QUENCH, ICMP_DEFAULT_CODE }, + + {ICMP_REDIRECT, ICMP_NET_REDIRECT }, + {ICMP_REDIRECT, ICMP_HOST_REDIRECT }, + {ICMP_REDIRECT, ICMP_NET_TOS_REDIRECT }, + {ICMP_REDIRECT, ICMP_HOST_TOS_REDIRECT }, + + {ICMP_ECHO_REQUEST, ICMP_DEFAULT_CODE }, + + {ICMP_TIME_EXCEEDED, ICMP_TIMEOUT_IN_TRANSIT }, + {ICMP_TIME_EXCEEDED, ICMP_TIMEOUT_REASSEMBLE }, + + {ICMP_PARAMETER_PROBLEM, ICMP_DEFAULT_CODE }, +}; + + + +/** + Process the ICMP redirect. Find the instance then update + its route cache. + + All kinds of redirect is treated as host redirect as + specified by RFC1122 3.3.1.2: + "Since the subnet mask appropriate to the destination + address is generally not known, a Network Redirect + message SHOULD be treated identically to a Host Redirect + message;" + + @param[in] IpSb The IP4 service binding instance that received + the packet. + @param[in] Head The IP head of the received ICMPpacket. + @param[in] Packet The content of the ICMP redirect packet with IP + head removed. + @param[in] Icmp The buffer to store the ICMP error message if + something is wrong. + + @retval EFI_INVALID_PARAMETER The parameter is invalid + @retval EFI_SUCCESS Successfully updated the route caches + +**/ +EFI_STATUS +Ip4ProcessIcmpRedirect ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN IP4_ICMP_ERROR_HEAD *Icmp + ) +{ + LIST_ENTRY *Entry; + IP4_PROTOCOL *Ip4Instance; + IP4_ROUTE_CACHE_ENTRY *CacheEntry; + IP4_INTERFACE *IpIf; + IP4_ADDR Gateway; + IP4_ADDR Src; + IP4_ADDR Dst; + + // + // Find the interface whose IP address is the source of the + // orgianl IP packet. + // + IpIf = Ip4FindInterface (IpSb, NTOHL (Icmp->IpHead.Src)); + Gateway = NTOHL (Icmp->Fourth); + + // + // discard the packet if the new gateway address it specifies + // is not on the same connected net through which the Redirect + // arrived. (RFC1122 3.2.2.2). + // + if ((IpIf == NULL) || !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask)) { + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; + } + + // + // Update each IP child's route cache on the interface. + // + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink); + + if (Ip4Instance->RouteTable == NULL) { + continue; + } + + Dst = NTOHL (Icmp->IpHead.Dst); + Src = NTOHL (Icmp->IpHead.Src); + CacheEntry = Ip4FindRouteCache (Ip4Instance->RouteTable, Dst, Src); + + // + // Only update the route cache's gateway if the source of the + // Redirect is the current first-hop gateway + // + if ((CacheEntry != NULL) && (NTOHL (Head->Src) == CacheEntry->NextHop)) { + CacheEntry->NextHop = Gateway; + } + } + + NetbufFree (Packet); + return EFI_SUCCESS; +} + + +/** + Process the ICMP error packet. If it is an ICMP redirect packet, + update call Ip4ProcessIcmpRedirect to update the IP instance's + route cache, otherwise, deliver the packet to upper layer. + + @param[in] IpSb The IP4 service that received the packet. + @param[in] Head The IP4 head of the ICMP error packet + @param[in] Packet The content of the ICMP error with IP4 head + removed. + + @retval EFI_SUCCESS The ICMP error is processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip4ProcessIcmpError ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_ICMP_ERROR_HEAD Icmp; + + if (Packet->TotalSize < sizeof (Icmp)) { + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + // + // If it is an ICMP redirect error, update the route cache + // as RFC1122. Otherwise, demultiplex it to IP instances. + // + if (Icmp.Head.Type == ICMP_REDIRECT) { + return Ip4ProcessIcmpRedirect (IpSb, Head, Packet, &Icmp); + } + + IP4_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR; + return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0); +} + + +/** + Replay an ICMP echo request. + + @param[in] IpSb The IP4 service that receivd the packet + @param[in] Head The IP4 head of the ICMP error packet + @param[in] Packet The content of the ICMP error with IP4 head + removed. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource. + @retval EFI_SUCCESS The ICMP Echo request is successfully answered. + @retval Others Failed to answer the ICMP echo request. + +**/ +EFI_STATUS +Ip4IcmpReplyEcho ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_ICMP_QUERY_HEAD *Icmp; + NET_BUF *Data; + EFI_STATUS Status; + IP4_HEAD ReplyHead; + + // + // make a copy the packet, it is really a bad idea to + // send the MNP's buffer back to MNP. + // + Data = NetbufDuplicate (Packet, NULL, IP4_MAX_HEADLEN); + + if (Data == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Change the ICMP type to echo reply, exchange the source + // and destination, then send it. The source is updated to + // use specific destination. See RFC1122. SRR/RR option + // update is omitted. + // + Icmp = (IP4_ICMP_QUERY_HEAD *) NetbufGetByte (Data, 0, NULL); + ASSERT (Icmp != NULL); + Icmp->Head.Type = ICMP_ECHO_REPLY; + Icmp->Head.Checksum = 0; + Icmp->Head.Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Icmp, Data->TotalSize)); + + ReplyHead.Tos = 0; + ReplyHead.Fragment = 0; + ReplyHead.Ttl = 64; + ReplyHead.Protocol = EFI_IP_PROTO_ICMP; + ReplyHead.Src = 0; + + // + // Ip4Output will select a source for us + // + ReplyHead.Dst = Head->Src; + + Status = Ip4Output ( + IpSb, + NULL, + Data, + &ReplyHead, + NULL, + 0, + IP4_ALLZERO_ADDRESS, + Ip4SysPacketSent, + NULL + ); + if (EFI_ERROR (Status)) { + NetbufFree (Data); + } + +ON_EXIT: + NetbufFree (Packet); + return Status; +} + + +/** + Process the ICMP query message. If it is an ICMP echo + request, answer it. Otherwise deliver it to upper layer. + + @param[in] IpSb The IP4 service that receivd the packet + @param[in] Head The IP4 head of the ICMP query packet + @param[in] Packet The content of the ICMP query with IP4 head + removed. + + @retval EFI_INVALID_PARAMETER The packet is invalid + @retval EFI_SUCCESS The ICMP query message is processed + @retval Others Failed to process ICMP query. + +**/ +EFI_STATUS +Ip4ProcessIcmpQuery ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_ICMP_QUERY_HEAD Icmp; + + if (Packet->TotalSize < sizeof (Icmp)) { + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + if (Icmp.Head.Type == ICMP_ECHO_REQUEST) { + return Ip4IcmpReplyEcho (IpSb, Head, Packet); + } + + return Ip4Demultiplex (IpSb, Head, Packet, NULL, 0); +} + + +/** + Handle the ICMP packet. First validate the message format, + then according to the message types, process it as query or + error packet. + + @param[in] IpSb The IP4 service that receivd the packet. + @param[in] Head The IP4 head of the ICMP query packet. + @param[in] Packet The content of the ICMP query with IP4 head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_SUCCESS The ICMP message is successfully processed. + @retval Others Failed to handle ICMP packet. + +**/ +EFI_STATUS +Ip4IcmpHandle ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_ICMP_HEAD Icmp; + UINT16 Checksum; + + if (Packet->TotalSize < sizeof (Icmp)) { + goto DROP; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + if (Icmp.Type > ICMP_TYPE_MAX) { + goto DROP; + } + + Checksum = (UINT16) (~NetbufChecksum (Packet)); + if ((Icmp.Checksum != 0) && (Checksum != 0)) { + goto DROP; + } + + if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_ERROR_MESSAGE) { + return Ip4ProcessIcmpError (IpSb, Head, Packet); + + } else if (mIcmpClass[Icmp.Type].IcmpClass == ICMP_QUERY_MESSAGE) { + return Ip4ProcessIcmpQuery (IpSb, Head, Packet); + + } + +DROP: + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Icmp.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Icmp.h new file mode 100644 index 00000000..958118b8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Icmp.h @@ -0,0 +1,97 @@ +/** @file + Header file for ICMP protocol. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_ICMP_H__ +#define __EFI_IP4_ICMP_H__ + + // + // ICMP type definitions + // +#define ICMP_ECHO_REPLY 0 +#define ICMP_DEST_UNREACHABLE 3 +#define ICMP_SOURCE_QUENCH 4 +#define ICMP_REDIRECT 5 +#define ICMP_ECHO_REQUEST 8 +#define ICMP_TIME_EXCEEDED 11 +#define ICMP_PARAMETER_PROBLEM 12 +#define ICMP_TIMESTAMP 13 +#define ICMP_INFO_REQUEST 15 +#define ICMP_INFO_REPLY 16 +#define ICMP_TYPE_MAX ICMP_INFO_REPLY + +#define ICMP_DEFAULT_CODE 0 + + // + // ICMP code definitions for ICMP_DEST_UNREACHABLE + // +#define ICMP_NET_UNREACHABLE 0 +#define ICMP_HOST_UNREACHABLE 1 +#define ICMP_PROTO_UNREACHABLE 2 // Host may generate +#define ICMP_PORT_UNREACHABLE 3 // Host may generate +#define ICMP_FRAGMENT_FAILED 4 +#define ICMP_SOURCEROUTE_FAILED 5 // Host may generate +#define ICMP_NET_UNKNOWN 6 +#define ICMP_HOST_UNKNOWN 7 +#define ICMP_SOURCE_ISOLATED 8 +#define ICMP_NET_PROHIBITED 9 +#define ICMP_HOST_PROHIBITED 10 +#define ICMP_NET_UNREACHABLE_TOS 11 +#define ICMP_HOST_UNREACHABLE_TOS 12 + + // + // ICMP code definitions for ICMP_TIME_EXCEEDED + // +#define ICMP_TIMEOUT_IN_TRANSIT 0 +#define ICMP_TIMEOUT_REASSEMBLE 1 // Host may generate + + // + // ICMP code definitions for ICMP_TIME_EXCEEDED + // +#define ICMP_NET_REDIRECT 0 +#define ICMP_HOST_REDIRECT 1 +#define ICMP_NET_TOS_REDIRECT 2 +#define ICMP_HOST_TOS_REDIRECT 3 + + // + // ICMP message classes, each class of ICMP message shares + // a common message format. INVALID_MESSAGE is only a flag. + // +#define ICMP_INVALID_MESSAGE 0 +#define ICMP_ERROR_MESSAGE 1 +#define ICMP_QUERY_MESSAGE 2 + +typedef struct { + UINT8 IcmpType; + UINT8 IcmpClass; +} IP4_ICMP_CLASS; + +extern IP4_ICMP_CLASS mIcmpClass[]; +extern EFI_IP4_ICMP_TYPE mIp4SupportedIcmp[]; + +/** + Handle the ICMP packet. First validate the message format, + then according to the message types, process it as query or + error packet. + + @param[in] IpSb The IP4 service that receivd the packet. + @param[in] Head The IP4 head of the ICMP query packet. + @param[in] Packet The content of the ICMP query with IP4 head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_SUCCESS The ICMP message is successfully processed. + @retval Others Failed to handle ICMP packet. + +**/ +EFI_STATUS +Ip4IcmpHandle ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4If.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4If.c new file mode 100644 index 00000000..67d51a09 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4If.c @@ -0,0 +1,1349 @@ +/** @file + Implement IP4 pseudo interface. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +// +// Mac address with all zero, used to determine whether the ARP +// resolve succeeded. Failed ARP requests zero the MAC address buffer. +// +EFI_MAC_ADDRESS mZeroMacAddress; + +/** + Callback function when frame transmission is finished. It will + call the frame owner's callback function to tell it the result. + + @param[in] Context Context which is point to the token. + +**/ +VOID +EFIAPI +Ip4OnFrameSentDpc ( + IN VOID *Context + ); + +/** + Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context Context which is point to the token. + +**/ +VOID +EFIAPI +Ip4OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Callback function when ARP request are finished. It will cancelled + all the queued frame if the ARP requests failed. Or transmit them + if the request succeed. + + @param[in] Context The context of the callback, a point to the ARP + queue + +**/ +VOID +EFIAPI +Ip4OnArpResolvedDpc ( + IN VOID *Context + ); + +/** + Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK. + + @param Event The Arp request event. + @param Context The context of the callback, a point to the ARP + queue. + +**/ +VOID +EFIAPI +Ip4OnArpResolved ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Received a frame from MNP, wrap it in net buffer then deliver + it to IP's input function. The ownship of the packet also + transferred to IP. When Ip is finished with this packet, it + will call NetbufFree to release the packet, NetbufFree will + again call the Ip4RecycleFrame to signal MNP's event and free + the token used. + + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip4OnFrameReceivedDpc ( + IN VOID *Context + ); + +/** + Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip4OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Remove all the frames on the ARP queue that pass the FrameToCancel, + that is, either FrameToCancel is NULL or it returns true for the frame. + + @param[in] ArpQue ARP frame to remove the frames from. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + @param[in] Context Opaque parameter to the FrameToCancel. + +**/ +VOID +Ip4CancelFrameArp ( + IN IP4_ARP_QUE *ArpQue, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context + ); + + +/** + Wrap a transmit request into a newly allocated IP4_LINK_TX_TOKEN. + + @param[in] Interface The interface to send out to. + @param[in] IpInstance The IpInstance that transmit the packet. NULL if + the packet is sent by the IP4 driver itself. + @param[in] Packet The packet to transmit + @param[in] CallBack Call back function to execute if transmission + finished. + @param[in] Context Opaque parameter to the call back. + @param[in] IpSb The pointer to the IP4 service binding instance. + + @retval Token The wrapped token if succeed + @retval NULL The wrapped token if NULL + +**/ +IP4_LINK_TX_TOKEN * +Ip4WrapLinkTxToken ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context, + IN IP4_SERVICE *IpSb + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData; + IP4_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + UINT32 Count; + + Token = AllocatePool (sizeof (IP4_LINK_TX_TOKEN) + \ + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA)); + + if (Token == NULL) { + return NULL; + } + + Token->Signature = IP4_FRAME_TX_SIGNATURE; + InitializeListHead (&Token->Link); + + Token->Interface = Interface; + Token->IpInstance = IpInstance; + Token->IpSb = IpSb; + Token->CallBack = CallBack; + Token->Packet = Packet; + Token->Context = Context; + CopyMem (&Token->DstMac, &mZeroMacAddress, sizeof (Token->DstMac)); + CopyMem (&Token->SrcMac, &Interface->Mac, sizeof (Token->SrcMac)); + + MnpToken = &(Token->MnpToken); + MnpToken->Status = EFI_NOT_READY; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip4OnFrameSent, + Token, + &MnpToken->Event + ); + + if (EFI_ERROR (Status)) { + FreePool (Token); + return NULL; + } + + MnpTxData = &Token->MnpTxData; + MnpToken->Packet.TxData = MnpTxData; + + MnpTxData->DestinationAddress = &Token->DstMac; + MnpTxData->SourceAddress = &Token->SrcMac; + MnpTxData->ProtocolType = IP4_ETHER_PROTO; + MnpTxData->DataLength = Packet->TotalSize; + MnpTxData->HeaderLength = 0; + + Count = Packet->BlockOpNum; + + NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count); + MnpTxData->FragmentCount = (UINT16)Count; + + return Token; +} + + +/** + Free the link layer transmit token. It will close the event + then free the memory used. + + @param[in] Token Token to free + +**/ +VOID +Ip4FreeLinkTxToken ( + IN IP4_LINK_TX_TOKEN *Token + ) +{ + NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE); + + gBS->CloseEvent (Token->MnpToken.Event); + FreePool (Token); +} + + +/** + Create an IP_ARP_QUE structure to request ARP service. + + @param[in] Interface The interface to send ARP from. + @param[in] DestIp The destination IP (host byte order) to request MAC + for + + @return Point to newly created IP4_ARP_QUE if succeed, otherwise NULL. + +**/ +IP4_ARP_QUE * +Ip4CreateArpQue ( + IN IP4_INTERFACE *Interface, + IN IP4_ADDR DestIp + ) +{ + IP4_ARP_QUE *ArpQue; + EFI_STATUS Status; + + ArpQue = AllocatePool (sizeof (IP4_ARP_QUE)); + + if (ArpQue == NULL) { + return NULL; + } + + ArpQue->Signature = IP4_FRAME_ARP_SIGNATURE; + InitializeListHead (&ArpQue->Link); + + InitializeListHead (&ArpQue->Frames); + ArpQue->Interface = Interface; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip4OnArpResolved, + ArpQue, + &ArpQue->OnResolved + ); + + if (EFI_ERROR (Status)) { + FreePool (ArpQue); + return NULL; + } + + ArpQue->Ip = DestIp; + CopyMem (&ArpQue->Mac, &mZeroMacAddress, sizeof (ArpQue->Mac)); + + return ArpQue; +} + + +/** + Remove all the transmit requests queued on the ARP queue, then free it. + + @param[in] ArpQue Arp queue to free + @param[in] IoStatus The transmit status returned to transmit requests' + callback. + +**/ +VOID +Ip4FreeArpQue ( + IN IP4_ARP_QUE *ArpQue, + IN EFI_STATUS IoStatus + ) +{ + NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE); + + // + // Remove all the frame waiting the ARP response + // + Ip4CancelFrameArp (ArpQue, IoStatus, NULL, NULL); + + gBS->CloseEvent (ArpQue->OnResolved); + FreePool (ArpQue); +} + + +/** + Create a link layer receive token to wrap the receive request + + @param[in] Interface The interface to receive from + @param[in] IpInstance The instance that request the receive (NULL for IP4 + driver itself) + @param[in] CallBack Call back function to execute when finished. + @param[in] Context Opaque parameters to the callback + + @return Point to created IP4_LINK_RX_TOKEN if succeed, otherwise NULL. + +**/ +IP4_LINK_RX_TOKEN * +Ip4CreateLinkRxToken ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + IP4_LINK_RX_TOKEN *Token; + EFI_STATUS Status; + + Token = AllocatePool (sizeof (IP4_LINK_RX_TOKEN)); + if (Token == NULL) { + return NULL; + } + + Token->Signature = IP4_FRAME_RX_SIGNATURE; + Token->Interface = Interface; + Token->IpInstance = IpInstance; + Token->CallBack = CallBack; + Token->Context = Context; + + MnpToken = &Token->MnpToken; + MnpToken->Status = EFI_NOT_READY; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip4OnFrameReceived, + Token, + &MnpToken->Event + ); + + if (EFI_ERROR (Status)) { + FreePool (Token); + return NULL; + } + + MnpToken->Packet.RxData = NULL; + return Token; +} + + +/** + Free the link layer request token. It will close the event + then free the memory used. + + @param[in] Token Request token to free. + +**/ +VOID +Ip4FreeFrameRxToken ( + IN IP4_LINK_RX_TOKEN *Token + ) +{ + + NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE); + + gBS->CloseEvent (Token->MnpToken.Event); + FreePool (Token); +} + + +/** + Remove all the frames on the ARP queue that pass the FrameToCancel, + that is, either FrameToCancel is NULL or it returns true for the frame. + + @param[in] ArpQue ARP frame to remove the frames from. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + @param[in] Context Opaque parameter to the FrameToCancel. + +**/ +VOID +Ip4CancelFrameArp ( + IN IP4_ARP_QUE *ArpQue, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_LINK_TX_TOKEN *Token; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link); + + if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { + RemoveEntryList (Entry); + + Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context); + Ip4FreeLinkTxToken (Token); + } + } +} + + +/** + Remove all the frames on the interface that pass the FrameToCancel, + either queued on ARP queues or that have already been delivered to + MNP and not yet recycled. + + @param[in] Interface Interface to remove the frames from. + @param[in] IoStatus The transmit status returned to the frames' + callback. + @param[in] FrameToCancel Function to select the frame to cancel, NULL to + select all. + @param[in] Context Opaque parameters passed to FrameToCancel. + +**/ +VOID +Ip4CancelFrames ( + IN IP4_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ARP_QUE *ArpQue; + IP4_LINK_TX_TOKEN *Token; + + // + // Cancel all the pending frames on ARP requests + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link); + + Ip4CancelFrameArp (ArpQue, IoStatus, FrameToCancel, Context); + + if (IsListEmpty (&ArpQue->Frames)) { + Interface->Arp->Cancel (Interface->Arp, &ArpQue->Ip, ArpQue->OnResolved); + } + } + + // + // Cancel all the frames that have been delivered to MNP + // but not yet recycled. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) { + Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link); + + if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { + Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken); + } + } +} + + +/** + Create an IP4_INTERFACE. Delay the creation of ARP instance until + the interface is configured. + + @param[in] Mnp The shared MNP child of this IP4 service binding + instance. + @param[in] Controller The controller this IP4 service binding instance + is installed. Most like the UNDI handle. + @param[in] ImageHandle This driver's image handle. + + @return Point to the created IP4_INTERFACE, otherwise NULL. + +**/ +IP4_INTERFACE * +Ip4CreateInterface ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ) +{ + IP4_INTERFACE *Interface; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + if (Mnp == NULL) { + return NULL; + } + + Interface = AllocatePool (sizeof (IP4_INTERFACE)); + + if (Interface == NULL) { + return NULL; + } + + Interface->Signature = IP4_INTERFACE_SIGNATURE; + InitializeListHead (&Interface->Link); + Interface->RefCnt = 1; + + Interface->Ip = IP4_ALLZERO_ADDRESS; + Interface->SubnetMask = IP4_ALLZERO_ADDRESS; + Interface->Configured = FALSE; + + Interface->Controller = Controller; + Interface->Image = ImageHandle; + Interface->Mnp = Mnp; + Interface->Arp = NULL; + Interface->ArpHandle = NULL; + + InitializeListHead (&Interface->ArpQues); + InitializeListHead (&Interface->SentFrames); + + Interface->RecvRequest = NULL; + + // + // Get the interface's Mac address and broadcast mac address from SNP + // + if (EFI_ERROR (Mnp->GetModeData (Mnp, NULL, &SnpMode))) { + FreePool (Interface); + return NULL; + } + + CopyMem (&Interface->Mac, &SnpMode.CurrentAddress, sizeof (Interface->Mac)); + CopyMem (&Interface->BroadcastMac, &SnpMode.BroadcastAddress, sizeof (Interface->BroadcastMac)); + Interface->HwaddrLen = SnpMode.HwAddressSize; + + InitializeListHead (&Interface->IpInstances); + Interface->PromiscRecv = FALSE; + + return Interface; +} + + +/** + Set the interface's address, create and configure + the ARP child if necessary. + + @param Interface The interface to set the address. + @param IpAddr The interface's IP address. + @param SubnetMask The interface's netmask. + + @retval EFI_SUCCESS The interface is configured with Ip/netmask pair, + and a ARP is created for it. + @retval Others Failed to set the interface's address. + +**/ +EFI_STATUS +Ip4SetAddress ( + IN OUT IP4_INTERFACE *Interface, + IN IP4_ADDR IpAddr, + IN IP4_ADDR SubnetMask + ) +{ + EFI_ARP_CONFIG_DATA ArpConfig; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE); + + // + // Set the ip/netmask, then compute the subnet broadcast + // and network broadcast for easy access. When computing + // network broadcast, the subnet mask is most like longer + // than the default netmask (not subneted) as defined in + // RFC793. If that isn't the case, we are aggregating the + // networks, use the subnet's mask instead. + // + Interface->Ip = IpAddr; + Interface->SubnetMask = SubnetMask; + Interface->SubnetBrdcast = (IpAddr | ~SubnetMask); + Interface->NetBrdcast = (IpAddr | ~SubnetMask); + + // + // Do clean up for Arp child + // + if (Interface->ArpHandle != NULL) { + if (Interface->Arp != NULL) { + gBS->CloseProtocol ( + Interface->ArpHandle, + &gEfiArpProtocolGuid, + Interface->Image, + Interface->Controller + ); + + Interface->Arp = NULL; + } + + NetLibDestroyServiceChild ( + Interface->Controller, + Interface->Image, + &gEfiArpServiceBindingProtocolGuid, + Interface->ArpHandle + ); + + Interface->ArpHandle = NULL; + } + + // + // If the address is NOT all zero, create then configure an ARP child. + // Pay attention: DHCP configures its station address as 0.0.0.0/0 + // + if (IpAddr != IP4_ALLZERO_ADDRESS) { + Status = NetLibCreateServiceChild ( + Interface->Controller, + Interface->Image, + &gEfiArpServiceBindingProtocolGuid, + &Interface->ArpHandle + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->OpenProtocol ( + Interface->ArpHandle, + &gEfiArpProtocolGuid, + (VOID **) &Interface->Arp, + Interface->Image, + Interface->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpAddr = HTONL (IpAddr); + ArpConfig.SwAddressType = IP4_ETHER_PROTO; + ArpConfig.SwAddressLength = 4; + ArpConfig.StationAddress = &IpAddr; + ArpConfig.EntryTimeOut = 0; + ArpConfig.RetryCount = 0; + ArpConfig.RetryTimeOut = 0; + + Status = Interface->Arp->Configure (Interface->Arp, &ArpConfig); + + if (EFI_ERROR (Status)) { + gBS->CloseProtocol ( + Interface->ArpHandle, + &gEfiArpProtocolGuid, + Interface->Image, + Interface->Controller + ); + + goto ON_ERROR; + } + } + + Interface->Configured = TRUE; + return EFI_SUCCESS; + +ON_ERROR: + NetLibDestroyServiceChild ( + Interface->Controller, + Interface->Image, + &gEfiArpServiceBindingProtocolGuid, + Interface->ArpHandle + ); + + return Status; +} + + +/** + Filter function to cancel all the frame related to an IP instance. + + @param[in] Frame The transmit request to test whether to cancel + @param[in] Context The context which is the Ip instance that issued + the transmit. + + @retval TRUE The frame belongs to this instance and is to be + removed + @retval FALSE The frame doesn't belong to this instance. + +**/ +BOOLEAN +Ip4CancelInstanceFrame ( + IN IP4_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if (Frame->IpInstance == (IP4_PROTOCOL *) Context) { + return TRUE; + } + + return FALSE; +} + + + +/** + If there is a pending receive request, cancel it. Don't call + the receive request's callback because this function can be only + called if the instance or driver is tearing itself down. It + doesn't make sense to call it back. But it is necessary to call + the transmit token's callback to give it a chance to free the + packet and update the upper layer's transmit request status, say + that from the UDP. + + @param[in] Interface The interface used by the IpInstance + +**/ +VOID +Ip4CancelReceive ( + IN IP4_INTERFACE *Interface + ) +{ + EFI_TPL OldTpl; + IP4_LINK_RX_TOKEN *Token; + + if ((Token = Interface->RecvRequest) != NULL) { + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Interface->RecvRequest = NULL; + Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken); + + gBS->RestoreTPL (OldTpl); + } +} + + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/Netmask pair share the same interface. It is reference + counted. All the frames haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list itself. + + @param[in] Interface The interface used by the IpInstance. + @param[in] IpInstance The Ip instance that free the interface. NULL if + the Ip driver is releasing the default interface. + + @retval EFI_SUCCESS The interface use IpInstance is freed. + +**/ +EFI_STATUS +Ip4FreeInterface ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL + ) +{ + NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE); + ASSERT (Interface->RefCnt > 0); + + // + // Remove all the pending transmit token related to this IP instance. + // + Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, IpInstance); + + if (--Interface->RefCnt > 0) { + return EFI_SUCCESS; + } + + // + // Destroy the interface if this is the last IP instance that + // has the address. Remove all the system transmitted packets + // from this interface, cancel the receive request if there is + // one, and destroy the ARP requests. + // + Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, NULL); + Ip4CancelReceive (Interface); + + ASSERT (IsListEmpty (&Interface->IpInstances)); + ASSERT (IsListEmpty (&Interface->ArpQues)); + ASSERT (IsListEmpty (&Interface->SentFrames)); + + if (Interface->Arp != NULL) { + gBS->CloseProtocol ( + Interface->ArpHandle, + &gEfiArpProtocolGuid, + Interface->Image, + Interface->Controller + ); + + NetLibDestroyServiceChild ( + Interface->Controller, + Interface->Image, + &gEfiArpServiceBindingProtocolGuid, + Interface->ArpHandle + ); + } + + RemoveEntryList (&Interface->Link); + FreePool (Interface); + + return EFI_SUCCESS; +} + +/** + This function tries to send all the queued frames in ArpQue to the default gateway if + the ARP resolve for direct destination address is failed when using /32 subnet mask. + + @param[in] ArpQue The ARP queue of a failed request. + + @retval EFI_SUCCESS All the queued frames have been send to the default route. + @retval Others Failed to send the queued frames. + +**/ +EFI_STATUS +Ip4SendFrameToDefaultRoute ( + IN IP4_ARP_QUE *ArpQue + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + IP4_LINK_TX_TOKEN *Token; + IP4_ADDR Gateway; + EFI_STATUS Status; + IP4_ROUTE_ENTRY *DefaultRoute; + + // + // ARP resolve failed when using /32 subnet mask. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + RemoveEntryList (Entry); + Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link); + ASSERT (Token->Interface->SubnetMask == IP4_ALLONE_ADDRESS); + // + // Find the default gateway IP address. The default route was saved to the RtCacheEntry->Tag in Ip4Route(). + // + RtCacheEntry = NULL; + if (Token->IpInstance != NULL) { + RtCacheEntry = Ip4FindRouteCache (Token->IpInstance->RouteTable, NTOHL (ArpQue->Ip), Token->Interface->Ip); + } + if (RtCacheEntry == NULL) { + RtCacheEntry = Ip4FindRouteCache (Token->IpSb->DefaultRouteTable, NTOHL (ArpQue->Ip), Token->Interface->Ip); + } + if (RtCacheEntry == NULL) { + Status= EFI_NO_MAPPING; + goto ON_ERROR; + } + DefaultRoute = (IP4_ROUTE_ENTRY*)RtCacheEntry->Tag; + if (DefaultRoute == NULL) { + Status= EFI_NO_MAPPING; + goto ON_ERROR; + } + // + // Try to send the frame to the default route. + // + Gateway = DefaultRoute->NextHop; + if (ArpQue->Ip == Gateway) { + // + // ARP resolve for the default route is failed, return error to caller. + // + Status= EFI_NO_MAPPING; + goto ON_ERROR; + } + RtCacheEntry->NextHop = Gateway; + Status = Ip4SendFrame (Token->Interface,Token->IpInstance,Token->Packet,Gateway,Token->CallBack,Token->Context,Token->IpSb); + if (EFI_ERROR (Status)) { + Status= EFI_NO_MAPPING; + goto ON_ERROR; + } + Ip4FreeRouteCacheEntry (RtCacheEntry); + } + + return EFI_SUCCESS; + +ON_ERROR: + if (RtCacheEntry != NULL) { + Ip4FreeRouteCacheEntry (RtCacheEntry); + } + Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context); + Ip4FreeLinkTxToken (Token); + return Status; +} + + +/** + Callback function when ARP request are finished. It will cancel + all the queued frame if the ARP requests failed. Or transmit them + if the request succeed. + + @param[in] Context The context of the callback, a point to the ARP + queue + +**/ +VOID +EFIAPI +Ip4OnArpResolvedDpc ( + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ARP_QUE *ArpQue; + IP4_INTERFACE *Interface; + IP4_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + EFI_STATUS IoStatus; + + ArpQue = (IP4_ARP_QUE *) Context; + NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE); + + RemoveEntryList (&ArpQue->Link); + + // + // ARP resolve failed for some reason. + // + if (NET_MAC_EQUAL (&ArpQue->Mac, &mZeroMacAddress, ArpQue->Interface->HwaddrLen)) { + if (ArpQue->Interface->SubnetMask != IP4_ALLONE_ADDRESS) { + // + // Release all the frame and ARP queue itself. Ip4FreeArpQue will call the frame's + // owner back. + // + IoStatus = EFI_NO_MAPPING; + } else { + // + // ARP resolve failed when using 32bit subnet mask, try to send the packets to the + // default route. + // + IoStatus = Ip4SendFrameToDefaultRoute (ArpQue); + } + goto ON_EXIT; + } + + // + // ARP resolve succeeded, Transmit all the frame. Release the ARP + // queue. It isn't necessary for us to cache the ARP binding because + // we always check the ARP cache first before transmit. + // + IoStatus = EFI_SUCCESS; + Interface = ArpQue->Interface; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + RemoveEntryList (Entry); + + Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link); + CopyMem (&Token->DstMac, &ArpQue->Mac, sizeof (Token->DstMac)); + + // + // Insert the tx token before transmitting it via MNP as the FrameSentDpc + // may be called before Mnp->Transmit returns which will remove this tx + // token from the SentFrames list. Remove it from the list if the returned + // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the + // FrameSentDpc won't be queued. + // + InsertTailList (&Interface->SentFrames, &Token->Link); + + Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context); + + Ip4FreeLinkTxToken (Token); + continue; + } + } + +ON_EXIT: + Ip4FreeArpQue (ArpQue, IoStatus); +} + +/** + Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK. + + @param Event The Arp request event. + @param Context The context of the callback, a point to the ARP + queue. + +**/ +VOID +EFIAPI +Ip4OnArpResolved ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip4OnArpResolvedDpc, Context); +} + + + +/** + Callback function when frame transmission is finished. It will + call the frame owner's callback function to tell it the result. + + @param[in] Context Context which is point to the token. + +**/ +VOID +EFIAPI +Ip4OnFrameSentDpc ( + IN VOID *Context + ) +{ + IP4_LINK_TX_TOKEN *Token; + + Token = (IP4_LINK_TX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE); + + RemoveEntryList (&Token->Link); + + Token->CallBack ( + Token->IpInstance, + Token->Packet, + Token->MnpToken.Status, + 0, + Token->Context + ); + + Ip4FreeLinkTxToken (Token); +} + +/** + Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context Context which is point to the token. + +**/ +VOID +EFIAPI +Ip4OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip4OnFrameSentDpc, Context); +} + + + +/** + Send a frame from the interface. If the next hop is broadcast or + multicast address, it is transmitted immediately. If the next hop + is a unicast, it will consult ARP to resolve the NextHop's MAC. + If some error happened, the CallBack won't be called. So, the caller + must test the return value, and take action when there is an error. + + @param[in] Interface The interface to send the frame from + @param[in] IpInstance The IP child that request the transmission. NULL + if it is the IP4 driver itself. + @param[in] Packet The packet to transmit. + @param[in] NextHop The immediate destination to transmit the packet + to. + @param[in] CallBack Function to call back when transmit finished. + @param[in] Context Opaque parameter to the call back. + @param[in] IpSb The pointer to the IP4 service binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame + @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop + @retval EFI_SUCCESS The packet is successfully transmitted. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip4SendFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP4_ADDR NextHop, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context, + IN IP4_SERVICE *IpSb + ) +{ + IP4_LINK_TX_TOKEN *Token; + LIST_ENTRY *Entry; + IP4_ARP_QUE *ArpQue; + EFI_ARP_PROTOCOL *Arp; + EFI_STATUS Status; + + ASSERT (Interface->Configured); + + Token = Ip4WrapLinkTxToken (Interface, IpInstance, Packet, CallBack, Context, IpSb); + + if (Token == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get the destination MAC address for multicast and broadcasts. + // Don't depend on ARP to solve the address since there maybe no + // ARP at all. Ip4Output has set NextHop to 255.255.255.255 for + // all the broadcasts. + // + if (NextHop == IP4_ALLONE_ADDRESS) { + CopyMem (&Token->DstMac, &Interface->BroadcastMac, sizeof (Token->DstMac)); + goto SEND_NOW; + + } else if (IP4_IS_MULTICAST (NextHop)) { + + Status = Ip4GetMulticastMac (Interface->Mnp, NextHop, &Token->DstMac); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + goto SEND_NOW; + } + + // + // Can only send out multicast/broadcast if the IP address is zero + // + if ((Arp = Interface->Arp) == NULL) { + Status = EFI_NO_MAPPING; + goto ON_ERROR; + } + + // + // First check whether this binding is in the ARP cache. + // + NextHop = HTONL (NextHop); + Status = Arp->Request (Arp, &NextHop, NULL, &Token->DstMac); + + if (Status == EFI_SUCCESS) { + goto SEND_NOW; + + } else if (Status != EFI_NOT_READY) { + goto ON_ERROR; + } + + // + // Have to do asynchronous ARP resolution. First check + // whether there is already a pending request. + // + ArpQue = NULL; + + NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link); + + if (ArpQue->Ip == NextHop) { + break; + } + } + + // + // Found a pending ARP request, enqueue the frame then return + // + if (Entry != &Interface->ArpQues) { + InsertTailList (&ArpQue->Frames, &Token->Link); + return EFI_SUCCESS; + } + + // + // First frame to NextHop, issue an asynchronous ARP requests + // + ArpQue = Ip4CreateArpQue (Interface, NextHop); + + if (ArpQue == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = Arp->Request (Arp, &ArpQue->Ip, ArpQue->OnResolved, ArpQue->Mac.Addr); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) { + Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING); + goto ON_ERROR; + } + + InsertHeadList (&ArpQue->Frames, &Token->Link); + InsertHeadList (&Interface->ArpQues, &ArpQue->Link); + return EFI_SUCCESS; + +SEND_NOW: + // + // Insert the tx token into the SentFrames list before calling Mnp->Transmit. + // Remove it if the returned status is not EFI_SUCCESS. + // + InsertTailList (&Interface->SentFrames, &Token->Link); + Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + Ip4FreeLinkTxToken (Token); + return Status; +} + + +/** + Call back function when the received packet is freed. + Check Ip4OnFrameReceived for information. + + @param Context Context, which is the IP4_LINK_RX_TOKEN. + +**/ +VOID +EFIAPI +Ip4RecycleFrame ( + IN VOID *Context + ) +{ + IP4_LINK_RX_TOKEN *Frame; + + Frame = (IP4_LINK_RX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Frame, IP4_FRAME_RX_SIGNATURE); + + gBS->SignalEvent (Frame->MnpToken.Packet.RxData->RecycleEvent); + Ip4FreeFrameRxToken (Frame); +} + + +/** + Received a frame from MNP, wrap it in net buffer then deliver + it to IP's input function. The ownship of the packet also + transferred to IP. When Ip is finished with this packet, it + will call NetbufFree to release the packet, NetbufFree will + again call the Ip4RecycleFrame to signal MNP's event and free + the token used. + + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip4OnFrameReceivedDpc ( + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData; + IP4_LINK_RX_TOKEN *Token; + NET_FRAGMENT Netfrag; + NET_BUF *Packet; + UINT32 Flag; + + Token = (IP4_LINK_RX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE); + + // + // First clear the interface's receive request in case the + // caller wants to call Ip4ReceiveFrame in the callback. + // + Token->Interface->RecvRequest = NULL; + + MnpToken = &Token->MnpToken; + MnpRxData = MnpToken->Packet.RxData; + + if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) { + Token->CallBack (Token->IpInstance, NULL, MnpToken->Status, 0, Token->Context); + Ip4FreeFrameRxToken (Token); + + return ; + } + + // + // Wrap the frame in a net buffer then deliver it to IP input. + // IP will reassemble the packet, and deliver it to upper layer + // + Netfrag.Len = MnpRxData->DataLength; + Netfrag.Bulk = MnpRxData->PacketData; + + Packet = NetbufFromExt (&Netfrag, 1, 0, IP4_MAX_HEADLEN, Ip4RecycleFrame, Token); + + if (Packet == NULL) { + gBS->SignalEvent (MnpRxData->RecycleEvent); + + Token->CallBack (Token->IpInstance, NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context); + Ip4FreeFrameRxToken (Token); + + return ; + } + + Flag = (MnpRxData->BroadcastFlag ? IP4_LINK_BROADCAST : 0); + Flag |= (MnpRxData->MulticastFlag ? IP4_LINK_MULTICAST : 0); + Flag |= (MnpRxData->PromiscuousFlag ? IP4_LINK_PROMISC : 0); + + Token->CallBack (Token->IpInstance, Packet, EFI_SUCCESS, Flag, Token->Context); +} + +/** + Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip4OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip4OnFrameReceivedDpc, Context); +} + + +/** + Request to receive the packet from the interface. + + @param[in] Interface The interface to receive the frames from. + @param[in] IpInstance The instance that requests the receive. NULL for + the driver itself. + @param[in] CallBack Function to call when receive finished. + @param[in] Context Opaque parameter to the callback. + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive. + @retval EFI_SUCCESS The receive request has been started. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip4ReceiveFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + IP4_LINK_RX_TOKEN *Token; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE); + + if (Interface->RecvRequest != NULL) { + return EFI_ALREADY_STARTED; + } + + Token = Ip4CreateLinkRxToken (Interface, IpInstance, CallBack, Context); + + if (Token == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Interface->RecvRequest = Token; + Status = Interface->Mnp->Receive (Interface->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + Interface->RecvRequest = NULL; + Ip4FreeFrameRxToken (Token); + return Status; + } + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4If.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4If.h new file mode 100644 index 00000000..77239416 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4If.h @@ -0,0 +1,340 @@ +/** @file + Definition for IP4 pseudo interface structure. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_IF_H__ +#define __EFI_IP4_IF_H__ + +#define IP4_FRAME_RX_SIGNATURE SIGNATURE_32 ('I', 'P', 'F', 'R') +#define IP4_FRAME_TX_SIGNATURE SIGNATURE_32 ('I', 'P', 'F', 'T') +#define IP4_FRAME_ARP_SIGNATURE SIGNATURE_32 ('I', 'P', 'F', 'A') +#define IP4_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', 'I', 'F') + +/** + This prototype is used by both receive and transmission. + When receiving Netbuf is allocated by IP4_INTERFACE, and + released by IP4. Flag shows whether the frame is received + as link broadcast/multicast... + + When transmitting, the Netbuf is from IP4, and provided + to the callback as a reference. Flag isn't used. + + @param[in] IpInstance The instance that sent or received the packet. + IpInstance can be NULL which means that it is the IP4 driver + itself sending the packets. IP4 driver may send packets that + don't belong to any instance, such as ICMP errors, ICMP echo + responses, or IGMP packets. IpInstance is used as a tag in + this module. + @param[in] Packet The sent or received packet. + @param[in] IoStatus Status of sending or receiving. + @param[in] LinkFlag Indicate if the frame is received as link broadcast/multicast. + When transmitting, it is not used. + @param[in] Context Additional data for callback. + + @retval None. +**/ +typedef +VOID +(*IP4_FRAME_CALLBACK)( + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 LinkFlag, + IN VOID *Context + ); + +/// +/// Each receive request is wrapped in an IP4_LINK_RX_TOKEN. +/// Upon completion, the Callback will be called. Only one +/// receive request is send to MNP. IpInstance is always NULL. +/// Reference MNP's spec for information. +/// +typedef struct { + UINT32 Signature; + IP4_INTERFACE *Interface; + + IP4_PROTOCOL *IpInstance; + IP4_FRAME_CALLBACK CallBack; + VOID *Context; + + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; +} IP4_LINK_RX_TOKEN; + +/// +/// Each transmit request is wrapped in an IP4_LINK_TX_TOKEN. +/// Upon completion, the Callback will be called. +/// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + IP4_INTERFACE *Interface; + IP4_SERVICE *IpSb; + + IP4_PROTOCOL *IpInstance; + IP4_FRAME_CALLBACK CallBack; + NET_BUF *Packet; + VOID *Context; + + EFI_MAC_ADDRESS DstMac; + EFI_MAC_ADDRESS SrcMac; + + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA MnpTxData; +} IP4_LINK_TX_TOKEN; + +/// +/// Only one ARP request is requested for all the frames in +/// a time. It is started for the first frames to the Ip. Any +/// subsequent transmission frame will be linked to Frames, and +/// be sent all at once the ARP requests succeed. +/// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + LIST_ENTRY Frames; + IP4_INTERFACE *Interface; + + // + // ARP requesting staffs + // + EFI_EVENT OnResolved; + IP4_ADDR Ip; + EFI_MAC_ADDRESS Mac; +} IP4_ARP_QUE; + +/** + Callback to select which frame to cancel. Caller can cancel a + single frame, or all the frame from an IP instance. + + @param Frame The sending frame to check for cancellation. + @param Context Additional data for callback. + + @retval TRUE The sending of the frame should be cancelled. + @retval FALSE Do not cancel the frame sending. +**/ +typedef +BOOLEAN +(*IP4_FRAME_TO_CANCEL)( + IP4_LINK_TX_TOKEN *Frame, + VOID *Context + ); + +// +// Each IP4 instance has its own station address. All the instances +// with the same station address share a single interface structure. +// Each interface has its own ARP child, and shares one MNP child. +// Notice the special cases that DHCP can configure the interface +// with 0.0.0.0/0.0.0.0. +// +struct _IP4_INTERFACE { + UINT32 Signature; + LIST_ENTRY Link; + INTN RefCnt; + + // + // IP address and subnet mask of the interface. It also contains + // the subnet/net broadcast address for quick access. The fields + // are invalid if (Configured == FALSE) + // + IP4_ADDR Ip; + IP4_ADDR SubnetMask; + IP4_ADDR SubnetBrdcast; + IP4_ADDR NetBrdcast; + BOOLEAN Configured; + + // + // Handle used to create/destroy ARP child. All the IP children + // share one MNP which is owned by IP service binding. + // + EFI_HANDLE Controller; + EFI_HANDLE Image; + + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + EFI_ARP_PROTOCOL *Arp; + EFI_HANDLE ArpHandle; + + // + // Queues to keep the frames sent and waiting ARP request. + // + LIST_ENTRY ArpQues; + LIST_ENTRY SentFrames; + IP4_LINK_RX_TOKEN *RecvRequest; + + // + // The interface's MAC and broadcast MAC address. + // + EFI_MAC_ADDRESS Mac; + EFI_MAC_ADDRESS BroadcastMac; + UINT32 HwaddrLen; + + // + // All the IP instances that have the same IP/SubnetMask are linked + // together through IpInstances. If any of the instance enables + // promiscuous receive, PromiscRecv is true. + // + LIST_ENTRY IpInstances; + BOOLEAN PromiscRecv; +}; + +/** + Create an IP4_INTERFACE. Delay the creation of ARP instance until + the interface is configured. + + @param[in] Mnp The shared MNP child of this IP4 service binding + instance. + @param[in] Controller The controller this IP4 service binding instance + is installed. Most like the UNDI handle. + @param[in] ImageHandle This driver's image handle. + + @return Point to the created IP4_INTERFACE, otherwise NULL. + +**/ +IP4_INTERFACE * +Ip4CreateInterface ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle + ); + +/** + Set the interface's address, create and configure + the ARP child if necessary. + + @param Interface The interface to set the address. + @param IpAddr The interface's IP address. + @param SubnetMask The interface's netmask. + + @retval EFI_SUCCESS The interface is configured with Ip/netmask pair, + and a ARP is created for it. + @retval Others Failed to set the interface's address. + +**/ +EFI_STATUS +Ip4SetAddress ( + IN OUT IP4_INTERFACE *Interface, + IN IP4_ADDR IpAddr, + IN IP4_ADDR SubnetMask + ); + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/Netmask pair share the same interface. It is reference + counted. All the frames haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list itself. + + @param[in] Interface The interface used by the IpInstance. + @param[in] IpInstance The Ip instance that free the interface. NULL if + the Ip driver is releasing the default interface. + + @retval EFI_SUCCESS The interface use IpInstance is freed. + +**/ +EFI_STATUS +Ip4FreeInterface ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL + ); + +/** + Send a frame from the interface. If the next hop is broadcast or + multicast address, it is transmitted immediately. If the next hop + is a unicast, it will consult ARP to resolve the NextHop's MAC. + If some error happened, the CallBack won't be called. So, the caller + must test the return value, and take action when there is an error. + + @param[in] Interface The interface to send the frame from + @param[in] IpInstance The IP child that request the transmission. NULL + if it is the IP4 driver itself. + @param[in] Packet The packet to transmit. + @param[in] NextHop The immediate destination to transmit the packet + to. + @param[in] CallBack Function to call back when transmit finished. + @param[in] Context Opaque parameter to the call back. + @param[in] IpSb The pointer to the IP4 service binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to send the frame + @retval EFI_NO_MAPPING Can't resolve the MAC for the nexthop + @retval EFI_SUCCESS The packet is successfully transmitted. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip4SendFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP4_ADDR NextHop, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context, + IN IP4_SERVICE *IpSb + ); + +/** + Remove all the frames on the interface that pass the FrameToCancel, + either queued on ARP queues or that have already been delivered to + MNP and not yet recycled. + + @param[in] Interface Interface to remove the frames from. + @param[in] IoStatus The transmit status returned to the frames' + callback. + @param[in] FrameToCancel Function to select the frame to cancel, NULL to + select all. + @param[in] Context Opaque parameters passed to FrameToCancel. + +**/ +VOID +Ip4CancelFrames ( + IN IP4_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP4_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context + ); + +/** + If there is a pending receive request, cancel it. Don't call + the receive request's callback because this function can be only + called if the instance or driver is tearing itself down. It + doesn't make sense to call it back. But it is necessary to call + the transmit token's callback to give it a chance to free the + packet and update the upper layer's transmit request status, say + that from the UDP. + + @param[in] Interface The interface used by the IpInstance + +**/ +VOID +Ip4CancelReceive ( + IN IP4_INTERFACE *Interface + ); + +/** + Request to receive the packet from the interface. + + @param[in] Interface The interface to receive the frames from. + @param[in] IpInstance The instance that requests the receive. NULL for + the driver itself. + @param[in] CallBack Function to call when receive finished. + @param[in] Context Opaque parameter to the callback. + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to receive. + @retval EFI_SUCCESS The receive request has been started. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip4ReceiveFrame ( + IN IP4_INTERFACE *Interface, + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN IP4_FRAME_CALLBACK CallBack, + IN VOID *Context + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Igmp.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Igmp.c new file mode 100644 index 00000000..3d0b7ead --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Igmp.c @@ -0,0 +1,615 @@ +/** @file + This file implements the RFC2236: IGMP v2. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +// +// Route Alert option in IGMP report to direct routers to +// examine the packet more closely. +// +UINT32 mRouteAlertOption = 0x00000494; + + +/** + Init the IGMP control data of the IP4 service instance, configure + MNP to receive ALL SYSTEM multicast. + + @param[in, out] IpSb The IP4 service whose IGMP is to be initialized. + + @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP. + @retval Others Failed to initialize the IGMP of IpSb. + +**/ +EFI_STATUS +Ip4InitIgmp ( + IN OUT IP4_SERVICE *IpSb + ) +{ + IGMP_SERVICE_DATA *IgmpCtrl; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + IGMP_GROUP *Group; + EFI_STATUS Status; + + IgmpCtrl = &IpSb->IgmpCtrl; + + // + // Configure MNP to receive ALL_SYSTEM multicast + // + Group = AllocatePool (sizeof (IGMP_GROUP)); + + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mnp = IpSb->Mnp; + + Group->Address = IP4_ALLSYSTEM_ADDRESS; + Group->RefCnt = 1; + Group->DelayTime = 0; + Group->ReportByUs = FALSE; + + Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Mnp->Groups (Mnp, TRUE, &Group->Mac); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + InsertHeadList (&IgmpCtrl->Groups, &Group->Link); + return EFI_SUCCESS; + +ON_ERROR: + FreePool (Group); + return Status; +} + + +/** + Find the IGMP_GROUP structure which contains the status of multicast + group Address in this IGMP control block + + @param[in] IgmpCtrl The IGMP control block to search from. + @param[in] Address The multicast address to search. + + @return NULL if the multicast address isn't in the IGMP control block. Otherwise + the point to the IGMP_GROUP which contains the status of multicast group + for Address. + +**/ +IGMP_GROUP * +Ip4FindGroup ( + IN IGMP_SERVICE_DATA *IgmpCtrl, + IN IP4_ADDR Address + ) +{ + LIST_ENTRY *Entry; + IGMP_GROUP *Group; + + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + + if (Group->Address == Address) { + return Group; + } + } + + return NULL; +} + + +/** + Count the number of IP4 multicast groups that are mapped to the + same MAC address. Several IP4 multicast address may be mapped to + the same MAC address. + + @param[in] IgmpCtrl The IGMP control block to search in. + @param[in] Mac The MAC address to search. + + @return The number of the IP4 multicast group that mapped to the same + multicast group Mac. + +**/ +INTN +Ip4FindMac ( + IN IGMP_SERVICE_DATA *IgmpCtrl, + IN EFI_MAC_ADDRESS *Mac + ) +{ + LIST_ENTRY *Entry; + IGMP_GROUP *Group; + INTN Count; + + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + + if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) { + Count++; + } + } + + return Count; +} + + +/** + Send an IGMP protocol message to the Dst, such as IGMP v1 membership report. + + @param[in] IpSb The IP4 service instance that requests the + transmission. + @param[in] Dst The destination to send to. + @param[in] Type The IGMP message type, such as IGMP v1 membership + report. + @param[in] Group The group address in the IGMP message head. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message. + @retval EFI_SUCCESS The IGMP message is successfully send. + @retval Others Failed to send the IGMP message. + +**/ +EFI_STATUS +Ip4SendIgmpMessage ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN UINT8 Type, + IN IP4_ADDR Group + ) +{ + IP4_HEAD Head; + NET_BUF *Packet; + IGMP_HEAD *Igmp; + + // + // Allocate a net buffer to hold the message + // + Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD)); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Fill in the IGMP and IP header, then transmit the message + // + NetbufReserve (Packet, IP4_MAX_HEADLEN); + + Igmp = (IGMP_HEAD *) NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE); + if (Igmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Igmp->Type = Type; + Igmp->MaxRespTime = 0; + Igmp->Checksum = 0; + Igmp->Group = HTONL (Group); + Igmp->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Igmp, sizeof (IGMP_HEAD))); + + Head.Tos = 0; + Head.Protocol = IP4_PROTO_IGMP; + Head.Ttl = 1; + Head.Fragment = 0; + Head.Dst = Dst; + Head.Src = IP4_ALLZERO_ADDRESS; + + return Ip4Output ( + IpSb, + NULL, + Packet, + &Head, + (UINT8 *) &mRouteAlertOption, + sizeof (UINT32), + IP4_ALLZERO_ADDRESS, + Ip4SysPacketSent, + NULL + ); +} + + +/** + Send an IGMP membership report. Depends on whether the server is + v1 or v2, it will send either a V1 or V2 membership report. + + @param[in] IpSb The IP4 service instance that requests the + transmission. + @param[in] Group The group address to report. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message. + @retval EFI_SUCCESS The IGMP report message is successfully send. + @retval Others Failed to send the report. + +**/ +EFI_STATUS +Ip4SendIgmpReport ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Group + ) +{ + if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) { + return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group); + } else { + return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group); + } +} + + +/** + Join the multicast group on behalf of this IP4 child + + @param[in] IpInstance The IP4 child that wants to join the group. + @param[in] Address The group to join. + + @retval EFI_SUCCESS Successfully join the multicast group. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip4JoinGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ) +{ + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + IP4_SERVICE *IpSb; + IGMP_SERVICE_DATA *IgmpCtrl; + IGMP_GROUP *Group; + EFI_STATUS Status; + + IpSb = IpInstance->Service; + IgmpCtrl = &IpSb->IgmpCtrl; + Mnp = IpSb->Mnp; + + // + // If the IP service already is a member in the group, just + // increase the reference count and return. + // + Group = Ip4FindGroup (IgmpCtrl, Address); + + if (Group != NULL) { + Group->RefCnt++; + return EFI_SUCCESS; + } + + // + // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address, + // send a report, then direct MNP to receive the multicast. + // + Group = AllocatePool (sizeof (IGMP_GROUP)); + + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Group->Address = Address; + Group->RefCnt = 1; + Group->DelayTime = IGMP_UNSOLICIATED_REPORT; + Group->ReportByUs = TRUE; + + Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip4SendIgmpReport (IpSb, Address); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Mnp->Groups (Mnp, TRUE, &Group->Mac); + + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ON_ERROR; + } + + InsertHeadList (&IgmpCtrl->Groups, &Group->Link); + return EFI_SUCCESS; + +ON_ERROR: + FreePool (Group); + return Status; +} + + +/** + Leave the IP4 multicast group on behalf of IpInstance. + + @param[in] IpInstance The IP4 child that wants to leave the group + address. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP4 service instance isn't in the group. + @retval EFI_SUCCESS Successfully leave the multicast group. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip4LeaveGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ) +{ + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + IP4_SERVICE *IpSb; + IGMP_SERVICE_DATA *IgmpCtrl; + IGMP_GROUP *Group; + EFI_STATUS Status; + + IpSb = IpInstance->Service; + IgmpCtrl = &IpSb->IgmpCtrl; + Mnp = IpSb->Mnp; + + Group = Ip4FindGroup (IgmpCtrl, Address); + + if (Group == NULL) { + return EFI_NOT_FOUND; + } + + // + // If more than one instance is in the group, decrease + // the RefCnt then return. + // + if (--Group->RefCnt > 0) { + return EFI_SUCCESS; + } + + // + // If multiple IP4 group addresses are mapped to the same + // multicast MAC address, don't configure the MNP to leave + // the MAC. + // + if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) { + Status = Mnp->Groups (Mnp, FALSE, &Group->Mac); + + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + return Status; + } + } + + // + // Send a leave report if the membership is reported by us + // and we are talking IGMPv2. + // + if (Group->ReportByUs && IgmpCtrl->Igmpv1QuerySeen == 0) { + Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address); + } + + RemoveEntryList (&Group->Link); + FreePool (Group); + + return EFI_SUCCESS; +} + + +/** + Handle the received IGMP message for the IP4 service instance. + + @param[in] IpSb The IP4 service instance that received the message. + @param[in] Head The IP4 header of the received message. + @param[in] Packet The IGMP message, without IP4 header. + + @retval EFI_INVALID_PARAMETER The IGMP message is malformatted. + @retval EFI_SUCCESS The IGMP message is successfully processed. + +**/ +EFI_STATUS +Ip4IgmpHandle ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IGMP_SERVICE_DATA *IgmpCtrl; + IGMP_HEAD Igmp; + IGMP_GROUP *Group; + IP4_ADDR Address; + LIST_ENTRY *Entry; + + IgmpCtrl = &IpSb->IgmpCtrl; + + // + // Must checksum over the whole packet, later IGMP version + // may employ message longer than 8 bytes. IP's header has + // already been trimmed off. + // + if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) { + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; + } + + // + // Copy the packet in case it is fragmented + // + NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp); + + switch (Igmp.Type) { + case IGMP_MEMBERSHIP_QUERY: + // + // If MaxRespTime is zero, it is most likely that we are + // talking to a V1 router + // + if (Igmp.MaxRespTime == 0) { + IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT; + Igmp.MaxRespTime = 100; + } + + // + // Igmp is ticking once per second but MaxRespTime is in + // the unit of 100ms. + // + Igmp.MaxRespTime /= 10; + Address = NTOHL (Igmp.Group); + + if (Address == IP4_ALLSYSTEM_ADDRESS) { + break; + } + + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + + // + // If address is all zero, all the memberships will be reported. + // otherwise only one is reported. + // + if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) { + // + // If the timer is pending, only update it if the time left + // is longer than the MaxRespTime. TODO: randomize the DelayTime. + // + if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) { + Group->DelayTime = MAX (1, Igmp.MaxRespTime); + } + } + } + + break; + + case IGMP_V1_MEMBERSHIP_REPORT: + case IGMP_V2_MEMBERSHIP_REPORT: + Address = NTOHL (Igmp.Group); + Group = Ip4FindGroup (IgmpCtrl, Address); + + if ((Group != NULL) && (Group->DelayTime > 0)) { + Group->DelayTime = 0; + Group->ReportByUs = FALSE; + } + + break; + } + + NetbufFree (Packet); + return EFI_SUCCESS; +} + + +/** + The periodical timer function for IGMP. It does the following + things: + 1. Decrease the Igmpv1QuerySeen to make it possible to refresh + the IGMP server type. + 2. Decrease the report timer for each IGMP group in "delaying + member" state. + + @param[in] IpSb The IP4 service instance that is ticking. + +**/ +VOID +Ip4IgmpTicking ( + IN IP4_SERVICE *IpSb + ) +{ + IGMP_SERVICE_DATA *IgmpCtrl; + LIST_ENTRY *Entry; + IGMP_GROUP *Group; + + IgmpCtrl = &IpSb->IgmpCtrl; + + if (IgmpCtrl->Igmpv1QuerySeen > 0) { + IgmpCtrl->Igmpv1QuerySeen--; + } + + // + // Decrease the report timer for each IGMP group in "delaying member" + // + NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link); + ASSERT (Group->DelayTime >= 0); + + if (Group->DelayTime > 0) { + Group->DelayTime--; + + if (Group->DelayTime == 0) { + Ip4SendIgmpReport (IpSb, Group->Address); + Group->ReportByUs = TRUE; + } + } + } +} + + +/** + Add a group address to the array of group addresses. + The caller should make sure that no duplicated address + existed in the array. Although the function doesn't + assume the byte order of the both Source and Addr, the + network byte order is used by the caller. + + @param[in] Source The array of group addresses to add to. + @param[in] Count The number of group addresses in the Source. + @param[in] Addr The IP4 multicast address to add. + + @return NULL if failed to allocate memory for the new groups, + otherwise the new combined group addresses. + +**/ +IP4_ADDR * +Ip4CombineGroups ( + IN IP4_ADDR *Source, + IN UINT32 Count, + IN IP4_ADDR Addr + ) +{ + IP4_ADDR *Groups; + + Groups = AllocatePool (sizeof (IP4_ADDR) * (Count + 1)); + + if (Groups == NULL) { + return NULL; + } + + CopyMem (Groups, Source, Count * sizeof (IP4_ADDR)); + Groups[Count] = Addr; + + return Groups; +} + + +/** + Remove a group address from the array of group addresses. + Although the function doesn't assume the byte order of the + both Groups and Addr, the network byte order is used by + the caller. + + @param Groups The array of group addresses to remove from. + @param Count The number of group addresses in the Groups. + @param Addr The IP4 multicast address to remove. + + @return The number of group addresses in the Groups after remove. + It is Count if the Addr isn't in the Groups. + +**/ +INTN +Ip4RemoveGroupAddr ( + IN OUT IP4_ADDR *Groups, + IN UINT32 Count, + IN IP4_ADDR Addr + ) +{ + UINT32 Index; + + for (Index = 0; Index < Count; Index++) { + if (Groups[Index] == Addr) { + break; + } + } + + while (Index < Count - 1) { + Groups[Index] = Groups[Index + 1]; + Index++; + } + + return Index; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Igmp.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Igmp.h new file mode 100644 index 00000000..833127a2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Igmp.h @@ -0,0 +1,201 @@ +/** @file + +Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_IGMP_H__ +#define __EFI_IP4_IGMP_H__ + +// +// IGMP message type +// +#define IGMP_MEMBERSHIP_QUERY 0x11 +#define IGMP_V1_MEMBERSHIP_REPORT 0x12 +#define IGMP_V2_MEMBERSHIP_REPORT 0x16 +#define IGMP_LEAVE_GROUP 0x17 + +#define IGMP_V1ROUTER_PRESENT 400 +#define IGMP_UNSOLICIATED_REPORT 10 + +#pragma pack(1) +typedef struct { + UINT8 Type; + UINT8 MaxRespTime; + UINT16 Checksum; + IP4_ADDR Group; +} IGMP_HEAD; +#pragma pack() + +/// +/// The status of multicast group. It isn't necessary to maintain +/// explicit state of host state diagram. A group with non-zero +/// DelayTime is in "delaying member" state. otherwise, it is in +/// "idle member" state. +/// +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + IP4_ADDR Address; + INTN DelayTime; + BOOLEAN ReportByUs; + EFI_MAC_ADDRESS Mac; +} IGMP_GROUP; + +/// +/// The IGMP status. Each IP4 service instance has a IGMP_SERVICE_DATA +/// attached. The Igmpv1QuerySeen remember whether the server on this +/// connected network is v1 or v2. +/// +typedef struct { + INTN Igmpv1QuerySeen; + LIST_ENTRY Groups; +} IGMP_SERVICE_DATA; + +/** + Init the IGMP control data of the IP4 service instance, configure + MNP to receive ALL SYSTEM multicast. + + @param[in, out] IpSb The IP4 service whose IGMP is to be initialized. + + @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP. + @retval Others Failed to initialize the IGMP of IpSb. + +**/ +EFI_STATUS +Ip4InitIgmp ( + IN OUT IP4_SERVICE *IpSb + ); + +/** + Join the multicast group on behalf of this IP4 child + + @param[in] IpInstance The IP4 child that wants to join the group. + @param[in] Address The group to join. + + @retval EFI_SUCCESS Successfully join the multicast group. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip4JoinGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ); + +/** + Leave the IP4 multicast group on behalf of IpInstance. + + @param[in] IpInstance The IP4 child that wants to leave the group + address. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP4 service instance isn't in the group. + @retval EFI_SUCCESS Successfully leave the multicast group. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip4LeaveGroup ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_ADDR Address + ); + +/** + Handle the received IGMP message for the IP4 service instance. + + @param[in] IpSb The IP4 service instance that received the message. + @param[in] Head The IP4 header of the received message. + @param[in] Packet The IGMP message, without IP4 header. + + @retval EFI_INVALID_PARAMETER The IGMP message is malformatted. + @retval EFI_SUCCESS The IGMP message is successfully processed. + +**/ +EFI_STATUS +Ip4IgmpHandle ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ); + +/** + The periodical timer function for IGMP. It does the following + things: + 1. Decrease the Igmpv1QuerySeen to make it possible to refresh + the IGMP server type. + 2. Decrease the report timer for each IGMP group in "delaying + member" state. + + @param[in] IpSb The IP4 service instance that is ticking. + +**/ +VOID +Ip4IgmpTicking ( + IN IP4_SERVICE *IpSb + ); + +/** + Add a group address to the array of group addresses. + The caller should make sure that no duplicated address + existed in the array. Although the function doesn't + assume the byte order of the both Source and Addr, the + network byte order is used by the caller. + + @param[in] Source The array of group addresses to add to. + @param[in] Count The number of group addresses in the Source. + @param[in] Addr The IP4 multicast address to add. + + @return NULL if failed to allocate memory for the new groups, + otherwise the new combined group addresses. + +**/ +IP4_ADDR * +Ip4CombineGroups ( + IN IP4_ADDR *Source, + IN UINT32 Count, + IN IP4_ADDR Addr + ); + +/** + Remove a group address from the array of group addresses. + Although the function doesn't assume the byte order of the + both Groups and Addr, the network byte order is used by + the caller. + + @param Groups The array of group addresses to remove from. + @param Count The number of group addresses in the Groups. + @param Addr The IP4 multicast address to remove. + + @return The number of group addresses in the Groups after remove. + It is Count if the Addr isn't in the Groups. + +**/ +INTN +Ip4RemoveGroupAddr ( + IN OUT IP4_ADDR *Groups, + IN UINT32 Count, + IN IP4_ADDR Addr + ); + +/** + Find the IGMP_GROUP structure which contains the status of multicast + group Address in this IGMP control block + + @param[in] IgmpCtrl The IGMP control block to search from. + @param[in] Address The multicast address to search. + + @return NULL if the multicast address isn't in the IGMP control block. Otherwise + the point to the IGMP_GROUP which contains the status of multicast group + for Address. + +**/ +IGMP_GROUP * +Ip4FindGroup ( + IN IGMP_SERVICE_DATA *IgmpCtrl, + IN IP4_ADDR Address + ); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Impl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Impl.c new file mode 100644 index 00000000..f08f18f0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Impl.c @@ -0,0 +1,2330 @@ +/** @file + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +EFI_IPSEC2_PROTOCOL *mIpSec = NULL; + +/** + Gets the current operational settings for this instance of the EFI IPv4 Protocol driver. + + The GetModeData() function returns the current operational mode data for this + driver instance. The data fields in EFI_IP4_MODE_DATA are read only. This + function is used optionally to retrieve the operational mode data of underlying + networks or drivers. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[out] Ip4ModeData Pointer to the EFI IPv4 Protocol mode data structure. + @param[out] MnpConfigData Pointer to the managed network configuration data structure. + @param[out] SnpModeData Pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIp4GetModeData ( + IN CONST EFI_IP4_PROTOCOL *This, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Assigns an IPv4 address and subnet mask to this EFI IPv4 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational + parameters and filter settings for this EFI IPv4 Protocol instance. Until these + parameters have been set, no network traffic can be sent or received by this + instance. Once the parameters have been reset (by calling this function with + IpConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv4 Protocol instance can be started + and stopped independently of each other by enabling or disabling their receive + filter settings with the Configure() function. + + When IpConfigData.UseDefaultAddress is set to FALSE, the new station address will + be appended as an alias address into the addresses list in the EFI IPv4 Protocol + driver. While set to TRUE, Configure() will trigger the EFI_IP4_CONFIG_PROTOCOL + to retrieve the default IPv4 address if it is not available yet. Clients could + frequently call GetModeData() to check the status to ensure that the default IPv4 + address is ready. + + If operational parameters are reset or changed, any pending transmit and receive + requests will be cancelled. Their completion token status will be set to EFI_ABORTED + and their events will be signaled. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] IpConfigData Pointer to the EFI IPv4 Protocol configuration data structure. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + @retval EFI_UNSUPPORTED One or more of the following conditions is TRUE: + A configuration protocol (DHCP, BOOTP, RARP, etc.) could + not be located when clients choose to use the default IPv4 + address. This EFI IPv4 Protocol implementation does not + support this requested filter or timeout setting. + @retval EFI_OUT_OF_RESOURCES The EFI IPv4 Protocol driver instance data could not be allocated. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the + IPv4 address or subnet mask can be changed. The interface must + also be stopped when switching to/from raw packet mode. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv4 + Protocol driver instance is not opened. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Configure ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_CONFIG_DATA *IpConfigData OPTIONAL + ); + +/** + Joins and leaves multicast groups. + + The Groups() function is used to join and leave multicast group sessions. Joining + a group will enable reception of matching multicast packets. Leaving a group will + disable the multicast packet reception. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave. + @param[in] GroupAddress Pointer to the IPv4 multicast address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - JoinFlag is TRUE and GroupAddress is NULL. + - GroupAddress is not NULL and *GroupAddress is + not a multicast IPv4 address. + @retval EFI_NOT_STARTED This 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 System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv4 Protocol implementation does not support multicast groups. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Groups ( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL + ); + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to or deletes a route from the routing table. + + Routes are determined by comparing the SubnetAddress with the destination IPv4 + address arithmetically AND-ed with the SubnetMask. The gateway address must be + on the same subnet as the configured station address. + + The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0. + The default route matches all destination IPv4 addresses that do not match any + other routes. + + A GatewayAddress that is zero is a nonroute. Packets are sent to the destination + IP address if it can be found in the ARP cache or on the local subnet. One automatic + nonroute entry will be inserted into the routing table for outgoing packets that + are addressed to a local subnet (gateway address of 0.0.0.0). + + Each EFI IPv4 Protocol instance has its own independent routing table. Those EFI + IPv4 Protocol instances that use the default IPv4 address will also have copies + of the routing table that was provided by the EFI_IP4_CONFIG_PROTOCOL, and these + copies will be updated whenever the EIF IPv4 Protocol driver reconfigures its + instances. As a result, client modification to the routing table will be lost. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to + FALSE to add this route to the routing table. SubnetAddress + and SubnetMask are used as the key to each route entry. + @param[in] SubnetAddress The address of the subnet that needs to be routed. + @param[in] SubnetMask The subnet mask of SubnetAddress. + @param[in] GatewayAddress The unicast gateway IPv4 address for this route. + + @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_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - SubnetAddress is NULL. + - SubnetMask is NULL. + - GatewayAddress is NULL. + - *SubnetAddress is not a valid subnet address. + - *SubnetMask is not a valid subnet mask. + - *GatewayAddress is not a valid unicast IPv4 address. + @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 DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when + DeleteRoute is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp4Routes ( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ); + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv4 Protocol instance. Whenever the packet in the token is sent out or some + errors occur, the event in the token will be signaled and the status is updated. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] Token Pointer to the transmit token. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This 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_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.Event + was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because the transmit + queue is full. + @retval EFI_NOT_FOUND Not route is found to destination address. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too + short to transmit. + @retval EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length + total data length is + greater than MTU (or greater than the maximum packet size if + Token.Packet.TxData.OverrideData. + DoNotFragment is TRUE.) + +**/ +EFI_STATUS +EFIAPI +EfiIp4Transmit ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ); + +/** + Places a receiving request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + + The Token.Event field in the completion token must be filled in by the caller + and cannot be NULL. When the receive operation completes, the EFI IPv4 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + @param[in] This Pointer to the EFI_IP4_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 IPv4 Protocol 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_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @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 IPv4 Protocol instance has been reset to startup defaults. + EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already + in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + @retval EFI_ICMP_ERROR An ICMP error packet was received. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Receive ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ); + +/** + Abort an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, 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 the + asynchronous operation has completed, this function will not signal the token + and EFI_NOT_FOUND is returned. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_IP4_PROTOCOL.Transmit() or + EFI_IP4_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP4_COMPLETION_TOKEN is + defined in EFI_IP4_PROTOCOL.Transmit(). + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token.->Event was signaled. When Token is NULL, all + pending requests were aborted and their events were signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This 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_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + +**/ +EFI_STATUS +EFIAPI +EfiIp4Cancel ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function polls for incoming data packets and processes outgoing data + packets. Network drivers and applications can call the EFI_IP4_PROTOCOL.Poll() + function to increase the rate that data packets are moved between the communications + device and the transmit and receive queues. + + In some systems the periodic timer event may not poll the underlying communications + device fast enough to transmit and/or receive all data packets without missing + incoming packets or dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the EFI_IP4_PROTOCOL.Poll() function + more often. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv4 Protocol 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_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 transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Poll ( + IN EFI_IP4_PROTOCOL *This + ); + +EFI_IP4_PROTOCOL +mEfiIp4ProtocolTemplete = { + EfiIp4GetModeData, + EfiIp4Configure, + EfiIp4Groups, + EfiIp4Routes, + EfiIp4Transmit, + EfiIp4Receive, + EfiIp4Cancel, + EfiIp4Poll +}; + +/** + Gets the current operational settings for this instance of the EFI IPv4 Protocol driver. + + The GetModeData() function returns the current operational mode data for this + driver instance. The data fields in EFI_IP4_MODE_DATA are read only. This + function is used optionally to retrieve the operational mode data of underlying + networks or drivers. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[out] Ip4ModeData Pointer to the EFI IPv4 Protocol mode data structure. + @param[out] MnpConfigData Pointer to the managed network configuration data structure. + @param[out] SnpModeData Pointer to the simple network mode data structure. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated. + +**/ +EFI_STATUS +EFIAPI +EfiIp4GetModeData ( + IN CONST EFI_IP4_PROTOCOL *This, + OUT EFI_IP4_MODE_DATA *Ip4ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + IP4_SERVICE *IpSb; + EFI_IP4_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP4_ADDR Ip; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (Ip4ModeData != NULL) { + // + // IsStarted is "whether the EfiIp4Configure has been called". + // IsConfigured is "whether the station address has been configured" + // + Ip4ModeData->IsStarted = (BOOLEAN)(IpInstance->State == IP4_STATE_CONFIGED); + CopyMem (&Ip4ModeData->ConfigData, &IpInstance->ConfigData, sizeof (Ip4ModeData->ConfigData)); + Ip4ModeData->IsConfigured = FALSE; + + Ip4ModeData->GroupCount = IpInstance->GroupCount; + Ip4ModeData->GroupTable = (EFI_IPv4_ADDRESS *) IpInstance->Groups; + + Ip4ModeData->IcmpTypeCount = 23; + Ip4ModeData->IcmpTypeList = mIp4SupportedIcmp; + + Ip4ModeData->RouteTable = NULL; + Ip4ModeData->RouteCount = 0; + + Ip4ModeData->MaxPacketSize = IpSb->MaxPacketSize; + + // + // return the current station address for this IP child. So, + // the user can get the default address through this. Some + // application wants to know it station address even it is + // using the default one, such as a ftp server. + // + if (Ip4ModeData->IsStarted) { + Config = &Ip4ModeData->ConfigData; + + Ip = HTONL (IpInstance->Interface->Ip); + CopyMem (&Config->StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Ip = HTONL (IpInstance->Interface->SubnetMask); + CopyMem (&Config->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS)); + + Ip4ModeData->IsConfigured = IpInstance->Interface->Configured; + + // + // Build a EFI route table for user from the internal route table. + // + Status = Ip4BuildEfiRouteTable (IpInstance); + + if (EFI_ERROR (Status)) { + gBS->RestoreTPL (OldTpl); + return Status; + } + + Ip4ModeData->RouteTable = IpInstance->EfiRouteTable; + Ip4ModeData->RouteCount = IpInstance->EfiRouteCount; + } + } + + // + // Get fresh mode data from MNP, since underlying media status may change + // + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData); + + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Config the MNP parameter used by IP. The IP driver use one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. And it will enable/disable + the promiscous receive according to whether there is IP child + enable that or not. If Force is FALSE, it will iterate through + all the IP children to check whether the promiscuous receive + setting has been changed. If it hasn't been changed, it won't + reconfigure the MNP. If Force is TRUE, the MNP is configured no + matter whether that is changed or not. + + @param[in] IpSb The IP4 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP is successfully configured/reconfigured. + @retval Others Configuration failed. + +**/ +EFI_STATUS +Ip4ServiceConfigMnp ( + IN IP4_SERVICE *IpSb, + IN BOOLEAN Force + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *ProtoEntry; + IP4_INTERFACE *IpIf; + IP4_PROTOCOL *IpInstance; + BOOLEAN Reconfig; + BOOLEAN PromiscReceive; + EFI_STATUS Status; + + Reconfig = FALSE; + PromiscReceive = FALSE; + + if (!Force) { + // + // Iterate through the IP children to check whether promiscuous + // receive setting has been changed. Update the interface's receive + // filter also. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + IpIf->PromiscRecv = FALSE; + + NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP4_PROTOCOL, AddrLink); + + if (IpInstance->ConfigData.AcceptPromiscuous) { + IpIf->PromiscRecv = TRUE; + PromiscReceive = TRUE; + } + } + } + + // + // If promiscuous receive isn't changed, it isn't necessary to reconfigure. + // + if (PromiscReceive == IpSb->MnpConfigData.EnablePromiscuousReceive) { + return EFI_SUCCESS; + } + + Reconfig = TRUE; + IpSb->MnpConfigData.EnablePromiscuousReceive = PromiscReceive; + } + + Status = IpSb->Mnp->Configure (IpSb->Mnp, &IpSb->MnpConfigData); + + // + // recover the original configuration if failed to set the configure. + // + if (EFI_ERROR (Status) && Reconfig) { + IpSb->MnpConfigData.EnablePromiscuousReceive = (BOOLEAN) !PromiscReceive; + } + + return Status; +} + + +/** + Initialize the IP4_PROTOCOL structure to the unconfigured states. + + @param IpSb The IP4 service instance. + @param IpInstance The IP4 child instance. + +**/ +VOID +Ip4InitProtocol ( + IN IP4_SERVICE *IpSb, + IN OUT IP4_PROTOCOL *IpInstance + ) +{ + ASSERT ((IpSb != NULL) && (IpInstance != NULL)); + + ZeroMem (IpInstance, sizeof (IP4_PROTOCOL)); + + IpInstance->Signature = IP4_PROTOCOL_SIGNATURE; + CopyMem (&IpInstance->Ip4Proto, &mEfiIp4ProtocolTemplete, sizeof (IpInstance->Ip4Proto)); + IpInstance->State = IP4_STATE_UNCONFIGED; + IpInstance->InDestroy = FALSE; + IpInstance->Service = IpSb; + + InitializeListHead (&IpInstance->Link); + NetMapInit (&IpInstance->RxTokens); + NetMapInit (&IpInstance->TxTokens); + InitializeListHead (&IpInstance->Received); + InitializeListHead (&IpInstance->Delivered); + InitializeListHead (&IpInstance->AddrLink); + + EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY); +} + + +/** + Configure the IP4 child. If the child is already configured, + change the configuration parameter. Otherwise configure it + for the first time. The caller should validate the configuration + before deliver them to it. It also don't do configure NULL. + + @param[in, out] IpInstance The IP4 child to configure. + @param[in] Config The configure data. + + @retval EFI_SUCCESS The IP4 child is successfully configured. + @retval EFI_DEVICE_ERROR Failed to free the pending transive or to + configure underlying MNP or other errors. + @retval EFI_NO_MAPPING The IP4 child is configured to use default + address, but the default address hasn't been + configured. The IP4 child doesn't need to be + reconfigured when default address is configured. + @retval EFI_OUT_OF_RESOURCES No more memory space is available. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip4ConfigProtocol ( + IN OUT IP4_PROTOCOL *IpInstance, + IN EFI_IP4_CONFIG_DATA *Config + ) +{ + IP4_SERVICE *IpSb; + IP4_INTERFACE *IpIf; + EFI_STATUS Status; + IP4_ADDR Ip; + IP4_ADDR Netmask; + EFI_ARP_PROTOCOL *Arp; + EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2; + EFI_IP4_CONFIG2_POLICY Policy; + + IpSb = IpInstance->Service; + + Ip4Config2 = NULL; + + // + // User is changing packet filters. It must be stopped + // before the station address can be changed. + // + if (IpInstance->State == IP4_STATE_CONFIGED) { + // + // Cancel all the pending transmit/receive from upper layer + // + Status = Ip4Cancel (IpInstance, NULL); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CopyMem (&IpInstance->ConfigData, Config, sizeof (IpInstance->ConfigData)); + return EFI_SUCCESS; + } + + // + // Configure a fresh IP4 protocol instance. Create a route table. + // Each IP child has its own route table, which may point to the + // default table if it is using default address. + // + Status = EFI_OUT_OF_RESOURCES; + IpInstance->RouteTable = Ip4CreateRouteTable (); + + if (IpInstance->RouteTable == NULL) { + return Status; + } + + // + // Set up the interface. + // + CopyMem (&Ip, &Config->StationAddress, sizeof (IP4_ADDR)); + CopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR)); + + Ip = NTOHL (Ip); + Netmask = NTOHL (Netmask); + + if (!Config->UseDefaultAddress) { + // + // Find whether there is already an interface with the same + // station address. All the instances with the same station + // address shares one interface. + // + IpIf = Ip4FindStationAddress (IpSb, Ip, Netmask); + + if (IpIf != NULL) { + NET_GET_REF (IpIf); + + } else { + IpIf = Ip4CreateInterface (IpSb->Mnp, IpSb->Controller, IpSb->Image); + + if (IpIf == NULL) { + goto ON_ERROR; + } + + Status = Ip4SetAddress (IpIf, Ip, Netmask); + + if (EFI_ERROR (Status)) { + Status = EFI_DEVICE_ERROR; + Ip4FreeInterface (IpIf, IpInstance); + goto ON_ERROR; + } + + InsertTailList (&IpSb->Interfaces, &IpIf->Link); + } + + // + // Add a route to this connected network in the instance route table. + // + Ip4AddRoute ( + IpInstance->RouteTable, + Ip & Netmask, + Netmask, + IP4_ALLZERO_ADDRESS + ); + } else { + // + // Use the default address. Check the state. + // + if (IpSb->State == IP4_SERVICE_UNSTARTED) { + // + // Trigger the EFI_IP4_CONFIG2_PROTOCOL to retrieve the + // default IPv4 address if it is not available yet. + // + Policy = IpSb->Ip4Config2Instance.Policy; + if (Policy != Ip4Config2PolicyDhcp) { + Ip4Config2 = &IpSb->Ip4Config2Instance.Ip4Config2; + Policy = Ip4Config2PolicyDhcp; + Status= Ip4Config2->SetData ( + Ip4Config2, + Ip4Config2DataTypePolicy, + sizeof (EFI_IP4_CONFIG2_POLICY), + &Policy + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + } + + IpIf = IpSb->DefaultInterface; + NET_GET_REF (IpSb->DefaultInterface); + + // + // If default address is used, so is the default route table. + // Any route set by the instance has the precedence over the + // routes in the default route table. Link the default table + // after the instance's table. Routing will search the local + // table first. + // + NET_GET_REF (IpSb->DefaultRouteTable); + IpInstance->RouteTable->Next = IpSb->DefaultRouteTable; + } + + IpInstance->Interface = IpIf; + if (IpIf->Arp != NULL) { + Arp = NULL; + Status = gBS->OpenProtocol ( + IpIf->ArpHandle, + &gEfiArpProtocolGuid, + (VOID **) &Arp, + gIp4DriverBinding.DriverBindingHandle, + IpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + Ip4FreeInterface (IpIf, IpInstance); + goto ON_ERROR; + } + } + InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink); + + CopyMem (&IpInstance->ConfigData, Config, sizeof (IpInstance->ConfigData)); + IpInstance->State = IP4_STATE_CONFIGED; + + // + // Although EFI_NO_MAPPING is an error code, the IP child has been + // successfully configured and doesn't need reconfiguration when + // default address is acquired. + // + if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + return EFI_NO_MAPPING; + } + + return EFI_SUCCESS; + +ON_ERROR: + Ip4FreeRouteTable (IpInstance->RouteTable); + IpInstance->RouteTable = NULL; + return Status; +} + + +/** + Clean up the IP4 child, release all the resources used by it. + + @param[in] IpInstance The IP4 child to clean up. + + @retval EFI_SUCCESS The IP4 child is cleaned up. + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip4CleanProtocol ( + IN IP4_PROTOCOL *IpInstance + ) +{ + if (EFI_ERROR (Ip4Cancel (IpInstance, NULL))) { + return EFI_DEVICE_ERROR; + } + + if (EFI_ERROR (Ip4Groups (IpInstance, FALSE, NULL))) { + return EFI_DEVICE_ERROR; + } + + // + // Some packets haven't been recycled. It is because either the + // user forgets to recycle the packets, or because the callback + // hasn't been called. Just leave it alone. + // + if (!IsListEmpty (&IpInstance->Delivered)) { + ; + } + + if (IpInstance->Interface != NULL) { + RemoveEntryList (&IpInstance->AddrLink); + if (IpInstance->Interface->Arp != NULL) { + gBS->CloseProtocol ( + IpInstance->Interface->ArpHandle, + &gEfiArpProtocolGuid, + gIp4DriverBinding.DriverBindingHandle, + IpInstance->Handle + ); + } + Ip4FreeInterface (IpInstance->Interface, IpInstance); + IpInstance->Interface = NULL; + } + + if (IpInstance->RouteTable != NULL) { + if (IpInstance->RouteTable->Next != NULL) { + Ip4FreeRouteTable (IpInstance->RouteTable->Next); + } + + Ip4FreeRouteTable (IpInstance->RouteTable); + IpInstance->RouteTable = NULL; + } + + if (IpInstance->EfiRouteTable != NULL) { + FreePool (IpInstance->EfiRouteTable); + IpInstance->EfiRouteTable = NULL; + IpInstance->EfiRouteCount = 0; + } + + if (IpInstance->Groups != NULL) { + FreePool (IpInstance->Groups); + IpInstance->Groups = NULL; + IpInstance->GroupCount = 0; + } + + NetMapClean (&IpInstance->TxTokens); + + NetMapClean (&IpInstance->RxTokens); + + return EFI_SUCCESS; +} + + +/** + Assigns an IPv4 address and subnet mask to this EFI IPv4 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational + parameters and filter settings for this EFI IPv4 Protocol instance. Until these + parameters have been set, no network traffic can be sent or received by this + instance. Once the parameters have been reset (by calling this function with + IpConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv4 Protocol instance can be started + and stopped independently of each other by enabling or disabling their receive + filter settings with the Configure() function. + + When IpConfigData.UseDefaultAddress is set to FALSE, the new station address will + be appended as an alias address into the addresses list in the EFI IPv4 Protocol + driver. While set to TRUE, Configure() will trigger the EFI_IP4_CONFIG_PROTOCOL + to retrieve the default IPv4 address if it is not available yet. Clients could + frequently call GetModeData() to check the status to ensure that the default IPv4 + address is ready. + + If operational parameters are reset or changed, any pending transmit and receive + requests will be cancelled. Their completion token status will be set to EFI_ABORTED + and their events will be signaled. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] IpConfigData Pointer to the EFI IPv4 Protocol configuration data structure. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_NO_MAPPING When using the default address, configuration (DHCP, BOOTP, + RARP, etc.) is not finished yet. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + @retval EFI_UNSUPPORTED One or more of the following conditions is TRUE: + A configuration protocol (DHCP, BOOTP, RARP, etc.) could + not be located when clients choose to use the default IPv4 + address. This EFI IPv4 Protocol implementation does not + support this requested filter or timeout setting. + @retval EFI_OUT_OF_RESOURCES The EFI IPv4 Protocol driver instance data could not be allocated. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the + IPv4 address or subnet mask can be changed. The interface must + also be stopped when switching to/from raw packet mode. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv4 + Protocol driver instance is not opened. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Configure ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_CONFIG_DATA *IpConfigData OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_IP4_CONFIG_DATA *Current; + EFI_TPL OldTpl; + EFI_STATUS Status; + BOOLEAN AddrOk; + IP4_ADDR IpAddress; + IP4_ADDR SubnetMask; + + // + // First, validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Validate the configuration first. + // + if (IpConfigData != NULL) { + + CopyMem (&IpAddress, &IpConfigData->StationAddress, sizeof (IP4_ADDR)); + CopyMem (&SubnetMask, &IpConfigData->SubnetMask, sizeof (IP4_ADDR)); + + IpAddress = NTOHL (IpAddress); + SubnetMask = NTOHL (SubnetMask); + + // + // Check whether the station address is a valid unicast address + // + if (!IpConfigData->UseDefaultAddress) { + AddrOk = Ip4StationAddressValid (IpAddress, SubnetMask); + + if (!AddrOk) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + } + + // + // User can only update packet filters when already configured. + // If it wants to change the station address, it must configure(NULL) + // the instance first. + // + if (IpInstance->State == IP4_STATE_CONFIGED) { + Current = &IpInstance->ConfigData; + + if (Current->UseDefaultAddress != IpConfigData->UseDefaultAddress) { + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + if (!Current->UseDefaultAddress && + (!EFI_IP4_EQUAL (&Current->StationAddress, &IpConfigData->StationAddress) || + !EFI_IP4_EQUAL (&Current->SubnetMask, &IpConfigData->SubnetMask))) { + Status = EFI_ALREADY_STARTED; + goto ON_EXIT; + } + + if (Current->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + } + } + + // + // Configure the instance or clean it up. + // + if (IpConfigData != NULL) { + Status = Ip4ConfigProtocol (IpInstance, IpConfigData); + } else { + Status = Ip4CleanProtocol (IpInstance); + + // + // Consider the following valid sequence: Mnp is unloaded-->Ip Stopped-->Udp Stopped, + // Configure (ThisIp, NULL). If the state is changed to UNCONFIGED, + // the unload fails miserably. + // + if (IpInstance->State == IP4_STATE_CONFIGED) { + IpInstance->State = IP4_STATE_UNCONFIGED; + } + } + + // + // Update the MNP's configure data. Ip4ServiceConfigMnp will check + // whether it is necessary to reconfigure the MNP. + // + Ip4ServiceConfigMnp (IpInstance->Service, FALSE); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; + +} + + +/** + Change the IP4 child's multicast setting. The caller + should make sure that the parameters is valid. + + @param[in] IpInstance The IP4 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it. + @param[in] GroupAddress The target group address. + + @retval EFI_ALREADY_STARTED Want to join the group, but already a member of it. + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_DEVICE_ERROR Failed to set the group configuration. + @retval EFI_SUCCESS Successfully updated the group setting. + @retval EFI_NOT_FOUND Try to leave the group which it isn't a member. + +**/ +EFI_STATUS +Ip4Groups ( + IN IP4_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL + ) +{ + IP4_ADDR *Members; + IP4_ADDR Group; + UINT32 Index; + + // + // Add it to the instance's Groups, and join the group by IGMP. + // IpInstance->Groups is in network byte order. IGMP operates in + // host byte order + // + if (JoinFlag) { + // + // When JoinFlag is TRUE, GroupAddress shouldn't be NULL. + // + ASSERT (GroupAddress != NULL); + CopyMem (&Group, GroupAddress, sizeof (IP4_ADDR)); + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (IpInstance->Groups[Index] == Group) { + return EFI_ALREADY_STARTED; + } + } + + Members = Ip4CombineGroups (IpInstance->Groups, IpInstance->GroupCount, Group); + + if (Members == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (EFI_ERROR (Ip4JoinGroup (IpInstance, NTOHL (Group)))) { + FreePool (Members); + return EFI_DEVICE_ERROR; + } + + if (IpInstance->Groups != NULL) { + FreePool (IpInstance->Groups); + } + + IpInstance->Groups = Members; + IpInstance->GroupCount++; + + return EFI_SUCCESS; + } + + // + // Leave the group. Leave all the groups if GroupAddress is NULL. + // Must iterate from the end to the beginning because the GroupCount + // is decremented each time an address is removed.. + // + for (Index = IpInstance->GroupCount; Index > 0 ; Index--) { + ASSERT (IpInstance->Groups != NULL); + Group = IpInstance->Groups[Index - 1]; + if ((GroupAddress == NULL) || EFI_IP4_EQUAL (&Group, GroupAddress)) { + if (EFI_ERROR (Ip4LeaveGroup (IpInstance, NTOHL (Group)))) { + return EFI_DEVICE_ERROR; + } + + Ip4RemoveGroupAddr (IpInstance->Groups, IpInstance->GroupCount, Group); + IpInstance->GroupCount--; + + if (IpInstance->GroupCount == 0) { + ASSERT (Index == 1); + + FreePool (IpInstance->Groups); + IpInstance->Groups = NULL; + } + + if (GroupAddress != NULL) { + return EFI_SUCCESS; + } + } + } + + return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS); +} + + +/** + Joins and leaves multicast groups. + + The Groups() function is used to join and leave multicast group sessions. Joining + a group will enable reception of matching multicast packets. Leaving a group will + disable the multicast packet reception. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave. + @param[in] GroupAddress Pointer to the IPv4 multicast address. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - JoinFlag is TRUE and GroupAddress is NULL. + - GroupAddress is not NULL and *GroupAddress is + not a multicast IPv4 address. + @retval EFI_NOT_STARTED This 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 System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv4 Protocol implementation does not support multicast groups. + @retval EFI_ALREADY_STARTED The group address is already in the group table (when + JoinFlag is TRUE). + @retval EFI_NOT_FOUND The group address is not in the group table (when JoinFlag is FALSE). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Groups ( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP4_ADDR McastIp; + + if ((This == NULL) || (JoinFlag && (GroupAddress == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (GroupAddress != NULL) { + CopyMem (&McastIp, GroupAddress, sizeof (IP4_ADDR)); + + if (!IP4_IS_MULTICAST (NTOHL (McastIp))) { + return EFI_INVALID_PARAMETER; + } + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + Status = Ip4Groups (IpInstance, JoinFlag, GroupAddress); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Adds and deletes routing table entries. + + The Routes() function adds a route to or deletes a route from the routing table. + + Routes are determined by comparing the SubnetAddress with the destination IPv4 + address arithmetically AND-ed with the SubnetMask. The gateway address must be + on the same subnet as the configured station address. + + The default route is added with SubnetAddress and SubnetMask both set to 0.0.0.0. + The default route matches all destination IPv4 addresses that do not match any + other routes. + + A GatewayAddress that is zero is a nonroute. Packets are sent to the destination + IP address if it can be found in the ARP cache or on the local subnet. One automatic + nonroute entry will be inserted into the routing table for outgoing packets that + are addressed to a local subnet (gateway address of 0.0.0.0). + + Each EFI IPv4 Protocol instance has its own independent routing table. Those EFI + IPv4 Protocol instances that use the default IPv4 address will also have copies + of the routing table that was provided by the EFI_IP4_CONFIG_PROTOCOL, and these + copies will be updated whenever the EIF IPv4 Protocol driver reconfigures its + instances. As a result, client modification to the routing table will be lost. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] DeleteRoute Set to TRUE to delete this route from the routing table. Set to + FALSE to add this route to the routing table. SubnetAddress + and SubnetMask are used as the key to each route entry. + @param[in] SubnetAddress The address of the subnet that needs to be routed. + @param[in] SubnetMask The subnet mask of SubnetAddress. + @param[in] GatewayAddress The unicast gateway IPv4 address for this route. + + @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_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - SubnetAddress is NULL. + - SubnetMask is NULL. + - GatewayAddress is NULL. + - *SubnetAddress is not a valid subnet address. + - *SubnetMask is not a valid subnet mask. + - *GatewayAddress is not a valid unicast IPv4 address. + @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 DeleteRoute is TRUE). + @retval EFI_ACCESS_DENIED The route is already defined in the routing table (when + DeleteRoute is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp4Routes ( + IN EFI_IP4_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv4_ADDRESS *SubnetAddress, + IN EFI_IPv4_ADDRESS *SubnetMask, + IN EFI_IPv4_ADDRESS *GatewayAddress + ) +{ + IP4_PROTOCOL *IpInstance; + IP4_INTERFACE *IpIf; + IP4_ADDR Dest; + IP4_ADDR Netmask; + IP4_ADDR Nexthop; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // First, validate the parameters + // + if ((This == NULL) || (SubnetAddress == NULL) || + (SubnetMask == NULL) || (GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + CopyMem (&Dest, SubnetAddress, sizeof (IP4_ADDR)); + CopyMem (&Netmask, SubnetMask, sizeof (IP4_ADDR)); + CopyMem (&Nexthop, GatewayAddress, sizeof (IP4_ADDR)); + + Dest = NTOHL (Dest); + Netmask = NTOHL (Netmask); + Nexthop = NTOHL (Nexthop); + + IpIf = IpInstance->Interface; + + if (!IP4_IS_VALID_NETMASK (Netmask)) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + // + // the gateway address must be a unicast on the connected network if not zero. + // + if ((Nexthop != IP4_ALLZERO_ADDRESS) && + ((IpIf->SubnetMask != IP4_ALLONE_ADDRESS && !IP4_NET_EQUAL (Nexthop, IpIf->Ip, IpIf->SubnetMask)) || + IP4_IS_BROADCAST (Ip4GetNetCast (Nexthop, IpIf)))) { + + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + if (DeleteRoute) { + Status = Ip4DelRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop); + } else { + Status = Ip4AddRoute (IpInstance->RouteTable, Dest, Netmask, Nexthop); + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Check whether the user's token or event has already + been enqueued on IP4's list. + + @param[in] Map The container of either user's transmit or receive + token. + @param[in] Item Current item to check against. + @param[in] Context The Token to check against. + + @retval EFI_ACCESS_DENIED The token or event has already been enqueued in IP. + @retval EFI_SUCCESS The current item isn't the same token/event as the + context. + +**/ +EFI_STATUS +EFIAPI +Ip4TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + EFI_IP4_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_IP4_COMPLETION_TOKEN *) Context; + TokenInItem = (EFI_IP4_COMPLETION_TOKEN *) Item->Key; + + if ((Token == TokenInItem) || (Token->Event == TokenInItem->Event)) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + +/** + Validate the user's token against current station address. + + @param[in] Token User's token to validate. + @param[in] IpIf The IP4 child's interface. + @param[in] RawData Set to TRUE to send unformatted packets. + + @retval EFI_INVALID_PARAMETER Some parameters are invalid. + @retval EFI_BAD_BUFFER_SIZE The user's option/data is too long. + @retval EFI_SUCCESS The token is valid. + +**/ +EFI_STATUS +Ip4TxTokenValid ( + IN EFI_IP4_COMPLETION_TOKEN *Token, + IN IP4_INTERFACE *IpIf, + IN BOOLEAN RawData + ) +{ + EFI_IP4_TRANSMIT_DATA *TxData; + EFI_IP4_OVERRIDE_DATA *Override; + IP4_ADDR Src; + IP4_ADDR Gateway; + UINT32 Offset; + UINT32 Index; + UINT32 HeadLen; + + if ((Token == NULL) || (Token->Event == NULL) || (Token->Packet.TxData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + TxData = Token->Packet.TxData; + + // + // Check the fragment table: no empty fragment, and length isn't bogus. + // + if ((TxData->TotalDataLength == 0) || (TxData->FragmentCount == 0)) { + return EFI_INVALID_PARAMETER; + } + + Offset = TxData->TotalDataLength; + + if (Offset > IP4_MAX_PACKET_SIZE) { + return EFI_BAD_BUFFER_SIZE; + } + + for (Index = 0; Index < TxData->FragmentCount; Index++) { + if ((TxData->FragmentTable[Index].FragmentBuffer == NULL) || + (TxData->FragmentTable[Index].FragmentLength == 0)) { + + return EFI_INVALID_PARAMETER; + } + + Offset -= TxData->FragmentTable[Index].FragmentLength; + } + + if (Offset != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // NOTE that OptionsLength/OptionsBuffer/OverrideData are ignored if RawData + // is TRUE. + // + if (RawData) { + return EFI_SUCCESS; + } + + // + // Check the IP options: no more than 40 bytes and format is OK + // + if (TxData->OptionsLength != 0) { + if ((TxData->OptionsLength > 40) || (TxData->OptionsBuffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!Ip4OptionIsValid (TxData->OptionsBuffer, TxData->OptionsLength, FALSE)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Check the source and gateway: they must be a valid unicast. + // Gateway must also be on the connected network. + // + if (TxData->OverrideData != NULL) { + Override = TxData->OverrideData; + + CopyMem (&Src, &Override->SourceAddress, sizeof (IP4_ADDR)); + CopyMem (&Gateway, &Override->GatewayAddress, sizeof (IP4_ADDR)); + + Src = NTOHL (Src); + Gateway = NTOHL (Gateway); + + if ((NetGetIpClass (Src) > IP4_ADDR_CLASSC) || + (Src == IP4_ALLONE_ADDRESS) || + IP4_IS_BROADCAST (Ip4GetNetCast (Src, IpIf))) { + + return EFI_INVALID_PARAMETER; + } + + // + // If gateway isn't zero, it must be a unicast address, and + // on the connected network. + // + if ((Gateway != IP4_ALLZERO_ADDRESS) && + ((NetGetIpClass (Gateway) > IP4_ADDR_CLASSC) || + !IP4_NET_EQUAL (Gateway, IpIf->Ip, IpIf->SubnetMask) || + IP4_IS_BROADCAST (Ip4GetNetCast (Gateway, IpIf)))) { + + return EFI_INVALID_PARAMETER; + } + } + + // + // Check the packet length: Head length and packet length all has a limit + // + HeadLen = sizeof (IP4_HEAD) + ((TxData->OptionsLength + 3) &~0x03); + + if ((HeadLen > IP4_MAX_HEADLEN) || + (TxData->TotalDataLength + HeadLen > IP4_MAX_PACKET_SIZE)) { + + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although it seems this function is pretty simple, + there are some subtle things. + When user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP4_TXTOKEN_WRAP and the data + is wrapped in an net buffer. the net buffer's Free function is + set to Ip4FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip4Output for + transmission. If something error happened before that, the buffer + is freed, which in turn will free the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be fired because we are still in the EfiIp4Transmit. If + the buffer has been sent by Ip4Output, it should be removed from + the TxToken map and user's event signaled. The token wrap and buffer + are bound together. Check the comments in Ip4Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip4FreeTxToken ( + IN VOID *Context + ) +{ + IP4_TXTOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + + Wrap = (IP4_TXTOKEN_WRAP *) Context; + + // + // Signal IpSecRecycleEvent to inform IPsec free the memory + // + if (Wrap->IpSecRecycleSignal != NULL) { + gBS->SignalEvent (Wrap->IpSecRecycleSignal); + } + + // + // Find the token in the instance's map. EfiIp4Transmit put the + // token to the map. If that failed, NetMapFindKey will return NULL. + // + Item = NetMapFindKey (&Wrap->IpInstance->TxTokens, Wrap->Token); + + if (Item != NULL) { + NetMapRemoveItem (&Wrap->IpInstance->TxTokens, Item, NULL); + } + + if (Wrap->Sent) { + gBS->SignalEvent (Wrap->Token->Event); + + // + // Dispatch the DPC queued by the NotifyFunction of Token->Event. + // + DispatchDpc (); + } + + FreePool (Wrap); +} + + +/** + The callback function to Ip4Output to update the transmit status. + + @param Ip4Instance The Ip4Instance that request the transmit. + @param Packet The user's transmit request. + @param IoStatus The result of the transmission. + @param Flag Not used during transmission. + @param Context The token's wrap. + +**/ +VOID +Ip4OnPacketSent ( + IP4_PROTOCOL *Ip4Instance, + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 Flag, + VOID *Context + ) +{ + IP4_TXTOKEN_WRAP *Wrap; + + // + // This is the transmission request from upper layer, + // not the IP4 driver itself. + // + ASSERT (Ip4Instance != NULL); + + // + // The first fragment of the packet has been sent. Update + // the token's status. That is, if fragmented, the transmit's + // status is the first fragment's status. The Wrap will be + // release when all the fragments are release. Check the comments + // in Ip4FreeTxToken and Ip4Output for information. + // + Wrap = (IP4_TXTOKEN_WRAP *) Context; + Wrap->Token->Status = IoStatus; + + NetbufFree (Wrap->Packet); +} + + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv4 Protocol instance. Whenever the packet in the token is sent out or some + errors occur, the event in the token will be signaled and the status is updated. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] Token Pointer to the transmit token. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This 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_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token.Event + was already in the transmit queue. + @retval EFI_NOT_READY The completion token could not be queued because the transmit + queue is full. + @retval EFI_NOT_FOUND Not route is found to destination address. + @retval EFI_OUT_OF_RESOURCES Could not queue the transmit data. + @retval EFI_BUFFER_TOO_SMALL Token.Packet.TxData.TotalDataLength is too + short to transmit. + @retval EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length + total data length is + greater than MTU (or greater than the maximum packet size if + Token.Packet.TxData.OverrideData. + DoNotFragment is TRUE). + +**/ +EFI_STATUS +EFIAPI +EfiIp4Transmit ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ) +{ + IP4_SERVICE *IpSb; + IP4_PROTOCOL *IpInstance; + IP4_INTERFACE *IpIf; + IP4_TXTOKEN_WRAP *Wrap; + EFI_IP4_TRANSMIT_DATA *TxData; + EFI_IP4_CONFIG_DATA *Config; + EFI_IP4_OVERRIDE_DATA *Override; + IP4_HEAD Head; + IP4_ADDR GateWay; + EFI_STATUS Status; + EFI_TPL OldTpl; + BOOLEAN DontFragment; + UINT32 HeadLen; + UINT8 RawHdrLen; + UINT32 OptionsLength; + UINT8 *OptionsBuffer; + VOID *FirstFragment; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + IpSb = IpInstance->Service; + IpIf = IpInstance->Interface; + Config = &IpInstance->ConfigData; + + if (Config->UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + // + // make sure that token is properly formatted + // + Status = Ip4TxTokenValid (Token, IpIf, Config->RawData); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check whether the token or signal already existed. + // + if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip4TokenExist, Token))) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Build the IP header, need to fill in the Tos, TotalLen, Id, + // fragment, Ttl, protocol, Src, and Dst. + // + TxData = Token->Packet.TxData; + + FirstFragment = NULL; + + if (Config->RawData) { + // + // When RawData is TRUE, first buffer in FragmentTable points to a raw + // IPv4 fragment including IPv4 header and options. + // + FirstFragment = TxData->FragmentTable[0].FragmentBuffer; + CopyMem (&RawHdrLen, FirstFragment, sizeof (UINT8)); + + RawHdrLen = (UINT8) (RawHdrLen & 0x0f); + if (RawHdrLen < 5) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + RawHdrLen = (UINT8) (RawHdrLen << 2); + + CopyMem (&Head, FirstFragment, IP4_MIN_HEADLEN); + + Ip4NtohHead (&Head); + HeadLen = 0; + DontFragment = IP4_DO_NOT_FRAGMENT (Head.Fragment); + + if (!DontFragment) { + Status = EFI_INVALID_PARAMETER; + goto ON_EXIT; + } + + GateWay = IP4_ALLZERO_ADDRESS; + + // + // Get IPv4 options from first fragment. + // + if (RawHdrLen == IP4_MIN_HEADLEN) { + OptionsLength = 0; + OptionsBuffer = NULL; + } else { + OptionsLength = RawHdrLen - IP4_MIN_HEADLEN; + OptionsBuffer = (UINT8 *) FirstFragment + IP4_MIN_HEADLEN; + } + + // + // Trim off IPv4 header and options from first fragment. + // + TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment + RawHdrLen; + TxData->FragmentTable[0].FragmentLength = TxData->FragmentTable[0].FragmentLength - RawHdrLen; + } else { + CopyMem (&Head.Dst, &TxData->DestinationAddress, sizeof (IP4_ADDR)); + Head.Dst = NTOHL (Head.Dst); + + if (TxData->OverrideData != NULL) { + Override = TxData->OverrideData; + Head.Protocol = Override->Protocol; + Head.Tos = Override->TypeOfService; + Head.Ttl = Override->TimeToLive; + DontFragment = Override->DoNotFragment; + + CopyMem (&Head.Src, &Override->SourceAddress, sizeof (IP4_ADDR)); + CopyMem (&GateWay, &Override->GatewayAddress, sizeof (IP4_ADDR)); + + Head.Src = NTOHL (Head.Src); + GateWay = NTOHL (GateWay); + } else { + Head.Src = IpIf->Ip; + GateWay = IP4_ALLZERO_ADDRESS; + Head.Protocol = Config->DefaultProtocol; + Head.Tos = Config->TypeOfService; + Head.Ttl = Config->TimeToLive; + DontFragment = Config->DoNotFragment; + } + + Head.Fragment = IP4_HEAD_FRAGMENT_FIELD (DontFragment, FALSE, 0); + HeadLen = (TxData->OptionsLength + 3) & (~0x03); + + OptionsLength = TxData->OptionsLength; + OptionsBuffer = (UINT8 *) (TxData->OptionsBuffer); + } + + // + // If don't fragment and fragment needed, return error + // + if (DontFragment && (TxData->TotalDataLength + HeadLen > IpSb->MaxPacketSize)) { + Status = EFI_BAD_BUFFER_SIZE; + goto ON_EXIT; + } + + // + // OK, it survives all the validation check. Wrap the token in + // a IP4_TXTOKEN_WRAP and the data in a netbuf + // + Status = EFI_OUT_OF_RESOURCES; + Wrap = AllocateZeroPool (sizeof (IP4_TXTOKEN_WRAP)); + if (Wrap == NULL) { + goto ON_EXIT; + } + + Wrap->IpInstance = IpInstance; + Wrap->Token = Token; + Wrap->Sent = FALSE; + Wrap->Life = IP4_US_TO_SEC (Config->TransmitTimeout); + Wrap->Packet = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + IP4_MAX_HEADLEN, + 0, + Ip4FreeTxToken, + Wrap + ); + + if (Wrap->Packet == NULL) { + FreePool (Wrap); + goto ON_EXIT; + } + + Token->Status = EFI_NOT_READY; + + if (EFI_ERROR (NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap))) { + // + // NetbufFree will call Ip4FreeTxToken, which in turn will + // free the IP4_TXTOKEN_WRAP. Now, the token wrap hasn't been + // enqueued. + // + if (Config->RawData) { + // + // Restore pointer of first fragment in RawData mode. + // + TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment; + } + + NetbufFree (Wrap->Packet); + goto ON_EXIT; + } + + // + // Mark the packet sent before output it. Mark it not sent again if the + // returned status is not EFI_SUCCESS; + // + Wrap->Sent = TRUE; + + Status = Ip4Output ( + IpSb, + IpInstance, + Wrap->Packet, + &Head, + OptionsBuffer, + OptionsLength, + GateWay, + Ip4OnPacketSent, + Wrap + ); + + if (EFI_ERROR (Status)) { + Wrap->Sent = FALSE; + + if (Config->RawData) { + // + // Restore pointer of first fragment in RawData mode. + // + TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment; + } + + NetbufFree (Wrap->Packet); + } + + if (Config->RawData) { + // + // Restore pointer of first fragment in RawData mode. + // + TxData->FragmentTable[0].FragmentBuffer = (UINT8 *) FirstFragment; + } + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Places a receiving request into the receiving queue. + + The Receive() function places a completion token into the receive packet queue. + This function is always asynchronous. + + The Token.Event field in the completion token must be filled in by the caller + and cannot be NULL. When the receive operation completes, the EFI IPv4 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + @param[in] This Pointer to the EFI_IP4_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 IPv4 Protocol 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_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + @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 IPv4 Protocol instance has been reset to startup defaults. + EFI_ACCESS_DENIED The receive completion token with the same Token.Event was already + in the receive queue. + @retval EFI_NOT_READY The receive request could not be queued because the receive queue is full. + @retval EFI_ICMP_ERROR An ICMP error packet was received. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Receive ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + + // + // First validate the parameters + // + if ((This == NULL) || (Token == NULL) || (Token->Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + // + // Check whether the toke is already on the receive queue. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip4TokenExist, Token); + + if (EFI_ERROR (Status)) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + + // + // Queue the token then check whether there is pending received packet. + // + Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = Ip4InstanceDeliverPacket (IpInstance); + + // + // Dispatch the DPC queued by the NotifyFunction of this instane's receive + // event. + // + DispatchDpc (); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Cancel the transmitted but not recycled packet. If a matching + token is found, it will call Ip4CancelPacket to cancel the + packet. Ip4CancelPacket will cancel all the fragments of the + packet. When all the fragments are freed, the IP4_TXTOKEN_WRAP + will be deleted from the Map, and user's event signalled. + Because Ip4CancelPacket and other functions are all called in + line, so, after Ip4CancelPacket returns, the Item has been freed. + + @param[in] Map The IP4 child's transmit queue. + @param[in] Item The current transmitted packet to test. + @param[in] Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next Item. + @retval EFI_ABORTED The user's Token (Token != NULL) is cancelled. + +**/ +EFI_STATUS +EFIAPI +Ip4CancelTxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + IP4_TXTOKEN_WRAP *Wrap; + + Token = (EFI_IP4_COMPLETION_TOKEN *) Context; + + // + // Return EFI_SUCCESS to check the next item in the map if + // this one doesn't match. + // + if ((Token != NULL) && (Token != Item->Key)) { + return EFI_SUCCESS; + } + + Wrap = (IP4_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + // + // Don't access the Item, Wrap and Token's members after this point. + // Item and wrap has been freed. And we no longer own the Token. + // + Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + + // + // If only one item is to be cancel, return EFI_ABORTED to stop + // iterating the map any more. + // + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the receive request. This is quiet simple, because + it is only enqueued in our local receive map. + + @param[in] Map The IP4 child's receive queue. + @param[in] Item Current receive request to cancel. + @param[in] Context The user's token to cancel. + + @retval EFI_SUCCESS Continue to check the next receive request on the + queue. + @retval EFI_ABORTED The user's token (token != NULL) has been + cancelled. + +**/ +EFI_STATUS +EFIAPI +Ip4CancelRxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + EFI_IP4_COMPLETION_TOKEN *This; + + Token = (EFI_IP4_COMPLETION_TOKEN *) Context; + This = Item->Key; + + if ((Token != NULL) && (Token != This)) { + return EFI_SUCCESS; + } + + NetMapRemoveItem (Map, Item, NULL); + + This->Status = EFI_ABORTED; + This->Packet.RxData = NULL; + gBS->SignalEvent (This->Event); + + if (Token != NULL) { + return EFI_ABORTED; + } + + return EFI_SUCCESS; +} + + +/** + Cancel the user's receive/transmit request. + + @param[in] IpInstance The IP4 child. + @param[in] Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled. + @retval EFI_NOT_FOUND The token isn't found on either the + transmit/receive queue. + @retval EFI_DEVICE_ERROR Not all token is cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip4Cancel ( + IN IP4_PROTOCOL *IpInstance, + IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // First check the transmitted packet. Ip4CancelTxTokens returns + // EFI_ABORTED to mean that the token has been cancelled when + // token != NULL. So, return EFI_SUCCESS for this condition. + // + Status = NetMapIterate (&IpInstance->TxTokens, Ip4CancelTxTokens, Token); + + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // Check the receive queue. Ip4CancelRxTokens also returns EFI_ABORT + // for Token!=NULL and it is cancelled. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip4CancelRxTokens, Token); + // + // Dispatch the DPCs queued by the NotifyFunction of the canceled rx token's + // events. + // + DispatchDpc (); + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // OK, if the Token is found when Token != NULL, the NetMapIterate + // will return EFI_ABORTED, which has been interrupted as EFI_SUCCESS. + // + if (Token != NULL) { + return EFI_NOT_FOUND; + } + + // + // If Token == NULL, cancel all the tokens. return error if no + // all of them are cancelled. + // + if (!NetMapIsEmpty (&IpInstance->TxTokens) || + !NetMapIsEmpty (&IpInstance->RxTokens)) { + + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + + +/** + Abort an asynchronous transmit or receive request. + + The Cancel() function is used to abort a pending transmit or receive request. + If the token is in the transmit or receive request queues, after calling this + function, 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 the + asynchronous operation has completed, this function will not signal the token + and EFI_NOT_FOUND is returned. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_IP4_PROTOCOL.Transmit() or + EFI_IP4_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP4_COMPLETION_TOKEN is + defined in EFI_IP4_PROTOCOL.Transmit(). + + @retval EFI_SUCCESS The asynchronous I/O request was aborted and + Token.->Event was signaled. When Token is NULL, all + pending requests were aborted and their events were signaled. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_NOT_STARTED This 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_NOT_FOUND When Token is not NULL, the asynchronous I/O request was + not found in the transmit or receive queue. It has either completed + or was not issued by Transmit() and Receive(). + +**/ +EFI_STATUS +EFIAPI +EfiIp4Cancel ( + IN EFI_IP4_PROTOCOL *This, + IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP4_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + if (IpInstance->ConfigData.UseDefaultAddress && IP4_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto ON_EXIT; + } + + Status = Ip4Cancel (IpInstance, Token); + +ON_EXIT: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Polls for incoming data packets and processes outgoing data packets. + + The Poll() function polls for incoming data packets and processes outgoing data + packets. Network drivers and applications can call the EFI_IP4_PROTOCOL.Poll() + function to increase the rate that data packets are moved between the communications + device and the transmit and receive queues. + + In some systems the periodic timer event may not poll the underlying communications + device fast enough to transmit and/or receive all data packets without missing + incoming packets or dropping outgoing packets. Drivers and applications that are + experiencing packet loss should try calling the EFI_IP4_PROTOCOL.Poll() function + more often. + + @param[in] This Pointer to the EFI_IP4_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv4 Protocol 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_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 transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp4Poll ( + IN EFI_IP4_PROTOCOL *This + ) +{ + IP4_PROTOCOL *IpInstance; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP4_INSTANCE_FROM_PROTOCOL (This); + + if (IpInstance->State == IP4_STATE_UNCONFIGED) { + return EFI_NOT_STARTED; + } + + Mnp = IpInstance->Service->Mnp; + + // + // Don't lock the Poll function to enable the deliver of + // the packet polled up. + // + return Mnp->Poll (Mnp); +} + +/** + Decrease the life of the transmitted packets. If it is + decreased to zero, cancel the packet. This function is + called by Ip4PacketTimerTicking which time out both the + received-but-not-delivered and transmitted-but-not-recycle + packets. + + @param[in] Map The IP4 child's transmit map. + @param[in] Item Current transmitted packet. + @param[in] Context Not used. + + @retval EFI_SUCCESS Always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +Ip4SentPacketTicking ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + IP4_TXTOKEN_WRAP *Wrap; + + Wrap = (IP4_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + if ((Wrap->Life > 0) && (--Wrap->Life == 0)) { + Ip4CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + } + + return EFI_SUCCESS; +} + +/** + This heart beat timer of IP4 service instance times out all of its IP4 children's + received-but-not-delivered and transmitted-but-not-recycle packets, and provides + time input for its IGMP protocol. + + @param[in] Event The IP4 service instance's heart beat timer. + @param[in] Context The IP4 service instance. + +**/ +VOID +EFIAPI +Ip4TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_SERVICE *IpSb; + + IpSb = (IP4_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE); + + Ip4PacketTimerTicking (IpSb); + Ip4IgmpTicking (IpSb); +} + +/** + This dedicated timer is used to poll underlying network media status. In case + of cable swap or wireless network switch, a new round auto configuration will + be initiated. The timer will signal the IP4 to run DHCP configuration again. + IP4 driver will free old IP address related resource, such as route table and + Interface, then initiate a DHCP process to acquire new IP, eventually create + route table for new IP address. + + @param[in] Event The IP4 service instance's heart beat timer. + @param[in] Context The IP4 service instance. + +**/ +VOID +EFIAPI +Ip4TimerReconfigChecking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_SERVICE *IpSb; + BOOLEAN OldMediaPresent; + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_MODE SnpModeData; + + IpSb = (IP4_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP4_SERVICE_SIGNATURE); + + OldMediaPresent = IpSb->MediaPresent; + + // + // Get fresh mode data from MNP, since underlying media status may change. + // Here, it needs to mention that the MediaPresent can also be checked even if + // EFI_NOT_STARTED returned while this MNP child driver instance isn't configured. + // + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &SnpModeData); + if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) { + return; + } + + IpSb->MediaPresent = SnpModeData.MediaPresent; + // + // Media transimit Unpresent to Present means new link movement is detected. + // + if (!OldMediaPresent && IpSb->MediaPresent && (IpSb->Ip4Config2Instance.Policy == Ip4Config2PolicyDhcp)) { + // + // Signal the IP4 to run the dhcp configuration again. IP4 driver will free + // old IP address related resource, such as route table and Interface, then + // initiate a DHCP round to acquire new IP, eventually + // create route table for new IP address. + // + if (IpSb->ReconfigEvent != NULL) { + Status = gBS->SignalEvent (IpSb->ReconfigEvent); + DispatchDpc (); + } + } +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Impl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Impl.h new file mode 100644 index 00000000..f99e7e7f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Impl.h @@ -0,0 +1,417 @@ +/** @file + Ip4 internal functions and type definitions. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_IMPL_H__ +#define __EFI_IP4_IMPL_H__ + +#include <Uefi.h> + +#include <Protocol/IpSec.h> +#include <Protocol/Ip4.h> +#include <Protocol/Ip4Config2.h> +#include <Protocol/Arp.h> +#include <Protocol/ManagedNetwork.h> +#include <Protocol/Dhcp4.h> +#include <Protocol/HiiConfigRouting.h> +#include <Protocol/HiiConfigAccess.h> + +#include <IndustryStandard/Dhcp.h> + +#include <Library/DebugLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/BaseLib.h> +#include <Library/UefiLib.h> +#include <Library/NetLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DpcLib.h> +#include <Library/PrintLib.h> +#include <Library/DevicePathLib.h> +#include <Library/HiiLib.h> +#include <Library/UefiHiiServicesLib.h> + +#include "Ip4Common.h" +#include "Ip4Driver.h" +#include "Ip4If.h" +#include "Ip4Icmp.h" +#include "Ip4Option.h" +#include "Ip4Igmp.h" +#include "Ip4Route.h" +#include "Ip4Input.h" +#include "Ip4Output.h" +#include "Ip4Config2Impl.h" +#include "Ip4Config2Nv.h" +#include "Ip4NvData.h" + +#define IP4_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '4', 'P') +#define IP4_SERVICE_SIGNATURE SIGNATURE_32 ('I', 'P', '4', 'S') + +// +// The state of IP4 protocol. It starts from UNCONFIGED. if it is +// successfully configured, it goes to CONFIGED. if configure NULL +// is called, it becomes UNCONFIGED again. +// +#define IP4_STATE_UNCONFIGED 0 +#define IP4_STATE_CONFIGED 1 + +// +// The state of IP4 service. It starts from UNSTARTED. It transits +// to STARTED if autoconfigure is started. If default address is +// configured, it becomes CONFIGED. and if partly destroyed, it goes +// to DESTROY. +// +#define IP4_SERVICE_UNSTARTED 0 +#define IP4_SERVICE_STARTED 1 +#define IP4_SERVICE_CONFIGED 2 +#define IP4_SERVICE_DESTROY 3 + + +/// +/// IP4_TXTOKEN_WRAP wraps the upper layer's transmit token. +/// The user's data is kept in the Packet. When fragment is +/// needed, each fragment of the Packet has a reference to the +/// Packet, no data is actually copied. The Packet will be +/// released when all the fragments of it have been recycled by +/// MNP. Upon then, the IP4_TXTOKEN_WRAP will be released, and +/// user's event signalled. +/// +typedef struct { + IP4_PROTOCOL *IpInstance; + EFI_IP4_COMPLETION_TOKEN *Token; + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; + BOOLEAN Sent; + INTN Life; +} IP4_TXTOKEN_WRAP; + +/// +/// IP4_IPSEC_WRAP wraps the packet received from MNP layer. The packet +/// will be released after it has been processed by the receiver. Upon then, +/// the IP4_IPSEC_WRAP will be released, and the IpSecRecycleSignal will be signaled +/// to notice IPsec to free the resources. +/// +typedef struct { + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; +} IP4_IPSEC_WRAP; + +/// +/// IP4_RXDATA_WRAP wraps the data IP4 child delivers to the +/// upper layers. The received packet is kept in the Packet. +/// The Packet itself may be constructured from some fragments. +/// All the fragments of the Packet is organized by a +/// IP4_ASSEMBLE_ENTRY structure. If the Packet is recycled by +/// the upper layer, the assemble entry and its associated +/// fragments will be freed at last. +/// +typedef struct { + LIST_ENTRY Link; + IP4_PROTOCOL *IpInstance; + NET_BUF *Packet; + EFI_IP4_RECEIVE_DATA RxData; +} IP4_RXDATA_WRAP; + + +struct _IP4_PROTOCOL { + UINT32 Signature; + + EFI_IP4_PROTOCOL Ip4Proto; + EFI_HANDLE Handle; + INTN State; + + BOOLEAN InDestroy; + + IP4_SERVICE *Service; + LIST_ENTRY Link; // Link to all the IP protocol from the service + + // + // User's transmit/receive tokens, and received/delivered packets + // + NET_MAP RxTokens; + NET_MAP TxTokens; // map between (User's Token, IP4_TXTOKE_WRAP) + LIST_ENTRY Received; // Received but not delivered packet + LIST_ENTRY Delivered; // Delivered and to be recycled packets + EFI_LOCK RecycleLock; + + // + // Instance's address and route tables. There are two route tables. + // RouteTable is used by the IP4 driver to route packet. EfiRouteTable + // is used to communicate the current route info to the upper layer. + // + IP4_INTERFACE *Interface; + LIST_ENTRY AddrLink; // Ip instances with the same IP address. + IP4_ROUTE_TABLE *RouteTable; + + EFI_IP4_ROUTE_TABLE *EfiRouteTable; + UINT32 EfiRouteCount; + + // + // IGMP data for this instance + // + IP4_ADDR *Groups; // stored in network byte order + UINT32 GroupCount; + + EFI_IP4_CONFIG_DATA ConfigData; + +}; + +struct _IP4_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + INTN State; + + // + // List of all the IP instances and interfaces, and default + // interface and route table and caches. + // + UINTN NumChildren; + LIST_ENTRY Children; + + LIST_ENTRY Interfaces; + + IP4_INTERFACE *DefaultInterface; + IP4_ROUTE_TABLE *DefaultRouteTable; + + // + // Ip reassemble utilities, and IGMP data + // + IP4_ASSEMBLE_TABLE Assemble; + IGMP_SERVICE_DATA IgmpCtrl; + + // + // Low level protocol used by this service instance + // + EFI_HANDLE Image; + EFI_HANDLE Controller; + + EFI_HANDLE MnpChildHandle; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + EFI_MANAGED_NETWORK_CONFIG_DATA MnpConfigData; + EFI_SIMPLE_NETWORK_MODE SnpMode; + + EFI_EVENT Timer; + EFI_EVENT ReconfigCheckTimer; + EFI_EVENT ReconfigEvent; + + BOOLEAN Reconfig; + + // + // Underlying media present status. + // + BOOLEAN MediaPresent; + + // + // IPv4 Configuration II Protocol instance + // + IP4_CONFIG2_INSTANCE Ip4Config2Instance; + + CHAR16 *MacString; + + UINT32 MaxPacketSize; + UINT32 OldMaxPacketSize; ///< The MTU before IPsec enable. +}; + +#define IP4_INSTANCE_FROM_PROTOCOL(Ip4) \ + CR ((Ip4), IP4_PROTOCOL, Ip4Proto, IP4_PROTOCOL_SIGNATURE) + +#define IP4_SERVICE_FROM_PROTOCOL(Sb) \ + CR ((Sb), IP4_SERVICE, ServiceBinding, IP4_SERVICE_SIGNATURE) + +#define IP4_SERVICE_FROM_CONFIG2_INSTANCE(This) \ + CR (This, IP4_SERVICE, Ip4Config2Instance, IP4_SERVICE_SIGNATURE) + + +#define IP4_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured) + +extern EFI_IP4_PROTOCOL mEfiIp4ProtocolTemplete; + +/** + Config the MNP parameter used by IP. The IP driver use one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. And it will enable/disable + the promiscous receive according to whether there is IP child + enable that or not. If Force is FALSE, it will iterate through + all the IP children to check whether the promiscuous receive + setting has been changed. If it hasn't been changed, it won't + reconfigure the MNP. If Force is TRUE, the MNP is configured no + matter whether that is changed or not. + + @param[in] IpSb The IP4 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP is successfully configured/reconfigured. + @retval Others Configuration failed. + +**/ +EFI_STATUS +Ip4ServiceConfigMnp ( + IN IP4_SERVICE *IpSb, + IN BOOLEAN Force + ); + +/** + Initialize the IP4_PROTOCOL structure to the unconfigured states. + + @param IpSb The IP4 service instance. + @param IpInstance The IP4 child instance. + +**/ +VOID +Ip4InitProtocol ( + IN IP4_SERVICE *IpSb, + IN OUT IP4_PROTOCOL *IpInstance + ); + +/** + Clean up the IP4 child, release all the resources used by it. + + @param[in] IpInstance The IP4 child to clean up. + + @retval EFI_SUCCESS The IP4 child is cleaned up. + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip4CleanProtocol ( + IN IP4_PROTOCOL *IpInstance + ); + +/** + Cancel the user's receive/transmit request. + + @param[in] IpInstance The IP4 child. + @param[in] Token The token to cancel. If NULL, all token will be + cancelled. + + @retval EFI_SUCCESS The token is cancelled. + @retval EFI_NOT_FOUND The token isn't found on either the + transmit/receive queue. + @retval EFI_DEVICE_ERROR Not all token is cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip4Cancel ( + IN IP4_PROTOCOL *IpInstance, + IN EFI_IP4_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Change the IP4 child's multicast setting. The caller + should make sure that the parameters is valid. + + @param[in] IpInstance The IP4 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it + @param[in] GroupAddress The target group address + + @retval EFI_ALREADY_STARTED Want to join the group, but already a member of it + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_DEVICE_ERROR Failed to set the group configuration + @retval EFI_SUCCESS Successfully updated the group setting. + @retval EFI_NOT_FOUND Try to leave the group which it isn't a member. + +**/ +EFI_STATUS +Ip4Groups ( + IN IP4_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv4_ADDRESS *GroupAddress OPTIONAL + ); + +/** + This heart beat timer of IP4 service instance times out all of its IP4 children's + received-but-not-delivered and transmitted-but-not-recycle packets, and provides + time input for its IGMP protocol. + + @param[in] Event The IP4 service instance's heart beat timer. + @param[in] Context The IP4 service instance. + +**/ +VOID +EFIAPI +Ip4TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + This dedicated timer is used to poll underlying network media status. In case + of cable swap or wireless network switch, a new round auto configuration will + be initiated. The timer will signal the IP4 to run DHCP configuration again. + IP4 driver will free old IP address related resource, such as route table and + Interface, then initiate a DHCP process to acquire new IP, eventually create + route table for new IP address. + + @param[in] Event The IP4 service instance's heart beat timer. + @param[in] Context The IP4 service instance. + +**/ +VOID +EFIAPI +Ip4TimerReconfigChecking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Decrease the life of the transmitted packets. If it is + decreased to zero, cancel the packet. This function is + called by Ip4PacketTimerTicking which time out both the + received-but-not-delivered and transmitted-but-not-recycle + packets. + + @param[in] Map The IP4 child's transmit map. + @param[in] Item Current transmitted packet. + @param[in] Context Not used. + + @retval EFI_SUCCESS Always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +Ip4SentPacketTicking ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ); + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although it seems this function is pretty simple, + there are some subtle things. + When user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP4_TXTOKEN_WRAP and the data + is wrapped in an net buffer. the net buffer's Free function is + set to Ip4FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip4Output for + transmission. If something error happened before that, the buffer + is freed, which in turn will free the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be fired because we are still in the EfiIp4Transmit. If + the buffer has been sent by Ip4Output, it should be removed from + the TxToken map and user's event signaled. The token wrap and buffer + are bound together. Check the comments in Ip4Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip4FreeTxToken ( + IN VOID *Context + ); + +extern EFI_IPSEC2_PROTOCOL *mIpSec; +extern BOOLEAN mIpSec2Installed; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Input.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Input.c new file mode 100644 index 00000000..497001c1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Input.c @@ -0,0 +1,1625 @@ +/** @file + IP4 input process. + +Copyright (c) 2005 - 2020, Intel Corporation. All rights reserved.<BR> +(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> + +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + + +/** + Create an empty assemble entry for the packet identified by + (Dst, Src, Id, Protocol). The default life for the packet is + 120 seconds. + + @param[in] Dst The destination address + @param[in] Src The source address + @param[in] Id The ID field in IP header + @param[in] Protocol The protocol field in IP header + + @return NULL if failed to allocate memory for the entry, otherwise + the point to just created reassemble entry. + +**/ +IP4_ASSEMBLE_ENTRY * +Ip4CreateAssembleEntry ( + IN IP4_ADDR Dst, + IN IP4_ADDR Src, + IN UINT16 Id, + IN UINT8 Protocol + ) +{ + + IP4_ASSEMBLE_ENTRY *Assemble; + + Assemble = AllocatePool (sizeof (IP4_ASSEMBLE_ENTRY)); + + if (Assemble == NULL) { + return NULL; + } + + InitializeListHead (&Assemble->Link); + InitializeListHead (&Assemble->Fragments); + + Assemble->Dst = Dst; + Assemble->Src = Src; + Assemble->Id = Id; + Assemble->Protocol = Protocol; + Assemble->TotalLen = 0; + Assemble->CurLen = 0; + Assemble->Head = NULL; + Assemble->Info = NULL; + Assemble->Life = IP4_FRAGMENT_LIFE; + + return Assemble; +} + + +/** + Release all the fragments of a packet, then free the assemble entry. + + @param[in] Assemble The assemble entry to free + +**/ +VOID +Ip4FreeAssembleEntry ( + IN IP4_ASSEMBLE_ENTRY *Assemble + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + NET_BUF *Fragment; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) { + Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + + RemoveEntryList (Entry); + NetbufFree (Fragment); + } + + FreePool (Assemble); +} + + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP4 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip4InitAssembleTable ( + IN OUT IP4_ASSEMBLE_TABLE *Table + ) +{ + UINT32 Index; + + for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { + InitializeListHead (&Table->Bucket[Index]); + } +} + + +/** + Clean up the assemble table: remove all the fragments + and assemble entries. + + @param[in] Table The assemble table to clean up + +**/ +VOID +Ip4CleanAssembleTable ( + IN IP4_ASSEMBLE_TABLE *Table + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ASSEMBLE_ENTRY *Assemble; + UINT32 Index; + + for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link); + + RemoveEntryList (Entry); + Ip4FreeAssembleEntry (Assemble); + } + } +} + + +/** + Trim the packet to fit in [Start, End), and update the per + packet information. + + @param Packet Packet to trim + @param Start The sequence of the first byte to fit in + @param End One beyond the sequence of last byte to fit in. + +**/ +VOID +Ip4TrimPacket ( + IN OUT NET_BUF *Packet, + IN INTN Start, + IN INTN End + ) +{ + IP4_CLIP_INFO *Info; + INTN Len; + + Info = IP4_GET_CLIP_INFO (Packet); + + ASSERT (Info->Start + Info->Length == Info->End); + ASSERT ((Info->Start < End) && (Start < Info->End)); + + if (Info->Start < Start) { + Len = Start - Info->Start; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD); + Info->Start = Start; + Info->Length -= Len; + } + + if (End < Info->End) { + Len = End - Info->End; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL); + Info->End = End; + Info->Length -= Len; + } +} + + +/** + Release all the fragments of the packet. This is the callback for + the assembled packet's OnFree. It will free the assemble entry, + which in turn will free all the fragments of the packet. + + @param[in] Arg The assemble entry to free + +**/ +VOID +EFIAPI +Ip4OnFreeFragments ( + IN VOID *Arg + ) +{ + Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *) Arg); +} + + +/** + Reassemble the IP fragments. If all the fragments of the packet + have been received, it will wrap the packet in a net buffer then + return it to caller. If the packet can't be assembled, NULL is + return. + + @param Table The assemble table used. New assemble entry will be created + if the Packet is from a new chain of fragments. + @param Packet The fragment to assemble. It might be freed if the fragment + can't be re-assembled. + + @return NULL if the packet can't be reassemble. The point to just assembled + packet if all the fragments of the packet have arrived. + +**/ +NET_BUF * +Ip4Reassemble ( + IN OUT IP4_ASSEMBLE_TABLE *Table, + IN OUT NET_BUF *Packet + ) +{ + IP4_HEAD *IpHead; + IP4_CLIP_INFO *This; + IP4_CLIP_INFO *Node; + IP4_ASSEMBLE_ENTRY *Assemble; + LIST_ENTRY *Head; + LIST_ENTRY *Prev; + LIST_ENTRY *Cur; + NET_BUF *Fragment; + NET_BUF *NewPacket; + INTN Index; + + IpHead = Packet->Ip.Ip4; + This = IP4_GET_CLIP_INFO (Packet); + + ASSERT (IpHead != NULL); + + // + // First: find the related assemble entry + // + Assemble = NULL; + Index = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol); + + NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link); + + if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) && + (Assemble->Id == IpHead->Id) && (Assemble->Protocol == IpHead->Protocol)) { + break; + } + } + + // + // Create a new assemble entry if no assemble entry is related to this packet + // + if (Cur == &Table->Bucket[Index]) { + Assemble = Ip4CreateAssembleEntry ( + IpHead->Dst, + IpHead->Src, + IpHead->Id, + IpHead->Protocol + ); + + if (Assemble == NULL) { + goto DROP; + } + + InsertHeadList (&Table->Bucket[Index], &Assemble->Link); + } + // + // Assemble shouldn't be NULL here + // + ASSERT (Assemble != NULL); + + // + // Find the point to insert the packet: before the first + // fragment with THIS.Start < CUR.Start. the previous one + // has PREV.Start <= THIS.Start < CUR.Start. + // + Head = &Assemble->Fragments; + + NET_LIST_FOR_EACH (Cur, Head) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (This->Start < IP4_GET_CLIP_INFO (Fragment)->Start) { + break; + } + } + + // + // Check whether the current fragment overlaps with the previous one. + // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to + // check whether THIS.Start < PREV.End for overlap. If two fragments + // overlaps, trim the overlapped part off THIS fragment. + // + if ((Prev = Cur->BackLink) != Head) { + Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + Node = IP4_GET_CLIP_INFO (Fragment); + + if (This->Start < Node->End) { + if (This->End <= Node->End) { + NetbufFree (Packet); + return NULL; + } + + Ip4TrimPacket (Packet, Node->End, This->End); + } + } + + // + // Insert the fragment into the packet. The fragment may be removed + // from the list by the following checks. + // + NetListInsertBefore (Cur, &Packet->List); + + // + // Check the packets after the insert point. It holds that: + // THIS.Start <= NODE.Start < NODE.End. The equality holds + // if PREV and NEXT are continuous. THIS fragment may fill + // several holes. Remove the completely overlapped fragments + // + while (Cur != Head) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Node = IP4_GET_CLIP_INFO (Fragment); + + // + // Remove fragments completely overlapped by this fragment + // + if (Node->End <= This->End) { + Cur = Cur->ForwardLink; + + RemoveEntryList (&Fragment->List); + Assemble->CurLen -= Node->Length; + + NetbufFree (Fragment); + continue; + } + + // + // The conditions are: THIS.Start <= NODE.Start, and THIS.End < + // NODE.End. Two fragments overlaps if NODE.Start < THIS.End. + // If two fragments start at the same offset, remove THIS fragment + // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)). + // + if (Node->Start < This->End) { + if (This->Start == Node->Start) { + RemoveEntryList (&Packet->List); + goto DROP; + } + + Ip4TrimPacket (Packet, This->Start, Node->Start); + } + + break; + } + + // + // Update the assemble info: increase the current length. If it is + // the frist fragment, update the packet's IP head and per packet + // info. If it is the last fragment, update the total length. + // + Assemble->CurLen += This->Length; + + if (This->Start == 0) { + // + // Once the first fragment is enqueued, it can't be removed + // from the fragment list. So, Assemble->Head always point + // to valid memory area. + // + ASSERT (Assemble->Head == NULL); + + Assemble->Head = IpHead; + Assemble->Info = IP4_GET_CLIP_INFO (Packet); + } + + // + // Don't update the length more than once. + // + if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (Assemble->TotalLen == 0)) { + Assemble->TotalLen = This->End; + } + + // + // Deliver the whole packet if all the fragments received. + // All fragments received if: + // 1. received the last one, so, the total length is know + // 2. received all the data. If the last fragment on the + // queue ends at the total length, all data is received. + // + if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) { + + RemoveEntryList (&Assemble->Link); + + // + // If the packet is properly formatted, the last fragment's End + // equals to the packet's total length. Otherwise, the packet + // is a fake, drop it now. + // + Fragment = NET_LIST_USER_STRUCT (Head->BackLink, NET_BUF, List); + + if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) { + Ip4FreeAssembleEntry (Assemble); + return NULL; + } + + // + // Wrap the packet in a net buffer then deliver it up + // + NewPacket = NetbufFromBufList ( + &Assemble->Fragments, + 0, + 0, + Ip4OnFreeFragments, + Assemble + ); + + if (NewPacket == NULL) { + Ip4FreeAssembleEntry (Assemble); + return NULL; + } + + NewPacket->Ip.Ip4 = Assemble->Head; + + ASSERT (Assemble->Info != NULL); + + CopyMem ( + IP4_GET_CLIP_INFO (NewPacket), + Assemble->Info, + sizeof (*IP4_GET_CLIP_INFO (NewPacket)) + ); + + return NewPacket; + } + + return NULL; + +DROP: + NetbufFree (Packet); + return NULL; +} + +/** + The callback function for the net buffer which wraps the packet processed by + IPsec. It releases the wrap packet and also signals IPsec to free the resources. + + @param[in] Arg The wrap context + +**/ +VOID +EFIAPI +Ip4IpSecFree ( + IN VOID *Arg + ) +{ + IP4_IPSEC_WRAP *Wrap; + + Wrap = (IP4_IPSEC_WRAP *) Arg; + + if (Wrap->IpSecRecycleSignal != NULL) { + gBS->SignalEvent (Wrap->IpSecRecycleSignal); + } + + NetbufFree (Wrap->Packet); + + FreePool (Wrap); + + return; +} + +/** + The work function to locate IPsec protocol to process the inbound or + outbound IP packets. The process routine handls the packet with following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP4 service instance. + @param[in, out] Head The caller supplied IP4 header. + @param[in, out] Netbuf The IP4 packet to be processed by IPsec. + @param[in, out] Options The caller supplied options. + @param[in, out] OptionsLen The length of the option. + @param[in] Direction The directionality in an SPD entry, + EfiIPsecInBound or EfiIPsecOutBound. + @param[in] Context The token's wrap. + + @retval EFI_SUCCESS The IPsec protocol is not available or disabled. + @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the + number of input data blocks when build a fragment table. + +**/ +EFI_STATUS +Ip4IpSecProcessPacket ( + IN IP4_SERVICE *IpSb, + IN OUT IP4_HEAD **Head, + IN OUT NET_BUF **Netbuf, + IN OUT UINT8 **Options, + IN OUT UINT32 *OptionsLen, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN VOID *Context + ) +{ + NET_FRAGMENT *FragmentTable; + NET_FRAGMENT *OriginalFragmentTable; + UINT32 FragmentCount; + UINT32 OriginalFragmentCount; + EFI_EVENT RecycleEvent; + NET_BUF *Packet; + IP4_TXTOKEN_WRAP *TxWrap; + IP4_IPSEC_WRAP *IpSecWrap; + EFI_STATUS Status; + IP4_HEAD ZeroHead; + + Status = EFI_SUCCESS; + + if (!mIpSec2Installed) { + goto ON_EXIT; + } + ASSERT (mIpSec != NULL); + + Packet = *Netbuf; + RecycleEvent = NULL; + IpSecWrap = NULL; + FragmentTable = NULL; + TxWrap = (IP4_TXTOKEN_WRAP *) Context; + FragmentCount = Packet->BlockOpNum; + + ZeroMem (&ZeroHead, sizeof (IP4_HEAD)); + + // + // Check whether the IPsec enable variable is set. + // + if (mIpSec->DisabledFlag) { + // + // If IPsec is disabled, restore the original MTU + // + IpSb->MaxPacketSize = IpSb->OldMaxPacketSize; + goto ON_EXIT; + } else { + // + // If IPsec is enabled, use the MTU which reduce the IPsec header length. + // + IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP4_MAX_IPSEC_HEADLEN; + } + + // + // Rebuild fragment table from netbuf to ease IPsec process. + // + FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT)); + + if (FragmentTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount); + + // + // Record the original FragmentTable and count. + // + OriginalFragmentTable = FragmentTable; + OriginalFragmentCount = FragmentCount; + + if (EFI_ERROR (Status)) { + FreePool (FragmentTable); + goto ON_EXIT; + } + + // + // Convert host byte order to network byte order + // + Ip4NtohHead (*Head); + + Status = mIpSec->ProcessExt ( + mIpSec, + IpSb->Controller, + IP_VERSION_4, + (VOID *) (*Head), + &(*Head)->Protocol, + (VOID **) Options, + OptionsLen, + (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable), + &FragmentCount, + Direction, + &RecycleEvent + ); + // + // Convert back to host byte order + // + Ip4NtohHead (*Head); + + if (EFI_ERROR (Status)) { + FreePool (OriginalFragmentTable); + goto ON_EXIT; + } + + if (OriginalFragmentTable == FragmentTable && OriginalFragmentCount == FragmentCount) { + // + // For ByPass Packet + // + FreePool (FragmentTable); + goto ON_EXIT; + } else { + // + // Free the FragmentTable which allocated before calling the IPsec. + // + FreePool (OriginalFragmentTable); + } + + if (Direction == EfiIPsecOutBound && TxWrap != NULL) { + + TxWrap->IpSecRecycleSignal = RecycleEvent; + TxWrap->Packet = NetbufFromExt ( + FragmentTable, + FragmentCount, + IP4_MAX_HEADLEN, + 0, + Ip4FreeTxToken, + TxWrap + ); + if (TxWrap->Packet == NULL) { + // + // Recover the TxWrap->Packet, if meet a error, and the caller will free + // the TxWrap. + // + TxWrap->Packet = *Netbuf; + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Free original Netbuf. + // + NetIpSecNetbufFree (*Netbuf); + *Netbuf = TxWrap->Packet; + + } else { + + IpSecWrap = AllocateZeroPool (sizeof (IP4_IPSEC_WRAP)); + + if (IpSecWrap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + gBS->SignalEvent (RecycleEvent); + goto ON_EXIT; + } + + IpSecWrap->IpSecRecycleSignal = RecycleEvent; + IpSecWrap->Packet = Packet; + Packet = NetbufFromExt ( + FragmentTable, + FragmentCount, + IP4_MAX_HEADLEN, + 0, + Ip4IpSecFree, + IpSecWrap + ); + + if (Packet == NULL) { + Packet = IpSecWrap->Packet; + gBS->SignalEvent (RecycleEvent); + FreePool (IpSecWrap); + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + if (Direction == EfiIPsecInBound && 0 != CompareMem (*Head, &ZeroHead, sizeof (IP4_HEAD))) { + Ip4PrependHead (Packet, *Head, *Options, *OptionsLen); + Ip4NtohHead (Packet->Ip.Ip4); + NetbufTrim (Packet, ((*Head)->HeadLen << 2), TRUE); + + CopyMem ( + IP4_GET_CLIP_INFO (Packet), + IP4_GET_CLIP_INFO (IpSecWrap->Packet), + sizeof (IP4_CLIP_INFO) + ); + } + *Netbuf = Packet; + } + +ON_EXIT: + return Status; +} + +/** + Pre-process the IPv4 packet. First validates the IPv4 packet, and + then reassembles packet if it is necessary. + + @param[in] IpSb Pointer to IP4_SERVICE. + @param[in, out] Packet Pointer to the Packet to be processed. + @param[in] Head Pointer to the IP4_HEAD. + @param[in] Option Pointer to a buffer which contains the IPv4 option. + @param[in] OptionLen The length of Option in bytes. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + + @retval EFI_SUCCESS The received packet is in well form. + @retval EFI_INVALID_PARAMETER The received packet is malformed. + +**/ +EFI_STATUS +Ip4PreProcessPacket ( + IN IP4_SERVICE *IpSb, + IN OUT NET_BUF **Packet, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptionLen, + IN UINT32 Flag + ) +{ + IP4_CLIP_INFO *Info; + UINT32 HeadLen; + UINT32 TotalLen; + UINT16 Checksum; + + // + // Check if the IP4 header is correctly formatted. + // + HeadLen = (Head->HeadLen << 2); + TotalLen = NTOHS (Head->TotalLen); + + // + // Mnp may deliver frame trailer sequence up, trim it off. + // + if (TotalLen < (*Packet)->TotalSize) { + NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE); + } + + if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) || + (TotalLen < HeadLen) || (TotalLen != (*Packet)->TotalSize)) { + return EFI_INVALID_PARAMETER; + } + + // + // Some OS may send IP packets without checksum. + // + Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Head, HeadLen)); + + if ((Head->Checksum != 0) && (Checksum != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the IP header to host byte order, then get the per packet info. + // + (*Packet)->Ip.Ip4 = Ip4NtohHead (Head); + + Info = IP4_GET_CLIP_INFO (*Packet); + Info->LinkFlag = Flag; + Info->CastType = Ip4GetHostCast (IpSb, Head->Dst, Head->Src); + Info->Start = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3; + Info->Length = Head->TotalLen - HeadLen; + Info->End = Info->Start + Info->Length; + Info->Status = EFI_SUCCESS; + + // + // The packet is destinated to us if the CastType is non-zero. + // + if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) { + return EFI_INVALID_PARAMETER; + } + + // + // Validate the options. Don't call the Ip4OptionIsValid if + // there is no option to save some CPU process. + // + + if ((OptionLen > 0) && !Ip4OptionIsValid (Option, OptionLen, TRUE)) { + return EFI_INVALID_PARAMETER; + } + + // + // Trim the head off, after this point, the packet is headless, + // and Packet->TotalLen == Info->Length. + // + NetbufTrim (*Packet, HeadLen, TRUE); + + // + // Reassemble the packet if this is a fragment. The packet is a + // fragment if its head has MF (more fragment) set, or it starts + // at non-zero byte. + // + if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) || (Info->Start != 0)) { + // + // Drop the fragment if DF is set but it is fragmented. Gateway + // need to send a type 4 destination unreache ICMP message here. + // + if ((Head->Fragment & IP4_HEAD_DF_MASK) != 0) { + return EFI_INVALID_PARAMETER; + } + + // + // The length of all but the last fragments is in the unit of 8 bytes. + // + if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) && (Info->Length % 8 != 0)) { + return EFI_INVALID_PARAMETER; + } + + *Packet = Ip4Reassemble (&IpSb->Assemble, *Packet); + + // + // Packet assembly isn't complete, start receive more packet. + // + if (*Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + } + + return EFI_SUCCESS; +} + +/** + This function checks the IPv4 packet length. + + @param[in] Packet Pointer to the IPv4 Packet to be checked. + + @retval TRUE The input IPv4 packet length is valid. + @retval FALSE The input IPv4 packet length is invalid. + +**/ +BOOLEAN +Ip4IsValidPacketLength ( + IN NET_BUF *Packet + ) +{ + // + // Check the IP4 packet length. + // + if (Packet->TotalSize < IP4_MIN_HEADLEN) { + return FALSE; + } + + return TRUE; +} + +/** + The IP4 input routine. It is called by the IP4_INTERFACE when a + IP4 fragment is received from MNP. + + @param[in] Ip4Instance The IP4 child that request the receive, most like + it is NULL. + @param[in] Packet The IP4 packet received. + @param[in] IoStatus The return status of receive request. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[in] Context The IP4 service instance that own the MNP. + +**/ +VOID +Ip4AccpetFrame ( + IN IP4_PROTOCOL *Ip4Instance, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP4_SERVICE *IpSb; + IP4_HEAD *Head; + EFI_STATUS Status; + IP4_HEAD ZeroHead; + UINT8 *Option; + UINT32 OptionLen; + + IpSb = (IP4_SERVICE *) Context; + Option = NULL; + + if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTROY)) { + goto DROP; + } + + if (!Ip4IsValidPacketLength (Packet)) { + goto RESTART; + } + + Head = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Head != NULL); + OptionLen = (Head->HeadLen << 2) - IP4_MIN_HEADLEN; + if (OptionLen > 0) { + Option = (UINT8 *) (Head + 1); + } + + // + // Validate packet format and reassemble packet if it is necessary. + // + Status = Ip4PreProcessPacket ( + IpSb, + &Packet, + Head, + Option, + OptionLen, + Flag + ); + + if (EFI_ERROR (Status)) { + goto RESTART; + } + + // + // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer, + // and no need consider any other ahead ext headers. + // + Status = Ip4IpSecProcessPacket ( + IpSb, + &Head, + &Packet, + &Option, + &OptionLen, + EfiIPsecInBound, + NULL + ); + + if (EFI_ERROR (Status)) { + goto RESTART; + } + + // + // If the packet is protected by tunnel mode, parse the inner Ip Packet. + // + ZeroMem (&ZeroHead, sizeof (IP4_HEAD)); + if (0 == CompareMem (Head, &ZeroHead, sizeof (IP4_HEAD))) { + // Packet may have been changed. Head, HeadLen, TotalLen, and + // info must be reloaded before use. The ownership of the packet + // is transferred to the packet process logic. + // + if (!Ip4IsValidPacketLength (Packet)) { + goto RESTART; + } + + Head = (IP4_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Head != NULL); + Status = Ip4PreProcessPacket ( + IpSb, + &Packet, + Head, + Option, + OptionLen, + Flag + ); + if (EFI_ERROR (Status)) { + goto RESTART; + } + } + + ASSERT (Packet != NULL); + Head = Packet->Ip.Ip4; + IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS; + + switch (Head->Protocol) { + case EFI_IP_PROTO_ICMP: + Ip4IcmpHandle (IpSb, Head, Packet); + break; + + case IP4_PROTO_IGMP: + Ip4IgmpHandle (IpSb, Head, Packet); + break; + + default: + Ip4Demultiplex (IpSb, Head, Packet, Option, OptionLen); + } + + Packet = NULL; + + // + // Dispatch the DPCs queued by the NotifyFunction of the rx token's events + // which are signaled with received data. + // + DispatchDpc (); + +RESTART: + Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb); + +DROP: + if (Packet != NULL) { + NetbufFree (Packet); + } + + return ; +} + + +/** + Check whether this IP child accepts the packet. + + @param[in] IpInstance The IP child to check + @param[in] Head The IP header of the packet + @param[in] Packet The data of the packet + + @retval TRUE If the child wants to receive the packet. + @retval FALSE Otherwise. + +**/ +BOOLEAN +Ip4InstanceFrameAcceptable ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_ICMP_ERROR_HEAD Icmp; + EFI_IP4_CONFIG_DATA *Config; + IP4_CLIP_INFO *Info; + UINT16 Proto; + UINT32 Index; + + Config = &IpInstance->ConfigData; + + // + // Dirty trick for the Tiano UEFI network stack implementation. If + // ReceiveTimeout == -1, the receive of the packet for this instance + // is disabled. The UEFI spec don't have such capability. We add + // this to improve the performance because IP will make a copy of + // the received packet for each accepting instance. Some IP instances + // used by UDP/TCP only send packets, they don't wants to receive. + // + if (Config->ReceiveTimeout == (UINT32)(-1)) { + return FALSE; + } + + if (Config->AcceptPromiscuous) { + return TRUE; + } + + // + // Use protocol from the IP header embedded in the ICMP error + // message to filter, instead of ICMP itself. ICMP handle will + // call Ip4Demultiplex to deliver ICMP errors. + // + Proto = Head->Protocol; + + if ((Proto == EFI_IP_PROTO_ICMP) && (!Config->AcceptAnyProtocol) && (Proto != Config->DefaultProtocol)) { + NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *) &Icmp.Head); + + if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) { + if (!Config->AcceptIcmpErrors) { + return FALSE; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Proto = Icmp.IpHead.Protocol; + } + } + + // + // Match the protocol + // + if (!Config->AcceptAnyProtocol && (Proto != Config->DefaultProtocol)) { + return FALSE; + } + + // + // Check for broadcast, the caller has computed the packet's + // cast type for this child's interface. + // + Info = IP4_GET_CLIP_INFO (Packet); + + if (IP4_IS_BROADCAST (Info->CastType)) { + return Config->AcceptBroadcast; + } + + // + // If it is a multicast packet, check whether we are in the group. + // + if (Info->CastType == IP4_MULTICAST) { + // + // Receive the multicast if the instance wants to receive all packets. + // + if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) { + return TRUE; + } + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (IpInstance->Groups[Index] == HTONL (Head->Dst)) { + break; + } + } + + return (BOOLEAN)(Index < IpInstance->GroupCount); + } + + return TRUE; +} + + +/** + Enqueue a shared copy of the packet to the IP4 child if the + packet is acceptable to it. Here the data of the packet is + shared, but the net buffer isn't. + + @param[in] IpInstance The IP4 child to enqueue the packet to + @param[in] Head The IP header of the received packet + @param[in] Packet The data of the received packet + + @retval EFI_NOT_STARTED The IP child hasn't been configured. + @retval EFI_INVALID_PARAMETER The child doesn't want to receive the packet + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resource + @retval EFI_SUCCESS A shared copy the packet is enqueued to the child. + +**/ +EFI_STATUS +Ip4InstanceEnquePacket ( + IN IP4_PROTOCOL *IpInstance, + IN IP4_HEAD *Head, + IN NET_BUF *Packet + ) +{ + IP4_CLIP_INFO *Info; + NET_BUF *Clone; + + // + // Check whether the packet is acceptable to this instance. + // + if (IpInstance->State != IP4_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (!Ip4InstanceFrameAcceptable (IpInstance, Head, Packet)) { + return EFI_INVALID_PARAMETER; + } + + // + // Enqueue a shared copy of the packet. + // + Clone = NetbufClone (Packet); + + if (Clone == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Set the receive time out for the assembled packet. If it expires, + // packet will be removed from the queue. + // + Info = IP4_GET_CLIP_INFO (Clone); + Info->Life = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout); + + InsertTailList (&IpInstance->Received, &Clone->List); + return EFI_SUCCESS; +} + + +/** + The signal handle of IP4's recycle event. It is called back + when the upper layer release the packet. + + @param Event The IP4's recycle event. + @param Context The context of the handle, which is a + IP4_RXDATA_WRAP + +**/ +VOID +EFIAPI +Ip4OnRecyclePacket ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP4_RXDATA_WRAP *Wrap; + + Wrap = (IP4_RXDATA_WRAP *) Context; + + EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock); + RemoveEntryList (&Wrap->Link); + EfiReleaseLock (&Wrap->IpInstance->RecycleLock); + + ASSERT (!NET_BUF_SHARED (Wrap->Packet)); + NetbufFree (Wrap->Packet); + + gBS->CloseEvent (Wrap->RxData.RecycleSignal); + FreePool (Wrap); +} + + +/** + Wrap the received packet to a IP4_RXDATA_WRAP, which will be + delivered to the upper layer. Each IP4 child that accepts the + packet will get a not-shared copy of the packet which is wrapped + in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed + to the upper layer. Upper layer will signal the recycle event in + it when it is done with the packet. + + @param[in] IpInstance The IP4 child to receive the packet. + @param[in] Packet The packet to deliver up. + + @retval Wrap if warp the packet succeed. + @retval NULL failed to wrap the packet . + +**/ +IP4_RXDATA_WRAP * +Ip4WrapRxData ( + IN IP4_PROTOCOL *IpInstance, + IN NET_BUF *Packet + ) +{ + IP4_RXDATA_WRAP *Wrap; + EFI_IP4_RECEIVE_DATA *RxData; + EFI_STATUS Status; + BOOLEAN RawData; + + Wrap = AllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum)); + + if (Wrap == NULL) { + return NULL; + } + + InitializeListHead (&Wrap->Link); + + Wrap->IpInstance = IpInstance; + Wrap->Packet = Packet; + RxData = &Wrap->RxData; + + ZeroMem (RxData, sizeof (EFI_IP4_RECEIVE_DATA)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip4OnRecyclePacket, + Wrap, + &RxData->RecycleSignal + ); + + if (EFI_ERROR (Status)) { + FreePool (Wrap); + return NULL; + } + + ASSERT (Packet->Ip.Ip4 != NULL); + + ASSERT (IpInstance != NULL); + RawData = IpInstance->ConfigData.RawData; + + // + // The application expects a network byte order header. + // + if (!RawData) { + RxData->HeaderLength = (Packet->Ip.Ip4->HeadLen << 2); + RxData->Header = (EFI_IP4_HEADER *) Ip4NtohHead (Packet->Ip.Ip4); + RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN; + RxData->Options = NULL; + + if (RxData->OptionsLength != 0) { + RxData->Options = (VOID *) (RxData->Header + 1); + } + } + + RxData->DataLength = Packet->TotalSize; + + // + // Build the fragment table to be delivered up. + // + RxData->FragmentCount = Packet->BlockOpNum; + NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount); + + return Wrap; +} + + +/** + Deliver the received packets to upper layer if there are both received + requests and enqueued packets. If the enqueued packet is shared, it will + duplicate it to a non-shared packet, release the shared packet, then + deliver the non-shared packet up. + + @param[in] IpInstance The IP child to deliver the packet up. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the + packets. + @retval EFI_SUCCESS All the enqueued packets that can be delivered + are delivered up. + +**/ +EFI_STATUS +Ip4InstanceDeliverPacket ( + IN IP4_PROTOCOL *IpInstance + ) +{ + EFI_IP4_COMPLETION_TOKEN *Token; + IP4_RXDATA_WRAP *Wrap; + NET_BUF *Packet; + NET_BUF *Dup; + UINT8 *Head; + UINT32 HeadLen; + + // + // Deliver a packet if there are both a packet and a receive token. + // + while (!IsListEmpty (&IpInstance->Received) && + !NetMapIsEmpty (&IpInstance->RxTokens)) { + + Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List); + + if (!NET_BUF_SHARED (Packet)) { + // + // If this is the only instance that wants the packet, wrap it up. + // + Wrap = Ip4WrapRxData (IpInstance, Packet); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Packet->List); + + } else { + // + // Create a duplicated packet if this packet is shared + // + if (IpInstance->ConfigData.RawData) { + HeadLen = 0; + } else { + HeadLen = IP4_MAX_HEADLEN; + } + + Dup = NetbufDuplicate (Packet, NULL, HeadLen); + + if (Dup == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (!IpInstance->ConfigData.RawData) { + // + // Copy the IP head over. The packet to deliver up is + // headless. Trim the head off after copy. The IP head + // may be not continuous before the data. + // + Head = NetbufAllocSpace (Dup, IP4_MAX_HEADLEN, NET_BUF_HEAD); + ASSERT (Head != NULL); + + Dup->Ip.Ip4 = (IP4_HEAD *) Head; + + CopyMem (Head, Packet->Ip.Ip4, Packet->Ip.Ip4->HeadLen << 2); + NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE); + } + + Wrap = Ip4WrapRxData (IpInstance, Dup); + + if (Wrap == NULL) { + NetbufFree (Dup); + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Packet->List); + NetbufFree (Packet); + + Packet = Dup; + } + + // + // Insert it into the delivered packet, then get a user's + // receive token, pass the wrapped packet up. + // + EfiAcquireLockOrFail (&IpInstance->RecycleLock); + InsertHeadList (&IpInstance->Delivered, &Wrap->Link); + EfiReleaseLock (&IpInstance->RecycleLock); + + Token = NetMapRemoveHead (&IpInstance->RxTokens, NULL); + Token->Status = IP4_GET_CLIP_INFO (Packet)->Status; + Token->Packet.RxData = &Wrap->RxData; + + gBS->SignalEvent (Token->Event); + } + + return EFI_SUCCESS; +} + + +/** + Enqueue a received packet to all the IP children that share + the same interface. + + @param[in] IpSb The IP4 service instance that receive the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + @param[in] Option Point to the IP4 packet header options. + @param[in] OptionLen Length of the IP4 packet header options. + @param[in] IpIf The interface to enqueue the packet to. + + @return The number of the IP4 children that accepts the packet + +**/ +INTN +Ip4InterfaceEnquePacket ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT32 OptionLen, + IN IP4_INTERFACE *IpIf + ) +{ + IP4_PROTOCOL *IpInstance; + IP4_CLIP_INFO *Info; + LIST_ENTRY *Entry; + INTN Enqueued; + INTN LocalType; + INTN SavedType; + + // + // First, check that the packet is acceptable to this interface + // and find the local cast type for the interface. A packet sent + // to say 192.168.1.1 should NOT be deliver to 10.0.0.1 unless + // promiscuous receiving. + // + LocalType = 0; + Info = IP4_GET_CLIP_INFO (Packet); + + if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) { + // + // If the CastType is multicast, don't need to filter against + // the group address here, Ip4InstanceFrameAcceptable will do + // that later. + // + LocalType = Info->CastType; + + } else { + // + // Check the destination against local IP. If the station + // address is 0.0.0.0, it means receiving all the IP destined + // to local non-zero IP. Otherwise, it is necessary to compare + // the destination to the interface's IP address. + // + if (IpIf->Ip == IP4_ALLZERO_ADDRESS) { + LocalType = IP4_LOCAL_HOST; + + } else { + LocalType = Ip4GetNetCast (Head->Dst, IpIf); + + if ((LocalType == 0) && IpIf->PromiscRecv) { + LocalType = IP4_PROMISCUOUS; + } + } + } + + if (LocalType == 0) { + return 0; + } + + // + // Iterate through the ip instances on the interface, enqueue + // the packet if filter passed. Save the original cast type, + // and pass the local cast type to the IP children on the + // interface. The global cast type will be restored later. + // + SavedType = Info->CastType; + Info->CastType = LocalType; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink); + NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE); + + // + // In RawData mode, add IPv4 headers and options back to packet. + // + if ((IpInstance->ConfigData.RawData) && (Option != NULL) && (OptionLen != 0)){ + Ip4PrependHead (Packet, Head, Option, OptionLen); + } + + if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) { + Enqueued++; + } + } + + Info->CastType = SavedType; + return Enqueued; +} + + +/** + Deliver the packet for each IP4 child on the interface. + + @param[in] IpSb The IP4 service instance that received the packet + @param[in] IpIf The IP4 interface to deliver the packet. + + @retval EFI_SUCCESS It always returns EFI_SUCCESS now + +**/ +EFI_STATUS +Ip4InterfaceDeliverPacket ( + IN IP4_SERVICE *IpSb, + IN IP4_INTERFACE *IpIf + ) +{ + IP4_PROTOCOL *Ip4Instance; + LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink); + Ip4InstanceDeliverPacket (Ip4Instance); + } + + return EFI_SUCCESS; +} + + +/** + Demultiple the packet. the packet delivery is processed in two + passes. The first pass will enqueue a shared copy of the packet + to each IP4 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP4 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet because each IP child needs + its own copy of the packet to make changes. + + @param[in] IpSb The IP4 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + @param[in] Option Point to the IP4 packet header options. + @param[in] OptionLen Length of the IP4 packet header options. + + @retval EFI_NOT_FOUND No IP child accepts the packet. + @retval EFI_SUCCESS The packet is enqueued or delivered to some IP + children. + +**/ +EFI_STATUS +Ip4Demultiplex ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT32 OptionLen + ) +{ + LIST_ENTRY *Entry; + IP4_INTERFACE *IpIf; + INTN Enqueued; + + // + // Two pass delivery: first, enqueue a shared copy of the packet + // to each instance that accept the packet. + // + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured) { + Enqueued += Ip4InterfaceEnquePacket ( + IpSb, + Head, + Packet, + Option, + OptionLen, + IpIf + ); + } + } + + // + // Second: deliver a duplicate of the packet to each instance. + // Release the local reference first, so that the last instance + // getting the packet will not copy the data. + // + NetbufFree (Packet); + + if (Enqueued == 0) { + return EFI_NOT_FOUND; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured) { + Ip4InterfaceDeliverPacket (IpSb, IpIf); + } + } + + return EFI_SUCCESS; +} + + +/** + Timeout the fragment and enqueued packets. + + @param[in] IpSb The IP4 service instance to timeout + +**/ +VOID +Ip4PacketTimerTicking ( + IN IP4_SERVICE *IpSb + ) +{ + LIST_ENTRY *InstanceEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_PROTOCOL *IpInstance; + IP4_ASSEMBLE_ENTRY *Assemble; + NET_BUF *Packet; + IP4_CLIP_INFO *Info; + UINT32 Index; + + // + // First, time out the fragments. The packet's life is counting down + // once the first-arrived fragment was received. + // + for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link); + + if ((Assemble->Life > 0) && (--Assemble->Life == 0)) { + RemoveEntryList (Entry); + Ip4FreeAssembleEntry (Assemble); + } + } + } + + NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_PROTOCOL, Link); + + // + // Second, time out the assembled packets enqueued on each IP child. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) { + Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List); + Info = IP4_GET_CLIP_INFO (Packet); + + if ((Info->Life > 0) && (--Info->Life == 0)) { + RemoveEntryList (Entry); + NetbufFree (Packet); + } + } + + // + // Third: time out the transmitted packets. + // + NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL); + } +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Input.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Input.h new file mode 100644 index 00000000..f5e2306c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Input.h @@ -0,0 +1,246 @@ +/** @file + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_INPUT_H__ +#define __EFI_IP4_INPUT_H__ + +#define IP4_MIN_HEADLEN 20 +#define IP4_MAX_HEADLEN 60 +/// +/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54 +/// +#define IP4_MAX_IPSEC_HEADLEN 54 + +#define IP4_ASSEMLE_HASH_SIZE 31 +#define IP4_FRAGMENT_LIFE 120 +#define IP4_MAX_PACKET_SIZE 65535 + +/// +/// Per packet information for input process. LinkFlag specifies whether +/// the packet is received as Link layer unicast, multicast or broadcast. +/// The CastType is the IP layer cast type, such as IP multicast or unicast. +/// Start, End and Length are staffs used to assemble the packets. Start +/// is the sequence number of the first byte of data in the packet. Length +/// is the number of bytes of data. End = Start + Length, that is, the +/// sequence number of last byte + 1. Each assembled packet has a count down +/// life. If it isn't consumed before Life reaches zero, the packet is released. +/// +typedef struct { + UINTN LinkFlag; + INTN CastType; + INTN Start; + INTN End; + INTN Length; + UINT32 Life; + EFI_STATUS Status; +} IP4_CLIP_INFO; + +/// +/// Structure used to assemble IP packets. +/// +typedef struct { + LIST_ENTRY Link; + + // + // Identity of one IP4 packet. Each fragment of a packet has + // the same (Dst, Src, Id, Protocol). + // + IP4_ADDR Dst; + IP4_ADDR Src; + UINT16 Id; + UINT8 Protocol; + + INTN TotalLen; + INTN CurLen; + LIST_ENTRY Fragments; // List of all the fragments of this packet + + IP4_HEAD *Head; // IP head of the first fragment + IP4_CLIP_INFO *Info; // Per packet info of the first fragment + INTN Life; // Count down life for the packet. +} IP4_ASSEMBLE_ENTRY; + +/// +/// Each Ip service instance has an assemble table to reassemble +/// the packets before delivery to its children. It is organized +/// as hash table. +/// +typedef struct { + LIST_ENTRY Bucket[IP4_ASSEMLE_HASH_SIZE]; +} IP4_ASSEMBLE_TABLE; + +#define IP4_GET_CLIP_INFO(Packet) ((IP4_CLIP_INFO *) ((Packet)->ProtoData)) + +#define IP4_ASSEMBLE_HASH(Dst, Src, Id, Proto) \ + (((Dst) + (Src) + ((Id) << 16) + (Proto)) % IP4_ASSEMLE_HASH_SIZE) + +#define IP4_RXDATA_WRAP_SIZE(NumFrag) \ + (sizeof (IP4_RXDATA_WRAP) + sizeof (EFI_IP4_FRAGMENT_DATA) * ((NumFrag) - 1)) + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP4 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip4InitAssembleTable ( + IN OUT IP4_ASSEMBLE_TABLE *Table + ); + +/** + Clean up the assemble table: remove all the fragments + and assemble entries. + + @param[in] Table The assemble table to clean up + +**/ +VOID +Ip4CleanAssembleTable ( + IN IP4_ASSEMBLE_TABLE *Table + ); + +/** + The IP4 input routine. It is called by the IP4_INTERFACE when a + IP4 fragment is received from MNP. + + @param[in] Ip4Instance The IP4 child that request the receive, most like + it is NULL. + @param[in] Packet The IP4 packet received. + @param[in] IoStatus The return status of receive request. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[in] Context The IP4 service instance that own the MNP. + +**/ +VOID +Ip4AccpetFrame ( + IN IP4_PROTOCOL *Ip4Instance, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ); + +/** + Demultiple the packet. the packet delivery is processed in two + passes. The first pass will enqueue a shared copy of the packet + to each IP4 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP4 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet because each IP child needs + its own copy of the packet to make changes. + + @param[in] IpSb The IP4 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + @param[in] Option Point to the IP4 packet header options. + @param[in] OptionLen Length of the IP4 packet header options. + + @retval EFI_NOT_FOUND No IP child accepts the packet. + @retval EFI_SUCCESS The packet is enqueued or delivered to some IP + children. + +**/ +EFI_STATUS +Ip4Demultiplex ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT32 OptionLen + ); + +/** + Enqueue a received packet to all the IP children that share + the same interface. + + @param[in] IpSb The IP4 service instance that receive the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + @param[in] Option Point to the IP4 packet header options. + @param[in] OptionLen Length of the IP4 packet header options. + @param[in] IpIf The interface to enqueue the packet to. + + @return The number of the IP4 children that accepts the packet + +**/ +INTN +Ip4InterfaceEnquePacket ( + IN IP4_SERVICE *IpSb, + IN IP4_HEAD *Head, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT32 OptionLen, + IN IP4_INTERFACE *IpIf + ); + +/** + Deliver the received packets to upper layer if there are both received + requests and enqueued packets. If the enqueued packet is shared, it will + duplicate it to a non-shared packet, release the shared packet, then + deliver the non-shared packet up. + + @param[in] IpInstance The IP child to deliver the packet up. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to deliver the + packets. + @retval EFI_SUCCESS All the enqueued packets that can be delivered + are delivered up. + +**/ +EFI_STATUS +Ip4InstanceDeliverPacket ( + IN IP4_PROTOCOL *IpInstance + ); + +/** + Timeout the fragment and enqueued packets. + + @param[in] IpSb The IP4 service instance to timeout + +**/ +VOID +Ip4PacketTimerTicking ( + IN IP4_SERVICE *IpSb + ); + +/** + The work function to locate IPsec protocol to process the inbound or + outbound IP packets. The process routine handls the packet with following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP4 service instance. + @param[in, out] Head The caller supplied IP4 header. + @param[in, out] Netbuf The IP4 packet to be processed by IPsec. + @param[in, out] Options The caller supplied options. + @param[in, out] OptionsLen The length of the option. + @param[in] Direction The directionality in an SPD entry, + EfiIPsecInBound or EfiIPsecOutBound. + @param[in] Context The token's wrap. + + @retval EFI_SUCCESS The IPsec protocol is not available or disabled. + @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. + @retval EFI_SUCCESS The packet was protected. + @retval EFI_ACCESS_DENIED The packet was discarded. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the + number of input data blocks when build a fragment table. + +**/ +EFI_STATUS +Ip4IpSecProcessPacket ( + IN IP4_SERVICE *IpSb, + IN OUT IP4_HEAD **Head, + IN OUT NET_BUF **Netbuf, + IN OUT UINT8 **Options, + IN OUT UINT32 *OptionsLen, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN VOID *Context + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4NvData.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4NvData.h new file mode 100644 index 00000000..57d03e75 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4NvData.h @@ -0,0 +1,45 @@ +/** @file + Routines used to operate the Ip4Dxe. + +Copyright (c) 2015, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _IP4_NV_DATA_H_ +#define _IP4_NV_DATA_H_ + +#include <Guid/Ip4Config2Hii.h> + +#define FORMID_MAIN_FORM 1 +#define FORMID_DEVICE_FORM 2 + +#define KEY_ENABLE 0x100 +#define KEY_DHCP_ENABLE 0x101 +#define KEY_LOCAL_IP 0x102 +#define KEY_SUBNET_MASK 0x103 +#define KEY_GATE_WAY 0x104 +#define KEY_DNS 0x105 +#define KEY_SAVE_CHANGES 0x106 + +#define IP_MIN_SIZE 7 +#define IP_MAX_SIZE 15 +#define IP4_STR_MAX_SIZE 16 +#define ADDRESS_STR_MAX_SIZE 255 +#define MAX_IP4_CONFIG_DNS 16 + +/// +/// IP4_CONFIG2_IFR_NVDATA contains the IP4 configure +/// parameters for that NIC. +/// +typedef struct { + UINT8 Configure; ///< NIC configure status + UINT8 DhcpEnable; ///< Static or DHCP + CHAR16 StationAddress[IP4_STR_MAX_SIZE]; ///< IP addresses + CHAR16 SubnetMask[IP4_STR_MAX_SIZE]; ///< Subnet address + CHAR16 GatewayAddress[IP4_STR_MAX_SIZE]; ///< Gateway address + CHAR16 DnsAddress[ADDRESS_STR_MAX_SIZE]; ///< DNS server address +} IP4_CONFIG2_IFR_NVDATA; + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Option.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Option.c new file mode 100644 index 00000000..3acda4c1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Option.c @@ -0,0 +1,204 @@ +/** @file + IP4 option support functions. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + + +/** + Validate the IP4 option format for both the packets we received + and will transmit. + + @param[in] Option The first byte of the option + @param[in] OptionLen The length of the whole option + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise the option we wants to transmit. + + @retval TRUE The option is properly formatted + @retval FALSE The option is malformatted + +**/ +BOOLEAN +Ip4OptionIsValid ( + IN UINT8 *Option, + IN UINT32 OptionLen, + IN BOOLEAN Rcvd + ) +{ + UINT32 Cur; + UINT32 Len; + UINT32 Point; + + Cur = 0; + + while (Cur < OptionLen) { + switch (Option[Cur]) { + case IP4_OPTION_NOP: + Cur++; + break; + + case IP4_OPTION_EOP: + Cur = OptionLen; + break; + + case IP4_OPTION_LSRR: + case IP4_OPTION_SSRR: + case IP4_OPTION_RR: + Len = Option[Cur + 1]; + Point = Option[Cur + 2]; + + // + // SRR/RR options are formatted as |Type|Len|Point|Ip1|Ip2|... + // + if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) { + return FALSE; + } + + if ((Point > Len + 1) || (Point % 4 != 0)) { + return FALSE; + } + + // + // The Point must point pass the last entry if the packet is received + // by us. It must point to 4 if the packet is to be sent by us for + // source route option. + // + if ((Option[Cur] != IP4_OPTION_RR) && + ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) { + + return FALSE; + } + + Cur += Len; + break; + + default: + Len = Option[Cur + 1]; + + if ((OptionLen - Cur < Len) || (Len < 2)) { + return FALSE; + } + + Cur = Cur + Len; + break; + } + + } + + return TRUE; +} + + +/** + Copy the option from the original option to buffer. It + handles the details such as: + 1. whether copy the single IP4 option to the first/non-first + fragments. + 2. Pad the options copied over to aligned to 4 bytes. + + @param[in] Option The original option to copy from + @param[in] OptionLen The length of the original option + @param[in] FirstFragment Whether it is the first fragment + @param[in, out] Buf The buffer to copy options to. NULL + @param[in, out] BufLen The length of the buffer + + @retval EFI_SUCCESS The options are copied over + @retval EFI_BUFFER_TOO_SMALL Buf is NULL or BufLen provided is too small. + +**/ +EFI_STATUS +Ip4CopyOption ( + IN UINT8 *Option, + IN UINT32 OptionLen, + IN BOOLEAN FirstFragment, + IN OUT UINT8 *Buf, OPTIONAL + IN OUT UINT32 *BufLen + ) +{ + UINT8 OptBuf[40]; + UINT32 Cur; + UINT32 Next; + UINT8 Type; + UINT32 Len; + + ASSERT ((BufLen != NULL) && (OptionLen <= 40)); + + Cur = 0; + Next = 0; + + while (Cur < OptionLen) { + Type = Option[Cur]; + Len = Option[Cur + 1]; + + if (Type == IP4_OPTION_NOP) { + // + // Keep the padding, in case that the sender wants to align + // the option, say, to 4 bytes + // + OptBuf[Next] = IP4_OPTION_NOP; + Next++; + Cur++; + + } else if (Type == IP4_OPTION_EOP) { + // + // Don't append the EOP to avoid including only a EOP option + // + break; + + } else { + // + // don't copy options that is only valid for the first fragment + // + if (FirstFragment || (Type & IP4_OPTION_COPY_MASK) != 0) { + CopyMem (OptBuf + Next, Option + Cur, Len); + Next += Len; + } + + Cur += Len; + } + } + + // + // Don't append an EOP only option. + // + if (Next == 0) { + *BufLen = 0; + return EFI_SUCCESS; + } + + // + // Append an EOP if the end of option doesn't coincide with the + // end of the IP header, that is, isn't aligned to 4 bytes.. + // + if ((Next % 4) != 0) { + OptBuf[Next] = IP4_OPTION_EOP; + Next++; + } + + // + // Head length is in the unit of 4 bytes. Now, Len is the + // actual option length to appear in the IP header. + // + Len = ((Next + 3) &~0x03); + + // + // If the buffer is too small, set the BufLen then return + // + if ((Buf == NULL) || (*BufLen < Len)) { + *BufLen = Len; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy the option to the Buf, zero the buffer first to pad + // the options with NOP to align to 4 bytes. + // + ZeroMem (Buf, Len); + CopyMem (Buf, OptBuf, Next); + *BufLen = Len; + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Option.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Option.h new file mode 100644 index 00000000..2edb269d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Option.h @@ -0,0 +1,66 @@ +/** @file + IP4 option support routines. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_OPTION_H__ +#define __EFI_IP4_OPTION_H__ + +#define IP4_OPTION_EOP 0 +#define IP4_OPTION_NOP 1 +#define IP4_OPTION_LSRR 131 // Loss source and record routing, 10000011 +#define IP4_OPTION_SSRR 137 // Strict source and record routing, 10001001 +#define IP4_OPTION_RR 7 // Record routing, 00000111 + +#define IP4_OPTION_COPY_MASK 0x80 + +/** + Validate the IP4 option format for both the packets we received + and will transmit. It will compute the ICMP error message fields + if the option is malformatted. But this information isn't used. + + @param[in] Option The first byte of the option + @param[in] OptionLen The length of the whole option + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise the option we wants to transmit. + + @retval TRUE The option is properly formatted + @retval FALSE The option is malformatted + +**/ +BOOLEAN +Ip4OptionIsValid ( + IN UINT8 *Option, + IN UINT32 OptionLen, + IN BOOLEAN Rcvd + ); + +/** + Copy the option from the original option to buffer. It + handles the details such as: + 1. whether copy the single IP4 option to the first/non-first + fragments. + 2. Pad the options copied over to aligned to 4 bytes. + + @param[in] Option The original option to copy from + @param[in] OptionLen The length of the original option + @param[in] FirstFragment Whether it is the first fragment + @param[in, out] Buf The buffer to copy options to. NULL + @param[in, out] BufLen The length of the buffer + + @retval EFI_SUCCESS The options are copied over + @retval EFI_BUFFER_TOO_SMALL Buf is NULL or BufLen provided is too small. + +**/ +EFI_STATUS +Ip4CopyOption ( + IN UINT8 *Option, + IN UINT32 OptionLen, + IN BOOLEAN FirstFragment, + IN OUT UINT8 *Buf, OPTIONAL + IN OUT UINT32 *BufLen + ); +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c new file mode 100644 index 00000000..c48b4b1c --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.c @@ -0,0 +1,482 @@ +/** @file + Transmit the IP4 packet. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + +UINT16 mIp4Id; + + +/** + Prepend an IP4 head to the Packet. It will copy the options and + build the IP4 header fields. Used for IP4 fragmentation. + + @param Packet The packet to prepend IP4 header to + @param Head The caller supplied header. The caller should set + the following header fields: Tos, TotalLen, Id, + Fragment, Ttl, Protocol, Src and Dst. All the fields + are in host byte order. This function will fill in + the Ver, HeadLen, and checksum. + @param Option The original IP4 option to copy from + @param OptLen The length of the IP4 option + + @retval EFI_BAD_BUFFER_SIZE There is no enough room in the head space of + Packet. + @retval EFI_SUCCESS The IP4 header is successfully added to the packet. + +**/ +EFI_STATUS +Ip4PrependHead ( + IN OUT NET_BUF *Packet, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptLen + ) +{ + UINT32 HeadLen; + UINT32 Len; + IP4_HEAD *PacketHead; + BOOLEAN FirstFragment; + + // + // Prepend the options: first get the option length, then copy it over. + // + HeadLen = 0; + FirstFragment = IP4_FIRST_FRAGMENT (Head->Fragment); + + Ip4CopyOption (Option, OptLen, FirstFragment, NULL, &Len); + + HeadLen = IP4_MIN_HEADLEN + Len; + ASSERT (((Len % 4) == 0) && (HeadLen <= IP4_MAX_HEADLEN)); + + PacketHead = (IP4_HEAD *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + + if (PacketHead == NULL) { + return EFI_BAD_BUFFER_SIZE; + } + + Ip4CopyOption (Option, OptLen, FirstFragment, (UINT8 *) (PacketHead + 1), &Len); + + // + // Set the head up, convert the host byte order to network byte order + // + PacketHead->Ver = 4; + PacketHead->HeadLen = (UINT8) (HeadLen >> 2); + PacketHead->Tos = Head->Tos; + PacketHead->TotalLen = HTONS ((UINT16) Packet->TotalSize); + PacketHead->Id = HTONS (Head->Id); + PacketHead->Fragment = HTONS (Head->Fragment); + PacketHead->Checksum = 0; + PacketHead->Ttl = Head->Ttl; + PacketHead->Protocol = Head->Protocol; + PacketHead->Src = HTONL (Head->Src); + PacketHead->Dst = HTONL (Head->Dst); + PacketHead->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) PacketHead, HeadLen)); + + Packet->Ip.Ip4 = PacketHead; + return EFI_SUCCESS; +} + + +/** + Select an interface to send the packet generated in the IP4 driver + itself, that is, not by the requests of IP4 child's consumer. Such + packets include the ICMP echo replies, and other ICMP error packets. + + @param[in] IpSb The IP4 service that wants to send the packets. + @param[in] Dst The destination of the packet + @param[in] Src The source of the packet + + @return NULL if no proper interface is found, otherwise the interface that + can be used to send the system packet from. + +**/ +IP4_INTERFACE * +Ip4SelectInterface ( + IN IP4_SERVICE *IpSb, + IN IP4_ADDR Dst, + IN IP4_ADDR Src + ) +{ + IP4_INTERFACE *IpIf; + IP4_INTERFACE *Selected; + LIST_ENTRY *Entry; + + // + // Select the interface the Dst is on if one of the connected + // network. Some IP instance may be configured with 0.0.0.0/0, + // don't select that interface now. + // + IpIf = Ip4FindNet (IpSb, Dst); + + if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) { + return IpIf; + } + + // + // If source is one of the interface address, select it. + // + IpIf = Ip4FindInterface (IpSb, Src); + + if ((IpIf != NULL) && (IpIf->Ip != IP4_ALLZERO_ADDRESS)) { + return IpIf; + } + + // + // Select a configured interface as the fall back. Always prefer + // an interface with non-zero address. + // + Selected = NULL; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link); + + if (IpIf->Configured && ((Selected == NULL) || (Selected->Ip == 0))) { + Selected = IpIf; + } + } + + return Selected; +} + + +/** + The default callback function for system generated packet. + It will free the packet. + + @param Ip4Instance The IP4 child that issued the transmission. It most + like is NULL. + @param Packet The packet that transmitted. + @param IoStatus The result of the transmission, succeeded or failed. + @param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK + for reference. + @param Context The context provided by us + +**/ +VOID +Ip4SysPacketSent ( + IP4_PROTOCOL *Ip4Instance, + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ) +{ + NetbufFree (Packet); +} + + +/** + Transmit an IP4 packet. The packet comes either from the IP4 + child's consumer (IpInstance != NULL) or the IP4 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through some interface. + + @param[in] IpSb The IP4 service instance to transmit the packet + @param[in] IpInstance The IP4 child that issues the transmission. It is + NULL if the packet is from the system. + @param[in] Packet The user data to send, excluding the IP header. + @param[in] Head The caller supplied header. The caller should set + the following header fields: Tos, TotalLen, Id, tl, + Fragment, Protocol, Src and Dst. All the fields are + in host byte order. This function will fill in the + Ver, HeadLen, Fragment, and checksum. The Fragment + only need to include the DF flag. Ip4Output will + compute the MF and offset for you. + @param[in] Option The original option to append to the IP headers + @param[in] OptLen The length of the option + @param[in] GateWay The next hop address to transmit packet to. + 255.255.255.255 means broadcast. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback + + @retval EFI_NO_MAPPING There is no interface to the destination. + @retval EFI_NOT_FOUND There is no route to the destination + @retval EFI_SUCCESS The packet is successfully transmitted. + @retval EFI_BAD_BUFFER_SIZE The length of the IPv4 header + option length + + total data length is greater than MTU (or greater + than the maximum packet size if Token.Packet.TxData. + OverrideData.DoNotFragment is TRUE.) + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip4Output ( + IN IP4_SERVICE *IpSb, + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptLen, + IN IP4_ADDR GateWay, + IN IP4_FRAME_CALLBACK Callback, + IN VOID *Context + ) +{ + IP4_INTERFACE *IpIf; + IP4_ROUTE_CACHE_ENTRY *CacheEntry; + IP4_ADDR Dest; + EFI_STATUS Status; + NET_BUF *Fragment; + UINT32 Index; + UINT32 HeadLen; + UINT32 PacketLen; + UINT32 Offset; + UINT32 Mtu; + UINT32 Num; + BOOLEAN RawData; + + // + // Select an interface/source for system packet, application + // should select them itself. + // + if (IpInstance == NULL) { + IpIf = Ip4SelectInterface (IpSb, Head->Dst, Head->Src); + } else { + IpIf = IpInstance->Interface; + } + + if (IpIf == NULL) { + return EFI_NO_MAPPING; + } + + if ((Head->Src == IP4_ALLZERO_ADDRESS) && (IpInstance == NULL)) { + Head->Src = IpIf->Ip; + } + + // + // Before IPsec process, prepared the IP head. + // If Ip4Output is transmitting RawData, don't update IPv4 header. + // + HeadLen = sizeof (IP4_HEAD) + ((OptLen + 3) & (~0x03)); + + if ((IpInstance != NULL) && IpInstance->ConfigData.RawData) { + RawData = TRUE; + } else { + Head->HeadLen = (UINT8) (HeadLen >> 2); + Head->Id = mIp4Id++; + Head->Ver = 4; + RawData = FALSE; + } + + // + // Call IPsec process. + // + Status = Ip4IpSecProcessPacket ( + IpSb, + &Head, + &Packet, + &Option, + &OptLen, + EfiIPsecOutBound, + Context + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + Dest = Head->Dst; + if (IP4_IS_BROADCAST (Ip4GetNetCast (Dest, IpIf)) || (Dest == IP4_ALLONE_ADDRESS)) { + // + // Set the gateway to local broadcast if the Dest is + // the broadcast address for the connected network or + // it is local broadcast. + // + GateWay = IP4_ALLONE_ADDRESS; + + } else if (IP4_IS_MULTICAST (Dest)) { + // + // Set the gateway to the destination if it is an multicast + // address. The IP4_INTERFACE won't consult ARP to send local + // broadcast and multicast. + // + GateWay = Head->Dst; + + } else if (GateWay == IP4_ALLZERO_ADDRESS) { + // + // Route the packet unless overridden, that is, GateWay isn't zero. + // + if (IpInstance == NULL) { + CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src, IpIf->SubnetMask, TRUE); + } else { + CacheEntry = Ip4Route (IpInstance->RouteTable, Head->Dst, Head->Src, IpIf->SubnetMask, FALSE); + // + // If failed to route the packet by using the instance's route table, + // try to use the default route table. + // + if (CacheEntry == NULL) { + CacheEntry = Ip4Route (IpSb->DefaultRouteTable, Head->Dst, Head->Src, IpIf->SubnetMask, TRUE); + } + } + + if (CacheEntry == NULL) { + return EFI_NOT_FOUND; + } + + GateWay = CacheEntry->NextHop; + Ip4FreeRouteCacheEntry (CacheEntry); + } + + // + // OK, selected the source and route, fragment the packet then send + // them. Tag each fragment other than the first one as spawn from it. + // + Mtu = IpSb->MaxPacketSize + sizeof (IP4_HEAD); + + if (Packet->TotalSize + HeadLen > Mtu) { + // + // Fragmentation is disabled for RawData mode. + // + if (RawData) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Packet is fragmented from the tail to the head, that is, the + // first frame sent is the last fragment of the packet. The first + // fragment is NOT sent in this loop. First compute how many + // fragments there are. + // + Mtu = (Mtu - HeadLen) & (~0x07); + Num = (Packet->TotalSize + Mtu - 1) / Mtu; + + // + // Initialize the packet length and Offset. Other than the last + // fragment, the packet length equals to MTU. The offset is always + // aligned to MTU. + // + PacketLen = Packet->TotalSize - (Num - 1) * Mtu; + Offset = Mtu * (Num - 1); + + for (Index = 0; Index < Num - 1; Index++, Offset -= Mtu) { + Fragment = NetbufGetFragment (Packet, Offset, PacketLen, IP4_MAX_HEADLEN); + + if (Fragment == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + // + // Update the header's fragment. The caller fills the IP4 header + // fields that are required by Ip4PrependHead except the fragment. + // + Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, (Index != 0), Offset); + Ip4PrependHead (Fragment, Head, Option, OptLen); + + // + // Transmit the fragments, pass the Packet address as the context. + // So, we can find all the fragments spawned from the Packet by + // compare the NetBuf and Context to the Packet. + // + Status = Ip4SendFrame ( + IpIf, + IpInstance, + Fragment, + GateWay, + Ip4SysPacketSent, + Packet, + IpSb + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + PacketLen = Mtu; + } + + // + // Trim the already sent data, then adjust the head's fragment field. + // + NetbufTrim (Packet, Packet->TotalSize - Mtu, FALSE); + Head->Fragment = IP4_HEAD_FRAGMENT_FIELD (FALSE, TRUE, 0); + } + + // + // Send the first fragment, it is either the original packet, or the + // first fragment of a fragmented packet. It seems that there is a subtle + // bug here: what if the caller free the packet in Callback and IpIf (or + // MNP child used by that interface) still holds the fragments and try + // to access the data? The caller can free the packet if it recycles the + // consumer's (such as UDP) data in the Callback. But this can't happen. + // The detailed sequence is: + // 1. for the packets generated by IP4 driver itself: + // The Callback is Ip4SysPacketSent, which is the same as the + // fragments' callback. Ip4SysPacketSent simply calls NetbufFree + // to release its reference to the packet. So, no problem for + // system packets. + // + // 2. for the upper layer's packets (use UDP as an example): + // UDP requests the IP layer to transmit some data which is + // wrapped in an asynchronous token, the token is wrapped + // in IP4_TXTOKEN_WRAP by IP4. IP4 also wrap the user's data + // in a net buffer, which is Packet we get here. IP4_TXTOKEN_WRAP + // is bound with the Packet. It will only be freed when all + // the references to Packet have been released. Upon then, the + // Packet's OnFree callback will release the IP4_TXTOKEN_WRAP, + // and signal the user's recycle event. So, also no problem for + // upper layer's packets. + // + Ip4PrependHead (Packet, Head, Option, OptLen); + Status = Ip4SendFrame (IpIf, IpInstance, Packet, GateWay, Callback, Context, IpSb); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + Ip4CancelPacket (IpIf, Packet, Status); + return Status; +} + + +/** + The filter function to find a packet and all its fragments. + The packet's fragments have their Context set to the packet. + + @param[in] Frame The frames hold by the low level interface + @param[in] Context Context to the function, which is the packet. + + @retval TRUE This is the packet to cancel or its fragments. + @retval FALSE This is unrelated packet. + +**/ +BOOLEAN +Ip4CancelPacketFragments ( + IN IP4_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) { + return TRUE; + } + + return FALSE; +} + + +/** + Cancel the Packet and all its fragments. + + @param IpIf The interface from which the Packet is sent + @param Packet The Packet to cancel + @param IoStatus The status returns to the sender. + +**/ +VOID +Ip4CancelPacket ( + IN IP4_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ) +{ + Ip4CancelFrames (IpIf, IoStatus, Ip4CancelPacketFragments, Packet); +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.h new file mode 100644 index 00000000..cf02591a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Output.h @@ -0,0 +1,120 @@ +/** @file + +Copyright (c) 2005 - 2006, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_OUTPUT_H__ +#define __EFI_IP4_OUTPUT_H__ + +/** + The default callback function for system generated packet. + It will free the packet. + + @param Ip4Instance The IP4 child that issued the transmission. It most + like is NULL. + @param Packet The packet that transmitted. + @param IoStatus The result of the transmission, succeeded or failed. + @param LinkFlag Not used when transmission. check IP4_FRAME_CALLBACK + for reference. + @param Context The context provided by us + +**/ +VOID +Ip4SysPacketSent ( + IP4_PROTOCOL *Ip4Instance, + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +/** + Transmit an IP4 packet. The packet comes either from the IP4 + child's consumer (IpInstance != NULL) or the IP4 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through some interface. + + @param[in] IpSb The IP4 service instance to transmit the packet + @param[in] IpInstance The IP4 child that issues the transmission. It is + NULL if the packet is from the system. + @param[in] Packet The user data to send, excluding the IP header. + @param[in] Head The caller supplied header. The caller should set + the following header fields: Tos, TotalLen, Id, tl, + Fragment, Protocol, Src and Dst. All the fields are + in host byte order. This function will fill in the + Ver, HeadLen, Fragment, and checksum. The Fragment + only need to include the DF flag. Ip4Output will + compute the MF and offset for you. + @param[in] Option The original option to append to the IP headers + @param[in] OptLen The length of the option + @param[in] GateWay The next hop address to transmit packet to. + 255.255.255.255 means broadcast. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback + + @retval EFI_NO_MAPPING There is no interface to the destination. + @retval EFI_NOT_FOUND There is no route to the destination + @retval EFI_SUCCESS The packet is successfully transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip4Output ( + IN IP4_SERVICE *IpSb, + IN IP4_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptLen, + IN IP4_ADDR GateWay, + IN IP4_FRAME_CALLBACK Callback, + IN VOID *Context + ); + +/** + Cancel the Packet and all its fragments. + + @param IpIf The interface from which the Packet is sent + @param Packet The Packet to cancel + @param IoStatus The status returns to the sender. + +**/ +VOID +Ip4CancelPacket ( + IN IP4_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ); + +/** + Prepend an IP4 head to the Packet. It will copy the options and + build the IP4 header fields. Used for IP4 fragmentation. + + @param Packet The packet to prepend IP4 header to + @param Head The caller supplied header. The caller should set + the following header fields: Tos, TotalLen, Id, + Fragment, Ttl, Protocol, Src and Dst. All the fields + are in host byte order. This function will fill in + the Ver, HeadLen, and checksum. + @param Option The original IP4 option to copy from + @param OptLen The length of the IP4 option + + @retval EFI_BAD_BUFFER_SIZE There is no enough room in the head space of + Packet. + @retval EFI_SUCCESS The IP4 header is successfully added to the packet. + +**/ +EFI_STATUS +Ip4PrependHead ( + IN OUT NET_BUF *Packet, + IN IP4_HEAD *Head, + IN UINT8 *Option, + IN UINT32 OptLen + ); + +extern UINT16 mIp4Id; + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Route.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Route.c new file mode 100644 index 00000000..1e4bb5ef --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Route.c @@ -0,0 +1,673 @@ +/** @file + +Copyright (c) 2005 - 2016, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip4Impl.h" + + +/** + Allocate a route entry then initialize it with the Dest/Netmask + and Gateway. + + @param[in] Dest The destination network + @param[in] Netmask The destination network mask + @param[in] GateWay The nexthop address + + @return NULL if failed to allocate memory, otherwise the newly created + route entry. + +**/ +IP4_ROUTE_ENTRY * +Ip4CreateRouteEntry ( + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR GateWay + ) +{ + IP4_ROUTE_ENTRY *RtEntry; + + RtEntry = AllocatePool (sizeof (IP4_ROUTE_ENTRY)); + + if (RtEntry == NULL) { + return NULL; + } + + InitializeListHead (&RtEntry->Link); + + RtEntry->RefCnt = 1; + RtEntry->Dest = Dest; + RtEntry->Netmask = Netmask; + RtEntry->NextHop = GateWay; + RtEntry->Flag = 0; + + return RtEntry; +} + + +/** + Free the route table entry. It is reference counted. + + @param RtEntry The route entry to free. + +**/ +VOID +Ip4FreeRouteEntry ( + IN IP4_ROUTE_ENTRY *RtEntry + ) +{ + ASSERT (RtEntry->RefCnt > 0); + + if (--RtEntry->RefCnt == 0) { + FreePool (RtEntry); + } +} + + +/** + Allocate and initialize an IP4 route cache entry. + + @param[in] Dst The destination address + @param[in] Src The source address + @param[in] GateWay The next hop address + @param[in] Tag The tag from the caller. This marks all the cache + entries spawned from one route table entry. + + @return NULL if failed to allocate memory for the cache, other point + to the created route cache entry. + +**/ +IP4_ROUTE_CACHE_ENTRY * +Ip4CreateRouteCacheEntry ( + IN IP4_ADDR Dst, + IN IP4_ADDR Src, + IN IP4_ADDR GateWay, + IN UINTN Tag + ) +{ + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + + RtCacheEntry = AllocatePool (sizeof (IP4_ROUTE_CACHE_ENTRY)); + + if (RtCacheEntry == NULL) { + return NULL; + } + + InitializeListHead (&RtCacheEntry->Link); + + RtCacheEntry->RefCnt = 1; + RtCacheEntry->Dest = Dst; + RtCacheEntry->Src = Src; + RtCacheEntry->NextHop = GateWay; + RtCacheEntry->Tag = Tag; + + return RtCacheEntry; +} + + +/** + Free the route cache entry. It is reference counted. + + @param RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip4FreeRouteCacheEntry ( + IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry + ) +{ + ASSERT (RtCacheEntry->RefCnt > 0); + + if (--RtCacheEntry->RefCnt == 0) { + FreePool (RtCacheEntry); + } +} + + +/** + Initialize an empty route cache table. + + @param[in, out] RtCache The route cache table to initialize. + +**/ +VOID +Ip4InitRouteCache ( + IN OUT IP4_ROUTE_CACHE *RtCache + ) +{ + UINT32 Index; + + for (Index = 0; Index < IP4_ROUTE_CACHE_HASH_VALUE; Index++) { + InitializeListHead (&(RtCache->CacheBucket[Index])); + } +} + + +/** + Clean up a route cache, that is free all the route cache + entries enqueued in the cache. + + @param[in] RtCache The route cache table to clean up + +**/ +VOID +Ip4CleanRouteCache ( + IN IP4_ROUTE_CACHE *RtCache + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + for (Index = 0; Index < IP4_ROUTE_CACHE_HASH_VALUE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtCache->CacheBucket[Index])) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + RemoveEntryList (Entry); + Ip4FreeRouteCacheEntry (RtCacheEntry); + } + } +} + + + +/** + Create an empty route table, includes its internal route cache + + @return NULL if failed to allocate memory for the route table, otherwise + the point to newly created route table. + +**/ +IP4_ROUTE_TABLE * +Ip4CreateRouteTable ( + VOID + ) +{ + IP4_ROUTE_TABLE *RtTable; + UINT32 Index; + + RtTable = AllocatePool (sizeof (IP4_ROUTE_TABLE)); + + if (RtTable == NULL) { + return NULL; + } + + RtTable->RefCnt = 1; + RtTable->TotalNum = 0; + + for (Index = 0; Index <= IP4_MASK_MAX; Index++) { + InitializeListHead (&(RtTable->RouteArea[Index])); + } + + RtTable->Next = NULL; + + Ip4InitRouteCache (&RtTable->Cache); + return RtTable; +} + + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in] RtTable The route table to free. + +**/ +VOID +Ip4FreeRouteTable ( + IN IP4_ROUTE_TABLE *RtTable + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ROUTE_ENTRY *RtEntry; + UINT32 Index; + + ASSERT (RtTable->RefCnt > 0); + + if (--RtTable->RefCnt > 0) { + return ; + } + + // + // Free all the route table entry and its route cache. + // + for (Index = 0; Index <= IP4_MASK_MAX; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &(RtTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + RemoveEntryList (Entry); + Ip4FreeRouteEntry (RtEntry); + } + } + + Ip4CleanRouteCache (&RtTable->Cache); + + FreePool (RtTable); +} + + + +/** + Remove all the cache entries bearing the Tag. When a route cache + entry is created, it is tagged with the address of route entry + from which it is spawned. When a route entry is deleted, the cache + entries spawned from it are also deleted. + + @param RtCache Route cache to remove the entries from + @param Tag The Tag of the entries to remove + +**/ +VOID +Ip4PurgeRouteCache ( + IN OUT IP4_ROUTE_CACHE *RtCache, + IN UINTN Tag + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + for (Index = 0; Index < IP4_ROUTE_CACHE_HASH_VALUE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) { + + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + if (RtCacheEntry->Tag == Tag) { + RemoveEntryList (Entry); + Ip4FreeRouteCacheEntry (RtCacheEntry); + } + } + } +} + + +/** + Add a route entry to the route table. All the IP4_ADDRs are in + host byte order. + + @param[in, out] RtTable Route table to add route to + @param[in] Dest The destination of the network + @param[in] Netmask The netmask of the destination + @param[in] Gateway The next hop address + + @retval EFI_ACCESS_DENIED The same route already exists + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry + @retval EFI_SUCCESS The route is added successfully. + +**/ +EFI_STATUS +Ip4AddRoute ( + IN OUT IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Entry; + IP4_ROUTE_ENTRY *RtEntry; + + // + // All the route entries with the same netmask length are + // linke to the same route area + // + Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]); + + // + // First check whether the route exists + // + NET_LIST_FOR_EACH (Entry, Head) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) { + return EFI_ACCESS_DENIED; + } + } + + // + // Create a route entry and insert it to the route area. + // + RtEntry = Ip4CreateRouteEntry (Dest, Netmask, Gateway); + + if (RtEntry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (Gateway == IP4_ALLZERO_ADDRESS) { + RtEntry->Flag = IP4_DIRECT_ROUTE; + } + + InsertHeadList (Head, &RtEntry->Link); + RtTable->TotalNum++; + + return EFI_SUCCESS; +} + + +/** + Remove a route entry and all the route caches spawn from it. + + @param RtTable The route table to remove the route from + @param Dest The destination network + @param Netmask The netmask of the Dest + @param Gateway The next hop address + + @retval EFI_SUCCESS The route entry is successfully removed + @retval EFI_NOT_FOUND There is no route entry in the table with that + property. + +**/ +EFI_STATUS +Ip4DelRoute ( + IN OUT IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ROUTE_ENTRY *RtEntry; + + Head = &(RtTable->RouteArea[NetGetMaskLength (Netmask)]); + + NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + if (IP4_NET_EQUAL (RtEntry->Dest, Dest, Netmask) && (RtEntry->NextHop == Gateway)) { + Ip4PurgeRouteCache (&RtTable->Cache, (UINTN) RtEntry); + RemoveEntryList (Entry); + Ip4FreeRouteEntry (RtEntry); + + RtTable->TotalNum--; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Find a route cache with the dst and src. This is used by ICMP + redirect message process. All kinds of redirect is treated as + host redirect according to RFC1122. So, only route cache entries + are modified according to the ICMP redirect message. + + @param[in] RtTable The route table to search the cache for + @param[in] Dest The destination address + @param[in] Src The source address + + @return NULL if no route entry to the (Dest, Src). Otherwise the point + to the correct route cache entry. + +**/ +IP4_ROUTE_CACHE_ENTRY * +Ip4FindRouteCache ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src + ) +{ + LIST_ENTRY *Entry; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + Index = IP4_ROUTE_CACHE_HASH (Dest, Src); + + NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + if ((RtCacheEntry->Dest == Dest) && (RtCacheEntry->Src == Src)) { + NET_GET_REF (RtCacheEntry); + return RtCacheEntry; + } + } + + return NULL; +} + + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (mask length == 32) to the shortest route area + (default routes). In each route area, it will first search the instance's + route table, then the default route table. This is required by the following + requirements: + 1. IP search the route table for a most specific match + 2. The local route entries have precedence over the default route entry. + + @param[in] RtTable The route table to search from + @param[in] Dst The destination address to search + + @return NULL if no route matches the Dst, otherwise the point to the + most specific route to the Dst. + +**/ +IP4_ROUTE_ENTRY * +Ip4FindRouteEntry ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dst + ) +{ + LIST_ENTRY *Entry; + IP4_ROUTE_ENTRY *RtEntry; + IP4_ROUTE_TABLE *Table; + INTN Index; + + RtEntry = NULL; + + for (Index = IP4_MASK_MAX; Index >= 0; Index--) { + for (Table = RtTable; Table != NULL; Table = Table->Next) { + NET_LIST_FOR_EACH (Entry, &Table->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + if (IP4_NET_EQUAL (RtEntry->Dest, Dst, RtEntry->Netmask)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } + } + } + + + return NULL; +} + + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] RtTable The route table to search from + @param[in] Dest The destination address to search for + @param[in] Src The source address to search for + @param[in] SubnetMask The subnet mask of the Src address, this field is + used to check if the station is using /32 subnet. + @param[in] AlwaysTryDestAddr Always try to use the dest address as next hop even + though we can't find a matching route entry. This + field is only valid when using /32 subnet. + + @return NULL if failed to route packet, otherwise a route cache + entry that can be used to route packet. + +**/ +IP4_ROUTE_CACHE_ENTRY * +Ip4Route ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src, + IN IP4_ADDR SubnetMask, + IN BOOLEAN AlwaysTryDestAddr + ) +{ + LIST_ENTRY *Head; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP4_ROUTE_CACHE_ENTRY *RtCacheEntry; + IP4_ROUTE_CACHE_ENTRY *Cache; + IP4_ROUTE_ENTRY *RtEntry; + IP4_ADDR NextHop; + UINT32 Count; + + ASSERT (RtTable != NULL); + + Head = &RtTable->Cache.CacheBucket[IP4_ROUTE_CACHE_HASH (Dest, Src)]; + RtCacheEntry = Ip4FindRouteCache (RtTable, Dest, Src); + + // + // If found, promote the cache entry to the head of the hash bucket. LRU + // + if (RtCacheEntry != NULL) { + RemoveEntryList (&RtCacheEntry->Link); + InsertHeadList (Head, &RtCacheEntry->Link); + return RtCacheEntry; + } + + // + // Search the route table for the most specific route + // + RtEntry = Ip4FindRouteEntry (RtTable, Dest); + + if (RtEntry == NULL) { + if (SubnetMask != IP4_ALLONE_ADDRESS) { + return NULL; + } else if (!AlwaysTryDestAddr) { + return NULL; + } + } + + // + // Found a route to the Dest, if it is a direct route, the packet + // will be sent directly to the destination, such as for connected + // network. Otherwise, it is an indirect route, the packet will be + // sent to the next hop router. + // + // When using /32 subnet mask, the packet will always be sent to the direct + // destination first, if we can't find a matching route cache. + // + if (SubnetMask == IP4_ALLONE_ADDRESS || ((RtEntry->Flag & IP4_DIRECT_ROUTE) != 0)) { + NextHop = Dest; + } else { + NextHop = RtEntry->NextHop; + } + + if (RtEntry != NULL) { + Ip4FreeRouteEntry (RtEntry); + } + + // + // Create a route cache entry, and tag it as spawned from this route entry + // For /32 subnet mask, the default route in RtEntry will be used if failed + // to send the packet to driect destination address. + // + RtCacheEntry = Ip4CreateRouteCacheEntry (Dest, Src, NextHop, (UINTN) RtEntry); + + if (RtCacheEntry == NULL) { + return NULL; + } + + InsertHeadList (Head, &RtCacheEntry->Link); + NET_GET_REF (RtCacheEntry); + + // + // Each bucket of route cache can contain at most 64 entries. + // Remove the entries at the tail of the bucket. These entries + // are likely to be used least. + // + Count = 0; + NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) { + if (++Count < IP4_ROUTE_CACHE_MAX) { + continue; + } + + Cache = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_CACHE_ENTRY, Link); + + RemoveEntryList (Entry); + Ip4FreeRouteCacheEntry (Cache); + } + + return RtCacheEntry; +} + + +/** + Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of + GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the + internal operation of the IP4 driver. + + @param[in] IpInstance The IP4 child that requests the route table. + + @retval EFI_SUCCESS The route table is successfully build + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip4BuildEfiRouteTable ( + IN IP4_PROTOCOL *IpInstance + ) +{ + LIST_ENTRY *Entry; + IP4_ROUTE_TABLE *RtTable; + IP4_ROUTE_ENTRY *RtEntry; + EFI_IP4_ROUTE_TABLE *Table; + UINT32 Count; + INT32 Index; + + RtTable = IpInstance->RouteTable; + + if (IpInstance->EfiRouteTable != NULL) { + FreePool (IpInstance->EfiRouteTable); + + IpInstance->EfiRouteTable = NULL; + IpInstance->EfiRouteCount = 0; + } + + Count = RtTable->TotalNum; + + if (RtTable->Next != NULL) { + Count += RtTable->Next->TotalNum; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + Table = AllocatePool (sizeof (EFI_IP4_ROUTE_TABLE) * Count); + + if (Table == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the route entry to EFI route table. Keep the order of + // route entry copied from most specific to default route. That + // is, interlevel the route entry from the instance's route area + // and those from the default route table's route area. + // + Count = 0; + + for (Index = IP4_MASK_MAX; Index >= 0; Index--) { + for (RtTable = IpInstance->RouteTable; RtTable != NULL; RtTable = RtTable->Next) { + NET_LIST_FOR_EACH (Entry, &(RtTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP4_ROUTE_ENTRY, Link); + + EFI_IP4 (Table[Count].SubnetAddress) = HTONL (RtEntry->Dest & RtEntry->Netmask); + EFI_IP4 (Table[Count].SubnetMask) = HTONL (RtEntry->Netmask); + EFI_IP4 (Table[Count].GatewayAddress) = HTONL (RtEntry->NextHop); + + Count++; + } + } + } + + IpInstance->EfiRouteTable = Table; + IpInstance->EfiRouteCount = Count; + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Route.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Route.h new file mode 100644 index 00000000..a1ea37ae --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip4Dxe/Ip4Route.h @@ -0,0 +1,225 @@ +/** @file + EFI IP4 route table and route cache table definitions. + +Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP4_ROUTE_H__ +#define __EFI_IP4_ROUTE_H__ + +#include "Ip4Common.h" + +#define IP4_DIRECT_ROUTE 0x00000001 + +#define IP4_ROUTE_CACHE_HASH_VALUE 31 +#define IP4_ROUTE_CACHE_MAX 64 // Max NO. of cache entry per hash bucket + +#define IP4_ROUTE_CACHE_HASH(Dst, Src) (((Dst) ^ (Src)) % IP4_ROUTE_CACHE_HASH_VALUE) + +/// +/// The route entry in the route table. Dest/Netmask is the destion +/// network. The nexthop is the gateway to send the packet to in +/// order to reach the Dest/Netmask. If the Flag has IP4_DIRECT_ROUTE +/// on, the gateway is the destination of the IP packet itself. Route +/// enties of the connected network have the flag on. +/// +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + IP4_ADDR Dest; + IP4_ADDR Netmask; + IP4_ADDR NextHop; + UINT32 Flag; +} IP4_ROUTE_ENTRY; + +/// +/// The route cache entry. The route cache entry is optional. +/// But it is necessary to support the ICMP redirect message. +/// Check Ip4ProcessIcmpRedirect for information. +/// +/// The cache entry field Tag is used to tag all the route +/// cache entry spawned from a route table entry. This makes +/// it simple to delete all the route cache entries from a +/// to-be-deleted route entry. +/// +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + IP4_ADDR Dest; + IP4_ADDR Src; + IP4_ADDR NextHop; + UINTN Tag; +} IP4_ROUTE_CACHE_ENTRY; + +/// +/// The route cache table is organized as a hash table. Each +/// IP4 route table has a embedded route cache. For now the +/// route cache and route table are binded together. But keep +/// the route cache a separated structure in case we want to +/// detach them later. +/// +typedef struct { + LIST_ENTRY CacheBucket[IP4_ROUTE_CACHE_HASH_VALUE]; +} IP4_ROUTE_CACHE; + +/// +/// Each IP4 instance has its own route table. Each ServiceBinding +/// instance has a default route table and default address. +/// +/// All the route table entries with the same mask are linked +/// together in one route area. For example, RouteArea[0] contains +/// the default routes. A route table also contains a route cache. +/// +typedef struct _IP4_ROUTE_TABLE IP4_ROUTE_TABLE; + +struct _IP4_ROUTE_TABLE { + INTN RefCnt; + UINT32 TotalNum; + LIST_ENTRY RouteArea[IP4_MASK_NUM]; + IP4_ROUTE_TABLE *Next; + IP4_ROUTE_CACHE Cache; +}; + +/** + Create an empty route table, includes its internal route cache + + @return NULL if failed to allocate memory for the route table, otherwise + the point to newly created route table. + +**/ +IP4_ROUTE_TABLE * +Ip4CreateRouteTable ( + VOID + ); + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in] RtTable The route table to free. + +**/ +VOID +Ip4FreeRouteTable ( + IN IP4_ROUTE_TABLE *RtTable + ); + +/** + Add a route entry to the route table. All the IP4_ADDRs are in + host byte order. + + @param[in, out] RtTable Route table to add route to + @param[in] Dest The destination of the network + @param[in] Netmask The netmask of the destination + @param[in] Gateway The next hop address + + @retval EFI_ACCESS_DENIED The same route already exists + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the entry + @retval EFI_SUCCESS The route is added successfully. + +**/ +EFI_STATUS +Ip4AddRoute ( + IN OUT IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ); + +/** + Remove a route entry and all the route caches spawn from it. + + @param RtTable The route table to remove the route from + @param Dest The destination network + @param Netmask The netmask of the Dest + @param Gateway The next hop address + + @retval EFI_SUCCESS The route entry is successfully removed + @retval EFI_NOT_FOUND There is no route entry in the table with that + property. + +**/ +EFI_STATUS +Ip4DelRoute ( + IN OUT IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Netmask, + IN IP4_ADDR Gateway + ); + +/** + Find a route cache with the dst and src. This is used by ICMP + redirect message process. All kinds of redirect is treated as + host redirect according to RFC1122. So, only route cache entries + are modified according to the ICMP redirect message. + + @param[in] RtTable The route table to search the cache for + @param[in] Dest The destination address + @param[in] Src The source address + + @return NULL if no route entry to the (Dest, Src). Otherwise the point + to the correct route cache entry. + +**/ +IP4_ROUTE_CACHE_ENTRY * +Ip4FindRouteCache ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src + ); + +/** + Free the route cache entry. It is reference counted. + + @param RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip4FreeRouteCacheEntry ( + IN IP4_ROUTE_CACHE_ENTRY *RtCacheEntry + ); + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] RtTable The route table to search from + @param[in] Dest The destination address to search for + @param[in] Src The source address to search for + @param[in] SubnetMask The subnet mask of the Src address, this field is + used to check if the station is using /32 subnet. + @param[in] AlwaysTryDestAddr Always try to use the dest address as next hop even + though we can't find a matching route entry. This + field is only valid when using /32 subnet. + + @return NULL if failed to route packet, otherwise a route cache + entry that can be used to route packet. + +**/ +IP4_ROUTE_CACHE_ENTRY * +Ip4Route ( + IN IP4_ROUTE_TABLE *RtTable, + IN IP4_ADDR Dest, + IN IP4_ADDR Src, + IN IP4_ADDR SubnetMask, + IN BOOLEAN AlwaysTryDestAddr + ); + +/** + Build a EFI_IP4_ROUTE_TABLE to be returned to the caller of + GetModeData. The EFI_IP4_ROUTE_TABLE is clumsy to use in the + internal operation of the IP4 driver. + + @param[in] IpInstance The IP4 child that requests the route table. + + @retval EFI_SUCCESS The route table is successfully build + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip4BuildEfiRouteTable ( + IN IP4_PROTOCOL *IpInstance + ); +#endif |