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/Ip6Dxe | |
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/Ip6Dxe')
33 files changed, 22696 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/ComponentName.c new file mode 100644 index 00000000..7247ebe5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/ComponentName.c @@ -0,0 +1,468 @@ +/** @file + Implementation of EFI_COMPONENT_NAME_PROTOCOL and + EFI_COMPONENT_NAME2_PROTOCOL protocol. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.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 +Ip6ComponentNameGetDriverName ( + 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 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 +Ip6ComponentNameGetControllerName ( + 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 gIp6ComponentName = { + Ip6ComponentNameGetDriverName, + Ip6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Ip6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Ip6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIp6DriverNameTable[] = { + { + "eng;en", + L"IP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gIp6ControllerNameTable = 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 +Ip6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mIp6DriverNameTable, + DriverName, + (BOOLEAN) (This == &gIp6ComponentName) + ); + +} + +/** + Update the component name for the IP6 child handle. + + @param Ip6[in] A pointer to the EFI_IP6_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_IP6_PROTOCOL *Ip6 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[128]; + EFI_IP6_MODE_DATA Ip6ModeData; + UINTN Offset; + CHAR16 Address[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]; + + if (Ip6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer. + // + Offset = 0; + Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL); + if (!EFI_ERROR (Status)) { + if (Ip6ModeData.AddressList != NULL) { + FreePool (Ip6ModeData.AddressList); + } + + if (Ip6ModeData.GroupTable != NULL) { + FreePool (Ip6ModeData.GroupTable); + } + + if (Ip6ModeData.RouteTable != NULL) { + FreePool (Ip6ModeData.RouteTable); + } + + if (Ip6ModeData.NeighborCache != NULL) { + FreePool (Ip6ModeData.NeighborCache); + } + + if (Ip6ModeData.PrefixTable != NULL) { + FreePool (Ip6ModeData.PrefixTable); + } + + if (Ip6ModeData.IcmpTypeList != NULL) { + FreePool (Ip6ModeData.IcmpTypeList); + } + } + + if (!EFI_ERROR (Status) && Ip6ModeData.IsStarted) { + Status = NetLibIp6ToStr (&Ip6ModeData.ConfigData.StationAddress, Address, sizeof(Address)); + if (EFI_ERROR (Status)) { + return Status; + } + Offset += UnicodeSPrint ( + HandleName, + sizeof(HandleName), + L"IPv6(StationAddress=%s, ", + Address + ); + Status = NetLibIp6ToStr (&Ip6ModeData.ConfigData.DestinationAddress, Address, sizeof(Address)); + if (EFI_ERROR (Status)) { + return Status; + } + UnicodeSPrint ( + HandleName + Offset, + sizeof(HandleName) - Offset * sizeof (CHAR16), + L"DestinationAddress=%s)", + Address + ); + } else if (!Ip6ModeData.IsStarted) { + UnicodeSPrint (HandleName, sizeof(HandleName), L"IPv6(Not started)"); + } else { + UnicodeSPrint (HandleName, sizeof(HandleName), L"IPv6(%r)", Status); + } + + if (gIp6ControllerNameTable != NULL) { + FreeUnicodeStringTable (gIp6ControllerNameTable); + gIp6ControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gIp6ComponentName.SupportedLanguages, + &gIp6ControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gIp6ComponentName2.SupportedLanguages, + &gIp6ControllerNameTable, + 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 +Ip6ComponentNameGetControllerName ( + 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_IP6_PROTOCOL *Ip6; + + // + // 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, + &gEfiIp6ProtocolGuid, + (VOID **)&Ip6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (Ip6); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gIp6ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gIp6ComponentName) + ); +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Common.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Common.c new file mode 100644 index 00000000..6466b495 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Common.c @@ -0,0 +1,667 @@ +/** @file + The implementation of common functions shared by IP6 driver. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL, + only the address count is returned. + + @param[in] IpSb The IP6 service binding instance. + @param[out] AddressCount The number of returned addresses. + @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO. + This is an optional parameter. + + + @retval EFI_SUCCESS The address array successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6BuildEfiAddressList ( + IN IP6_SERVICE *IpSb, + OUT UINT32 *AddressCount, + OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL + ) +{ + UINT32 Count; + LIST_ENTRY *Entry; + EFI_IP6_ADDRESS_INFO *EfiAddrInfo; + IP6_ADDRESS_INFO *AddrInfo; + + if (AddressCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (IpSb->LinkLocalOk) { + Count = 1 + IpSb->DefaultInterface->AddressCount; + } else { + Count = 0; + } + + *AddressCount = Count; + + if ((AddressList == NULL) || (Count == 0)) { + return EFI_SUCCESS; + } + + if (*AddressList == NULL) { + *AddressList = AllocatePool (sizeof (EFI_IP6_ADDRESS_INFO) * Count); + if (*AddressList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + EfiAddrInfo = *AddressList; + + IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &IpSb->LinkLocalAddr); + EfiAddrInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + + EfiAddrInfo++; + Count = 1; + + NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + IP6_COPY_ADDRESS (&EfiAddrInfo->Address, &AddrInfo->Address); + EfiAddrInfo->PrefixLength = AddrInfo->PrefixLength; + + EfiAddrInfo++; + Count++; + } + + ASSERT (Count == *AddressCount); + + return EFI_SUCCESS; +} + +/** + Generate the multicast addresses identify the group of all IPv6 nodes or IPv6 + routers defined in RFC4291. + + All Nodes Addresses: FF01::1, FF02::1. + All Router Addresses: FF01::2, FF02::2, FF05::2. + + @param[in] Router If TRUE, generate all routers addresses, + else generate all node addresses. + @param[in] Scope interface-local(1), link-local(2), or site-local(5) + @param[out] Ip6Addr The generated multicast address. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The address is generated. + +**/ +EFI_STATUS +Ip6SetToAllNodeMulticast ( + IN BOOLEAN Router, + IN UINT8 Scope, + OUT EFI_IPv6_ADDRESS *Ip6Addr + ) +{ + if (Ip6Addr == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!Router && Scope == IP6_SITE_LOCAL_SCOPE) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (Ip6Addr, sizeof (EFI_IPv6_ADDRESS)); + Ip6Addr->Addr[0] = 0xFF; + Ip6Addr->Addr[1] = Scope; + + if (!Router) { + Ip6Addr->Addr[15] = 0x1; + } else { + Ip6Addr->Addr[15] = 0x2; + } + + return EFI_SUCCESS; +} + +/** + This function converts MAC address to 64 bits interface ID according to RFC4291 + and returns the interface ID. Currently only 48-bit MAC address is supported by + this function. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL The operation fails. + @return Pointer to the generated interface ID. + +**/ +UINT8 * +Ip6CreateInterfaceID ( + IN OUT IP6_SERVICE *IpSb + ) +{ + UINT8 InterfaceId[8]; + UINT8 Byte; + EFI_MAC_ADDRESS *MacAddr; + UINT32 AddrLen; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + AddrLen = IpSb->SnpMode.HwAddressSize; + + // + // Currently only IEEE 802 48-bit MACs are supported to create link local address. + // + if (AddrLen != IP6_MAC_LEN || IpSb->InterfaceIdLen != IP6_IF_ID_LEN) { + return NULL; + } + + MacAddr = &IpSb->SnpMode.CurrentAddress; + + // + // Convert MAC address to 64 bits interface ID according to Appendix A of RFC4291: + // 1. Insert 0xFFFE to the middle + // 2. Invert the universal/local bit - bit 6 in network order + // + CopyMem (InterfaceId, MacAddr, 3); + InterfaceId[3] = 0xFF; + InterfaceId[4] = 0xFE; + CopyMem (&InterfaceId[5], &MacAddr->Addr[3], 3); + + Byte = (UINT8) (InterfaceId[0] & IP6_U_BIT); + if (Byte == IP6_U_BIT) { + InterfaceId[0] &= ~IP6_U_BIT; + } else { + InterfaceId[0] |= IP6_U_BIT; + } + + // + // Return the interface ID. + // + return AllocateCopyPool (IpSb->InterfaceIdLen, InterfaceId); +} + +/** + This function creates link-local address from interface identifier. The + interface identifier is normally created from MAC address. It might be manually + configured by administrator if the link-local address created from MAC address + is a duplicate address. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL If the operation fails. + @return The generated Link Local address, in network order. + +**/ +EFI_IPv6_ADDRESS * +Ip6CreateLinkLocalAddr ( + IN OUT IP6_SERVICE *IpSb + ) +{ + EFI_IPv6_ADDRESS *Ip6Addr; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + UINTN DataSize; + EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + if (IpSb->InterfaceId != NULL) { + FreePool (IpSb->InterfaceId); + } + + // + // Get the interface id if it is manually configured. + // + Ip6Config = &IpSb->Ip6ConfigInstance.Ip6Config; + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + ZeroMem (&InterfaceId, DataSize); + + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + &InterfaceId + ); + if (Status == EFI_NOT_FOUND) { + // + // Since the interface id is not configured, generate the interface id from + // MAC address. + // + IpSb->InterfaceId = Ip6CreateInterfaceID (IpSb); + if (IpSb->InterfaceId == NULL) { + return NULL; + } + + CopyMem (&InterfaceId, IpSb->InterfaceId, IpSb->InterfaceIdLen); + // + // Record the interface id. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + DataSize, + &InterfaceId + ); + if (EFI_ERROR (Status)) { + FreePool (IpSb->InterfaceId); + IpSb->InterfaceId = NULL; + return NULL; + } + } else if (!EFI_ERROR (Status)) { + IpSb->InterfaceId = AllocateCopyPool (DataSize, &InterfaceId); + if (IpSb->InterfaceId == NULL) { + return NULL; + } + } else { + return NULL; + } + + // + // Append FE80::/64 to the left of IPv6 address then return. + // + Ip6Addr = AllocateZeroPool (sizeof (EFI_IPv6_ADDRESS)); + if (Ip6Addr == NULL) { + FreePool (IpSb->InterfaceId); + IpSb->InterfaceId = NULL; + return NULL; + } + + CopyMem (&Ip6Addr->Addr[8], IpSb->InterfaceId, IpSb->InterfaceIdLen); + Ip6Addr->Addr[1] = 0x80; + Ip6Addr->Addr[0] = 0xFE; + + return Ip6Addr; +} + +/** + Compute the solicited-node multicast address for an unicast or anycast address, + by taking the low-order 24 bits of this address, and appending those bits to + the prefix FF02:0:0:0:0:1:FF00::/104. + + @param[in] Ip6Addr The unicast or anycast address, in network order. + @param[out] MulticastAddr The generated solicited-node multicast address, + in network order. + +**/ +VOID +Ip6CreateSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6Addr, + OUT EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + ASSERT (Ip6Addr != NULL && MulticastAddr != NULL); + + ZeroMem (MulticastAddr, sizeof (EFI_IPv6_ADDRESS)); + + MulticastAddr->Addr[0] = 0xFF; + MulticastAddr->Addr[1] = 0x02; + MulticastAddr->Addr[11] = 0x1; + MulticastAddr->Addr[12] = 0xFF; + + CopyMem (&MulticastAddr->Addr[13], &Ip6Addr->Addr[13], 3); +} + +/** + Insert a node IP6_ADDRESS_INFO to an IP6 interface. + + @param[in, out] IpIf Points to an IP6 interface. + @param[in] AddrInfo Points to IP6_ADDRESS_INFO + +**/ +VOID +Ip6AddAddr ( + IN OUT IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddrInfo + ) +{ + InsertHeadList (&IpIf->AddressList, &AddrInfo->Link); + IpIf->AddressCount++; +} + +/** + 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 +Ip6DestroyChildEntryByAddr ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + IP6_PROTOCOL *Instance; + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + EFI_IPv6_ADDRESS *Address; + + Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + ServiceBinding = ((IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT*) Context)->ServiceBinding; + Address = ((IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT*) Context)->Address; + + if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) { + return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); + } + + return EFI_SUCCESS; +} + +/** + Destroy the IP instance if its StationAddress is removed. It is the help function + for Ip6RemoveAddr(). + + @param[in, out] IpSb Points to an IP6 service binding instance. + @param[in] Address The to be removed address + +**/ +VOID +Ip6DestroyInstanceByAddress ( + IN OUT IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ) +{ + LIST_ENTRY *List; + IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT Context; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + List = &IpSb->Children; + Context.ServiceBinding = &IpSb->ServiceBinding; + Context.Address = Address; + NetDestroyLinkList ( + List, + Ip6DestroyChildEntryByAddr, + &Context, + NULL + ); +} + +/** + Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO. + + This function removes the matching IPv6 addresses from the address list and + adjusts the address count of the address list. If IpSb is not NULL, this function + calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the + its solicited-node multicast MAC address from the filter list and sends out + a Multicast Listener Done. If Prefix is NULL, all address in the address list + will be removed. If Prefix is not NULL, the address that matching the Prefix + with PrefixLength in the address list will be removed. + + @param[in] IpSb NULL or points to IP6 service binding instance. + @param[in, out] AddressList Address list array. + @param[in, out] AddressCount The count of addresses in address list array. + @param[in] Prefix NULL or an IPv6 address prefix. + @param[in] PrefixLength The length of Prefix. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength + cannot be found in the address list. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6RemoveAddr ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN OUT LIST_ENTRY *AddressList, + IN OUT UINT32 *AddressCount, + IN EFI_IPv6_ADDRESS *Prefix OPTIONAL, + IN UINT8 PrefixLength + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ADDRESS_INFO *AddrInfo; + EFI_IPv6_ADDRESS SnMCastAddr; + + if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_MAX) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_NOT_FOUND; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (Prefix == NULL || + (PrefixLength == 128 && EFI_IP6_EQUAL (Prefix, &AddrInfo->Address)) || + (PrefixLength == AddrInfo->PrefixLength && NetIp6IsNetEqual (Prefix, &AddrInfo->Address, PrefixLength)) + ) { + if (IpSb != NULL) { + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + Ip6CreateSNMulticastAddr (&AddrInfo->Address, &SnMCastAddr); + Ip6LeaveGroup (IpSb, &SnMCastAddr); + + // + // Destroy any instance who is using the dying address as the source address. + // + Ip6DestroyInstanceByAddress (IpSb, &AddrInfo->Address); + } + + RemoveEntryList (Entry); + FreePool (AddrInfo); + (*AddressCount)--; + + Status = EFI_SUCCESS; + } + } + + return Status; +} + +/** + Check whether the incoming Ipv6 address is a solicited-node multicast address. + + @param[in] Ip6 Ip6 address, in network order. + + @retval TRUE Yes, solicited-node multicast address + @retval FALSE No + +**/ +BOOLEAN +Ip6IsSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ) +{ + EFI_IPv6_ADDRESS Sn; + BOOLEAN Flag; + + Ip6CreateSNMulticastAddr (Ip6, &Sn); + Flag = FALSE; + + if (CompareMem (Sn.Addr, Ip6->Addr, 13) == 0) { + Flag = TRUE; + } + + return Flag; +} + +/** + Check whether the incoming IPv6 address is one of the maintained addresses in + the IP6 service binding instance. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Address The IP6 address to be checked. + @param[out] Interface If not NULL, output the IP6 interface which + maintains the Address. + @param[out] AddressInfo If not NULL, output the IP6 address information + of the Address. + + @retval TRUE Yes, it is one of the maintained address. + @retval FALSE No, it is not one of the maintained address. + +**/ +BOOLEAN +Ip6IsOneOfSetAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address, + OUT IP6_INTERFACE **Interface OPTIONAL, + OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_ADDRESS_INFO *TmpAddressInfo; + + // + // Check link-local address first + // + if (IpSb->LinkLocalOk && EFI_IP6_EQUAL (&IpSb->LinkLocalAddr, Address)) { + if (Interface != NULL) { + *Interface = IpSb->DefaultInterface; + } + + if (AddressInfo != NULL) { + *AddressInfo = NULL; + } + + return TRUE; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + TmpAddressInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (EFI_IP6_EQUAL (&TmpAddressInfo->Address, Address)) { + if (Interface != NULL) { + *Interface = IpIf; + } + + if (AddressInfo != NULL) { + *AddressInfo = TmpAddressInfo; + } + + return TRUE; + } + } + } + + return FALSE; +} + +/** + Check whether the incoming MAC address is valid. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] LinkAddress The MAC address. + + @retval TRUE Yes, it is valid. + @retval FALSE No, it is not valid. + +**/ +BOOLEAN +Ip6IsValidLinkAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_MAC_ADDRESS *LinkAddress + ) +{ + UINT32 Index; + + // + // TODO: might be updated later to be more acceptable. + // + for (Index = IpSb->SnpMode.HwAddressSize; Index < sizeof (EFI_MAC_ADDRESS); Index++) { + if (LinkAddress->Addr[Index] != 0) { + return FALSE; + } + } + + return TRUE; +} + +/** + Copy the PrefixLength bits from Src to Dest. + + @param[out] Dest A pointer to the buffer to copy to. + @param[in] Src A pointer to the buffer to copy from. + @param[in] PrefixLength The number of bits to copy. + +**/ +VOID +Ip6CopyAddressByPrefix ( + OUT EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src, + IN UINT8 PrefixLength + ) +{ + UINT8 Byte; + UINT8 Bit; + UINT8 Mask; + + ASSERT (Dest != NULL && Src != NULL); + ASSERT (PrefixLength <= IP6_PREFIX_MAX); + + Byte = (UINT8) (PrefixLength / 8); + Bit = (UINT8) (PrefixLength % 8); + + ZeroMem (Dest, sizeof (EFI_IPv6_ADDRESS)); + + CopyMem (Dest, Src, Byte); + + if (Bit > 0) { + Mask = (UINT8) (0xFF << (8 - Bit)); + ASSERT (Byte < 16); + Dest->Addr[Byte] = (UINT8) (Src->Addr[Byte] & Mask); + } +} + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address instead of + hard-coding 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 The multicast IP successfully + translated to a multicast MAC address. + @retval Other The address is not converted because an error occurred. + +**/ +EFI_STATUS +Ip6GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_IPv6_ADDRESS *Multicast, + OUT EFI_MAC_ADDRESS *Mac + ) +{ + EFI_IP_ADDRESS EfiIp; + + IP6_COPY_ADDRESS (&EfiIp.v6, Multicast); + + return Mnp->McastIpToMac (Mnp, TRUE, &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, out] Head The IP head to convert. + + @return Point to the converted IP head. + +**/ +EFI_IP6_HEADER * +Ip6NtohHead ( + IN OUT EFI_IP6_HEADER *Head + ) +{ + Head->FlowLabelL = NTOHS (Head->FlowLabelL); + Head->PayloadLength = NTOHS (Head->PayloadLength); + + return Head; +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Common.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Common.h new file mode 100644 index 00000000..efac540b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Common.h @@ -0,0 +1,312 @@ +/** @file + Common definition and functions for IP6 driver. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_COMMON_H__ +#define __EFI_IP6_COMMON_H__ + +#define IP6_LINK_EQUAL(Mac1, Mac2) (CompareMem ((Mac1), (Mac2), sizeof (EFI_MAC_ADDRESS)) == 0) + +// +// Convert the Microsecond to second. IP transmit/receive time is +// in the unit of microsecond. IP ticks once per second. +// +#define IP6_US_TO_SEC(Us) (((Us) + 999999) / 1000000) + +#define IP6_ETHER_PROTO 0x86DD + +#define IP6_MAC_LEN 6 +#define IP6_IF_ID_LEN 8 + +#define IP6_INTERFACE_LOCAL_SCOPE 1 +#define IP6_LINK_LOCAL_SCOPE 2 +#define IP6_SITE_LOCAL_SCOPE 5 + +#define IP6_INFINIT_LIFETIME 0xFFFFFFFF + +#define IP6_HOP_LIMIT 255 +// +// Make it to 64 since all 54 bits are zero. +// +#define IP6_LINK_LOCAL_PREFIX_LENGTH 64 + +#define IP6_TIMER_INTERVAL_IN_MS 100 +#define IP6_ONE_SECOND_IN_MS 1000 + +// +// The packet is received as link level broadcast/multicast/promiscuous. +// +#define IP6_LINK_BROADCAST 0x00000001 +#define IP6_LINK_MULTICAST 0x00000002 +#define IP6_LINK_PROMISC 0x00000004 + +#define IP6_U_BIT 0x02 + +typedef enum { + Ip6Promiscuous = 1, + Ip6Unicast, + Ip6Multicast, + Ip6AnyCast +} IP6_ADDRESS_TYPE; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + EFI_IPv6_ADDRESS *Address; +} IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT; + +typedef struct _IP6_INTERFACE IP6_INTERFACE; +typedef struct _IP6_PROTOCOL IP6_PROTOCOL; +typedef struct _IP6_SERVICE IP6_SERVICE; +typedef struct _IP6_ADDRESS_INFO IP6_ADDRESS_INFO; + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of EFI_IP6_ADDRESS_INFO is also returned. If AddressList is NULL, + only the address count is returned. + + @param[in] IpSb The IP6 service binding instance. + @param[out] AddressCount The number of returned addresses. + @param[out] AddressList The pointer to the array of EFI_IP6_ADDRESS_INFO. + This is an optional parameter. + + + @retval EFI_SUCCESS The address array is successfully build + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the address info. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6BuildEfiAddressList ( + IN IP6_SERVICE *IpSb, + OUT UINT32 *AddressCount, + OUT EFI_IP6_ADDRESS_INFO **AddressList OPTIONAL + ); + +/** + Generate the multicast addresses identify the group of all IPv6 nodes or IPv6 + routers defined in RFC4291. + + All Nodes Addresses: FF01::1, FF02::1. + All Router Addresses: FF01::2, FF02::2, FF05::2. + + @param[in] Router If TRUE, generate all routers addresses, + else generate all node addresses. + @param[in] Scope interface-local(1), link-local(2), or site-local(5) + @param[out] Ip6Addr The generated multicast address. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The address is generated. + +**/ +EFI_STATUS +Ip6SetToAllNodeMulticast ( + IN BOOLEAN Router, + IN UINT8 Scope, + OUT EFI_IPv6_ADDRESS *Ip6Addr + ); + +/** + This function converts MAC address to 64 bits interface ID according to RFC4291 + and returns the interface ID. Currently only 48-bit MAC address is supported by + this function. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL The operation fails. + @return Pointer to the generated interface ID. + +**/ +UINT8 * +Ip6CreateInterfaceID ( + IN OUT IP6_SERVICE *IpSb + ); + +/** + This function creates link-local address from interface identifier. The + interface identifier is normally created from MAC address. It might be manually + configured by administrator if the link-local address created from MAC address + is a duplicate address. + + @param[in, out] IpSb The IP6 service binding instance. + + @retval NULL If the operation fails. + @return The generated Link Local address, in network order. + +**/ +EFI_IPv6_ADDRESS * +Ip6CreateLinkLocalAddr ( + IN OUT IP6_SERVICE *IpSb + ); + +/** + Compute the solicited-node multicast address for an unicast or anycast address, + by taking the low-order 24 bits of this address, and appending those bits to + the prefix FF02:0:0:0:0:1:FF00::/104. + + @param Ip6Addr The unicast or anycast address, in network order. + @param MulticastAddr The generated solicited-node multicast address, + in network order. + +**/ +VOID +Ip6CreateSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6Addr, + OUT EFI_IPv6_ADDRESS *MulticastAddr + ); + +/** + Check whether the incoming Ipv6 address is a solicited-node multicast address. + + @param[in] Ip6 Ip6 address, in network order. + + @retval TRUE Yes, solicited-node multicast address + @retval FALSE No + +**/ +BOOLEAN +Ip6IsSNMulticastAddr ( + IN EFI_IPv6_ADDRESS *Ip6 + ); + +/** + Check whether the incoming IPv6 address is one of the maintained address in + the IP6 service binding instance. + + @param[in] IpSb Points to a IP6 service binding instance + @param[in] Address The IP6 address to be checked. + @param[out] Interface If not NULL, output the IP6 interface which + maintains the Address. + @param[out] AddressInfo If not NULL, output the IP6 address information + of the Address. + + @retval TRUE Yes, it is one of the maintained addresses. + @retval FALSE No, it is not one of the maintained addresses. + +**/ +BOOLEAN +Ip6IsOneOfSetAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address, + OUT IP6_INTERFACE **Interface OPTIONAL, + OUT IP6_ADDRESS_INFO **AddressInfo OPTIONAL + ); + +/** + Check whether the incoming MAC address is valid. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] LinkAddress The MAC address. + + @retval TRUE Yes, it is valid. + @retval FALSE No, it is not valid. + +**/ +BOOLEAN +Ip6IsValidLinkAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_MAC_ADDRESS *LinkAddress + ); + + +/** + Copy the PrefixLength bits from Src to Dest. + + @param[out] Dest A pointer to the buffer to copy to. + @param[in] Src A pointer to the buffer to copy from. + @param[in] PrefixLength The number of bits to copy. + +**/ +VOID +Ip6CopyAddressByPrefix ( + OUT EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src, + IN UINT8 PrefixLength + ); + +/** + Insert a node IP6_ADDRESS_INFO to an IP6 interface. + + @param[in, out] IpIf Points to an IP6 interface. + @param[in] AddrInfo Points to an IP6_ADDRESS_INFO. + +**/ +VOID +Ip6AddAddr ( + IN OUT IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddrInfo + ); + +/** + Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO. + + This function removes the matching IPv6 addresses from the address list and + adjusts the address count of the address list. If IpSb is not NULL, this function + calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the + its solicited-node multicast MAC address from the filter list and sends out + a Multicast Listener Done. If Prefix is NULL, all address in the address list + will be removed. If Prefix is not NULL, the address that matching the Prefix + with PrefixLength in the address list will be removed. + + @param[in] IpSb NULL or points to IP6 service binding instance. + @param[in, out] AddressList address list array + @param[in, out] AddressCount the count of addresses in address list array + @param[in] Prefix NULL or an IPv6 address prefix + @param[in] PrefixLength the length of Prefix + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength + cannot be found in address list. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6RemoveAddr ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN OUT LIST_ENTRY *AddressList, + IN OUT UINT32 *AddressCount, + IN EFI_IPv6_ADDRESS *Prefix OPTIONAL, + IN UINT8 PrefixLength + ); + +/** + Get the MAC address for a multicast IP address. Call + Mnp's McastIpToMac to find the MAC address instead of + hard-coding 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 The multicast IP is successfully + translated to a multicast MAC address. + @retval Other The address is not converted because an error occurred. + +**/ +EFI_STATUS +Ip6GetMulticastMac ( + IN EFI_MANAGED_NETWORK_PROTOCOL *Mnp, + IN EFI_IPv6_ADDRESS *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, out] Head The IP head to convert. + + @return Point to the converted IP head. + +**/ +EFI_IP6_HEADER * +Ip6NtohHead ( + IN OUT EFI_IP6_HEADER *Head + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Config.vfr b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Config.vfr new file mode 100644 index 00000000..39b3c519 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Config.vfr @@ -0,0 +1,172 @@ +/** @file + VFR file used by the IP6 configuration component. + + Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6NvData.h" + +#define EFI_NETWORK_DEVICE_CLASS 0x04 + +formset + guid = IP6_CONFIG_NVDATA_GUID, + title = STRING_TOKEN(STR_IP6_CONFIG_FORM_TITLE), + help = STRING_TOKEN(STR_IP6_CONFIG_FORM_HELP), + + varstore IP6_CONFIG_IFR_NVDATA, + name = IP6_CONFIG_IFR_NVDATA, + guid = IP6_CONFIG_NVDATA_GUID; + + form formid = FORMID_HEAD_FORM, + title = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE); + + goto FORMID_MAIN_FORM, + prompt = STRING_TOKEN (STR_GET_CURRENT_SETTING), + help = STRING_TOKEN (STR_GET_CURRENT_SETTING_HELP), + flags = INTERACTIVE, + key = KEY_GET_CURRENT_SETTING; + + endform; + + form formid = FORMID_MAIN_FORM, + title = STRING_TOKEN(STR_IP6_DEVICE_FORM_TITLE); + + text + help = STRING_TOKEN(STR_IP6_INTERFACE_NAME_HELP), + text = STRING_TOKEN(STR_IP6_INTERFACE_NAME), + text = STRING_TOKEN(STR_IP6_INTERFACE_NAME_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_HELP), + text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE), + text = STRING_TOKEN(STR_IP6_INTERFACE_TYPE_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_MAC_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_MAC_ADDRESS), + text = STRING_TOKEN(STR_IP6_MAC_ADDRESS_CONTENT); + + text + help = STRING_TOKEN(STR_IP6_HOST_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_HOST_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label HOST_ADDRESS_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_ROUTE_TABLE_HELP), + text = STRING_TOKEN(STR_IP6_ROUTE_TABLE), + text = STRING_TOKEN(STR_NULL); + + label ROUTE_TABLE_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_GATEWAY_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label GATEWAY_ADDRESS_LABEL; + label LABEL_END; + + text + help = STRING_TOKEN(STR_IP6_DNS_ADDRESS_HELP), + text = STRING_TOKEN(STR_IP6_DNS_ADDRESS), + text = STRING_TOKEN(STR_NULL); + + label DNS_ADDRESS_LABEL; + label LABEL_END; + + string varid = IP6_CONFIG_IFR_NVDATA.InterfaceId, + prompt = STRING_TOKEN(STR_IP6_INTERFACE_ID), + help = STRING_TOKEN(STR_IP6_INTERFACE_ID_HELP), + flags = INTERACTIVE, + key = KEY_INTERFACE_ID, + minsize = INTERFACE_ID_STR_MIN_SIZE, + maxsize = INTERFACE_ID_STR_MAX_SIZE, + endstring; + + numeric varid = IP6_CONFIG_IFR_NVDATA.DadTransmitCount, + prompt = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT), + help = STRING_TOKEN(STR_IP6_DAD_TRANSMIT_COUNT_HELP), + flags = 0, + minimum = 0, + maximum = DAD_MAX_TRANSMIT_COUNT, + step = 0, + endnumeric; + + oneof varid = IP6_CONFIG_IFR_NVDATA.Policy, + prompt = STRING_TOKEN(STR_POLICY_TYPE_PROMPT), + help = STRING_TOKEN(STR_POLICY_TYPE_HELP), + option text = STRING_TOKEN(STR_POLICY_TYPE_AUTO), value = IP6_POLICY_AUTO, flags = DEFAULT; + option text = STRING_TOKEN(STR_POLICY_TYPE_MANUAL), value = IP6_POLICY_MANUAL, flags = 0; + endoneof; + + subtitle text = STRING_TOKEN(STR_NULL); + + suppressif ideqval IP6_CONFIG_IFR_NVDATA.Policy == IP6_POLICY_AUTO; + goto FORMID_MANUAL_CONFIG_FORM, + prompt = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM), + help = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM_HELP), + flags = 0; + subtitle text = STRING_TOKEN(STR_NULL); + endif; + + text + help = STRING_TOKEN (STR_SAVE_CHANGES_HELP), + text = STRING_TOKEN (STR_SAVE_CHANGES), + flags = INTERACTIVE, + key = KEY_SAVE_CHANGES; + + endform; + + form formid = FORMID_MANUAL_CONFIG_FORM, + title = STRING_TOKEN(STR_IP6_AD_CONFIG_FORM); + + string varid = IP6_CONFIG_IFR_NVDATA.ManualAddress, + prompt = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS), + help = STRING_TOKEN(STR_IP6_MANUAL_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_MANUAL_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + string varid = IP6_CONFIG_IFR_NVDATA.GatewayAddress, + prompt = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDRESS), + help = STRING_TOKEN(STR_IP6_NEW_GATEWAY_ADDR_HELP), + flags = INTERACTIVE, + key = KEY_GATEWAY_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + string varid = IP6_CONFIG_IFR_NVDATA.DnsAddress, + prompt = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS), + help = STRING_TOKEN(STR_IP6_NEW_DNS_ADDRESS_HELP), + flags = INTERACTIVE, + key = KEY_DNS_ADDRESS, + minsize = ADDRESS_STR_MIN_SIZE, + maxsize = ADDRESS_STR_MAX_SIZE, + endstring; + + text + help = STRING_TOKEN (STR_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_SAVE_CONFIG_CHANGES; + + text + help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + text = STRING_TOKEN (STR_NO_SAVE_AND_EXIT), + flags = INTERACTIVE, + key = KEY_IGNORE_CONFIG_CHANGES; + + endform; + +endformset; + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c new file mode 100644 index 00000000..a147d1ff --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c @@ -0,0 +1,2533 @@ +/** @file + The implementation of EFI IPv6 Configuration Protocol. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + Copyright (c) Microsoft Corporation.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +LIST_ENTRY mIp6ConfigInstanceList = {&mIp6ConfigInstanceList, &mIp6ConfigInstanceList}; + +/** + The event process routine when the DHCPv6 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context Pointer to the IP6 config instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Update the current policy to NewPolicy. During the transition + period, the default router list, on-link prefix list, autonomous prefix list + and address list in all interfaces will be released. + + @param[in] IpSb The IP6 service binding instance. + @param[in] NewPolicy The new policy to be updated to. + +**/ +VOID +Ip6ConfigOnPolicyChanged ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_CONFIG_POLICY NewPolicy + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + LIST_ENTRY *Next; + IP6_INTERFACE *IpIf; + IP6_DAD_ENTRY *DadEntry; + IP6_DELAY_JOIN_LIST *DelayNode; + IP6_ADDRESS_INFO *AddrInfo; + IP6_PROTOCOL *Instance; + BOOLEAN Recovery; + + Recovery = FALSE; + + // + // Currently there are only two policies: Manual and Automatic. Regardless of + // what transition is going on, i.e., Manual -> Automatic and Automatic -> + // Manual, we have to free default router list, on-link prefix list, autonomous + // prefix list, address list in all the interfaces and destroy any IPv6 child + // instance whose local IP is neither 0 nor the link-local address. + // + Ip6CleanDefaultRouterList (IpSb); + Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); + Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); + + // + // It's tricky... If the LinkLocal address is O.K., add back the link-local + // prefix to the on-link prefix table. + // + if (IpSb->LinkLocalOk) { + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + IP6_LINK_LOCAL_PREFIX_LENGTH, + &IpSb->LinkLocalAddr + ); + } + + if (!IsListEmpty (&IpSb->DefaultInterface->AddressList) && IpSb->DefaultInterface->AddressCount > 0) { + // + // If any IPv6 children (Instance) in configured state and use global unicast address, it will be + // destroyed in Ip6RemoveAddr() function later. Then, the upper layer driver's Stop() function will be + // called, which may break the upper layer network stacks. So, the driver should take the responsibility + // for the recovery by using ConnectController() after Ip6RemoveAddr(). + // Here, just check whether need to recover the upper layer network stacks later. + // + NET_LIST_FOR_EACH (Entry, &IpSb->DefaultInterface->AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + if (!IsListEmpty (&IpSb->Children)) { + NET_LIST_FOR_EACH (Entry2, &IpSb->Children) { + Instance = NET_LIST_USER_STRUCT_S (Entry2, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, &AddrInfo->Address)) { + Recovery = TRUE; + break; + } + } + } + } + + // + // All IPv6 children that use global unicast address as its source address + // should be destroyed now. The survivers are those use the link-local address + // or the unspecified address as the source address. + // TODO: Conduct a check here. + Ip6RemoveAddr ( + IpSb, + &IpSb->DefaultInterface->AddressList, + &IpSb->DefaultInterface->AddressCount, + NULL, + 0 + ); + + if (IpSb->Controller != NULL && Recovery) { + // + // ConnectController() to recover the upper layer network stacks. + // + gBS->ConnectController (IpSb->Controller, NULL, NULL, TRUE); + } + } + + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + // + // remove all pending delay node and DAD entries for the global addresses. + // + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { + DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); + if (!NetIp6IsLinkLocalAddr (&DelayNode->AddressInfo->Address)) { + RemoveEntryList (&DelayNode->Link); + FreePool (DelayNode); + } + } + + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { + DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); + + if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) { + // + // Fail this DAD entry if the address is not link-local. + // + Ip6OnDADFinished (FALSE, IpIf, DadEntry); + } + } + } + + if (NewPolicy == Ip6ConfigPolicyAutomatic) { + // + // Set parameters to trigger router solicitation sending in timer handler. + // + IpSb->RouterAdvertiseReceived = FALSE; + IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS; + // + // delay 1 second + // + IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_ONE_SECOND_IN_MS); + } +} + +/** + The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] OtherInfoOnly If FALSE, get stateful address and other information + via DHCPv6. Otherwise, only get the other information. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_UNSUPPORTED The DHCP6 driver is not available. + +**/ +EFI_STATUS +Ip6ConfigStartStatefulAutoConfig ( + IN IP6_CONFIG_INSTANCE *Instance, + IN BOOLEAN OtherInfoOnly + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + EFI_DHCP6_CONFIG_DATA Dhcp6CfgData; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_DHCP6_PACKET_OPTION *OptList[1]; + UINT16 OptBuf[4]; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + + // + // A host must not invoke stateful address configuration if it is already + // participating in the statuful protocol as a result of an earlier advertisement. + // + if (Instance->Dhcp6Handle != NULL) { + return EFI_SUCCESS; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + Instance->OtherInfoOnly = OtherInfoOnly; + + Status = NetLibCreateServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + &Instance->Dhcp6Handle + ); + + if (Status == EFI_UNSUPPORTED) { + // + // No DHCPv6 Service Binding protocol, register a notify. + // + if (Instance->Dhcp6SbNotifyEvent == NULL) { + Instance->Dhcp6SbNotifyEvent = EfiCreateProtocolNotifyEvent ( + &gEfiDhcp6ServiceBindingProtocolGuid, + TPL_CALLBACK, + Ip6ConfigOnDhcp6SbInstalled, + (VOID *) Instance, + &Instance->Registration + ); + } + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Instance->Dhcp6SbNotifyEvent != NULL) { + gBS->CloseEvent (Instance->Dhcp6SbNotifyEvent); + } + + Status = gBS->OpenProtocol ( + Instance->Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + (VOID **) &Instance->Dhcp6, + IpSb->Image, + IpSb->Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + ASSERT_EFI_ERROR (Status); + + Dhcp6 = Instance->Dhcp6; + Dhcp6->Configure (Dhcp6, NULL); + + // + // Set the exta options to send. Here we only want the option request option + // with DNS SERVERS. + // + Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf; + Oro->OpCode = HTONS (DHCP6_OPT_ORO); + Oro->OpLen = HTONS (2); + *((UINT16 *) &Oro->Data[0]) = HTONS (DHCP6_OPT_DNS_SERVERS); + OptList[0] = Oro; + + Status = EFI_SUCCESS; + + if (!OtherInfoOnly) { + // + // Get stateful address and other information via DHCPv6. + // + Dhcp6CfgData.Dhcp6Callback = NULL; + Dhcp6CfgData.CallbackContext = NULL; + Dhcp6CfgData.OptionCount = 1; + Dhcp6CfgData.OptionList = &OptList[0]; + Dhcp6CfgData.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA; + Dhcp6CfgData.IaDescriptor.IaId = Instance->IaId; + Dhcp6CfgData.IaInfoEvent = Instance->Dhcp6Event; + Dhcp6CfgData.ReconfigureAccept = FALSE; + Dhcp6CfgData.RapidCommit = FALSE; + Dhcp6CfgData.SolicitRetransmission = NULL; + + Status = Dhcp6->Configure (Dhcp6, &Dhcp6CfgData); + + if (!EFI_ERROR (Status)) { + + if (IpSb->LinkLocalOk) { + Status = Dhcp6->Start (Dhcp6); + } else { + IpSb->Dhcp6NeedStart = TRUE; + } + + } + } else { + // + // Only get other information via DHCPv6, this doesn't require a config + // action. + // + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 64; + InfoReqReXmit.Mrt = 60; + InfoReqReXmit.Mrd = 0; + + if (IpSb->LinkLocalOk) { + Status = Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + Instance->Dhcp6Event, + Ip6ConfigOnDhcp6Reply, + Instance + ); + } else { + IpSb->Dhcp6NeedInfoRequest = TRUE; + } + + } + + return Status; +} + +/** + 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. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigSignalEvent ( + 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 + gEfiIp6ConfigProtocolGuid. 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 IP6_CONFIG_INSTANCE. + + @param[in] VarName The pointer to the variable name + @param[in, out] Instance The pointer to the IP6 config 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 +Ip6ConfigReadConfigData ( + IN CHAR16 *VarName, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + UINTN VarSize; + IP6_CONFIG_VARIABLE *Variable; + IP6_CONFIG_DATA_ITEM *DataItem; + UINTN Index; + IP6_CONFIG_DATA_RECORD DataRecord; + CHAR8 *Data; + + // + // Try to read the configuration variable. + // + VarSize = 0; + Status = gRT->GetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + 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, + &gEfiIp6ConfigProtocolGuid, + NULL, + &VarSize, + Variable + ); + if (EFI_ERROR (Status) || (UINT16) (~NetblockChecksum ((UINT8 *) Variable, (UINT32) VarSize)) != 0) { + // + // GetVariable error or the variable is corrupted. + // + goto Error; + } + + // + // Get the IAID we use. + // + Instance->IaId = Variable->IaId; + + 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. + // Check that the length is contained within the variable before allocating. + // + if (DataRecord.DataSize > VarSize - DataRecord.Offset) { + goto Error; + } + + 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; + +Error: + // + // Fall back to the default value. + // + if (Variable != NULL) { + FreePool (Variable); + } + + // + // Remove the problematic variable and return EFI_NOT_FOUND, a new + // variable will be set again. + // + gRT->SetVariable ( + VarName, + &gEfiIp6ConfigProtocolGuid, + IP6_CONFIG_VARIABLE_ATTRIBUTE, + 0, + NULL + ); + + return EFI_NOT_FOUND; +} + +/** + Write the configuration data from IP6_CONFIG_INSTANCE to variable storage. + + @param[in] VarName The pointer to the variable name. + @param[in] Instance The pointer to the IP6 configuration 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 +Ip6ConfigWriteConfigData ( + IN CHAR16 *VarName, + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + UINTN Index; + UINTN VarSize; + IP6_CONFIG_DATA_ITEM *DataItem; + IP6_CONFIG_VARIABLE *Variable; + IP6_CONFIG_DATA_RECORD *DataRecord; + CHAR8 *Heap; + EFI_STATUS Status; + + VarSize = sizeof (IP6_CONFIG_VARIABLE) - sizeof (IP6_CONFIG_DATA_RECORD); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; Index++) { + + DataItem = &Instance->DataItem[Index]; + if (!DATA_ATTRIB_SET (DataItem->Attribute, DATA_ATTRIB_VOLATILE) && !EFI_ERROR (DataItem->Status)) { + + VarSize += sizeof (IP6_CONFIG_DATA_RECORD) + DataItem->DataSize; + } + } + + Variable = AllocatePool (VarSize); + if (Variable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Variable->IaId = Instance->IaId; + Heap = (CHAR8 *) Variable + VarSize; + Variable->DataRecordCount = 0; + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; 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_IP6_CONFIG_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, + &gEfiIp6ConfigProtocolGuid, + IP6_CONFIG_VARIABLE_ATTRIBUTE, + VarSize, + Variable + ); + + FreePool (Variable); + + return Status; +} + +/** + The work function for EfiIp6ConfigGetData() to get the interface information + of the communication device this IP6Config instance manages. + + @param[in] Instance Pointer to the IP6 config 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 +Ip6ConfigGetIfInfo ( + IN IP6_CONFIG_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + UINTN Length; + IP6_CONFIG_DATA_ITEM *Item; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + UINT32 AddressCount; + UINT32 RouteCount; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Length = sizeof (EFI_IP6_CONFIG_INTERFACE_INFO); + + // + // Calculate the required length, add the buffer size for AddressInfo and + // RouteTable + // + Ip6BuildEfiAddressList (IpSb, &AddressCount, NULL); + Ip6BuildEfiRouteTable (IpSb->RouteTable, &RouteCount, NULL); + + Length += AddressCount * sizeof (EFI_IP6_ADDRESS_INFO) + RouteCount * sizeof (EFI_IP6_ROUTE_TABLE); + + if (*DataSize < Length) { + *DataSize = Length; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy the fixed size part of the interface info. + // + Item = &Instance->DataItem[Ip6ConfigDataTypeInterfaceInfo]; + IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data; + CopyMem (IfInfo, Item->Data.Ptr, sizeof (EFI_IP6_CONFIG_INTERFACE_INFO)); + + // + // AddressInfo + // + IfInfo->AddressInfo = (EFI_IP6_ADDRESS_INFO *) (IfInfo + 1); + Ip6BuildEfiAddressList (IpSb, &IfInfo->AddressInfoCount, &IfInfo->AddressInfo); + + // + // RouteTable + // + IfInfo->RouteTable = (EFI_IP6_ROUTE_TABLE *) (IfInfo->AddressInfo + IfInfo->AddressInfoCount); + Ip6BuildEfiRouteTable (IpSb->RouteTable, &IfInfo->RouteCount, &IfInfo->RouteTable); + + if (IfInfo->AddressInfoCount == 0) { + IfInfo->AddressInfo = NULL; + } + + if (IfInfo->RouteCount == 0) { + IfInfo->RouteTable = NULL; + } + + return EFI_SUCCESS; +} + +/** + The work function for EfiIp6ConfigSetData() to set the alternative interface ID + for the communication device managed by this IP6Config instance, if the link local + IPv6 addresses generated from the interface ID based on the default source the + EFI IPv6 Protocol uses is a duplicate address. + + @param[in] Instance Pointer to the IP6 configuration 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, + 8 bytes. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetAltIfId ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_INTERFACE_ID *OldIfId; + EFI_IP6_CONFIG_INTERFACE_ID *NewIfId; + IP6_CONFIG_DATA_ITEM *DataItem; + + if (DataSize != sizeof (EFI_IP6_CONFIG_INTERFACE_ID)) { + return EFI_BAD_BUFFER_SIZE; + } + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; + OldIfId = DataItem->Data.AltIfId; + NewIfId = (EFI_IP6_CONFIG_INTERFACE_ID *) Data; + + CopyMem (OldIfId, NewIfId, DataSize); + DataItem->Status = EFI_SUCCESS; + + return EFI_SUCCESS; +} + +/** + The work function for EfiIp6ConfigSetData() to set the general configuration + policy for the EFI IPv6 network stack that is running on the communication device + managed by this IP6Config instance. The policy will affect other configuration settings. + + @param[in] Instance Pointer to the IP6 config 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 +Ip6ConfigSetPolicy ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_POLICY NewPolicy; + IP6_CONFIG_DATA_ITEM *DataItem; + IP6_SERVICE *IpSb; + + if (DataSize != sizeof (EFI_IP6_CONFIG_POLICY)) { + return EFI_BAD_BUFFER_SIZE; + } + + NewPolicy = *((EFI_IP6_CONFIG_POLICY *) Data); + + if (NewPolicy > Ip6ConfigPolicyAutomatic) { + return EFI_INVALID_PARAMETER; + } + + if (NewPolicy == Instance->Policy) { + + return EFI_ABORTED; + } else { + // + // Clean the ManualAddress, Gateway and DnsServers, shrink the variable + // data size, and fire up all the related events. + // + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + DataItem->Data.Ptr = NULL; + DataItem->DataSize = 0; + DataItem->Status = EFI_NOT_FOUND; + NetMapIterate (&DataItem->EventMap, Ip6ConfigSignalEvent, NULL); + + if (NewPolicy == Ip6ConfigPolicyManual) { + // + // The policy is changed from automatic to manual. Stop the DHCPv6 process + // and destroy the DHCPv6 child. + // + if (Instance->Dhcp6Handle != NULL) { + Ip6ConfigDestroyDhcp6 (Instance); + } + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Ip6ConfigOnPolicyChanged (IpSb, NewPolicy); + + Instance->Policy = NewPolicy; + + return EFI_SUCCESS; + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the number of consecutive + Neighbor Solicitation messages sent while performing Duplicate Address Detection + on a tentative address. A value of ZERO indicates that Duplicate Address Detection + will not be performed on a tentative address. + + @param[in] Instance The Instance Pointer to the IP6 config 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_ABORTED The new transmit count equals the current configuration. + @retval EFI_SUCCESS The specified configuration data for the EFI IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetDadXmits ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *OldDadXmits; + + if (DataSize != sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS)) { + return EFI_BAD_BUFFER_SIZE; + } + + OldDadXmits = Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits].Data.DadXmits; + + if ((*(UINT32 *) Data) == OldDadXmits->DupAddrDetectTransmits) { + + return EFI_ABORTED; + } else { + + OldDadXmits->DupAddrDetectTransmits = *((UINT32 *) Data); + return EFI_SUCCESS; + } +} + +/** + The callback function for Ip6SetAddr. The prototype is defined + as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed + for the manual address set by Ip6ConfigSetManualAddress. + + @param[in] IsDadPassed If TRUE, Duplicate Address Detection passed. + @param[in] TargetAddress The tentative IPv6 address to be checked. + @param[in] Context Pointer to the IP6 configuration instance data. + +**/ +VOID +Ip6ManualAddrDadCallback ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + UINTN Index; + IP6_CONFIG_DATA_ITEM *Item; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddr; + EFI_IP6_CONFIG_MANUAL_ADDRESS *PassedAddr; + UINTN DadPassCount; + UINTN DadFailCount; + IP6_SERVICE *IpSb; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Item = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + ManualAddr = NULL; + + if (Item->DataSize == 0) { + return; + } + + for (Index = 0; Index < Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); Index++) { + // + // Find the original tag used to place into the NET_MAP. + // + ManualAddr = Item->Data.ManualAddress + Index; + if (EFI_IP6_EQUAL (TargetAddress, &ManualAddr->Address)) { + break; + } + } + + ASSERT (Index != Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + + if (IsDadPassed) { + NetMapInsertTail (&Instance->DadPassedMap, ManualAddr, NULL); + } else { + NetMapInsertTail (&Instance->DadFailedMap, ManualAddr, NULL); + } + + DadPassCount = NetMapGetCount (&Instance->DadPassedMap); + DadFailCount = NetMapGetCount (&Instance->DadFailedMap); + + if ((DadPassCount + DadFailCount) == (Item->DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS))) { + // + // All addresses have finished the configuration process. + // + if (DadFailCount != 0) { + // + // There is at least one duplicate address. + // + FreePool (Item->Data.Ptr); + + Item->DataSize = DadPassCount * sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + if (Item->DataSize == 0) { + // + // All failed, bad luck. + // + Item->Data.Ptr = NULL; + Item->Status = EFI_NOT_FOUND; + } else { + // + // Part of addresses are detected to be duplicates, so update the + // data with those passed. + // + PassedAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AllocatePool (Item->DataSize); + ASSERT (PassedAddr != NULL); + + Item->Data.Ptr = PassedAddr; + Item->Status = EFI_SUCCESS; + + while (!NetMapIsEmpty (&Instance->DadPassedMap)) { + ManualAddr = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) NetMapRemoveHead (&Instance->DadPassedMap, NULL); + CopyMem (PassedAddr, ManualAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)); + + PassedAddr++; + } + + ASSERT ((UINTN) PassedAddr - (UINTN) Item->Data.Ptr == Item->DataSize); + } + } else { + // + // All addresses are valid. + // + Item->Status = EFI_SUCCESS; + } + + // + // Remove the tags we put in the NET_MAPs. + // + while (!NetMapIsEmpty (&Instance->DadFailedMap)) { + NetMapRemoveHead (&Instance->DadFailedMap, NULL); + } + + while (!NetMapIsEmpty (&Instance->DadPassedMap)) { + NetMapRemoveHead (&Instance->DadPassedMap, NULL); + } + + // + // Signal the waiting events. + // + NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } +} + +/** + The work function for EfiIp6ConfigSetData() to set the station addresses manually + for the EFI IPv6 network stack. It is only configurable when the policy is + Ip6ConfigPolicyManual. + + @param[in] Instance Pointer to the IP6 configuration 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 +Ip6ConfigSetManualAddress ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_IP6_CONFIG_MANUAL_ADDRESS *NewAddress; + EFI_IP6_CONFIG_MANUAL_ADDRESS *TmpAddress; + IP6_CONFIG_DATA_ITEM *DataItem; + UINTN NewAddressCount; + UINTN Index1; + UINTN Index2; + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *CurrentAddrInfo; + IP6_ADDRESS_INFO *Copy; + LIST_ENTRY CurrentSourceList; + UINT32 CurrentSourceCount; + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + EFI_STATUS Status; + BOOLEAN IsUpdated; + LIST_ENTRY *Next; + IP6_DAD_ENTRY *DadEntry; + IP6_DELAY_JOIN_LIST *DelayNode; + + NewAddress = NULL; + TmpAddress = NULL; + CurrentAddrInfo = NULL; + Copy = NULL; + Entry = NULL; + Entry2 = NULL; + IpIf = NULL; + PrefixEntry = NULL; + Next = NULL; + DadEntry = NULL; + DelayNode = NULL; + Status = EFI_SUCCESS; + + ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY); + + if ((DataSize != 0) && ((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + + if (Data != NULL && DataSize != 0) { + NewAddressCount = DataSize / sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + NewAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) Data; + + for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { + + if (NetIp6IsLinkLocalAddr (&NewAddress->Address) || + !NetIp6IsValidUnicast (&NewAddress->Address) || + (NewAddress->PrefixLength > 128) + ) { + // + // make sure the IPv6 address is unicast and not link-local address && + // the prefix length is valid. + // + return EFI_INVALID_PARAMETER; + } + + TmpAddress = NewAddress + 1; + for (Index2 = Index1 + 1; Index2 < NewAddressCount; Index2++, TmpAddress++) { + // + // Any two addresses in the array can't be equal. + // + if (EFI_IP6_EQUAL (&TmpAddress->Address, &NewAddress->Address)) { + + return EFI_INVALID_PARAMETER; + } + } + } + + // + // Build the current source address list. + // + InitializeListHead (&CurrentSourceList); + CurrentSourceCount = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + CurrentAddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), CurrentAddrInfo); + if (Copy == NULL) { + break; + } + + InsertTailList (&CurrentSourceList, &Copy->Link); + CurrentSourceCount++; + } + } + + // + // Update the value... a long journey starts + // + NewAddress = AllocateCopyPool (DataSize, Data); + if (NewAddress == NULL) { + Ip6RemoveAddr (NULL, &CurrentSourceList, &CurrentSourceCount, NULL, 0); + + return EFI_OUT_OF_RESOURCES; + } + + // + // Store the new data, and init the DataItem status to EFI_NOT_READY because + // we may have an asynchronous configuration process. + // + if (DataItem->Data.Ptr != NULL) { + FreePool (DataItem->Data.Ptr); + } + DataItem->Data.Ptr = NewAddress; + DataItem->DataSize = DataSize; + DataItem->Status = EFI_NOT_READY; + + // + // Trigger DAD, it's an asynchronous process. + // + IsUpdated = FALSE; + + for (Index1 = 0; Index1 < NewAddressCount; Index1++, NewAddress++) { + if (Ip6IsOneOfSetAddress (IpSb, &NewAddress->Address, NULL, &CurrentAddrInfo)) { + ASSERT (CurrentAddrInfo != NULL); + // + // Remove this already existing source address from the CurrentSourceList + // built before. + // + Ip6RemoveAddr ( + NULL, + &CurrentSourceList, + &CurrentSourceCount, + &CurrentAddrInfo->Address, + 128 + ); + + // + // If the new address's prefix length is not specified, just use the previous configured + // prefix length for this address. + // + if (NewAddress->PrefixLength == 0) { + NewAddress->PrefixLength = CurrentAddrInfo->PrefixLength; + } + + // + // This manual address is already in use, see whether prefix length is changed. + // + if (NewAddress->PrefixLength != CurrentAddrInfo->PrefixLength) { + // + // Remove the on-link prefix table, the route entry will be removed + // implicitly. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + CurrentAddrInfo->PrefixLength, + &CurrentAddrInfo->Address + ); + if (PrefixEntry != NULL) { + Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); + } + + // + // Save the prefix length. + // + CurrentAddrInfo->PrefixLength = NewAddress->PrefixLength; + IsUpdated = TRUE; + } + + // + // create a new on-link prefix entry. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + NewAddress->PrefixLength, + &NewAddress->Address + ); + if (PrefixEntry == NULL) { + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + NewAddress->PrefixLength, + &NewAddress->Address + ); + } + + CurrentAddrInfo->IsAnycast = NewAddress->IsAnycast; + // + // Artificially mark this address passed DAD be'coz it is already in use. + // + Ip6ManualAddrDadCallback (TRUE, &NewAddress->Address, Instance); + } else { + // + // A new address. + // + IsUpdated = TRUE; + + // + // Set the new address, this will trigger DAD and activate the address if + // DAD succeeds. + // + Ip6SetAddress ( + IpSb->DefaultInterface, + &NewAddress->Address, + NewAddress->IsAnycast, + NewAddress->PrefixLength, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + Ip6ManualAddrDadCallback, + Instance + ); + } + } + + // + // Check the CurrentSourceList, it now contains those addresses currently in + // use and will be removed. + // + IpIf = IpSb->DefaultInterface; + + while (!IsListEmpty (&CurrentSourceList)) { + IsUpdated = TRUE; + + CurrentAddrInfo = NET_LIST_HEAD (&CurrentSourceList, IP6_ADDRESS_INFO, Link); + + // + // This local address is going to be removed, the IP instances that are + // currently using it will be destroyed. + // + Ip6RemoveAddr ( + IpSb, + &IpIf->AddressList, + &IpIf->AddressCount, + &CurrentAddrInfo->Address, + 128 + ); + + // + // Remove the on-link prefix table, the route entry will be removed + // implicitly. + // + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + CurrentAddrInfo->PrefixLength, + &CurrentAddrInfo->Address + ); + if (PrefixEntry != NULL) { + Ip6DestroyPrefixListEntry (IpSb, PrefixEntry, TRUE, FALSE); + } + + RemoveEntryList (&CurrentAddrInfo->Link); + FreePool (CurrentAddrInfo); + } + + if (IsUpdated) { + if (DataItem->Status == EFI_NOT_READY) { + // + // If DAD is disabled on this interface, the configuration process is + // actually synchronous, and the data item's status will be changed to + // the final status before we reach here, just check it. + // + Status = EFI_NOT_READY; + } else { + Status = EFI_SUCCESS; + } + } else { + // + // No update is taken, reset the status to success and return EFI_ABORTED. + // + DataItem->Status = EFI_SUCCESS; + Status = EFI_ABORTED; + } + } 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; + + Ip6CleanDefaultRouterList (IpSb); + Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); + Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); + Ip6CleanAssembleTable (&IpSb->Assemble); + + if (IpSb->LinkLocalOk) { + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + IP6_LINK_LOCAL_PREFIX_LENGTH, + &IpSb->LinkLocalAddr + ); + } + + Ip6RemoveAddr ( + IpSb, + &IpSb->DefaultInterface->AddressList, + &IpSb->DefaultInterface->AddressCount, + NULL, + 0 + ); + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + // + // Remove all pending delay node and DAD entries for the global addresses. + // + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { + DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); + if (!NetIp6IsLinkLocalAddr (&DelayNode->AddressInfo->Address)) { + RemoveEntryList (&DelayNode->Link); + FreePool (DelayNode); + } + } + + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { + DadEntry = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); + + if (!NetIp6IsLinkLocalAddr (&DadEntry->AddressInfo->Address)) { + // + // Fail this DAD entry if the address is not link-local. + // + Ip6OnDADFinished (FALSE, IpIf, DadEntry); + } + } + } + } + + return Status; +} + +/** + The work function for EfiIp6ConfigSetData() to set the gateway addresses manually + for the EFI IPv6 network stack that is running on the communication device that + this EFI IPv6 Configuration Protocol manages. It is not configurable when the policy is + Ip6ConfigPolicyAutomatic. The gateway addresses must be unicast IPv6 addresses. + + @param[in] Instance The pointer to the IP6 config 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 +Ip6ConfigSetGateway ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + UINTN Index1; + UINTN Index2; + EFI_IPv6_ADDRESS *OldGateway; + EFI_IPv6_ADDRESS *NewGateway; + UINTN OldGatewayCount; + UINTN NewGatewayCount; + IP6_CONFIG_DATA_ITEM *Item; + BOOLEAN OneRemoved; + BOOLEAN OneAdded; + IP6_SERVICE *IpSb; + IP6_DEFAULT_ROUTER *DefaultRouter; + VOID *Tmp; + + OldGateway = NULL; + NewGateway = NULL; + Item = NULL; + DefaultRouter = NULL; + Tmp = NULL; + OneRemoved = FALSE; + OneAdded = FALSE; + + if ((DataSize != 0) && (DataSize % sizeof (EFI_IPv6_ADDRESS) != 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + Item = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + OldGateway = Item->Data.Gateway; + OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS); + + for (Index1 = 0; Index1 < OldGatewayCount; Index1++) { + // + // Remove this default router. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + OneRemoved = TRUE; + } + } + + if (Data != NULL && DataSize != 0) { + NewGateway = (EFI_IPv6_ADDRESS *) Data; + NewGatewayCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + for (Index1 = 0; Index1 < NewGatewayCount; Index1++) { + + if (!NetIp6IsValidUnicast (NewGateway + Index1)) { + + return EFI_INVALID_PARAMETER; + } + + for (Index2 = Index1 + 1; Index2 < NewGatewayCount; Index2++) { + if (EFI_IP6_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++) { + + DefaultRouter = Ip6FindDefaultRouter (IpSb, NewGateway + Index1); + if (DefaultRouter == NULL) { + Ip6CreateDefaultRouter (IpSb, NewGateway + Index1, IP6_INF_ROUTER_LIFETIME); + OneAdded = TRUE; + } + } + + if (!OneRemoved && !OneAdded) { + 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; + } + } else { + // + // DataSize is 0 and Data is NULL, clean up the Gateway address. + // + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + Item->Data.Ptr = NULL; + Item->DataSize = 0; + Item->Status = EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + The work function for EfiIp6ConfigSetData() to set the DNS server list for the + EFI IPv6 network stack running on the communication device that this EFI IPv6 + Configuration Protocol manages. It is not configurable when the policy is + Ip6ConfigPolicyAutomatic. The DNS server addresses must be unicast IPv6 addresses. + + @param[in] Instance The pointer to the IP6 config 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_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 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 IPv6 + network stack was set. + +**/ +EFI_STATUS +Ip6ConfigSetDnsServer ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ) +{ + UINTN OldIndex; + UINTN NewIndex; + EFI_IPv6_ADDRESS *OldDns; + EFI_IPv6_ADDRESS *NewDns; + UINTN OldDnsCount; + UINTN NewDnsCount; + IP6_CONFIG_DATA_ITEM *Item; + BOOLEAN OneAdded; + VOID *Tmp; + + OldDns = NULL; + NewDns = NULL; + Item = NULL; + Tmp = NULL; + + if ((DataSize != 0) && (DataSize % sizeof (EFI_IPv6_ADDRESS) != 0)) { + return EFI_BAD_BUFFER_SIZE; + } + + if (Instance->Policy != Ip6ConfigPolicyManual) { + return EFI_WRITE_PROTECTED; + } + + Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + + if (Data != NULL && DataSize != 0) { + NewDns = (EFI_IPv6_ADDRESS *) Data; + OldDns = Item->Data.DnsServers; + NewDnsCount = DataSize / sizeof (EFI_IPv6_ADDRESS); + OldDnsCount = Item->DataSize / sizeof (EFI_IPv6_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++) { + + if (!NetIp6IsValidUnicast (NewDns + NewIndex)) { + // + // 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_IP6_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; + } + } 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 EFI_SUCCESS; +} + +/** + Generate the operational state of the interface this IP6 config instance manages + and output in EFI_IP6_CONFIG_INTERFACE_INFO. + + @param[in] IpSb The pointer to the IP6 service binding instance. + @param[out] IfInfo The pointer to the IP6 configuration interface information structure. + +**/ +VOID +Ip6ConfigInitIfInfo ( + IN IP6_SERVICE *IpSb, + OUT EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo + ) +{ + UnicodeSPrint ( + IfInfo->Name, + sizeof (IfInfo->Name), + L"eth%d", + IpSb->Ip6ConfigInstance.IfIndex + ); + + IfInfo->IfType = IpSb->SnpMode.IfType; + IfInfo->HwAddressSize = IpSb->SnpMode.HwAddressSize; + CopyMem (&IfInfo->HwAddress, &IpSb->SnpMode.CurrentAddress, IfInfo->HwAddressSize); +} + +/** + Parse DHCPv6 reply packet to get the DNS server list. + It is the work function for Ip6ConfigOnDhcp6Reply and Ip6ConfigOnDhcp6Event. + + @param[in] Dhcp6 The pointer to the EFI_DHCP6_PROTOCOL instance. + @param[in, out] Instance The pointer to the IP6 configuration instance data. + @param[in] Reply The pointer to the DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +Ip6ConfigParseDhcpReply ( + IN EFI_DHCP6_PROTOCOL *Dhcp6, + IN OUT IP6_CONFIG_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Reply + ) +{ + EFI_STATUS Status; + UINT32 OptCount; + EFI_DHCP6_PACKET_OPTION **OptList; + UINT16 OpCode; + UINT16 Length; + UINTN Index; + UINTN Index2; + EFI_IPv6_ADDRESS *DnsServer; + IP6_CONFIG_DATA_ITEM *Item; + + // + // A DHCPv6 reply packet is received as the response to our InfoRequest + // packet. + // + OptCount = 0; + Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, NULL); + if (Status != EFI_BUFFER_TOO_SMALL) { + return EFI_NOT_READY; + } + + OptList = AllocatePool (OptCount * sizeof (EFI_DHCP6_PACKET_OPTION *)); + if (OptList == NULL) { + return EFI_NOT_READY; + } + + Status = Dhcp6->Parse (Dhcp6, Reply, &OptCount, OptList); + if (EFI_ERROR (Status)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + Status = EFI_SUCCESS; + + for (Index = 0; Index < OptCount; Index++) { + // + // Go through all the options to check the ones we are interested in. + // The OpCode and Length are in network byte-order and may not be naturally + // aligned. + // + CopyMem (&OpCode, &OptList[Index]->OpCode, sizeof (OpCode)); + OpCode = NTOHS (OpCode); + + if (OpCode == DHCP6_OPT_DNS_SERVERS) { + CopyMem (&Length, &OptList[Index]->OpLen, sizeof (Length)); + Length = NTOHS (Length); + + if ((Length == 0) || ((Length % sizeof (EFI_IPv6_ADDRESS)) != 0)) { + // + // The length should be a multiple of 16 bytes. + // + Status = EFI_NOT_READY; + break; + } + + // + // Validate the DnsServers: whether they are unicast addresses. + // + DnsServer = (EFI_IPv6_ADDRESS *) OptList[Index]->Data; + for (Index2 = 0; Index2 < Length / sizeof (EFI_IPv6_ADDRESS); Index2++) { + if (!NetIp6IsValidUnicast (DnsServer)) { + Status = EFI_NOT_READY; + goto ON_EXIT; + } + + DnsServer++; + } + + Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + + if (Item->DataSize != Length) { + if (Item->Data.Ptr != NULL) { + FreePool (Item->Data.Ptr); + } + + Item->Data.Ptr = AllocatePool (Length); + ASSERT (Item->Data.Ptr != NULL); + } + + CopyMem (Item->Data.Ptr, OptList[Index]->Data, Length); + Item->DataSize = Length; + Item->Status = EFI_SUCCESS; + + // + // Signal the waiting events. + // + NetMapIterate (&Item->EventMap, Ip6ConfigSignalEvent, NULL); + + break; + } + } + +ON_EXIT: + + FreePool (OptList); + return Status; +} + +/** + The callback function for Ip6SetAddr. The prototype is defined + as IP6_DAD_CALLBACK. It is called after Duplicate Address Detection is performed + on the tentative address by DHCPv6 in Ip6ConfigOnDhcp6Event(). + + @param[in] IsDadPassed If TRUE, Duplicate Address Detection passes. + @param[in] TargetAddress The tentative IPv6 address to be checked. + @param[in] Context Pointer to the IP6 configuration instance data. + +**/ +VOID +Ip6ConfigSetStatefulAddrCallback ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + // + // We should record the addresses that fail the DAD, and DECLINE them. + // + if (IsDadPassed) { + // + // Decrease the count, no interests in those passed DAD. + // + if (Instance->FailedIaAddressCount > 0 ) { + Instance->FailedIaAddressCount--; + } + } else { + // + // Record it. + // + IP6_COPY_ADDRESS (Instance->DeclineAddress + Instance->DeclineAddressCount, TargetAddress); + Instance->DeclineAddressCount++; + } + + if (Instance->FailedIaAddressCount == Instance->DeclineAddressCount) { + // + // The checking on all addresses are finished. + // + if (Instance->DeclineAddressCount != 0) { + // + // Decline those duplicates. + // + if (Instance->Dhcp6 != NULL) { + Instance->Dhcp6->Decline ( + Instance->Dhcp6, + Instance->DeclineAddressCount, + Instance->DeclineAddress + ); + } + } + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + Instance->DeclineAddress = NULL; + Instance->DeclineAddressCount = 0; + } +} + +/** + The event handle routine when DHCPv6 process is finished or is updated. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP6 configuration instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6Event ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + EFI_DHCP6_PROTOCOL *Dhcp6; + EFI_STATUS Status; + EFI_DHCP6_MODE_DATA Dhcp6ModeData; + EFI_DHCP6_IA *Ia; + EFI_DHCP6_IA_ADDRESS *IaAddr; + UINT32 Index; + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddrInfo; + IP6_INTERFACE *IpIf; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + + if ((Instance->Policy != Ip6ConfigPolicyAutomatic) || Instance->OtherInfoOnly) { + // + // IPv6 is not operating in the automatic policy now or + // the DHCPv6 information request message exchange is aborted. + // + return ; + } + + // + // The stateful address autoconfiguration is done or updated. + // + Dhcp6 = Instance->Dhcp6; + + Status = Dhcp6->GetModeData (Dhcp6, &Dhcp6ModeData, NULL); + if (EFI_ERROR (Status)) { + return ; + } + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + IpIf = IpSb->DefaultInterface; + Ia = Dhcp6ModeData.Ia; + IaAddr = Ia->IaAddress; + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + + Instance->DeclineAddress = (EFI_IPv6_ADDRESS *) AllocatePool (Ia->IaAddressCount * sizeof (EFI_IPv6_ADDRESS)); + if (Instance->DeclineAddress == NULL) { + goto ON_EXIT; + } + + Instance->FailedIaAddressCount = Ia->IaAddressCount; + Instance->DeclineAddressCount = 0; + + for (Index = 0; Index < Ia->IaAddressCount; Index++, IaAddr++) { + if (Ia->IaAddress[Index].ValidLifetime != 0 && Ia->State == Dhcp6Bound) { + // + // Set this address, either it's a new address or with updated lifetimes. + // An appropriate prefix length will be set. + // + Ip6SetAddress ( + IpIf, + &IaAddr->IpAddress, + FALSE, + 0, + IaAddr->ValidLifetime, + IaAddr->PreferredLifetime, + Ip6ConfigSetStatefulAddrCallback, + Instance + ); + } else { + // + // discard this address, artificially decrease the count as if this address + // passed DAD. + // + if (Ip6IsOneOfSetAddress (IpSb, &IaAddr->IpAddress, NULL, &AddrInfo)) { + ASSERT (AddrInfo != NULL); + Ip6RemoveAddr ( + IpSb, + &IpIf->AddressList, + &IpIf->AddressCount, + &AddrInfo->Address, + AddrInfo->PrefixLength + ); + } + + if (Instance->FailedIaAddressCount > 0) { + Instance->FailedIaAddressCount--; + } + } + } + + // + // Parse the Reply packet to get the options we need. + // + if (Dhcp6ModeData.Ia->ReplyPacket != NULL) { + Ip6ConfigParseDhcpReply (Dhcp6, Instance, Dhcp6ModeData.Ia->ReplyPacket); + } + +ON_EXIT: + + FreePool (Dhcp6ModeData.ClientId); + FreePool (Dhcp6ModeData.Ia); +} + +/** + The event process routine when the DHCPv6 server is answered with a reply packet + for an information request. + + @param[in] This Points to the EFI_DHCP6_PROTOCOL. + @param[in] Context The pointer to the IP6 configuration instance data. + @param[in] Packet The DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigOnDhcp6Reply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ) +{ + return Ip6ConfigParseDhcpReply (This, (IP6_CONFIG_INSTANCE *) Context, Packet); +} + +/** + The event process routine when the DHCPv6 service binding protocol is installed + in the system. + + @param[in] Event Not used. + @param[in] Context The pointer to the IP6 config instance data. + +**/ +VOID +EFIAPI +Ip6ConfigOnDhcp6SbInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_CONFIG_INSTANCE *Instance; + + Instance = (IP6_CONFIG_INSTANCE *) Context; + + if ((Instance->Dhcp6Handle != NULL) || (Instance->Policy != Ip6ConfigPolicyAutomatic)) { + // + // The DHCP6 child is already created or the policy is no longer AUTOMATIC. + // + return ; + } + + Ip6ConfigStartStatefulAutoConfig (Instance, Instance->OtherInfoOnly); +} + +/** + Set the configuration for the EFI IPv6 network stack running on the communication + device this EFI IPv6 Configuration Protocol instance manages. + + This function is used to set the configuration data of type DataType for the EFI + IPv6 network stack that is running on the communication device that this EFI IPv6 + 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 IPv6 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_IP6_CONFIG_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 DataSizedo 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 +EfiIp6ConfigSetData ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN UINTN DataSize, + IN VOID *Data + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (Data == NULL && DataSize != 0) || (Data != NULL && DataSize == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + Instance = IP6_CONFIG_INSTANCE_FROM_PROTOCOL (This); + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + 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, Ip6ConfigSignalEvent, NULL); + Ip6ConfigWriteConfigData (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, Ip6ConfigSignalEvent, 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 IPv6 network stack running on the communication + device that this EFI IPv6 Configuration Protocol instance manages. + + This function returns the configuration data of type DataType for the EFI IPv6 network + stack running on the communication device that this EFI IPv6 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_IP6_CONFIG_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 +EfiIp6ConfigGetData ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + IP6_CONFIG_DATA_ITEM *DataItem; + + if ((This == NULL) || (DataSize == NULL) || ((*DataSize != 0) && (Data == NULL))) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_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_IP6_CONFIG_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 +EfiIp6ConfigRegisterDataNotify ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + NET_MAP *EventMap; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_UNSUPPORTED; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_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_IP6_CONFIG_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 +EfiIp6ConfigUnregisterDataNotify ( + IN EFI_IP6_CONFIG_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + IN EFI_EVENT Event + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_CONFIG_INSTANCE *Instance; + NET_MAP_ITEM *Item; + + if ((This == NULL) || (Event == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (DataType >= Ip6ConfigDataTypeMaximum) { + return EFI_NOT_FOUND; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance = IP6_CONFIG_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 IP6_CONFIG_INSTANCE. + + @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip6ConfigInitInstance ( + OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + IP6_CONFIG_INSTANCE *TmpInstance; + LIST_ENTRY *Entry; + EFI_STATUS Status; + UINTN Index; + UINT16 IfIndex; + IP6_CONFIG_DATA_ITEM *DataItem; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + Instance->Signature = IP6_CONFIG_INSTANCE_SIGNATURE; + + // + // Determine the index of this interface. + // + IfIndex = 0; + NET_LIST_FOR_EACH (Entry, &mIp6ConfigInstanceList) { + TmpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_CONFIG_INSTANCE, Link, IP6_CONFIG_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 < Ip6ConfigDataTypeMaximum; Index++) { + // + // Initialize the event map for each data item. + // + NetMapInit (&Instance->DataItem[Index].EventMap); + } + + // + // Initialize the NET_MAPs used for DAD on manually configured source addresses. + // + NetMapInit (&Instance->DadFailedMap); + NetMapInit (&Instance->DadPassedMap); + + // + // 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[Ip6ConfigDataTypeInterfaceInfo]; + DataItem->GetData = Ip6ConfigGetIfInfo; + DataItem->Data.Ptr = &Instance->InterfaceInfo; + DataItem->DataSize = sizeof (Instance->InterfaceInfo); + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED | DATA_ATTRIB_VOLATILE); + Ip6ConfigInitIfInfo (IpSb, &Instance->InterfaceInfo); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeAltInterfaceId]; + DataItem->SetData = Ip6ConfigSetAltIfId; + DataItem->Data.Ptr = &Instance->AltIfId; + DataItem->DataSize = sizeof (Instance->AltIfId); + DataItem->Status = EFI_NOT_FOUND; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypePolicy]; + DataItem->SetData = Ip6ConfigSetPolicy; + DataItem->Data.Ptr = &Instance->Policy; + DataItem->DataSize = sizeof (Instance->Policy); + Instance->Policy = Ip6ConfigPolicyManual; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDupAddrDetectTransmits]; + DataItem->SetData = Ip6ConfigSetDadXmits; + DataItem->Data.Ptr = &Instance->DadXmits; + DataItem->DataSize = sizeof (Instance->DadXmits); + Instance->DadXmits.DupAddrDetectTransmits = IP6_CONFIG_DEFAULT_DAD_XMITS; + SET_DATA_ATTRIB (DataItem->Attribute, DATA_ATTRIB_SIZE_FIXED); + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress]; + DataItem->SetData = Ip6ConfigSetManualAddress; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeGateway]; + DataItem->SetData = Ip6ConfigSetGateway; + DataItem->Status = EFI_NOT_FOUND; + + DataItem = &Instance->DataItem[Ip6ConfigDataTypeDnsServer]; + DataItem->SetData = Ip6ConfigSetDnsServer; + DataItem->Status = EFI_NOT_FOUND; + + // + // Create the event used for DHCP. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + Ip6ConfigOnDhcp6Event, + Instance, + &Instance->Dhcp6Event + ); + ASSERT_EFI_ERROR (Status); + + Instance->Configured = TRUE; + + // + // Try to read the config data from NV variable. + // + Status = Ip6ConfigReadConfigData (IpSb->MacString, Instance); + if (Status == EFI_NOT_FOUND) { + // + // The NV variable is not set, so generate a random IAID, and write down the + // fresh new configuration as the NV variable now. + // + Instance->IaId = NET_RANDOM (NetRandomInitSeed ()); + + for (Index = 0; Index < IpSb->SnpMode.HwAddressSize; Index++) { + Instance->IaId |= (IpSb->SnpMode.CurrentAddress.Addr[Index] << ((Index << 3) & 31)); + } + + Ip6ConfigWriteConfigData (IpSb->MacString, Instance); + } else if (EFI_ERROR (Status)) { + return Status; + } + + Instance->Ip6Config.SetData = EfiIp6ConfigSetData; + Instance->Ip6Config.GetData = EfiIp6ConfigGetData; + Instance->Ip6Config.RegisterDataNotify = EfiIp6ConfigRegisterDataNotify; + Instance->Ip6Config.UnregisterDataNotify = EfiIp6ConfigUnregisterDataNotify; + + + // + // Publish the IP6 configuration form + // + return Ip6ConfigFormInit (Instance); +} + +/** + Release an IP6_CONFIG_INSTANCE. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + +**/ +VOID +Ip6ConfigCleanInstance ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + UINTN Index; + IP6_CONFIG_DATA_ITEM *DataItem; + + if (Instance->DeclineAddress != NULL) { + FreePool (Instance->DeclineAddress); + } + + if (!Instance->Configured) { + return ; + } + + if (Instance->Dhcp6Handle != NULL) { + + Ip6ConfigDestroyDhcp6 (Instance); + } + + // + // Close the event. + // + if (Instance->Dhcp6Event != NULL) { + gBS->CloseEvent (Instance->Dhcp6Event); + } + + NetMapClean (&Instance->DadPassedMap); + NetMapClean (&Instance->DadFailedMap); + + for (Index = 0; Index < Ip6ConfigDataTypeMaximum; 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); + } + + Ip6ConfigFormUnload (Instance); + + RemoveEntryList (&Instance->Link); +} + +/** + Destroy the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destroy the child. + +**/ +EFI_STATUS +Ip6ConfigDestroyDhcp6 ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_DHCP6_PROTOCOL *Dhcp6; + + Dhcp6 = Instance->Dhcp6; + ASSERT (Dhcp6 != NULL); + + Dhcp6->Stop (Dhcp6); + Dhcp6->Configure (Dhcp6, NULL); + Instance->Dhcp6 = NULL; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + + // + // Close DHCPv6 protocol and destroy the child. + // + Status = gBS->CloseProtocol ( + Instance->Dhcp6Handle, + &gEfiDhcp6ProtocolGuid, + IpSb->Image, + IpSb->Controller + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = NetLibDestroyServiceChild ( + IpSb->Controller, + IpSb->Image, + &gEfiDhcp6ServiceBindingProtocolGuid, + Instance->Dhcp6Handle + ); + + Instance->Dhcp6Handle = NULL; + + return Status; +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h new file mode 100644 index 00000000..9375c97f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h @@ -0,0 +1,307 @@ +/** @file + Definitions for EFI IPv6 Configuration Protocol implementation. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __IP6_CONFIG_IMPL_H__ +#define __IP6_CONFIG_IMPL_H__ + +#define IP6_CONFIG_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'C') +#define IP6_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'F', 'C', 'I') +#define IP6_CONFIG_VARIABLE_ATTRIBUTE (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS) + +#define IP6_CONFIG_DEFAULT_DAD_XMITS 1 + +#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)) + +typedef struct _IP6_CONFIG_INSTANCE IP6_CONFIG_INSTANCE; + +#define IP6_CONFIG_INSTANCE_FROM_PROTOCOL(Proto) \ + CR ((Proto), \ + IP6_CONFIG_INSTANCE, \ + Ip6Config, \ + IP6_CONFIG_INSTANCE_SIGNATURE \ + ) + + +#define IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK(Callback) \ + CR ((Callback), \ + IP6_CONFIG_INSTANCE, \ + CallbackInfo, \ + IP6_CONFIG_INSTANCE_SIGNATURE \ + ) + +#define IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE(Instance) \ + CR ((Instance), \ + IP6_SERVICE, \ + Ip6ConfigInstance, \ + IP6_SERVICE_SIGNATURE \ + ) + +#define IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(ConfigAccess) \ + CR ((ConfigAccess), \ + IP6_FORM_CALLBACK_INFO, \ + HiiConfigAccess, \ + IP6_FORM_CALLBACK_INFO_SIGNATURE \ + ) + +/** + The prototype of work function for EfiIp6ConfigSetData(). + + @param[in] Instance The pointer to the IP6 config 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 IPv6 + network stack was set successfully. + +**/ +typedef +EFI_STATUS +(*IP6_CONFIG_SET_DATA) ( + IN IP6_CONFIG_INSTANCE *Instance, + IN UINTN DataSize, + IN VOID *Data + ); + +/** + The prototype of work function for EfiIp6ConfigGetData(). + + @param[in] Instance The pointer to the IP6 config 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 +(*IP6_CONFIG_GET_DATA) ( + IN IP6_CONFIG_INSTANCE *Instance, + IN OUT UINTN *DataSize, + IN VOID *Data OPTIONAL + ); + +typedef union { + VOID *Ptr; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + EFI_IP6_CONFIG_INTERFACE_ID *AltIfId; + EFI_IP6_CONFIG_POLICY *Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + EFI_IPv6_ADDRESS *Gateway; + EFI_IPv6_ADDRESS *DnsServers; +} IP6_CONFIG_DATA; + +typedef struct { + IP6_CONFIG_SET_DATA SetData; + IP6_CONFIG_GET_DATA GetData; + EFI_STATUS Status; + UINT8 Attribute; + NET_MAP EventMap; + IP6_CONFIG_DATA Data; + UINTN DataSize; +} IP6_CONFIG_DATA_ITEM; + +typedef struct { + UINT16 Offset; + UINT32 DataSize; + EFI_IP6_CONFIG_DATA_TYPE DataType; +} IP6_CONFIG_DATA_RECORD; + +#pragma pack(1) + +// +// heap data that contains the data for each data record. +// +// BOOLEAN IsAltIfIdSet; +// EFI_IP6_CONFIG_POLICY Policy; +// EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; +// UINT32 ManualaddressCount; +// UINT32 GatewayCount; +// UINT32 DnsServersCount; +// EFI_IP6_CONFIG_INTERFACE_ID AltIfId; +// EFI_IP6_CONFIG_MANUAL_ADDRESS ManualAddress[]; +// EFI_IPv6_ADDRESS Gateway[]; +// EFI_IPv6_ADDRESS DnsServers[]; +// +typedef struct { + UINT32 IaId; + UINT16 Checksum; + UINT16 DataRecordCount; + IP6_CONFIG_DATA_RECORD DataRecord[1]; +} IP6_CONFIG_VARIABLE; + +#pragma pack() + +typedef struct { + LIST_ENTRY Link; + EFI_IP6_ADDRESS_INFO AddrInfo; +} IP6_ADDRESS_INFO_ENTRY; + +typedef struct { + EFI_IP6_CONFIG_POLICY Policy; ///< manual or automatic + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadTransmitCount; ///< dad transmits count + EFI_IP6_CONFIG_INTERFACE_ID InterfaceId; ///< alternative interface id + LIST_ENTRY ManualAddress; ///< IP addresses + UINT32 ManualAddressCount; ///< IP addresses count + LIST_ENTRY GatewayAddress; ///< Gateway address + UINT32 GatewayAddressCount; ///< Gateway address count + LIST_ENTRY DnsAddress; ///< DNS server address + UINT32 DnsAddressCount; ///< DNS server address count +} IP6_CONFIG_NVDATA; + +typedef struct _IP6_FORM_CALLBACK_INFO { + UINT32 Signature; + EFI_HANDLE ChildHandle; + EFI_HII_CONFIG_ACCESS_PROTOCOL HiiConfigAccess; + EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath; + EFI_HII_HANDLE RegisteredHandle; +} IP6_FORM_CALLBACK_INFO; + +struct _IP6_CONFIG_INSTANCE { + UINT32 Signature; + BOOLEAN Configured; + LIST_ENTRY Link; + UINT16 IfIndex; + + EFI_IP6_CONFIG_INTERFACE_INFO InterfaceInfo; + EFI_IP6_CONFIG_INTERFACE_ID AltIfId; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + + IP6_CONFIG_DATA_ITEM DataItem[Ip6ConfigDataTypeMaximum]; + NET_MAP DadFailedMap; + NET_MAP DadPassedMap; + + EFI_IP6_CONFIG_PROTOCOL Ip6Config; + + EFI_EVENT Dhcp6SbNotifyEvent; + VOID *Registration; + EFI_HANDLE Dhcp6Handle; + EFI_DHCP6_PROTOCOL *Dhcp6; + BOOLEAN OtherInfoOnly; + UINT32 IaId; + EFI_EVENT Dhcp6Event; + UINT32 FailedIaAddressCount; + EFI_IPv6_ADDRESS *DeclineAddress; + UINT32 DeclineAddressCount; + + IP6_FORM_CALLBACK_INFO CallbackInfo; + IP6_CONFIG_NVDATA Ip6NvData; +}; + +/** + Read the configuration data from variable storage according to the VarName and + gEfiIp6ConfigProtocolGuid. 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 IP6_CONFIG_INSTANCE. + + @param[in] VarName The pointer to the variable name + @param[in, out] Instance The pointer to the IP6 config 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 +Ip6ConfigReadConfigData ( + IN CHAR16 *VarName, + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + The event process routine when the DHCPv6 server is answered with a reply packet + for an information request. + + @param[in] This Points to the EFI_DHCP6_PROTOCOL. + @param[in] Context The pointer to the IP6 configuration instance data. + @param[in] Packet The DHCPv6 reply packet. + + @retval EFI_SUCCESS The DNS server address was retrieved from the reply packet. + @retval EFI_NOT_READY The reply packet does not contain the DNS server option, or + the DNS server address is not valid. + +**/ +EFI_STATUS +EFIAPI +Ip6ConfigOnDhcp6Reply ( + IN EFI_DHCP6_PROTOCOL *This, + IN VOID *Context, + IN EFI_DHCP6_PACKET *Packet + ); + +/** + The work function to trigger the DHCPv6 process to perform a stateful autoconfiguration. + + @param[in] Instance Pointer to the IP6 config instance data. + @param[in] OtherInfoOnly If FALSE, get stateful address and other information + via DHCPv6. Otherwise, only get the other information. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_UNSUPPORTED The DHCP6 driver is not available. + +**/ +EFI_STATUS +Ip6ConfigStartStatefulAutoConfig ( + IN IP6_CONFIG_INSTANCE *Instance, + IN BOOLEAN OtherInfoOnly + ); + +/** + Initialize an IP6_CONFIG_INSTANCE. + + @param[out] Instance The buffer of IP6_CONFIG_INSTANCE to be initialized. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + @retval EFI_SUCCESS The IP6_CONFIG_INSTANCE initialized successfully. + +**/ +EFI_STATUS +Ip6ConfigInitInstance ( + OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Release an IP6_CONFIG_INSTANCE. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + +**/ +VOID +Ip6ConfigCleanInstance ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Destroy the Dhcp6 child in IP6_CONFIG_INSTANCE and release the resources. + + @param[in, out] Instance The buffer of IP6_CONFIG_INSTANCE to be freed. + + @retval EFI_SUCCESS The child was successfully destroyed. + @retval Others Failed to destroy the child. + +**/ +EFI_STATUS +Ip6ConfigDestroyDhcp6 ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c new file mode 100644 index 00000000..af9b4d2e --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c @@ -0,0 +1,2089 @@ +/** @file + Helper functions for configuring or obtaining the parameters relating to IP6. + + Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +CHAR16 mIp6ConfigStorageName[] = L"IP6_CONFIG_IFR_NVDATA"; + +/** + 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 +Ip6ConfigManualAddressNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + *((BOOLEAN *) Context) = TRUE; +} + +/** + Get the configuration data for the EFI IPv6 network stack running on the + communication. It is a help function to the call EfiIp6ConfigGetData(). + + @param[in] Ip6Config The pointer to the EFI_IP6_CONFIG_PROTOCOL instance. + @param[in] DataType The type of data to get. + @param[out] DataSize The size of buffer required in bytes. + @param[out] Data The data buffer in which the configuration data is returned. The + type of the data buffer associated with the DataType. + It is the caller's responsibility to free the resource. + + @retval EFI_SUCCESS The specified configuration data was obtained successfully. + @retval EFI_INVALID_PARAMETER One or more of the followings are TRUE: + - Ip6Config is NULL or invalid. + - DataSize is NULL. + - Data is NULL. + @retval EFI_OUT_OF_RESOURCES Fail to perform the operation due to lack of resources. + @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 was not found. + +**/ +EFI_STATUS +Ip6ConfigNvGetData ( + IN EFI_IP6_CONFIG_PROTOCOL *Ip6Config, + IN EFI_IP6_CONFIG_DATA_TYPE DataType, + OUT UINTN *DataSize, + OUT VOID **Data + ) +{ + UINTN BufferSize; + VOID *Buffer; + EFI_STATUS Status; + + if ((Ip6Config == NULL) || (Data == NULL) || (DataSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + BufferSize = 0; + Status = Ip6Config->GetData ( + Ip6Config, + DataType, + &BufferSize, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + Buffer = AllocateZeroPool (BufferSize); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6Config->GetData ( + Ip6Config, + DataType, + &BufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + FreePool (Buffer); + return Status; + } + + *DataSize = BufferSize; + *Data = Buffer; + + return EFI_SUCCESS; +} + +/** + Free all nodes in IP6_ADDRESS_INFO_ENTRY in the list array specified + with ListHead. + + @param[in] ListHead The head of the list array in IP6_ADDRESS_INFO_ENTRY. + +**/ +VOID +Ip6FreeAddressInfoList ( + IN LIST_ENTRY *ListHead + ) +{ + IP6_ADDRESS_INFO_ENTRY *Node; + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, ListHead) { + Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link); + RemoveEntryList (&Node->Link); + FreePool (Node); + } +} + +/** + Convert the IPv6 address into a formatted string. + + @param[in] Ip6 The IPv6 address. + @param[out] Str The formatted IP string. + +**/ +VOID +Ip6ToStr ( + IN EFI_IPv6_ADDRESS *Ip6, + OUT CHAR16 *Str + ) +{ + UINTN Index; + BOOLEAN Short; + UINTN Number; + CHAR16 FormatString[8]; + + Short = FALSE; + + for (Index = 0; Index < 15; Index = Index + 2) { + if (!Short && + Index % 2 == 0 && + Ip6->Addr[Index] == 0 && + Ip6->Addr[Index + 1] == 0 + ) { + // + // Deal with the case of ::. + // + if (Index == 0) { + *Str = L':'; + *(Str + 1) = L':'; + Str = Str + 2; + } else { + *Str = L':'; + Str = Str + 1; + } + + while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) { + Index = Index + 2; + } + + Short = TRUE; + + if (Index == 16) { + // + // :: is at the end of the address. + // + *Str = L'\0'; + break; + } + } + + ASSERT (Index < 15); + + if (Ip6->Addr[Index] == 0) { + Number = UnicodeSPrint (Str, 2 * IP6_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]); + } else { + if (Ip6->Addr[Index + 1] < 0x10) { + CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:")); + } else { + CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:")); + } + + Number = UnicodeSPrint ( + Str, + 2 * IP6_STR_MAX_SIZE, + (CONST CHAR16 *) FormatString, + (UINTN) Ip6->Addr[Index], + (UINTN) Ip6->Addr[Index + 1] + ); + } + + Str = Str + Number; + + if (Index + 2 == 16) { + *Str = L'\0'; + if (*(Str - 1) == L':') { + *(Str - 1) = L'\0'; + } + } + } +} + +/** + Convert EFI_IP6_CONFIG_INTERFACE_ID to string format. + + @param[out] String The buffer to store the converted string. + @param[in] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The string converted successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6ConvertInterfaceIdToString ( + OUT CHAR16 *String, + IN EFI_IP6_CONFIG_INTERFACE_ID *IfId + ) +{ + UINT8 Index; + UINTN Number; + + if ((String == NULL) || (IfId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; Index < 8; Index++) { + Number = UnicodeSPrint ( + String, + 2 * INTERFACE_ID_STR_STORAGE, + L"%x:", + (UINTN) IfId->Id[Index] + ); + String = String + Number; + } + + *(String - 1) = '\0'; + + return EFI_SUCCESS; +} + +/** + Parse InterfaceId in string format and convert it to EFI_IP6_CONFIG_INTERFACE_ID. + + @param[in] String The buffer of the string to be parsed. + @param[out] IfId The pointer of EFI_IP6_CONFIG_INTERFACE_ID. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + +**/ +EFI_STATUS +Ip6ParseInterfaceIdFromString ( + IN CONST CHAR16 *String, + OUT EFI_IP6_CONFIG_INTERFACE_ID *IfId + ) +{ + UINT8 Index; + CHAR16 *IfIdStr; + CHAR16 *TempStr; + UINTN NodeVal; + + if ((String == NULL) || (IfId == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IfIdStr = (CHAR16 *) String; + + ZeroMem (IfId, sizeof (EFI_IP6_CONFIG_INTERFACE_ID)); + + for (Index = 0; Index < 8; Index++) { + TempStr = IfIdStr; + + while ((*IfIdStr != L'\0') && (*IfIdStr != L':')) { + IfIdStr++; + } + + // + // The InterfaceId format is X:X:X:X, the number of X should not exceed 8. + // If the number of X is less than 8, zero is appended to the InterfaceId. + // + if ((*IfIdStr == ':') && (Index == 7)) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the string to interface id. AsciiStrHexToUintn stops at the + // first character that is not a valid hex character, ':' or '\0' here. + // + NodeVal = StrHexToUintn (TempStr); + if (NodeVal > 0xFF) { + return EFI_INVALID_PARAMETER; + } + + IfId->Id[Index] = (UINT8) NodeVal; + + IfIdStr++; + } + + return EFI_SUCCESS; +} + +/** + Create Hii Extend Label OpCode as the start opcode and end opcode. It is + a help function. + + @param[in] StartLabelNumber The number of start label. + @param[out] StartOpCodeHandle Points to the start opcode handle. + @param[out] StartLabel Points to the created start opcode. + @param[out] EndOpCodeHandle Points to the end opcode handle. + @param[out] EndLabel Points to the created end opcode. + + @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this + operation. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_SUCCESS The operation completed successfully. + +**/ +EFI_STATUS +Ip6CreateOpCode ( + IN UINT16 StartLabelNumber, + OUT VOID **StartOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **StartLabel, + OUT VOID **EndOpCodeHandle, + OUT EFI_IFR_GUID_LABEL **EndLabel + ) +{ + EFI_STATUS Status; + EFI_IFR_GUID_LABEL *InternalStartLabel; + EFI_IFR_GUID_LABEL *InternalEndLabel; + + if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) { + return EFI_INVALID_PARAMETER; + } + + *StartOpCodeHandle = NULL; + *EndOpCodeHandle = NULL; + Status = EFI_OUT_OF_RESOURCES; + + // + // Initialize the container for dynamic opcodes. + // + *StartOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*StartOpCodeHandle == NULL) { + return Status; + } + + *EndOpCodeHandle = HiiAllocateOpCodeHandle (); + if (*EndOpCodeHandle == NULL) { + goto Exit; + } + + // + // Create Hii Extend Label OpCode as the start opcode. + // + InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *StartOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalStartLabel == NULL) { + goto Exit; + } + + InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalStartLabel->Number = StartLabelNumber; + + // + // Create Hii Extend Label OpCode as the end opcode. + // + InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( + *EndOpCodeHandle, + &gEfiIfrTianoGuid, + NULL, + sizeof (EFI_IFR_GUID_LABEL) + ); + if (InternalEndLabel == NULL) { + goto Exit; + } + + InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; + InternalEndLabel->Number = LABEL_END; + + *StartLabel = InternalStartLabel; + *EndLabel = InternalEndLabel; + + return EFI_SUCCESS; + +Exit: + + if (*StartOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*StartOpCodeHandle); + } + + if (*EndOpCodeHandle != NULL) { + HiiFreeOpCodeHandle (*EndOpCodeHandle); + } + + return Status; +} + +/** + This function converts the different format of address list to string format and + then generates the corresponding text opcode to illustrate the address info in + IP6 configuration page. Currently, the following formats are supported: + EFI_IP6_ADDRESS_INFO AddressType: Ip6ConfigNvHostAddress; + EFI_IPv6_ADDRESS AddressType: Ip6ConfigNvGatewayAddress and Ip6ConfigNvDnsAddress; + EFI_IP6_ROUTE_TABLE AddressType: Ip6ConfigNvRouteTable. + + @param[in, out] String The pointer to the buffer to store the converted + string. + @param[in] HiiHandle A handle that was previously registered in the + HII Database. + @param[in] AddressType The address type. + @param[in] AddressInfo Pointer to the address list. + @param[in] AddressCount The address count of the address list. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The AddressType is not supported. + + +**/ +EFI_STATUS +Ip6ConvertAddressListToString ( + IN OUT CHAR16 *String, + IN EFI_HII_HANDLE HiiHandle, + IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType, + IN VOID *AddressInfo, + IN UINTN AddressCount + ) +{ + UINTN Index; + UINTN Number; + CHAR16 *TempStr; + EFI_STATUS Status; + VOID *StartOpCodeHandle; + EFI_IFR_GUID_LABEL *StartLabel; + VOID *EndOpCodeHandle; + EFI_IFR_GUID_LABEL *EndLabel; + UINT16 StartLabelNumber; + EFI_STRING_ID TextTwo; + UINT8 *AddressHead; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS *Address; + + if ((String == NULL) || (HiiHandle == NULL) || (AddressInfo == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (AddressType == Ip6ConfigNvHostAddress) { + StartLabelNumber = HOST_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvGatewayAddress) { + StartLabelNumber = GATEWAY_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvDnsAddress) { + StartLabelNumber = DNS_ADDRESS_LABEL; + } else if (AddressType == Ip6ConfigNvRouteTable) { + StartLabelNumber = ROUTE_TABLE_LABEL; + } else { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Status = Ip6CreateOpCode ( + StartLabelNumber, + &StartOpCodeHandle, + &StartLabel, + &EndOpCodeHandle, + &EndLabel + ); + if (EFI_ERROR (Status)) { + return Status; + } + + AddressHead = (UINT8 *) AddressInfo; + + for (Index = 0; Index < AddressCount; Index++) { + if (AddressType == Ip6ConfigNvHostAddress) { + AddressInfo = AddressHead + sizeof (EFI_IP6_ADDRESS_INFO) * Index; + Address = &((EFI_IP6_ADDRESS_INFO *) AddressInfo)->Address; + } else if (AddressType == Ip6ConfigNvRouteTable) { + AddressInfo = AddressHead + sizeof (EFI_IP6_ROUTE_TABLE) * Index; + Address = &((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Destination; + } else { + AddressInfo = AddressHead + sizeof (EFI_IPv6_ADDRESS) * Index; + Address = AddressInfo; + } + + // + // Convert the IP address info to string. + // + Ip6ToStr (Address, String); + TempStr = String + StrLen (String); + + if ((AddressType == Ip6ConfigNvHostAddress) || (AddressType == Ip6ConfigNvRouteTable)) { + if (AddressType == Ip6ConfigNvHostAddress) { + PrefixLength = ((EFI_IP6_ADDRESS_INFO *) AddressInfo)->PrefixLength; + } else { + PrefixLength = ((EFI_IP6_ROUTE_TABLE *) AddressInfo)->PrefixLength; + } + + // + // Append the prefix length to the string. + // + *TempStr = L'/'; + TempStr++; + Number = UnicodeSPrint (TempStr, 6, L"%d", PrefixLength); + TempStr = TempStr + Number; + } + + if (AddressType == Ip6ConfigNvRouteTable) { + // + // Append " >> " to the string. + // + Number = UnicodeSPrint (TempStr, 8, L" >> "); + TempStr = TempStr + Number; + + // + // Append the gateway address to the string. + // + Ip6ToStr (&((EFI_IP6_ROUTE_TABLE *) AddressInfo)->Gateway, TempStr); + TempStr = TempStr + StrLen (TempStr); + } + + // + // Generate a text opcode and update the UI. + // + TextTwo = HiiSetString (HiiHandle, 0, String, NULL); + if (TextTwo == 0) { + Status = EFI_INVALID_PARAMETER; + goto Exit; + } + + HiiCreateTextOpCode (StartOpCodeHandle, STR_NULL, STR_NULL, TextTwo); + + String = TempStr; + *String = IP6_ADDRESS_DELIMITER; + String++; + } + + *(String - 1) = '\0'; + + Status = HiiUpdateForm ( + HiiHandle, // HII handle + &gIp6ConfigNvDataGuid, // Formset GUID + FORMID_MAIN_FORM, // Form ID + StartOpCodeHandle, // Label for where to insert opcodes + EndOpCodeHandle // Replace data + ); + +Exit: + HiiFreeOpCodeHandle (StartOpCodeHandle); + HiiFreeOpCodeHandle (EndOpCodeHandle); + + return Status; +} + +/** + Parse address list in string format and convert it to a list array of node in + IP6_ADDRESS_INFO_ENTRY. + + @param[in] String The buffer to string to be parsed. + @param[out] ListHead The list head of array. + @param[out] AddressCount The number of list nodes in the array. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of resource. + +**/ +EFI_STATUS +Ip6ParseAddressListFromString ( + IN CONST CHAR16 *String, + OUT LIST_ENTRY *ListHead, + OUT UINT32 *AddressCount + ) +{ + EFI_STATUS Status; + CHAR16 *LocalString; + CHAR16 *Temp; + CHAR16 *TempStr; + EFI_IP6_ADDRESS_INFO AddressInfo; + IP6_ADDRESS_INFO_ENTRY *Node; + BOOLEAN Last; + UINT32 Count; + + if ((String == NULL) || (ListHead == NULL) || (AddressCount == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO)); + LocalString = (CHAR16 *) AllocateCopyPool (StrSize (String), String); + if (LocalString == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Clean the original address list. + // + Ip6FreeAddressInfoList (ListHead); + + Temp = LocalString; + Last = FALSE; + Count = 0; + + while (*LocalString != L'\0') { + TempStr = LocalString; + while ((*LocalString != L'\0') && (*LocalString != IP6_ADDRESS_DELIMITER)) { + LocalString++; + } + + if (*LocalString == L'\0') { + Last = TRUE; + } + + *LocalString = L'\0'; + + Status = NetLibStrToIp6andPrefix (TempStr, &AddressInfo.Address, &AddressInfo.PrefixLength); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (AddressInfo.PrefixLength == 0xFF) { + AddressInfo.PrefixLength = 0; + } + + if (!NetIp6IsValidUnicast (&AddressInfo.Address)) { + Status = EFI_INVALID_PARAMETER; + goto Error; + } + + Node = AllocatePool (sizeof (IP6_ADDRESS_INFO_ENTRY)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + CopyMem (&Node->AddrInfo, &AddressInfo, sizeof (EFI_IP6_ADDRESS_INFO)); + InsertTailList (ListHead, &Node->Link); + Count++; + + if (Last) { + break; + } + + LocalString++; + } + + FreePool (Temp); + *AddressCount = Count; + return EFI_SUCCESS; + +Error: + Ip6FreeAddressInfoList (ListHead); + FreePool (Temp); + return Status; +} + +/** + This function converts the interface info to string and draws it to the IP6 UI. + The interface information includes interface name, interface type, hardware + address and route table information. + + @param[in] IfInfo The pointer of EFI_IP6_CONFIG_INTERFACE_INFO. + @param[in] HiiHandle The handle that was previously registered in the + HII Database. + @param[in, out] IfrNvData Points to IP6_CONFIG_IFR_NVDATA. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES The operation failed due to lack of resources. + +**/ +EFI_STATUS +Ip6ConvertInterfaceInfoToString ( + IN EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo, + IN EFI_HII_HANDLE HiiHandle, + IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData + ) +{ + UINT32 Index; + UINTN Number; + CHAR16 *String; + CHAR16 PortString[ADDRESS_STR_MAX_SIZE]; + CHAR16 FormatString[8]; + EFI_STRING_ID StringId; + + if ((IfInfo == NULL) || (HiiHandle == NULL) || (IfrNvData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Print the interface name. + // + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_INTERFACE_NAME_CONTENT), + IfInfo->Name, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Print the interface type. + // + if (IfInfo->IfType == Ip6InterfaceTypeEthernet) { + CopyMem (PortString, IP6_ETHERNET, sizeof (IP6_ETHERNET)); + } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) { + CopyMem (PortString, IP6_EXPERIMENTAL_ETHERNET, sizeof (IP6_EXPERIMENTAL_ETHERNET)); + } else { + // + // Refer to RFC1700, chapter Number Hardware Type. + // + UnicodeSPrint (PortString, 6, L"%d", IfInfo->IfType); + } + + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_INTERFACE_TYPE_CONTENT), + PortString, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Convert the hardware address. + // + String = PortString; + ASSERT (IfInfo->HwAddressSize <= 32); + + for (Index = 0; Index < IfInfo->HwAddressSize; Index++) { + + if (IfInfo->HwAddress.Addr[Index] < 0x10) { + CopyMem (FormatString, L"0%x-", sizeof (L"0%x-")); + } else { + CopyMem (FormatString, L"%x-", sizeof (L"%x-")); + } + + Number = UnicodeSPrint ( + String, + 8, + (CONST CHAR16 *) FormatString, + (UINTN) IfInfo->HwAddress.Addr[Index] + ); + String = String + Number; + } + + if (Index != 0) { + ASSERT (String > PortString); + String--; + *String = '\0'; + } + + // + // Print the hardware address. + // + StringId = HiiSetString ( + HiiHandle, + STRING_TOKEN (STR_IP6_MAC_ADDRESS_CONTENT), + PortString, + NULL + ); + if (StringId == 0) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** + Build the address info list from list array of node in IP6_ADDRESS_INFO_ENTRY. + + @param[in] Instance Points to IP6 config instance data. + @param[in] AddressType The address type. + @param[out] AddressInfo The pointer to the buffer to store the address list. + @param[out] AddressSize The address size of the address list. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The AddressType is not supported. + +**/ +EFI_STATUS +Ip6BuildNvAddressInfo ( + IN IP6_CONFIG_INSTANCE *Instance, + IN IP6_CONFIG_NV_ADDRESS_TYPE AddressType, + OUT VOID **AddressInfo, + OUT UINTN *AddressSize + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + LIST_ENTRY *Entry; + LIST_ENTRY *ListHead; + IP6_ADDRESS_INFO_ENTRY *Node; + VOID *AddressList; + VOID *TmpStr; + UINTN DataSize; + EFI_IPv6_ADDRESS *Ip6Address; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + + if ((Instance == NULL) || (AddressInfo == NULL) || (AddressSize == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + Ip6NvData = &Instance->Ip6NvData; + + if (AddressType == Ip6ConfigNvHostAddress) { + ListHead = &Ip6NvData->ManualAddress; + DataSize = sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS) * Ip6NvData->ManualAddressCount; + } else if (AddressType == Ip6ConfigNvGatewayAddress) { + ListHead = &Ip6NvData->GatewayAddress; + DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->GatewayAddressCount; + } else if (AddressType == Ip6ConfigNvDnsAddress) { + ListHead = &Ip6NvData->DnsAddress; + DataSize = sizeof (EFI_IPv6_ADDRESS) * Ip6NvData->DnsAddressCount; + } else { + return EFI_UNSUPPORTED; + } + + AddressList = AllocateZeroPool (DataSize); + if (AddressList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TmpStr = AddressList; + + NET_LIST_FOR_EACH (Entry, ListHead) { + Node = NET_LIST_USER_STRUCT (Entry, IP6_ADDRESS_INFO_ENTRY, Link); + if (AddressType == Ip6ConfigNvHostAddress) { + ManualAddress = (EFI_IP6_CONFIG_MANUAL_ADDRESS *) AddressList; + IP6_COPY_ADDRESS (&ManualAddress->Address, &Node->AddrInfo.Address); + ManualAddress->PrefixLength = Node->AddrInfo.PrefixLength; + AddressList = (UINT8 *) AddressList + sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS); + } else { + Ip6Address = (EFI_IPv6_ADDRESS *) AddressList; + IP6_COPY_ADDRESS (Ip6Address, &Node->AddrInfo.Address); + AddressList = (UINT8 *) AddressList + sizeof (EFI_IPv6_ADDRESS); + } + } + + *AddressInfo = TmpStr; + *AddressSize = DataSize; + return EFI_SUCCESS; +} + +/** + Convert the IP6 configuration data into the IFR data. + + @param[in, out] IfrNvData The IFR NV data. + @param[in] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_UNSUPPORTED The policy is not supported in the current implementation. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertConfigNvDataToIfrNvData ( + IN OUT IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + UINTN DataSize; + VOID *Data; + EFI_STATUS Status; + EFI_IP6_CONFIG_POLICY Policy; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits; + EFI_HII_HANDLE HiiHandle; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + + Ip6Config = &Instance->Ip6Config; + Ip6NvData = &Instance->Ip6NvData; + Data = NULL; + DataSize = 0; + HiiHandle = Instance->CallbackInfo.RegisteredHandle; + + // + // Get the current interface info. + // + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Convert the interface info to string and print. + // + Status = Ip6ConvertInterfaceInfoToString ( + (EFI_IP6_CONFIG_INTERFACE_INFO *) Data, + HiiHandle, + IfrNvData + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Get the interface id. + // + DataSize = sizeof (EFI_IP6_CONFIG_INTERFACE_ID); + ZeroMem (&Ip6NvData->InterfaceId, DataSize); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + &DataSize, + &Ip6NvData->InterfaceId + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId); + + // + // Get current policy. + // + DataSize = sizeof (EFI_IP6_CONFIG_POLICY); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + &DataSize, + &Policy + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + if (Policy == Ip6ConfigPolicyManual) { + IfrNvData->Policy = IP6_POLICY_MANUAL; + } else if (Policy == Ip6ConfigPolicyAutomatic) { + IfrNvData->Policy = IP6_POLICY_AUTO; + } else { + ASSERT (FALSE); + Status = EFI_UNSUPPORTED; + goto Exit; + } + + // + // Get Duplicate Address Detection Transmits count. + // + DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS); + Status = Ip6Config->GetData ( + Ip6Config, + Ip6ConfigDataTypeDupAddrDetectTransmits, + &DataSize, + &DadXmits + ); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + IfrNvData->DadTransmitCount = DadXmits.DupAddrDetectTransmits; + +Exit: + if (Data != NULL) { + FreePool (Data); + } + + return Status; +} + +/** + Convert IFR data into IP6 configuration data. The policy, alternative interface + ID, and DAD transmit counts, and will be saved. + + @param[in] IfrNvData The IFR NV data. + @param[in, out] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertIfrNvDataToConfigNvDataGeneral ( + IN IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Ip6NvData = &Instance->Ip6NvData; + Ip6Config = &Instance->Ip6Config; + + // + // Update those fields which don't have INTERACTIVE attribute. + // + if (IfrNvData->Policy == IP6_POLICY_AUTO) { + Ip6NvData->Policy = Ip6ConfigPolicyAutomatic; + } else if (IfrNvData->Policy == IP6_POLICY_MANUAL) { + Ip6NvData->Policy = Ip6ConfigPolicyManual; + } + + Ip6NvData->DadTransmitCount.DupAddrDetectTransmits = IfrNvData->DadTransmitCount; + + // + // Set the configured policy. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Ip6NvData->Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the duplicate address detection transmits count. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDupAddrDetectTransmits, + sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS), + &Ip6NvData->DadTransmitCount + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set the alternative interface ID + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeAltInterfaceId, + sizeof (EFI_IP6_CONFIG_INTERFACE_ID), + &Ip6NvData->InterfaceId + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Convert IFR data into IP6 configuration data. The policy, configured + manual address, gateway address, and DNS server address will be saved. + + @param[in] IfrNvData The IFR NV data. + @param[in, out] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6ConvertIfrNvDataToConfigNvDataAdvanced ( + IN IP6_CONFIG_IFR_NVDATA *IfrNvData, + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_CONFIG_NVDATA *Ip6NvData; + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_STATUS Status; + EFI_IP6_CONFIG_MANUAL_ADDRESS *ManualAddress; + EFI_IPv6_ADDRESS *Address; + BOOLEAN IsAddressOk; + EFI_EVENT SetAddressEvent; + EFI_EVENT TimeoutEvent; + UINTN DataSize; + + if ((IfrNvData == NULL) || (Instance == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (IfrNvData->Policy == IP6_POLICY_AUTO) { + return EFI_SUCCESS; + } + + NET_CHECK_SIGNATURE (Instance, IP6_CONFIG_INSTANCE_SIGNATURE); + Ip6NvData = &Instance->Ip6NvData; + Ip6Config = &Instance->Ip6Config; + + // + // Update those fields which don't have INTERACTIVE attribute. + // + Ip6NvData->Policy = Ip6ConfigPolicyManual; + + // + // Set the configured policy. + // + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypePolicy, + sizeof (EFI_IP6_CONFIG_POLICY), + &Ip6NvData->Policy + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Create events & timers for asynchronous settings. + // + SetAddressEvent = NULL; + TimeoutEvent = NULL; + ManualAddress = NULL; + Address = NULL; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6ConfigManualAddressNotify, + &IsAddressOk, + &SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &TimeoutEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + // + // Set the manual address list. This is an asynchronous process. + // + if (!IsListEmpty (&Ip6NvData->ManualAddress) && (Ip6NvData->ManualAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvHostAddress, + (VOID **) &ManualAddress, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + IsAddressOk = FALSE; + + Status = Ip6Config->RegisterDataNotify ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + DataSize, + (VOID *) ManualAddress + ); + if (Status == EFI_NOT_READY) { + gBS->SetTimer (TimeoutEvent, TimerRelative, 50000000); + while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) { + if (IsAddressOk) { + Status = EFI_SUCCESS; + } + break; + } + } + + Status = Ip6Config->UnregisterDataNotify ( + Ip6Config, + Ip6ConfigDataTypeManualAddress, + SetAddressEvent + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + // + // Set gateway address list. + // + if (!IsListEmpty (&Ip6NvData->GatewayAddress) && (Ip6NvData->GatewayAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvGatewayAddress, + (VOID **) &Address, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + DataSize, + (VOID *) Address + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + FreePool (Address); + Address = NULL; + } + + // + // Set DNS server address list. + // + if (!IsListEmpty (&Ip6NvData->DnsAddress) && (Ip6NvData->DnsAddressCount != 0)) { + Status = Ip6BuildNvAddressInfo ( + Instance, + Ip6ConfigNvDnsAddress, + (VOID **) &Address, + &DataSize + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6Config->SetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + DataSize, + (VOID *) Address + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Status = EFI_SUCCESS; + +Exit: + if (SetAddressEvent != NULL) { + gBS->CloseEvent (SetAddressEvent); + } + + if (TimeoutEvent != NULL) { + gBS->CloseEvent (TimeoutEvent); + } + + if (ManualAddress != NULL) { + FreePool (ManualAddress); + } + + if (Address != NULL) { + FreePool (Address); + } + + 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_INVALID_PARAMETER For example, passing in a NULL + for the Request parameter + would result in this type of + error. In this case, the + Progress parameter would be + set to NULL. + @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 +Ip6FormExtractConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Request, + OUT EFI_STRING *Progress, + OUT EFI_STRING *Results + ) +{ + + EFI_STATUS Status; + IP6_FORM_CALLBACK_INFO *Private; + IP6_CONFIG_INSTANCE *Ip6ConfigInstance; + IP6_CONFIG_IFR_NVDATA *IfrNvData; + EFI_STRING ConfigRequestHdr; + EFI_STRING ConfigRequest; + BOOLEAN AllocatedRequest; + UINTN Size; + UINTN BufferSize; + + if (This == NULL || Progress == NULL || Results == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Progress = Request; + if ((Request != NULL) && + !HiiIsConfigHdrMatch (Request, &gIp6ConfigNvDataGuid, mIp6ConfigStorageName)) { + return EFI_NOT_FOUND; + } + + ConfigRequestHdr = NULL; + ConfigRequest = NULL; + AllocatedRequest = FALSE; + Size = 0; + + Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + Ip6ConfigInstance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private); + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + + IfrNvData = (IP6_CONFIG_IFR_NVDATA *) AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6ConvertConfigNvDataToIfrNvData (IfrNvData, Ip6ConfigInstance); + if (EFI_ERROR (Status)) { + goto Exit; + } + + ConfigRequest = Request; + 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 ( + &gIp6ConfigNvDataGuid, + mIp6ConfigStorageName, + Private->ChildHandle + ); + Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (Size); + ASSERT (ConfigRequest != NULL); + 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 *) IfrNvData, + BufferSize, + Results, + Progress + ); + +Exit: + FreePool (IfrNvData); + // + // Free the allocated config request string. + // + if (AllocatedRequest) { + FreePool (ConfigRequest); + ConfigRequest = NULL; + } + // + // 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 +Ip6FormRouteConfig ( + IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, + IN CONST EFI_STRING Configuration, + OUT EFI_STRING *Progress + ) +{ + if (This == NULL || Configuration == NULL || Progress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check routing data in <ConfigHdr>. + // Note: if only one Storage is used, then this checking could be skipped. + // + if (!HiiIsConfigHdrMatch (Configuration, &gIp6ConfigNvDataGuid, mIp6ConfigStorageName)) { + *Progress = Configuration; + return EFI_NOT_FOUND; + } + + *Progress = Configuration + StrLen (Configuration); + + return EFI_SUCCESS; +} + +/** + Display host addresses, route table, DNS addresses and gateway addresses in + "IPv6 Current Setting" page. + + @param[in] Instance The IP6 config instance data. + + @retval EFI_SUCCESS The operation finished successfully. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +Ip6GetCurrentSetting ( + IN IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_IP6_CONFIG_PROTOCOL *Ip6Config; + EFI_HII_HANDLE HiiHandle; + EFI_IP6_CONFIG_INTERFACE_INFO *Data; + UINTN DataSize; + EFI_STATUS Status; + CHAR16 PortString[ADDRESS_STR_MAX_SIZE]; + EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo; + + + Ip6Config = &Instance->Ip6Config; + HiiHandle = Instance->CallbackInfo.RegisteredHandle; + Data = NULL; + + // + // Get current interface info. + // + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeInterfaceInfo, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Generate dynamic text opcode for host address and draw it. + // + IfInfo = (EFI_IP6_CONFIG_INTERFACE_INFO *) Data; + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvHostAddress, + IfInfo->AddressInfo, + IfInfo->AddressInfoCount + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + + // + // Generate the dynamic text opcode for route table and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvRouteTable, + IfInfo->RouteTable, + IfInfo->RouteCount + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + + // + // Get DNS server list. + // + FreePool (Data); + DataSize = 0; + Data = NULL; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeDnsServer, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + if (Data != NULL) { + FreePool (Data); + } + return Status; + } + + if (DataSize > 0) { + // + // Generate the dynamic text opcode for DNS server and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvDnsAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + } + + // + // Get gateway address list. + // + if (Data != NULL) { + FreePool (Data); + } + + DataSize = 0; + Data = NULL; + Status = Ip6ConfigNvGetData ( + Ip6Config, + Ip6ConfigDataTypeGateway, + &DataSize, + (VOID **) &Data + ); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + if (Data != NULL) { + FreePool (Data); + } + return Status; + } + + if (DataSize > 0) { + // + // Generate the dynamic text opcode for gateway and draw it. + // + Status = Ip6ConvertAddressListToString ( + PortString, + HiiHandle, + Ip6ConfigNvGatewayAddress, + Data, + DataSize / sizeof (EFI_IPv6_ADDRESS) + ); + if (EFI_ERROR (Status)) { + FreePool (Data); + return Status; + } + } + + if (Data != NULL) { + FreePool (Data); + } + + return EFI_SUCCESS; +} + +/** + 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 generated 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_PARAMETER Passed in the wrong parameter. + @retval Others Other errors as indicated. + +**/ +EFI_STATUS +EFIAPI +Ip6FormCallback ( + 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 + ) +{ + IP6_FORM_CALLBACK_INFO *Private; + UINTN BufferSize; + IP6_CONFIG_IFR_NVDATA *IfrNvData; + EFI_STATUS Status; + EFI_INPUT_KEY Key; + IP6_CONFIG_INSTANCE *Instance; + IP6_CONFIG_NVDATA *Ip6NvData; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Private = IP6_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This); + Instance = IP6_CONFIG_INSTANCE_FROM_FORM_CALLBACK (Private); + Ip6NvData = &Instance->Ip6NvData; + + if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)){ + return EFI_SUCCESS; + } + + if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) { + return EFI_UNSUPPORTED; + } + + if ((Value == NULL) || (ActionRequest == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve uncommitted data from Browser + // + + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + IfrNvData = AllocateZeroPool (BufferSize); + if (IfrNvData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = EFI_SUCCESS; + + HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData); + + if (Action == EFI_BROWSER_ACTION_CHANGING) { + switch (QuestionId) { + case KEY_GET_CURRENT_SETTING: + Status = Ip6GetCurrentSetting (Instance); + break; + + default: + break; + } + } else if (Action == EFI_BROWSER_ACTION_CHANGED) { + switch (QuestionId) { + case KEY_SAVE_CONFIG_CHANGES: + Status = Ip6ConvertIfrNvDataToConfigNvDataAdvanced (IfrNvData, Instance); + if (EFI_ERROR (Status)) { + break; + } + + Status = Ip6GetCurrentSetting (Instance); + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT; + break; + + case KEY_IGNORE_CONFIG_CHANGES: + Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress); + Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress); + Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress); + + Ip6NvData->ManualAddressCount = 0; + Ip6NvData->GatewayAddressCount = 0; + Ip6NvData->DnsAddressCount = 0; + + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT; + break; + + case KEY_SAVE_CHANGES: + Status = Ip6ConvertIfrNvDataToConfigNvDataGeneral (IfrNvData, Instance); + if (EFI_ERROR (Status)) { + break; + } + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; + break; + + case KEY_INTERFACE_ID: + Status = Ip6ParseInterfaceIdFromString (IfrNvData->InterfaceId, &Ip6NvData->InterfaceId); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Interface ID!", + NULL + ); + } + + break; + + case KEY_MANUAL_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->ManualAddress, + &Ip6NvData->ManualAddress, + &Ip6NvData->ManualAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Host Addresses!", + NULL + ); + } + + break; + + case KEY_GATEWAY_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->GatewayAddress, + &Ip6NvData->GatewayAddress, + &Ip6NvData->GatewayAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid Gateway Addresses!", + NULL + ); + } + + break; + + case KEY_DNS_ADDRESS: + Status = Ip6ParseAddressListFromString ( + IfrNvData->DnsAddress, + &Ip6NvData->DnsAddress, + &Ip6NvData->DnsAddressCount + ); + if (EFI_ERROR (Status)) { + CreatePopUp ( + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, + &Key, + L"Invalid DNS Addresses!", + NULL + ); + } + + break; + + default: + break; + } + } + + if (!EFI_ERROR (Status)) { + // + // Pass changed uncommitted data back to Form Browser. + // + BufferSize = sizeof (IP6_CONFIG_IFR_NVDATA); + HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL); + } + + FreePool (IfrNvData); + return Status; +} + +/** + Install HII Config Access protocol for network device and allocate resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form. + + @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 +Ip6ConfigFormInit ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + IP6_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 = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance); + ASSERT (IpSb != NULL); + + Status = gBS->HandleProtocol ( + IpSb->Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CallbackInfo = &Instance->CallbackInfo; + CallbackInfo->Signature = IP6_FORM_CALLBACK_INFO_SIGNATURE; + + // + // 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->HiiConfigAccess; + ConfigAccess->ExtractConfig = Ip6FormExtractConfig; + ConfigAccess->RouteConfig = Ip6FormRouteConfig; + ConfigAccess->Callback = Ip6FormCallback; + + // + // 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 ( + &gIp6ConfigNvDataGuid, + CallbackInfo->ChildHandle, + Ip6DxeStrings, + Ip6ConfigBin, + 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_IP6_CONFIG_FORM_HELP), + NULL) + ; + UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_CONFIG_FORM_HELP), + MenuString, + NULL + ); + UnicodeSPrint (PortString, 128, L"MAC:%s", MacString); + HiiSetString ( + CallbackInfo->RegisteredHandle, + STRING_TOKEN (STR_IP6_DEVICE_FORM_HELP), + PortString, + NULL + ); + + FreePool (MacString); + FreePool (OldMenuString); + + InitializeListHead (&Instance->Ip6NvData.ManualAddress); + InitializeListHead (&Instance->Ip6NvData.GatewayAddress); + InitializeListHead (&Instance->Ip6NvData.DnsAddress); + + return EFI_SUCCESS; + } + +Error: + Ip6ConfigFormUnload (Instance); + return Status; +} + +/** + Uninstall the HII Config Access protocol for network devices and free up the resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form. + +**/ +VOID +Ip6ConfigFormUnload ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ) +{ + IP6_SERVICE *IpSb; + IP6_FORM_CALLBACK_INFO *CallbackInfo; + IP6_CONFIG_NVDATA *Ip6NvData; + + IpSb = IP6_SERVICE_FROM_IP6_CONFIG_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->HiiConfigAccess, + NULL + ); + } + + if (CallbackInfo->HiiVendorDevicePath != NULL) { + FreePool (CallbackInfo->HiiVendorDevicePath); + } + + if (CallbackInfo->RegisteredHandle != NULL) { + // + // Remove HII package list + // + HiiRemovePackages (CallbackInfo->RegisteredHandle); + } + + Ip6NvData = &Instance->Ip6NvData; + + Ip6FreeAddressInfoList (&Ip6NvData->ManualAddress); + Ip6FreeAddressInfoList (&Ip6NvData->GatewayAddress); + Ip6FreeAddressInfoList (&Ip6NvData->DnsAddress); + + Ip6NvData->ManualAddressCount = 0; + Ip6NvData->GatewayAddressCount = 0; + Ip6NvData->DnsAddressCount = 0; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h new file mode 100644 index 00000000..6aaa17ad --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h @@ -0,0 +1,62 @@ +/** @file + The header file of Ip6ConfigNv.c. + + Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _IP6_CONFIGNV_H_ +#define _IP6_CONFIGNV_H_ + +#include "Ip6NvData.h" +#include "Ip6ConfigImpl.h" + +extern UINT8 Ip6ConfigBin[]; +extern UINT8 Ip6DxeStrings[]; + +#define IP6_ETHERNET L"Ethernet" +#define IP6_EXPERIMENTAL_ETHERNET L"Experimental Ethernet" +#define IP6_ADDRESS_DELIMITER L' ' +#define IP6_LINK_LOCAL_PREFIX L"FE80::" + +typedef enum { + Ip6InterfaceTypeEthernet = 1, + Ip6InterfaceTypeExperimentalEthernet +} IP6_INTERFACE_TYPE; + +typedef enum { + Ip6ConfigNvHostAddress, + Ip6ConfigNvGatewayAddress, + Ip6ConfigNvDnsAddress, + Ip6ConfigNvRouteTable +} IP6_CONFIG_NV_ADDRESS_TYPE; + +/** + Install HII Config Access protocol for network device and allocate resources. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to create a form. + + @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 +Ip6ConfigFormInit ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +/** + Uninstall HII Config Access protocol for network device and free resource. + + @param[in, out] Instance The IP6_CONFIG_INSTANCE to unload a form. + +**/ +VOID +Ip6ConfigFormUnload ( + IN OUT IP6_CONFIG_INSTANCE *Instance + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Driver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Driver.c new file mode 100644 index 00000000..24bb5779 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Driver.c @@ -0,0 +1,1054 @@ +/** @file + The driver binding and service binding protocol for IP6 driver. + + Copyright (c) 2009 - 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 "Ip6Impl.h" + +EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = { + Ip6DriverBindingSupported, + Ip6DriverBindingStart, + Ip6DriverBindingStop, + 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 IP6 driver which installs 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 +Ip6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *Registration; + + EfiCreateProtocolNotifyEvent ( + &gEfiIpSec2ProtocolGuid, + TPL_CALLBACK, + IpSec2InstalledCallback, + NULL, + &Registration + ); + + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gIp6DriverBinding, + ImageHandle, + &gIp6ComponentName, + &gIp6ComponentName2 + ); +} + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + // + // Test for the MNP service binding Protocol + // + return gBS->OpenProtocol ( + ControllerHandle, + &gEfiManagedNetworkServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + +/** + Clean up an IP6 service binding instance. It releases 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 IP6 service binding instance to clean up. + + @retval EFI_SUCCESS The resource used by the instance are cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip6CleanService ( + IN IP6_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS AllNodes; + IP6_NEIGHBOR_ENTRY *NeighborCache; + + IpSb->State = IP6_SERVICE_DESTROY; + + if (IpSb->Timer != NULL) { + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->CloseEvent (IpSb->Timer); + + IpSb->Timer = NULL; + } + + if (IpSb->FasterTimer != NULL) { + gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0); + gBS->CloseEvent (IpSb->FasterTimer); + + IpSb->FasterTimer = NULL; + } + + Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance); + + if (!IpSb->LinkLocalDadFail) { + // + // Leave link-scope all-nodes multicast address (FF02::1) + // + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + + Status = Ip6LeaveGroup (IpSb, &AllNodes); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (IpSb->DefaultInterface != NULL) { + Ip6CleanInterface (IpSb->DefaultInterface, NULL); + IpSb->DefaultInterface = NULL; + } + + Ip6CleanDefaultRouterList (IpSb); + + Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix); + Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix); + + if (IpSb->RouteTable != NULL) { + Ip6CleanRouteTable (IpSb->RouteTable); + IpSb->RouteTable = NULL; + } + + if (IpSb->InterfaceId != NULL) { + FreePool (IpSb->InterfaceId); + } + + IpSb->InterfaceId = NULL; + + Ip6CleanAssembleTable (&IpSb->Assemble); + + if (IpSb->MnpChildHandle != NULL) { + if (IpSb->Mnp != NULL) { + IpSb->Mnp->Cancel (IpSb->Mnp, NULL); + IpSb->Mnp->Configure (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->RecvRequest.MnpToken.Event != NULL) { + gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event); + } + + // + // Free the Neighbor Discovery resources + // + while (!IsListEmpty (&IpSb->NeighborTable)) { + NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link); + Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL); + } + + return EFI_SUCCESS; +} + +/** + Create a new IP6 driver service binding protocol. + + @param[in] Controller The controller that has MNP service binding + installed. + @param[in] ImageHandle The IP6 driver's image handle. + @param[out] Service The variable to receive the newly created IP6 + service. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate some resources. + @retval EFI_SUCCESS A new IP6 service binding private is created. + +**/ +EFI_STATUS +Ip6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE ImageHandle, + OUT IP6_SERVICE **Service + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_CONFIG_DATA *Config; + + 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, Ip6CleanService can be called to clean it up. + // + IpSb = AllocateZeroPool (sizeof (IP6_SERVICE)); + + if (IpSb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + IpSb->Signature = IP6_SERVICE_SIGNATURE; + IpSb->ServiceBinding.CreateChild = Ip6ServiceBindingCreateChild; + IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild; + IpSb->State = IP6_SERVICE_UNSTARTED; + + IpSb->NumChildren = 0; + InitializeListHead (&IpSb->Children); + + InitializeListHead (&IpSb->Interfaces); + IpSb->DefaultInterface = NULL; + IpSb->RouteTable = NULL; + + IpSb->RecvRequest.Signature = IP6_LINK_RX_SIGNATURE; + IpSb->RecvRequest.CallBack = NULL; + IpSb->RecvRequest.Context = NULL; + MnpToken = &IpSb->RecvRequest.MnpToken; + MnpToken->Event = NULL; + MnpToken->Status = EFI_NOT_READY; + MnpToken->Packet.RxData = NULL; + + Ip6CreateAssembleTable (&IpSb->Assemble); + + IpSb->MldCtrl.Mldv1QuerySeen = 0; + InitializeListHead (&IpSb->MldCtrl.Groups); + + ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS)); + IpSb->LinkLocalOk = FALSE; + IpSb->LinkLocalDadFail = FALSE; + IpSb->Dhcp6NeedStart = FALSE; + IpSb->Dhcp6NeedInfoRequest = FALSE; + + IpSb->CurHopLimit = IP6_HOP_LIMIT; + IpSb->LinkMTU = IP6_MIN_LINK_MTU; + IpSb->BaseReachableTime = IP6_REACHABLE_TIME; + Ip6UpdateReachableTime (IpSb); + // + // RFC4861 RETRANS_TIMER: 1,000 milliseconds + // + IpSb->RetransTimer = IP6_RETRANS_TIMER; + + IpSb->RoundRobin = 0; + + InitializeListHead (&IpSb->NeighborTable); + InitializeListHead (&IpSb->DefaultRouterList); + InitializeListHead (&IpSb->OnlinkPrefix); + InitializeListHead (&IpSb->AutonomousPrefix); + + IpSb->InterfaceIdLen = IP6_IF_ID_LEN; + IpSb->InterfaceId = NULL; + + IpSb->RouterAdvertiseReceived = FALSE; + IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS; + IpSb->Ticks = 0; + + IpSb->Image = ImageHandle; + IpSb->Controller = Controller; + + IpSb->MnpChildHandle = NULL; + IpSb->Mnp = NULL; + + Config = &IpSb->MnpConfigData; + Config->ReceivedQueueTimeoutValue = 0; + Config->TransmitQueueTimeoutValue = 0; + Config->ProtocolTypeFilter = IP6_ETHER_PROTO; + Config->EnableUnicastReceive = TRUE; + Config->EnableMulticastReceive = TRUE; + Config->EnableBroadcastReceive = TRUE; + Config->EnablePromiscuousReceive = FALSE; + Config->FlushQueuesOnReset = TRUE; + Config->EnableReceiveTimestamps = FALSE; + Config->DisableBackgroundPolling = FALSE; + + ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE)); + + IpSb->Timer = NULL; + IpSb->FasterTimer = NULL; + + ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE)); + + IpSb->MacString = NULL; + + // + // Create various resources. First create the route table, timer + // event, MNP token event and MNP child. + // + + IpSb->RouteTable = Ip6CreateRouteTable (); + if (IpSb->RouteTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip6TimerTicking, + IpSb, + &IpSb->Timer + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Ip6NdFasterTimerTicking, + IpSb, + &IpSb->FasterTimer + ); + 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 = Ip6ServiceConfigMnp (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; + } + + IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER); + 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; + + // + // Currently only ETHERNET is supported in IPv6 stack, since + // link local address requires an IEEE 802 48-bit MACs for + // EUI-64 format interface identifier mapping. + // + if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) { + Status = EFI_UNSUPPORTED; + goto ON_ERROR; + } + + Status = Ip6InitMld (IpSb); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE); + if (IpSb->DefaultInterface == NULL) { + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnFrameReceived, + &IpSb->RecvRequest, + &MnpToken->Event + ); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link); + + *Service = IpSb; + return EFI_SUCCESS; + +ON_ERROR: + Ip6CleanService (IpSb); + FreePool (IpSb); + return Status; +} + + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used 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 +Ip6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + EFI_STATUS Status; + EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg; + IP6_CONFIG_DATA_ITEM *DataItem; + + IpSb = NULL; + Ip6Cfg = NULL; + DataItem = NULL; + + // + // Test for the Ip6 service binding protocol + // + Status = gBS->OpenProtocol ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (Status == EFI_SUCCESS) { + return EFI_ALREADY_STARTED; + } + + Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (IpSb != NULL); + + Ip6Cfg = &IpSb->Ip6ConfigInstance.Ip6Config; + + // + // Install the Ip6ServiceBinding Protocol onto ControllerHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &IpSb->ServiceBinding, + &gEfiIp6ConfigProtocolGuid, + Ip6Cfg, + 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 = Ip6ConfigReadConfigData (IpSb->MacString, &IpSb->Ip6ConfigInstance); + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + // + // If there is any default manual address, set it. + // + DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress]; + if (DataItem->Data.Ptr != NULL) { + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + DataItem->DataSize, + DataItem->Data.Ptr + ); + if (Status == EFI_INVALID_PARAMETER || Status == EFI_BAD_BUFFER_SIZE) { + // + // Clean the invalid ManualAddress configuration. + // + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeManualAddress, + 0, + NULL + ); + DEBUG ((EFI_D_WARN, "Ip6DriverBindingStart: Clean the invalid ManualAddress configuration.\n")); + } + } + + // + // If there is any default gateway address, set it. + // + DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeGateway]; + if (DataItem->Data.Ptr != NULL) { + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeGateway, + DataItem->DataSize, + DataItem->Data.Ptr + ); + if (Status == EFI_INVALID_PARAMETER || Status == EFI_BAD_BUFFER_SIZE) { + // + // Clean the invalid Gateway configuration. + // + Status = Ip6Cfg->SetData ( + Ip6Cfg, + Ip6ConfigDataTypeGateway, + 0, + NULL + ); + DEBUG ((EFI_D_WARN, "Ip6DriverBindingStart: Clean the invalid Gateway configuration.\n")); + } + } + + // + // ready to go: start the receiving and timer + // + Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb); + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + // + // The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds. + // + Status = gBS->SetTimer ( + IpSb->FasterTimer, + TimerPeriodic, + TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS + ); + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + // + // The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds. + // + Status = gBS->SetTimer ( + IpSb->Timer, + TimerPeriodic, + TICKS_PER_MS * IP6_ONE_SECOND_IN_MS + ); + if (EFI_ERROR (Status)) { + goto UNINSTALL_PROTOCOL; + } + + // + // Initialize the IP6 ID + // + mIp6Id = NET_RANDOM (NetRandomInitSeed ()); + + return EFI_SUCCESS; + +UNINSTALL_PROTOCOL: + gBS->UninstallMultipleProtocolInterfaces ( + ControllerHandle, + &gEfiIp6ServiceBindingProtocolGuid, + &IpSb->ServiceBinding, + &gEfiIp6ConfigProtocolGuid, + Ip6Cfg, + NULL + ); + +FREE_SERVICE: + Ip6CleanService (IpSb); + FreePool (IpSb); + return Status; +} + +/** + Callback function which provided by user to remove one node in NetDestroyLinkList process. + + @param[in] Entry The entry to be removed. + @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. + + @retval EFI_SUCCESS The entry has been removed successfully. + @retval Others Fail to remove the entry. + +**/ +EFI_STATUS +EFIAPI +Ip6DestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + IP6_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, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + ServiceBinding = ((IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (IpInstance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle); +} + +/** + Stop this driver on ControllerHandle. + + @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 An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + IP6_SERVICE *IpSb; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + LIST_ENTRY *List; + INTN State; + BOOLEAN IsDhcp6; + IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + IsDhcp6 = FALSE; + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid); + if (NicHandle == NULL) { + NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid); + if (NicHandle != NULL) { + IsDhcp6 = TRUE; + } else { + return EFI_SUCCESS; + } + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding); + + if (IsDhcp6) { + Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance); + gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event); + IpSb->Ip6ConfigInstance.Dhcp6Event = NULL; + } else if (NumberOfChildren != 0) { + // + // NumberOfChildren is not zero, destroy the IP6 children instances in ChildHandleBuffer. + // + List = &IpSb->Children; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + Ip6DestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } else if (IsListEmpty (&IpSb->Children)) { + State = IpSb->State; + Status = Ip6CleanService (IpSb); + if (EFI_ERROR (Status)) { + IpSb->State = State; + goto Exit; + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + NicHandle, + &gEfiIp6ServiceBindingProtocolGuid, + ServiceBinding, + &gEfiIp6ConfigProtocolGuid, + &IpSb->Ip6ConfigInstance.Ip6Config, + NULL + ); + ASSERT_EFI_ERROR (Status); + FreePool (IpSb); + Status = EFI_SUCCESS; + } + +Exit: + return Status; +} + + +/** + Creates a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCESS The child handle was created with the I/O services. + @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 +Ip6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ) +{ + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_TPL OldTpl; + EFI_STATUS Status; + VOID *Mnp; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + IpSb = IP6_SERVICE_FROM_PROTOCOL (This); + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + IpInstance = AllocatePool (sizeof (IP6_PROTOCOL)); + + if (IpInstance == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ip6InitProtocol (IpSb, IpInstance); + + // + // Install Ip6 onto ChildHandle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto, + 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, + gIp6DriverBinding.DriverBindingHandle, + IpInstance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + *ChildHandle, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto, + 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)) { + + Ip6CleanProtocol (IpInstance); + + FreePool (IpInstance); + } + + return Status; +} + +/** + Destroys a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCESS The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_IP6_PROTOCOL *Ip6; + EFI_TPL OldTpl; + + if ((This == NULL) || (ChildHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Retrieve the private context data structures + // + IpSb = IP6_SERVICE_FROM_PROTOCOL (This); + + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiIp6ProtocolGuid, + (VOID **) &Ip6, + gIp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6); + + if (IpInstance->Service != IpSb) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // A child can be destroyed more than once. For example, + // Ip6DriverBindingStop 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, + gIp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + // + // Uninstall the IP6 protocol first. Many thing happens during + // this: + // 1. The consumer of the IP6 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_IP6_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, + &gEfiIp6ProtocolGuid, + &IpInstance->Ip6Proto + ); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Status = Ip6CleanProtocol (IpInstance); + if (EFI_ERROR (Status)) { + gBS->InstallMultipleProtocolInterfaces ( + &ChildHandle, + &gEfiIp6ProtocolGuid, + Ip6, + NULL + ); + + goto ON_ERROR; + } + + RemoveEntryList (&IpInstance->Link); + ASSERT (IpSb->NumChildren > 0); + 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/Ip6Dxe/Ip6Driver.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Driver.h new file mode 100644 index 00000000..fa4cb81d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Driver.h @@ -0,0 +1,186 @@ +/** @file + The driver binding and service binding protocol for IP6 driver. + + Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_DRIVER_H__ +#define __EFI_IP6_DRIVER_H__ + +extern EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gIp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gIp6ComponentName2; +extern EFI_UNICODE_STRING_TABLE *gIp6ControllerNameTable; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +}IP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Clean up an IP6 service binding instance. It releases 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 IP6 service binding instance to clean up. + + @retval EFI_SUCCESS The resource used by the instance are cleaned up. + @retval Others Failed to clean up some of the resources. + +**/ +EFI_STATUS +Ip6CleanService ( + IN IP6_SERVICE *IpSb + ); + +// +// 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 IP6 driver which installs 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 +Ip6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// +// Function prototypes for the Driver Binding Protocol +// + +/** + Test to see if this driver supports ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of 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 +Ip6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Start this driver on ControllerHandle. + + @param[in] This Protocol instance pointer. + @param[in] ControllerHandle Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter used 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 +Ip6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL + ); + +/** + Stop this driver on ControllerHandle. + + @param[in] This 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 An array of child handles to be freed. May be NULL + if NumberOfChildren is 0. + + @retval EFI_SUCCESS The device was stopped. + @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. + +**/ +EFI_STATUS +EFIAPI +Ip6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer OPTIONAL + ); + +// +// Function prototypes for the ServiceBinding Protocol +// + +/** + Creates a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Pointer to the handle of the child to create. If + it is NULL, then a new handle is created. If it + is not NULL, then the I/O services are added to + the existing child handle. + + @retval EFI_SUCCESS The child handle was created with the I/O services. + @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 +Ip6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a set of I/O services. + + @param[in] This Protocol instance pointer. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCESS The I/O services were removed from the child + handle. + @retval EFI_UNSUPPORTED The child handle does not support the I/O services + that are being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval EFI_ACCESS_DENIED The child handle could not be destroyed because + its I/O services are being used. + @retval other The child handle was not destroyed. + +**/ +EFI_STATUS +EFIAPI +Ip6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Dxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Dxe.inf new file mode 100644 index 00000000..21eab61b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Dxe.inf @@ -0,0 +1,110 @@ +## @file +# Basic IPv6 packet I/O Service. +# +# This module provides basic network IPv6 packet I/O services which includes support for +# Neighbor Discovery Protocol (ND), Multicast Listener Discovery Protocol (MLD), +# and a subset of the Internet Control Message Protocol (ICMPv6). This driver +# also provides the mechanism to set and get various types of configurations for +# the EFI IPv6 network stack. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Ip6Dxe + FILE_GUID = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Ip6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = Ip6Dxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gIp6DriverBinding +# COMPONENT_NAME = gIp6ComponentName +# COMPONENT_NAME2 = gIp6ComponentName2 +# + +[Sources] + Ip6Output.h + Ip6Option.h + Ip6Input.h + Ip6Nd.h + Ip6Mld.h + Ip6Impl.c + Ip6Driver.c + ComponentName.c + Ip6Nd.c + Ip6Input.c + Ip6ConfigImpl.c + Ip6ConfigImpl.h + Ip6Impl.h + Ip6Option.c + Ip6If.h + Ip6Icmp.h + Ip6Mld.c + Ip6Common.c + Ip6Route.c + Ip6If.c + Ip6Driver.h + Ip6Output.c + Ip6Icmp.c + Ip6Common.h + Ip6Route.h + Ip6DxeStrings.uni + Ip6NvData.h + Ip6ConfigNv.c + Ip6ConfigNv.h + Ip6Config.vfr + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + NetworkPkg/NetworkPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DevicePathLib + HiiLib + UefiHiiServicesLib + PrintLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + UefiLib + DebugLib + NetLib + DpcLib + +[Protocols] + gEfiManagedNetworkServiceBindingProtocolGuid ## TO_START + gEfiManagedNetworkProtocolGuid ## TO_START + gEfiIp6ServiceBindingProtocolGuid ## BY_START + gEfiIp6ProtocolGuid ## BY_START + gEfiIp6ConfigProtocolGuid ## BY_START + gEfiDhcp6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES + gEfiDhcp6ProtocolGuid ## SOMETIMES_CONSUMES + gEfiIpSec2ProtocolGuid ## SOMETIMES_CONSUMES + gEfiHiiConfigAccessProtocolGuid ## SOMETIMES_CONSUMES + +[Guids] + ## SOMETIMES_CONSUMES ## HII + gEfiIfrTianoGuid + ## SOMETIMES_CONSUMES ## UNDEFINED # HiiIsConfigHdrMatch mIp6ConfigStorageName + ## SOMETIMES_PRODUCES ## UNDEFINED # HiiConstructConfigHdr mIp6ConfigStorageName + ## SOMETIMES_PRODUCES ## UNDEFINED # HiiAddPackages Ip6DxeStrings Ip6ConfigBin + ## SOMETIMES_CONSUMES ## UNDEFINED # HiiUpdateForm + ## SOMETIMES_CONSUMES ## HII + gIp6ConfigNvDataGuid +[UserExtensions.TianoCore."ExtraFiles"] + Ip6DxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Dxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Dxe.uni new file mode 100644 index 00000000..947389ae --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Dxe.uni @@ -0,0 +1,20 @@ +// /** @file
+// Basic IPv6 packet I/O Service.
+//
+// This module provides basic network IPv6 packet I/O services which includes support for
+// Neighbor Discovery Protocol (ND), Multicast Listener Discovery Protocol (MLD),
+// and a subset of the Internet Control Message Protocol (ICMPv6). This driver
+// also provides the mechanism to set and get various types of configurations for
+// the EFI IPv6 network stack.
+//
+// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Basic IPv6 packet I/O Service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module provides basic network IPv6 packet I/O services which includes support for Neighbor Discovery Protocol (ND), Multicast Listener Discovery Protocol (MLD), and a subset of the Internet Control Message Protocol (ICMPv6). This driver also provides the mechanism to set and get various types of configurations for the EFI IPv6 network stack."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni new file mode 100644 index 00000000..3f16fddb --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6DxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// Ip6Dxe 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
+"Ip6 DXE"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni new file mode 100644 index 00000000..6b77176d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni @@ -0,0 +1,55 @@ +/** @file
+ String definitions for IP6 configuration.
+
+ Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#langdef en-US "English"
+
+#string STR_IP6_CONFIG_FORM_TITLE #language en-US "IPv6 Network Configuration"
+#string STR_IP6_CONFIG_FORM_HELP #language en-US "Configure IPv6 network parameters."
+#string STR_IP6_DEVICE_FORM_TITLE #language en-US "IPv6 Current Setting"
+#string STR_IP6_DEVICE_FORM_HELP #language en-US "Display IPv6 network parameters."
+#string STR_IP6_INTERFACE_NAME #language en-US "Interface Name :"
+#string STR_IP6_INTERFACE_NAME_HELP #language en-US "The name of the interface."
+#string STR_IP6_INTERFACE_NAME_CONTENT #language en-US ""
+#string STR_IP6_INTERFACE_TYPE #language en-US "Interface Type :"
+#string STR_IP6_INTERFACE_TYPE_HELP #language en-US "The interface type of the network interface, defined in RFC1700."
+#string STR_IP6_INTERFACE_TYPE_CONTENT #language en-US ""
+#string STR_IP6_MAC_ADDRESS #language en-US "MAC address :"
+#string STR_IP6_MAC_ADDRESS_HELP #language en-US "The hardware address of the network interface."
+#string STR_IP6_MAC_ADDRESS_CONTENT #language en-US ""
+#string STR_IP6_HOST_ADDRESS #language en-US "Host addresses :"
+#string STR_IP6_HOST_ADDRESS_HELP #language en-US "The address list which contain the local IPv6 addresses and the corresponding prefix length information."
+#string STR_IP6_ROUTE_TABLE #language en-US "Route Table :"
+#string STR_IP6_ROUTE_TABLE_HELP #language en-US "The route table of the IPv6 network stack runs on this interface."
+#string STR_IP6_GATEWAY_ADDRESS #language en-US "Gateway addresses :"
+#string STR_IP6_GATEWAY_ADDRESS_HELP #language en-US "Current gateway IPv6 addresses."
+#string STR_IP6_DNS_ADDRESS #language en-US "DNS addresses :"
+#string STR_IP6_DNS_ADDRESS_HELP #language en-US "Current DNS server list."
+#string STR_IP6_INTERFACE_ID #language en-US "Interface ID"
+#string STR_IP6_INTERFACE_ID_HELP #language en-US "The 64 bit alternative interface ID for the device. The string is colon separated. e.g. ff:dd:88:66:cc:1:2:3"
+#string STR_IP6_DAD_TRANSMIT_COUNT #language en-US "DAD Transmit Count"
+#string STR_IP6_DAD_TRANSMIT_COUNT_HELP #language en-US "The number of consecutive Neighbor Solicitation messages sent while performing Duplicate Address Detection on a tentative address. A value of zero indicates that Duplicate Address Detection is not performed."
+#string STR_POLICY_TYPE_PROMPT #language en-US "Policy"
+#string STR_POLICY_TYPE_HELP #language en-US "automatic or manual"
+#string STR_POLICY_TYPE_AUTO #language en-US "automatic"
+#string STR_POLICY_TYPE_MANUAL #language en-US "manual"
+#string STR_IP6_AD_CONFIG_FORM #language en-US "Advanced Configuration"
+#string STR_IP6_AD_CONFIG_FORM_HELP #language en-US "Configure the interface manually. IP address, gateway address, and DNS server address can be configured."
+#string STR_IP6_MANUAL_ADDRESS #language en-US "New IPv6 address"
+#string STR_IP6_MANUAL_ADDRESS_HELP #language en-US "Manual IP address can only be configured under manual policy. Separate the IP address with blank space to configure more than one address. e.g. 2002::1/64 2002::2/64"
+#string STR_IP6_NEW_GATEWAY_ADDRESS #language en-US "New Gateway addresses"
+#string STR_IP6_NEW_GATEWAY_ADDR_HELP #language en-US "Gateway IP address can only be configured under manual policy. e.g. 2002::3. Separate the IP address with blank space to configure more than one address."
+#string STR_IP6_NEW_DNS_ADDRESS #language en-US "New DNS addresses"
+#string STR_IP6_NEW_DNS_ADDRESS_HELP #language en-US "DNS addresses can only be configured under manual policy. e.g. 2002::4. Separate the IP address with blank space to configure more than one address."
+#string STR_SAVE_CHANGES #language en-US "Save Changes and Exit"
+#string STR_SAVE_CHANGES_HELP #language en-US "Save Changes for interface ID, DAD transmit count, policy, and data in advanced configuration."
+#string STR_SAVE_AND_EXIT #language en-US "Commit Changes and Exit"
+#string STR_NO_SAVE_AND_EXIT #language en-US "Discard Changes and Exit"
+#string STR_NULL #language en-US ""
+#string STR_GET_CURRENT_SETTING #language en-US "Enter Configuration Menu"
+#string STR_GET_CURRENT_SETTING_HELP #language en-US "Press ENTER to enter configuration menu for IPv6 configuration."
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Icmp.c new file mode 100644 index 00000000..92bef882 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Icmp.c @@ -0,0 +1,679 @@ +/** @file + The ICMPv6 handle routines to process the ICMPv6 control messages. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[23] = { + + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_NO_ROUTE_TO_DEST + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_COMM_PROHIBITED + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_BEYOND_SCOPE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ADDR_UNREACHABLE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_PORT_UNREACHABLE + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_SOURCE_ADDR_FAILED + }, + { + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ROUTE_REJECTED + }, + + { + ICMP_V6_PACKET_TOO_BIG, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_HOP_LIMIT + }, + { + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_REASSEMBLE + }, + + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_ERRONEOUS_HEADER + }, + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_UNRECOGNIZE_NEXT_HDR + }, + { + ICMP_V6_PARAMETER_PROBLEM, + ICMP_V6_UNRECOGNIZE_OPTION + }, + + { + ICMP_V6_ECHO_REQUEST, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_ECHO_REPLY, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_LISTENER_QUERY, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_REPORT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_REPORT_2, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_LISTENER_DONE, + ICMP_V6_DEFAULT_CODE + }, + + { + ICMP_V6_ROUTER_SOLICIT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_ROUTER_ADVERTISE, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_NEIGHBOR_SOLICIT, + ICMP_V6_DEFAULT_CODE + }, + { + ICMP_V6_NEIGHBOR_ADVERTISE, + ICMP_V6_DEFAULT_CODE + }, +}; + +/** + Reply an ICMPv6 echo request. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 informational message. + @param[in] Packet The content of the ICMPv6 message with the IP head + removed. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS Successfully answered the ICMPv6 Echo request. + @retval Others Failed to answer the ICMPv6 Echo request. + +**/ +EFI_STATUS +Ip6IcmpReplyEcho ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD *Icmp; + NET_BUF *Data; + EFI_STATUS Status; + EFI_IP6_HEADER ReplyHead; + + Status = EFI_OUT_OF_RESOURCES; + // + // make a copy the packet, it is really a bad idea to + // send the MNP's buffer back to MNP. + // + Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN); + if (Data == NULL) { + goto 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 = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL); + if (Icmp == NULL) { + NetbufFree (Data); + goto Exit; + } + + Icmp->Head.Type = ICMP_V6_ECHO_REPLY; + Icmp->Head.Checksum = 0; + + // + // Generate the IPv6 basic header + // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address, + // the Source address of the Echo Reply must be the same address. + // + ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER)); + + ReplyHead.PayloadLength = HTONS ((UINT16) (Packet->TotalSize)); + ReplyHead.NextHeader = IP6_ICMP; + ReplyHead.HopLimit = IpSb->CurHopLimit; + IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress); + + if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { + IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress); + } + + // + // If source is unspecified, Ip6Output will select a source for us + // + Status = Ip6Output ( + IpSb, + NULL, + NULL, + Data, + &ReplyHead, + NULL, + 0, + Ip6SysPacketSent, + NULL + ); + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process Packet Too Big message sent by a router in response to a packet that + it cannot forward because the packet is larger than the MTU of outgoing link. + Since this driver already uses IPv6 minimum link MTU as the maximum packet size, + if Packet Too Big message is still received, do not reduce the packet size, but + rather include a Fragment header in the subsequent packets. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 error packet. + @param[in] Packet The content of the ICMPv6 error with the IP head + removed. + + @retval EFI_SUCCESS The ICMPv6 error processed successfully. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resource. + @retval EFI_NOT_FOUND The packet too big message is not sent to us. + +**/ +EFI_STATUS +Ip6ProcessPacketTooBig ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + UINT32 Mtu; + IP6_ROUTE_ENTRY *RouteEntry; + EFI_IPv6_ADDRESS *DestAddress; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Mtu = NTOHL (Icmp.Fourth); + DestAddress = &Icmp.IpHead.DestinationAddress; + + if (Mtu < IP6_MIN_LINK_MTU) { + // + // Normally the multicast address is considered to be on-link and not recorded + // in route table. Here it is added into the table since the MTU information + // need be recorded. + // + if (IP6_IS_MULTICAST (DestAddress)) { + RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL); + if (RouteEntry == NULL) { + NetbufFree (Packet); + return EFI_OUT_OF_RESOURCES; + } + + RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG; + InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link); + IpSb->RouteTable->TotalNum++; + } else { + RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL); + if (RouteEntry == NULL) { + NetbufFree (Packet); + return EFI_NOT_FOUND; + } + + RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG; + + Ip6FreeRouteEntry (RouteEntry); + } + } + + NetbufFree (Packet); + return EFI_SUCCESS; +} + +/** + Process the ICMPv6 error packet, and deliver the packet to upper layer. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 error packet. + @param[in] Packet The content of the ICMPv6 error with the IP head + removed. + + @retval EFI_SUCCESS The ICMPv6 error processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessIcmpError ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + + // + // Check the validity of the packet + // + if (Packet->TotalSize < sizeof (Icmp)) { + goto DROP; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) { + return Ip6ProcessPacketTooBig (IpSb, Head, Packet); + } + + // + // Notify the upper-layer process that an ICMPv6 error message is received. + // + IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR; + return Ip6Demultiplex (IpSb, Head, Packet); + +DROP: + NetbufFree (Packet); + Packet = NULL; + return EFI_INVALID_PARAMETER; +} + +/** + Process the ICMPv6 informational messages. If it is an ICMPv6 echo + request, answer it. If it is a MLD message, trigger MLD routines to + process it. If it is a ND message, trigger ND routines to process it. + Otherwise, deliver it to upper layer. + + @param[in] IpSb The IP service that receivd the packet. + @param[in] Head The IP head of the ICMPv6 informational packet. + @param[in] Packet The content of the ICMPv6 informational packet + with IP head removed. + + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_SUCCESS The ICMPv6 informational message processed. + @retval Others Failed to process ICMPv6 informational message. + +**/ +EFI_STATUS +Ip6ProcessIcmpInformation ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_STATUS Status; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + NET_CHECK_SIGNATURE (Packet, NET_BUF_SIGNATURE); + ASSERT (Head != NULL); + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + Status = EFI_INVALID_PARAMETER; + + switch (Icmp.Head.Type) { + case ICMP_V6_ECHO_REQUEST: + // + // If ICMPv6 echo, reply it + // + if (Icmp.Head.Code == 0) { + Status = Ip6IcmpReplyEcho (IpSb, Head, Packet); + } + break; + case ICMP_V6_LISTENER_QUERY: + Status = Ip6ProcessMldQuery (IpSb, Head, Packet); + break; + case ICMP_V6_LISTENER_REPORT: + case ICMP_V6_LISTENER_REPORT_2: + Status = Ip6ProcessMldReport (IpSb, Head, Packet); + break; + case ICMP_V6_NEIGHBOR_SOLICIT: + Status = Ip6ProcessNeighborSolicit (IpSb, Head, Packet); + break; + case ICMP_V6_NEIGHBOR_ADVERTISE: + Status = Ip6ProcessNeighborAdvertise (IpSb, Head, Packet); + break; + case ICMP_V6_ROUTER_ADVERTISE: + Status = Ip6ProcessRouterAdvertise (IpSb, Head, Packet); + break; + case ICMP_V6_REDIRECT: + Status = Ip6ProcessRedirect (IpSb, Head, Packet); + break; + case ICMP_V6_ECHO_REPLY: + Status = Ip6Demultiplex (IpSb, Head, Packet); + break; + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + return Status; +} + +/** + Handle the ICMPv6 packet. First validate the message format, + then, according to the message types, process it as an informational packet or + an error packet. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 packet with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_SUCCESS The ICMPv6 message successfully processed. + @retval Others Failed to handle the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6IcmpHandle ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_HEAD Icmp; + UINT16 PseudoCheckSum; + UINT16 CheckSum; + + // + // Check the validity of the incoming packet. + // + if (Packet->TotalSize < sizeof (Icmp)) { + goto DROP; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + // + // Make sure checksum is valid. + // + PseudoCheckSum = NetIp6PseudoHeadChecksum ( + &Head->SourceAddress, + &Head->DestinationAddress, + IP6_ICMP, + Packet->TotalSize + ); + CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet)); + if (CheckSum != 0) { + goto DROP; + } + + // + // According to the packet type, call corresponding process + // + if (Icmp.Type <= ICMP_V6_ERROR_MAX) { + return Ip6ProcessIcmpError (IpSb, Head, Packet); + } else { + return Ip6ProcessIcmpInformation (IpSb, Head, Packet); + } + +DROP: + NetbufFree (Packet); + return EFI_INVALID_PARAMETER; +} + +/** + Retrieve the Prefix address according to the PrefixLength by clear the useless + bits. + + @param[in] PrefixLength The prefix length of the prefix. + @param[in, out] Prefix On input, points to the original prefix address + with dirty bits; on output, points to the updated + address with useless bit clear. + +**/ +VOID +Ip6GetPrefix ( + IN UINT8 PrefixLength, + IN OUT EFI_IPv6_ADDRESS *Prefix + ) +{ + UINT8 Byte; + UINT8 Bit; + UINT8 Mask; + UINT8 Value; + + ASSERT ((Prefix != NULL) && (PrefixLength < IP6_PREFIX_MAX)); + + if (PrefixLength == 0) { + ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS)); + return ; + } + + if (PrefixLength >= IP6_PREFIX_MAX) { + return ; + } + + Byte = (UINT8) (PrefixLength / 8); + Bit = (UINT8) (PrefixLength % 8); + Value = Prefix->Addr[Byte]; + + if (Byte > 0) { + ZeroMem (Prefix->Addr + Byte, 16 - Byte); + } + + if (Bit > 0) { + Mask = (UINT8) (0xFF << (8 - Bit)); + Prefix->Addr[Byte] = (UINT8) (Value & Mask); + } + +} + +/** + Check whether the DestinationAddress is an anycast address. + + @param[in] IpSb The IP service that received the packet. + @param[in] DestinationAddress Points to the Destination Address of the packet. + + @retval TRUE The DestinationAddress is anycast address. + @retval FALSE The DestinationAddress is not anycast address. + +**/ +BOOLEAN +Ip6IsAnycast ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *DestinationAddress + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + EFI_IPv6_ADDRESS Prefix; + BOOLEAN Flag; + + ZeroMem (&Prefix, sizeof (EFI_IPv6_ADDRESS)); + + Flag = FALSE; + + // + // If the address is known as on-link or autonomous prefix, record it as + // anycast address. + // + do { + PrefixEntry = Ip6FindPrefixListEntry (IpSb, Flag, 255, DestinationAddress); + if (PrefixEntry != NULL) { + IP6_COPY_ADDRESS (&Prefix, &PrefixEntry->Prefix); + Ip6GetPrefix (PrefixEntry->PrefixLength, &Prefix); + if (EFI_IP6_EQUAL (&Prefix, DestinationAddress)) { + return TRUE; + } + } + + Flag = (BOOLEAN) !Flag; + } while (Flag); + + return FALSE; +} + +/** + Generate ICMPv6 error message and send it out to DestinationAddress. Currently + Destination Unreachable message, Time Exceeded message and Parameter Problem + message are supported. + + @param[in] IpSb The IP service that received the packet. + @param[in] Packet The packet which invoking ICMPv6 error. + @param[in] SourceAddress If not NULL, points to the SourceAddress. + Otherwise, the IP layer will select a source address + according to the DestinationAddress. + @param[in] DestinationAddress Points to the Destination Address of the ICMPv6 + error message. + @param[in] Type The type of the ICMPv6 message. + @param[in] Code The additional level of the ICMPv6 message. + @param[in] Pointer If not NULL, identifies the octet offset within + the invoking packet where the error was detected. + + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the + operation. + @retval EFI_SUCCESS The ICMPv6 message was successfully sent out. + @retval Others Failed to generate the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6SendIcmpError ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN UINT8 Type, + IN UINT8 Code, + IN UINT32 *Pointer OPTIONAL + ) +{ + UINT32 PacketLen; + NET_BUF *ErrorMsg; + UINT16 PayloadLen; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + UINT8 *ErrorBody; + + if (DestinationAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // An ICMPv6 error message must not be originated as a result of receiving + // a packet whose source address does not uniquely identify a single node -- + // e.g., the IPv6 Unspecified Address, an IPv6 multicast address, or an address + // known by the ICMP message originator to be an IPv6 anycast address. + // + if (NetIp6IsUnspecifiedAddr (DestinationAddress) || + IP6_IS_MULTICAST (DestinationAddress) || + Ip6IsAnycast (IpSb, DestinationAddress) + ) { + return EFI_INVALID_PARAMETER; + } + + switch (Type) { + case ICMP_V6_DEST_UNREACHABLE: + case ICMP_V6_TIME_EXCEEDED: + break; + + case ICMP_V6_PARAMETER_PROBLEM: + if (Pointer == NULL) { + return EFI_INVALID_PARAMETER; + } + + break; + + default: + return EFI_INVALID_PARAMETER; + } + + PacketLen = sizeof (IP6_ICMP_ERROR_HEAD) + Packet->TotalSize; + + if (PacketLen > IpSb->MaxPacketSize) { + PacketLen = IpSb->MaxPacketSize; + } + + ErrorMsg = NetbufAlloc (PacketLen); + if (ErrorMsg == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PayloadLen = (UINT16) (PacketLen - sizeof (EFI_IP6_HEADER)); + + // + // Create the basic IPv6 header. + // + ZeroMem (&Head, sizeof (EFI_IP6_HEADER)); + + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IpSb->CurHopLimit; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (ErrorMsg, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP error message head + // + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (ErrorMsg, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + if (IcmpHead == NULL) { + NetbufFree (ErrorMsg); + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = Type; + IcmpHead->Head.Code = Code; + + if (Pointer != NULL) { + IcmpHead->Fourth = HTONL (*Pointer); + } + + // + // Fill in the ICMP error message body + // + PayloadLen -= sizeof (IP6_ICMP_INFORMATION_HEAD); + ErrorBody = NetbufAllocSpace (ErrorMsg, PayloadLen, FALSE); + if (ErrorBody != NULL) { + ZeroMem (ErrorBody, PayloadLen); + NetbufCopy (Packet, 0, PayloadLen, ErrorBody); + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, ErrorMsg, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Icmp.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Icmp.h new file mode 100644 index 00000000..75df3589 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Icmp.h @@ -0,0 +1,102 @@ +/** @file + Header file for ICMPv6 protocol. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_ICMP_H__ +#define __EFI_IP6_ICMP_H__ + +#define ICMP_V6_DEFAULT_CODE 0 + +#define ICMP_V6_ERROR_MAX 127 + +// +// ICMPv6 message classes, each class of ICMPv6 message shares +// a common message format. INVALID_MESSAGE is only a flag. +// +#define ICMP_V6_INVALID_MESSAGE 0 +#define ICMP_V6_ERROR_MESSAGE 1 +#define ICMP_V6_INFORMATION_MESSAGE 2 + + +extern EFI_IP6_ICMP_TYPE mIp6SupportedIcmp[]; + +/** + Handle the ICMPv6 packet. First validate the message format, + then, according to the message types, process it as an informational packet or + an error packet. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 packet with IP head + removed. + + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_SUCCESS The ICMPv6 message successfully processed. + @retval Others Failed to handle the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6IcmpHandle ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Check whether the DestinationAddress is an anycast address. + + @param[in] IpSb The IP service that received the packet. + @param[in] DestinationAddress Points to the Destination Address of the packet. + + @retval TRUE The DestinationAddress is anycast address. + @retval FALSE The DestinationAddress is not anycast address. + +**/ +BOOLEAN +Ip6IsAnycast ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *DestinationAddress + ); + +/** + Generate ICMPv6 error message and send it out to DestinationAddress. Currently + Destination Unreachable message, Time Exceeded message and Parameter Problem + message are supported. + + @param[in] IpSb The IP service that received the packet. + @param[in] Packet The packet which invoking ICMPv6 error. + @param[in] SourceAddress If not NULL, points to the SourceAddress. + Otherwise, the IP layer will select a source address + according to the DestinationAddress. + @param[in] DestinationAddress Points to the Destination Address of the ICMPv6 + error message. + @param[in] Type The type of the ICMPv6 message. + @param[in] Code The additional level of the ICMPv6 message. + @param[in] Pointer If not NULL, identifies the octet offset within + the invoking packet where the error was detected. + + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to complete the + operation. + @retval EFI_SUCCESS The ICMPv6 message was successfully sent out. + @retval Others Failed to generate the ICMPv6 packet. + +**/ +EFI_STATUS +Ip6SendIcmpError ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN UINT8 Type, + IN UINT8 Code, + IN UINT32 *Pointer OPTIONAL + ); + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6If.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6If.c new file mode 100644 index 00000000..f5f4d50d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6If.c @@ -0,0 +1,792 @@ +/** @file + Implement IP6 pseudo interface. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +/** + Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context The Context which is pointed to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Fileter 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 +Ip6CancelInstanceFrame ( + IN IP6_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if (Frame->IpInstance == (IP6_PROTOCOL *) Context) { + return TRUE; + } + + return FALSE; +} + +/** + Set the interface's address. This will trigger the DAD process for the + address to set. To set an already set address, the lifetimes wil be + updated to the new value passed in. + + @param[in] Interface The interface to set the address. + @param[in] Ip6Addr The interface's to be assigned IPv6 address. + @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast. + Otherwise, it is not anycast. + @param[in] PrefixLength The prefix length of the Ip6Addr. + @param[in] ValidLifetime The valid lifetime for this address. + @param[in] PreferredLifetime The preferred lifetime for this address. + @param[in] DadCallback The caller's callback to trigger when DAD finishes. + This is an optional parameter that may be NULL. + @param[in] Context The context that will be passed to DadCallback. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The interface is scheduled to be configured with + the specified address. + @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to + lack of resources. + +**/ +EFI_STATUS +Ip6SetAddress ( + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Ip6Addr, + IN BOOLEAN IsAnycast, + IN UINT8 PrefixLength, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN IP6_DAD_CALLBACK DadCallback OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddressInfo; + LIST_ENTRY *Entry; + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + UINT64 Delay; + IP6_DELAY_JOIN_LIST *DelayNode; + + NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE); + + IpSb = Interface->Service; + + if (Ip6IsOneOfSetAddress (IpSb, Ip6Addr, NULL, &AddressInfo)) { + ASSERT (AddressInfo != NULL); + // + // Update the lifetime. + // + AddressInfo->ValidLifetime = ValidLifetime; + AddressInfo->PreferredLifetime = PreferredLifetime; + + if (DadCallback != NULL) { + DadCallback (TRUE, Ip6Addr, Context); + } + + return EFI_SUCCESS; + } + + AddressInfo = (IP6_ADDRESS_INFO *) AllocatePool (sizeof (IP6_ADDRESS_INFO)); + if (AddressInfo == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + AddressInfo->Signature = IP6_ADDR_INFO_SIGNATURE; + IP6_COPY_ADDRESS (&AddressInfo->Address, Ip6Addr); + AddressInfo->IsAnycast = IsAnycast; + AddressInfo->PrefixLength = PrefixLength; + AddressInfo->ValidLifetime = ValidLifetime; + AddressInfo->PreferredLifetime = PreferredLifetime; + + if (AddressInfo->PrefixLength == 0) { + // + // Find an appropriate prefix from on-link prefixes and update the prefixlength. + // Longest prefix match is used here. + // + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) { + AddressInfo->PrefixLength = PrefixEntry->PrefixLength; + break; + } + } + } + + if (AddressInfo->PrefixLength == 0) { + // + // If the prefix length is still zero, try the autonomous prefixes. + // Longest prefix match is used here. + // + NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) { + PrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (NetIp6IsNetEqual (&PrefixEntry->Prefix, &AddressInfo->Address, PrefixEntry->PrefixLength)) { + AddressInfo->PrefixLength = PrefixEntry->PrefixLength; + break; + } + } + } + + if (AddressInfo->PrefixLength == 0) { + // + // BUGBUG: Stil fail, use 64 as the default prefix length. + // + AddressInfo->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + } + + + // + // Node should delay joining the solicited-node multicast address by a random delay + // between 0 and MAX_RTR_SOLICITATION_DELAY (1 second). + // Thus queue the address to be processed in Duplicate Address Detection module + // after the delay time (in milliseconds). + // + Delay = (UINT64) NET_RANDOM (NetRandomInitSeed ()); + Delay = MultU64x32 (Delay, IP6_ONE_SECOND_IN_MS); + Delay = RShiftU64 (Delay, 32); + + DelayNode = (IP6_DELAY_JOIN_LIST *) AllocatePool (sizeof (IP6_DELAY_JOIN_LIST)); + if (DelayNode == NULL) { + FreePool (AddressInfo); + return EFI_OUT_OF_RESOURCES; + } + + DelayNode->DelayTime = (UINT32) (DivU64x32 (Delay, IP6_TIMER_INTERVAL_IN_MS)); + DelayNode->Interface = Interface; + DelayNode->AddressInfo = AddressInfo; + DelayNode->DadCallback = DadCallback; + DelayNode->Context = Context; + + InsertTailList (&Interface->DelayJoinList, &DelayNode->Link); + return EFI_SUCCESS; +} + +/** + Create an IP6_INTERFACE. + + @param[in] IpSb The IP6 service binding instance. + @param[in] LinkLocal If TRUE, the instance is created for link-local address. + Otherwise, it is not for a link-local address. + + @return Point to the created IP6_INTERFACE, otherwise NULL. + +**/ +IP6_INTERFACE * +Ip6CreateInterface ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN LinkLocal + ) +{ + EFI_STATUS Status; + IP6_INTERFACE *Interface; + EFI_IPv6_ADDRESS *Ip6Addr; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Interface = AllocatePool (sizeof (IP6_INTERFACE)); + if (Interface == NULL) { + return NULL; + } + + Interface->Signature = IP6_INTERFACE_SIGNATURE; + Interface->RefCnt = 1; + + InitializeListHead (&Interface->AddressList); + Interface->AddressCount = 0; + Interface->Configured = FALSE; + + Interface->Service = IpSb; + Interface->Controller = IpSb->Controller; + Interface->Image = IpSb->Image; + + InitializeListHead (&Interface->ArpQues); + InitializeListHead (&Interface->SentFrames); + + Interface->DupAddrDetect = IpSb->Ip6ConfigInstance.DadXmits.DupAddrDetectTransmits; + InitializeListHead (&Interface->DupAddrDetectList); + + InitializeListHead (&Interface->DelayJoinList); + + InitializeListHead (&Interface->IpInstances); + Interface->PromiscRecv = FALSE; + + if (!LinkLocal) { + return Interface; + } + + // + // Get the link local addr + // + Ip6Addr = Ip6CreateLinkLocalAddr (IpSb); + if (Ip6Addr == NULL) { + goto ON_ERROR; + } + + // + // Perform DAD - Duplicate Address Detection. + // + Status = Ip6SetAddress ( + Interface, + Ip6Addr, + FALSE, + IP6_LINK_LOCAL_PREFIX_LENGTH, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + NULL, + NULL + ); + + FreePool (Ip6Addr); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return Interface; + +ON_ERROR: + + FreePool (Interface); + return NULL; +} + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/prefix pair share the same interface. It is reference + counted. All the frames that haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list. + + @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. + +**/ +VOID +Ip6CleanInterface ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL + ) +{ + IP6_DAD_ENTRY *Duplicate; + IP6_DELAY_JOIN_LIST *Delay; + + NET_CHECK_SIGNATURE (Interface, IP6_INTERFACE_SIGNATURE); + ASSERT (Interface->RefCnt > 0); + + // + // Remove all the pending transmit token related to this IP instance. + // + Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, IpInstance); + + if (--Interface->RefCnt > 0) { + return; + } + + // + // Destroy the interface if this is the last IP instance. + // Remove all the system transmitted packets + // from this interface, cancel the receive request if exists. + // + Ip6CancelFrames (Interface, EFI_ABORTED, Ip6CancelInstanceFrame, NULL); + + ASSERT (IsListEmpty (&Interface->IpInstances)); + ASSERT (IsListEmpty (&Interface->ArpQues)); + ASSERT (IsListEmpty (&Interface->SentFrames)); + + while (!IsListEmpty (&Interface->DupAddrDetectList)) { + Duplicate = NET_LIST_HEAD (&Interface->DupAddrDetectList, IP6_DAD_ENTRY, Link); + NetListRemoveHead (&Interface->DupAddrDetectList); + FreePool (Duplicate); + } + + while (!IsListEmpty (&Interface->DelayJoinList)) { + Delay = NET_LIST_HEAD (&Interface->DelayJoinList, IP6_DELAY_JOIN_LIST, Link); + NetListRemoveHead (&Interface->DelayJoinList); + FreePool (Delay); + } + + Ip6RemoveAddr (Interface->Service, &Interface->AddressList, &Interface->AddressCount, NULL, 0); + + RemoveEntryList (&Interface->Link); + FreePool (Interface); +} + +/** + Create and wrap a transmit request into a newly allocated IP6_LINK_TX_TOKEN. + + @param[in] Interface The interface to send out from. + @param[in] IpInstance The IpInstance that transmit the packet. NULL if + the packet is sent by the IP6 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 callback. + + @return The wrapped token if succeed or NULL. + +**/ +IP6_LINK_TX_TOKEN * +Ip6CreateLinkTxToken ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_TRANSMIT_DATA *MnpTxData; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + UINT32 Count; + + Token = AllocatePool (sizeof (IP6_LINK_TX_TOKEN) + (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA)); + + if (Token == NULL) { + return NULL; + } + + Token->Signature = IP6_LINK_TX_SIGNATURE; + InitializeListHead (&Token->Link); + + Token->IpInstance = IpInstance; + Token->CallBack = CallBack; + Token->Packet = Packet; + Token->Context = Context; + ZeroMem (&Token->DstMac, sizeof (EFI_MAC_ADDRESS)); + IP6_COPY_LINK_ADDRESS (&Token->SrcMac, &Interface->Service->SnpMode.CurrentAddress); + + MnpToken = &(Token->MnpToken); + MnpToken->Status = EFI_NOT_READY; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnFrameSent, + 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 = IP6_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 +Ip6FreeLinkTxToken ( + IN IP6_LINK_TX_TOKEN *Token + ) +{ + NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE); + + gBS->CloseEvent (Token->MnpToken.Event); + FreePool (Token); +} + +/** + Callback function when the received packet is freed. + Check Ip6OnFrameReceived for information. + + @param[in] Context Points to EFI_MANAGED_NETWORK_RECEIVE_DATA. + +**/ +VOID +EFIAPI +Ip6RecycleFrame ( + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_RECEIVE_DATA *RxData; + + RxData = (EFI_MANAGED_NETWORK_RECEIVE_DATA *) Context; + + gBS->SignalEvent (RxData->RecycleEvent); +} + +/** + Received a frame from MNP. Wrap it in net buffer then deliver + it to IP's input function. The ownship of the packet also + is transferred to IP. When Ip is finished with this packet, it + will call NetbufFree to release the packet, NetbufFree will + again call the Ip6RecycleFrame to signal MNP's event and free + the token used. + + @param[in] Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceivedDpc ( + IN VOID *Context + ) +{ + EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken; + EFI_MANAGED_NETWORK_RECEIVE_DATA *MnpRxData; + IP6_LINK_RX_TOKEN *Token; + NET_FRAGMENT Netfrag; + NET_BUF *Packet; + UINT32 Flag; + IP6_SERVICE *IpSb; + + Token = (IP6_LINK_RX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP6_LINK_RX_SIGNATURE); + + // + // First clear the interface's receive request in case the + // caller wants to call Ip6ReceiveFrame in the callback. + // + IpSb = (IP6_SERVICE *) Token->Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + + MnpToken = &Token->MnpToken; + MnpRxData = MnpToken->Packet.RxData; + + if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) { + Token->CallBack (NULL, MnpToken->Status, 0, Token->Context); + 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, IP6_MAX_HEADLEN, 0, Ip6RecycleFrame, Token->MnpToken.Packet.RxData); + + if (Packet == NULL) { + gBS->SignalEvent (MnpRxData->RecycleEvent); + + Token->CallBack (NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context); + + return ; + } + + Flag = (MnpRxData->BroadcastFlag ? IP6_LINK_BROADCAST : 0); + Flag |= (MnpRxData->MulticastFlag ? IP6_LINK_MULTICAST : 0); + Flag |= (MnpRxData->PromiscuousFlag ? IP6_LINK_PROMISC : 0); + + Token->CallBack (Packet, EFI_SUCCESS, Flag, Token->Context); +} + +/** + Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK. + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip6OnFrameReceivedDpc, Context); +} + +/** + Request to receive the packet from the interface. + + @param[in] CallBack Function to call when receive finished. + @param[in] IpSb Points to IP6 service binding instance. + + @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. + +**/ +EFI_STATUS +Ip6ReceiveFrame ( + IN IP6_FRAME_CALLBACK CallBack, + IN IP6_SERVICE *IpSb + ) +{ + EFI_STATUS Status; + IP6_LINK_RX_TOKEN *Token; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Token = &IpSb->RecvRequest; + Token->CallBack = CallBack; + Token->Context = (VOID *) IpSb; + + Status = IpSb->Mnp->Receive (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + 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 points to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSentDpc ( + IN VOID *Context + ) +{ + IP6_LINK_TX_TOKEN *Token; + + Token = (IP6_LINK_TX_TOKEN *) Context; + NET_CHECK_SIGNATURE (Token, IP6_LINK_TX_SIGNATURE); + + RemoveEntryList (&Token->Link); + + Token->CallBack ( + Token->Packet, + Token->MnpToken.Status, + 0, + Token->Context + ); + + Ip6FreeLinkTxToken (Token); +} + +/** + Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK. + + @param[in] Event The transmit token's event. + @param[in] Context Context which points to the token. + +**/ +VOID +EFIAPI +Ip6OnFrameSent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + // + // Request Ip6OnFrameSentDpc as a DPC at TPL_CALLBACK + // + QueueDpc (TPL_CALLBACK, Ip6OnFrameSentDpc, Context); +} + +/** + Send a frame from the interface. If the next hop is a multicast address, + it is transmitted immediately. If the next hop is a unicast, + and the NextHop's MAC is not known, it will perform address resolution. + If an error occurred, 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 IP6 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 callback. + + @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 successfully transmitted. + +**/ +EFI_STATUS +Ip6SendFrame ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *NextHop, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + IP6_NEIGHBOR_ENTRY *NeighborCache; + LIST_ENTRY *Entry; + IP6_NEIGHBOR_ENTRY *ArpQue; + + IpSb = Interface->Service; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // Only when link local address is performing DAD, the interface could be used in unconfigured. + // + if (IpSb->LinkLocalOk) { + ASSERT (Interface->Configured); + } + + Token = Ip6CreateLinkTxToken (Interface, IpInstance, Packet, CallBack, Context); + + if (Token == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (IP6_IS_MULTICAST (NextHop)) { + Status = Ip6GetMulticastMac (IpSb->Mnp, NextHop, &Token->DstMac); + if (EFI_ERROR (Status)) { + goto Error; + } + + goto SendNow; + } + + // + // If send to itself, directly send out + // + if (EFI_IP6_EQUAL (&Packet->Ip.Ip6->DestinationAddress, &Packet->Ip.Ip6->SourceAddress)) { + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &IpSb->SnpMode.CurrentAddress); + goto SendNow; + } + + // + // If unicast, check the neighbor state. + // + + NeighborCache = Ip6FindNeighborEntry (IpSb, NextHop); + ASSERT (NeighborCache != NULL); + + if (NeighborCache->Interface == NULL) { + NeighborCache->Interface = Interface; + } + + switch (NeighborCache->State) { + case EfiNeighborStale: + NeighborCache->State = EfiNeighborDelay; + NeighborCache->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME); + // + // Fall through + // + case EfiNeighborReachable: + case EfiNeighborDelay: + case EfiNeighborProbe: + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &NeighborCache->LinkAddress); + goto SendNow; + break; + + default: + break; + } + + // + // Have to do asynchronous ARP resolution. First check whether there is + // already a pending request. + // + NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList); + if (ArpQue == NeighborCache) { + InsertTailList (&NeighborCache->Frames, &Token->Link); + NeighborCache->ArpFree = TRUE; + return EFI_SUCCESS; + } + } + + // + // First frame requires ARP. + // + InsertTailList (&NeighborCache->Frames, &Token->Link); + InsertTailList (&Interface->ArpQues, &NeighborCache->ArpList); + + NeighborCache->ArpFree = TRUE; + + return EFI_SUCCESS; + +SendNow: + // + // 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 = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + goto Error; + } + + return EFI_SUCCESS; + +Error: + Ip6FreeLinkTxToken (Token); + return Status; +} + +/** + The heartbeat timer of IP6 service instance. It times out + all of its IP6 children's received-but-not-delivered and + transmitted-but-not-recycle packets. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Ip6PacketTimerTicking (IpSb); + Ip6NdTimerTicking (IpSb); + Ip6MldTimerTicking (IpSb); +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6If.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6If.h new file mode 100644 index 00000000..f1419950 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6If.h @@ -0,0 +1,261 @@ +/** @file + Definition for IP6 pseudo interface structure. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_IF_H__ +#define __EFI_IP6_IF_H__ + +#define IP6_LINK_RX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'R') +#define IP6_LINK_TX_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'T') +#define IP6_INTERFACE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'I') +#define IP6_ADDR_INFO_SIGNATURE SIGNATURE_32 ('I', 'P', 'A', 'I') + +// +// This prototype is used by both receive and transmission. +// When receiving Netbuf is allocated by IP6_INTERFACE, and +// released by IP6. Flag shows whether the frame is received +// as unicast/multicast/anycast... +// +// When transmitting, the Netbuf is from IP6, and provided +// to the callback as a reference. Flag isn't used. +// +// IpInstance can be NULL which means that it is the IP6 driver +// itself sending the packets. IP6 driver may send packets that +// don't belong to any instance, such as ICMP errors, ICMP +// informational packets. IpInstance is used as a tag in +// this module. +// +typedef +VOID +(*IP6_FRAME_CALLBACK) ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +// +// Each receive request is wrapped in an IP6_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; + IP6_FRAME_CALLBACK CallBack; + VOID *Context; + EFI_MANAGED_NETWORK_COMPLETION_TOKEN MnpToken; +} IP6_LINK_RX_TOKEN; + +// +// Each transmit request is wrapped in an IP6_LINK_TX_TOKEN. +// Upon completion, the Callback will be called. +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + + IP6_PROTOCOL *IpInstance; + IP6_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; +} IP6_LINK_TX_TOKEN; + +struct _IP6_ADDRESS_INFO { + UINT32 Signature; + LIST_ENTRY Link; + EFI_IPv6_ADDRESS Address; + BOOLEAN IsAnycast; + UINT8 PrefixLength; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; +}; + +// +// Callback to select which frame to cancel. Caller can cancel a +// single frame, or all the frame from an IP instance. +// +typedef +BOOLEAN +(*IP6_FRAME_TO_CANCEL) ( + IP6_LINK_TX_TOKEN *Frame, + VOID *Context + ); + +struct _IP6_INTERFACE { + UINT32 Signature; + LIST_ENTRY Link; + INTN RefCnt; + + // + // IP address and prefix length of the interface. The fileds + // are invalid if (Configured == FALSE) + // + LIST_ENTRY AddressList; + UINT32 AddressCount; + BOOLEAN Configured; + + IP6_SERVICE *Service; + + EFI_HANDLE Controller; + EFI_HANDLE Image; + + + // + // Queues to keep the frames sent and waiting ARP request. + // + LIST_ENTRY ArpQues; + LIST_ENTRY SentFrames; + + + // + // The interface's configuration variables + // + UINT32 DupAddrDetect; + LIST_ENTRY DupAddrDetectList; + LIST_ENTRY DelayJoinList; + + // + // 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 IP6_INTERFACE. + + @param[in] IpSb The IP6 service binding instance. + @param[in] LinkLocal If TRUE, the instance is created for link-local address. + Otherwise, it is not for a link-local address. + + @return Point to the created IP6_INTERFACE, otherwise NULL. + +**/ +IP6_INTERFACE * +Ip6CreateInterface ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN LinkLocal + ); + +/** + Free the interface used by IpInstance. All the IP instance with + the same Ip/prefix pair share the same interface. It is reference + counted. All the frames that haven't been sent will be cancelled. + Because the IpInstance is optional, the caller must remove + IpInstance from the interface's instance list. + + @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. + +**/ +VOID +Ip6CleanInterface ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL + ); + +/** + Free the link layer transmit token. It will close the event + then free the memory used. + + @param[in] Token Token to free. + +**/ +VOID +Ip6FreeLinkTxToken ( + IN IP6_LINK_TX_TOKEN *Token + ); + +/** + Request Ip6OnFrameReceivedDpc as a DPC at TPL_CALLBACK + + @param Event The receive event delivered to MNP for receive. + @param Context Context for the callback. + +**/ +VOID +EFIAPI +Ip6OnFrameReceived ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Request to receive the packet from the interface. + + @param[in] CallBack Function to call when the receive finished. + @param[in] IpSb Points to the IP6 service binding instance. + + @retval EFI_ALREADY_STARTED There is already a pending receive request. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to receive. + @retval EFI_SUCCESS The receive request has been started. + +**/ +EFI_STATUS +Ip6ReceiveFrame ( + IN IP6_FRAME_CALLBACK CallBack, + IN IP6_SERVICE *IpSb + ); + +/** + Send a frame from the interface. If the next hop is multicast address, + it is transmitted immediately. If the next hop is a unicast, + and the NextHop's MAC is not known, it will perform address resolution. + 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 IP6 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. + + @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 successfully transmitted. + +**/ +EFI_STATUS +Ip6SendFrame ( + IN IP6_INTERFACE *Interface, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IPv6_ADDRESS *NextHop, + IN IP6_FRAME_CALLBACK CallBack, + IN VOID *Context + ); + +/** + The heartbeat timer of IP6 service instance. It times out + all of its IP6 children's received-but-not-delivered and + transmitted-but-not-recycle packets. + + @param[in] Event The IP6 service instance's heart beat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6TimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Impl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Impl.c new file mode 100644 index 00000000..5bbb95dc --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Impl.c @@ -0,0 +1,1841 @@ +/** @file + Implementation of EFI_IP6_PROTOCOL protocol interfaces. + + (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR> + Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +EFI_IPSEC2_PROTOCOL *mIpSec = NULL; + +EFI_IP6_PROTOCOL mEfiIp6ProtocolTemplete = { + EfiIp6GetModeData, + EfiIp6Configure, + EfiIp6Groups, + EfiIp6Routes, + EfiIp6Neighbors, + EfiIp6Transmit, + EfiIp6Receive, + EfiIp6Cancel, + EfiIp6Poll +}; + +/** + Gets the current operational settings for this instance of the EFI IPv6 Protocol driver. + + The GetModeData() function returns the current operational mode data for this driver instance. + The data fields in EFI_IP6_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_IP6_PROTOCOL instance. + @param[out] Ip6ModeData Pointer to the EFI IPv6 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 +EfiIp6GetModeData ( + IN EFI_IP6_PROTOCOL *This, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + IP6_INTERFACE *IpIf; + EFI_IP6_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + IpIf = IpInstance->Interface; + + if (IpSb->LinkLocalDadFail) { + return EFI_INVALID_PARAMETER; + } + + if (Ip6ModeData != NULL) { + // + // IsStarted is "whether the EfiIp6Configure has been called". + // IsConfigured is "whether the station address has been configured" + // + Ip6ModeData->IsStarted = (BOOLEAN) (IpInstance->State == IP6_STATE_CONFIGED); + Ip6ModeData->MaxPacketSize = IpSb->MaxPacketSize; + CopyMem (&Ip6ModeData->ConfigData, &IpInstance->ConfigData, sizeof (EFI_IP6_CONFIG_DATA)); + Ip6ModeData->IsConfigured = FALSE; + + Ip6ModeData->AddressCount = 0; + Ip6ModeData->AddressList = NULL; + + Ip6ModeData->GroupCount = IpInstance->GroupCount; + Ip6ModeData->GroupTable = NULL; + + Ip6ModeData->RouteCount = 0; + Ip6ModeData->RouteTable = NULL; + + Ip6ModeData->NeighborCount = 0; + Ip6ModeData->NeighborCache = NULL; + + Ip6ModeData->PrefixCount = 0; + Ip6ModeData->PrefixTable = NULL; + + Ip6ModeData->IcmpTypeCount = 23; + Ip6ModeData->IcmpTypeList = AllocateCopyPool ( + Ip6ModeData->IcmpTypeCount * sizeof (EFI_IP6_ICMP_TYPE), + mIp6SupportedIcmp + ); + if (Ip6ModeData->IcmpTypeList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + // + // Return the currently configured IPv6 addresses and corresponding prefix lengths. + // + Status = Ip6BuildEfiAddressList ( + IpSb, + &Ip6ModeData->AddressCount, + &Ip6ModeData->AddressList + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Return the current station address for this IP child. + // If UseAnyStationAddress is set to TRUE, IP6 driver will + // select a source address from its address list. Otherwise use the + // StationAddress in config data. + // + if (Ip6ModeData->IsStarted) { + Config = &Ip6ModeData->ConfigData; + + if (IpIf->Configured || NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + Ip6ModeData->IsConfigured = TRUE; + } else { + Ip6ModeData->IsConfigured = FALSE; + } + + // + // Build a EFI route table for user from the internal route table. + // + Status = Ip6BuildEfiRouteTable ( + IpSb->RouteTable, + &Ip6ModeData->RouteCount, + &Ip6ModeData->RouteTable + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + } + + if (Ip6ModeData->IsConfigured) { + // + // Return the joined multicast group addresses. + // + if (IpInstance->GroupCount != 0) { + Ip6ModeData->GroupTable = AllocateCopyPool ( + IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS), + IpInstance->GroupList + ); + if (Ip6ModeData->GroupTable == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + } + // + // Return the neighbor cache entries + // + Status = Ip6BuildEfiNeighborCache ( + IpInstance, + &Ip6ModeData->NeighborCount, + &Ip6ModeData->NeighborCache + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // Return the prefix table entries + // + Status = Ip6BuildPrefixTable ( + IpInstance, + &Ip6ModeData->PrefixCount, + &Ip6ModeData->PrefixTable + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + } + } + + // + // Get fresh mode data from MNP, since underlying media status may change + // + Status = IpSb->Mnp->GetModeData (IpSb->Mnp, MnpConfigData, SnpModeData); + + goto Exit; + +Error: + if (Ip6ModeData != NULL) { + if (Ip6ModeData->AddressList != NULL) { + FreePool (Ip6ModeData->AddressList); + } + + if (Ip6ModeData->GroupTable != NULL) { + FreePool (Ip6ModeData->GroupTable); + } + + if (Ip6ModeData->RouteTable != NULL) { + FreePool (Ip6ModeData->RouteTable); + } + + if (Ip6ModeData->NeighborCache != NULL) { + FreePool (Ip6ModeData->NeighborCache); + } + + if (Ip6ModeData->PrefixTable != NULL) { + FreePool (Ip6ModeData->PrefixTable); + } + + if (Ip6ModeData->IcmpTypeList != NULL) { + FreePool (Ip6ModeData->IcmpTypeList); + } + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Validate that Ipv6 address is OK to be used as station address or next hop address/ neighbor. + + @param[in] IpSb The IP6 service instance. + @param[in] Ip The IPv6 address to validate. + @param[in] Flag If TRUE, validate if the address is OK to be used + as station address. If FALSE, validate if the + address is OK to be used as the next hop address/ + neighbor. + + @retval TRUE The Ip address is valid and could be used. + @retval FALSE Invalid Ip address. + +**/ +BOOLEAN +Ip6IsValidAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip, + IN BOOLEAN Flag + ) +{ + if (!NetIp6IsUnspecifiedAddr (Ip)) { + if (!NetIp6IsValidUnicast(Ip)) { + return FALSE; + } + if (Ip6IsOneOfSetAddress (IpSb, Ip, NULL, NULL)) { + return Flag; + } + } else { + return Flag; + } + + return (BOOLEAN) !Flag; +} + +/** + Validate whether the value of protocol is illegal or not. Protocol is the 'Next Header' field + in the last IPv6 extension header, or basic IPv6 header is there's no extension header. + + @param[in] Protocol Default value of 'Next Header' + + @retval TRUE The protocol is illegal. + @retval FALSE The protocol is legal. + +**/ +BOOLEAN +Ip6IsIllegalProtocol ( + IN UINT8 Protocol + ) +{ + if (Protocol == IP6_HOP_BY_HOP || Protocol == EFI_IP_PROTO_ICMP || Protocol == IP4_PROTO_IGMP) { + return TRUE; + } + + if (Protocol == 41 || Protocol == 43 || Protocol == 44 || Protocol == 59 || Protocol == 60 || Protocol == 124) { + return TRUE; + } + + return FALSE; +} + +/** + Initialize the IP6_PROTOCOL structure to the unconfigured states. + + @param[in] IpSb The IP6 service instance. + @param[in, out] IpInstance The IP6 child instance. + +**/ +VOID +Ip6InitProtocol ( + IN IP6_SERVICE *IpSb, + IN OUT IP6_PROTOCOL *IpInstance + ) +{ + ASSERT ((IpSb != NULL) && (IpInstance != NULL)); + + ZeroMem (IpInstance, sizeof (IP6_PROTOCOL)); + + IpInstance->Signature = IP6_PROTOCOL_SIGNATURE; + IpInstance->State = IP6_STATE_UNCONFIGED; + IpInstance->Service = IpSb; + IpInstance->GroupList = NULL; + CopyMem (&IpInstance->Ip6Proto, &mEfiIp6ProtocolTemplete, sizeof (EFI_IP6_PROTOCOL)); + + NetMapInit (&IpInstance->RxTokens); + NetMapInit (&IpInstance->TxTokens); + InitializeListHead (&IpInstance->Received); + InitializeListHead (&IpInstance->Delivered); + + EfiInitializeLock (&IpInstance->RecycleLock, TPL_NOTIFY); +} + +/** + Configure the IP6 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 IP6 child to configure. + @param[in] Config The configure data. + + @retval EFI_SUCCESS The IP6 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 IP6 child is configured to use the default + address, but the default address hasn't been + configured. The IP6 child doesn't need to be + reconfigured when the default address is configured. + @retval EFI_OUT_OF_RESOURCES No more memory space is available. + @retval other Other error occurs. + +**/ +EFI_STATUS +Ip6ConfigProtocol ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IP6_CONFIG_DATA *Config + ) +{ + IP6_SERVICE *IpSb; + IP6_INTERFACE *IpIf; + EFI_STATUS Status; + EFI_IP6_CONFIG_DATA *Current; + IP6_ADDRESS_INFO *AddressInfo; + BOOLEAN StationZero; + BOOLEAN DestZero; + EFI_IPv6_ADDRESS Source; + BOOLEAN AddrOk; + + IpSb = IpInstance->Service; + Current = &IpInstance->ConfigData; + + // + // User is changing packet filters. It must be stopped + // before the station address can be changed. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + // + // Cancel all the pending transmit/receive from upper layer + // + Status = Ip6Cancel (IpInstance, NULL); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + return EFI_SUCCESS; + } + + // + // Set up the interface. + // + StationZero = NetIp6IsUnspecifiedAddr (&Config->StationAddress); + DestZero = NetIp6IsUnspecifiedAddr (&Config->DestinationAddress); + + if (StationZero && DestZero) { + // + // StationAddress is still zero. + // + + NET_GET_REF (IpSb->DefaultInterface); + IpInstance->Interface = IpSb->DefaultInterface; + InsertTailList (&IpSb->DefaultInterface->IpInstances, &IpInstance->AddrLink); + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + IpInstance->State = IP6_STATE_CONFIGED; + + return EFI_SUCCESS; + } + + if (StationZero && !DestZero) { + Status = Ip6SelectSourceAddress (IpSb, &Config->DestinationAddress, &Source); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + IP6_COPY_ADDRESS (&Source, &Config->StationAddress); + } + + AddrOk = Ip6IsOneOfSetAddress (IpSb, &Source, &IpIf, &AddressInfo); + if (AddrOk) { + if (AddressInfo != NULL) { + IpInstance->PrefixLength = AddressInfo->PrefixLength; + } else { + IpInstance->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + } + } else { + // + // The specified source address is not one of the addresses IPv6 maintains. + // + return EFI_INVALID_PARAMETER; + } + + + NET_GET_REF (IpIf); + IpInstance->Interface = IpIf; + InsertTailList (&IpIf->IpInstances, &IpInstance->AddrLink); + + CopyMem (Current, Config, sizeof (EFI_IP6_CONFIG_DATA)); + IP6_COPY_ADDRESS (&Current->StationAddress, &Source); + IpInstance->State = IP6_STATE_CONFIGED; + + return EFI_SUCCESS; +} + +/** + Clean up the IP6 child, and release all the resources used by it. + + @param[in, out] IpInstance The IP6 child to clean up. + + @retval EFI_SUCCESS The IP6 child is cleaned up. + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip6CleanProtocol ( + IN OUT IP6_PROTOCOL *IpInstance + ) +{ + if (EFI_ERROR (Ip6Cancel (IpInstance, NULL))) { + return EFI_DEVICE_ERROR; + } + + if (EFI_ERROR (Ip6Groups (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); + Ip6CleanInterface (IpInstance->Interface, IpInstance); + IpInstance->Interface = NULL; + } + + if (IpInstance->GroupList != NULL) { + FreePool (IpInstance->GroupList); + IpInstance->GroupList = NULL; + IpInstance->GroupCount = 0; + } + + NetMapClean (&IpInstance->TxTokens); + + NetMapClean (&IpInstance->RxTokens); + + return EFI_SUCCESS; +} + +/** + Configure the MNP parameter used by IP. The IP driver uses one MNP + child to transmit/receive frames. By default, it configures MNP + to receive unicast/multicast/broadcast. Also, it will enable/disable + the promiscuous 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 + whether that is changed or not. + + @param[in] IpSb The IP6 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP successfully configured/reconfigured. + @retval Others Configuration failed. + +**/ +EFI_STATUS +Ip6ServiceConfigMnp ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN Force + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *ProtoEntry; + IP6_INTERFACE *IpIf; + IP6_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, IP6_INTERFACE, Link); + IpIf->PromiscRecv = FALSE; + + NET_LIST_FOR_EACH (ProtoEntry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (ProtoEntry, IP6_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; +} + +/** + Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational parameters and filter + settings for this EFI IPv6 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 Ip6ConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped + independently of each other by enabling or disabling their receive filter settings with the + Configure() function. + + If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required + to be one of the currently configured IPv6 addresses listed in the EFI IPv6 drivers, or else + EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is + unspecified, the IPv6 driver will bind a source address according to the source address selection + algorithm. Clients could frequently call GetModeData() to check get currently configured IPv6 + address list in the EFI IPv6 driver. If both Ip6ConfigData.StationAddress and + Ip6ConfigData.Destination are unspecified, when transmitting the packet afterwards, the + source address filled in each outgoing IPv6 packet is decided based on the destination of this packet. + + 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_IP6_PROTOCOL instance. + @param[in] Ip6ConfigData Pointer to the EFI IPv6 Protocol configuration data structure. + If NULL, reset the configuration data. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Ip6ConfigData.StationAddress is neither zero nor + a unicast IPv6 address. + - Ip6ConfigData.StationAddress is neither zero nor + one of the configured IP addresses in the EFI IPv6 driver. + - Ip6ConfigData.DefaultProtocol is illegal. + @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for + this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6 + address or prefix length can be changed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6 + Protocol driver instance was not opened. + @retval EFI_UNSUPPORTED Default protocol specified through + Ip6ConfigData.DefaultProtocol isn't supported. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Configure ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA *Ip6ConfigData OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_IP6_CONFIG_DATA *Current; + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_SERVICE *IpSb; + + // + // First, validate the parameters + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail && Ip6ConfigData != NULL) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Status = EFI_INVALID_PARAMETER; + + // + // Validate the configuration first. + // + if (Ip6ConfigData != NULL) { + // + // Check whether the station address is valid. + // + if (!Ip6IsValidAddress (IpSb, &Ip6ConfigData->StationAddress, TRUE)) { + goto Exit; + } + // + // Check whether the default protocol is valid. + // + if (Ip6IsIllegalProtocol (Ip6ConfigData->DefaultProtocol)) { + goto Exit; + } + + // + // User can only update packet filters when already configured. + // If it wants to change the station address, it must configure(NULL) + // the instance firstly. + // + if (IpInstance->State == IP6_STATE_CONFIGED) { + Current = &IpInstance->ConfigData; + + if (!EFI_IP6_EQUAL (&Current->StationAddress, &Ip6ConfigData->StationAddress)) { + Status = EFI_ALREADY_STARTED; + goto Exit; + } + + if (NetIp6IsUnspecifiedAddr (&Current->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + } + } + + // + // Configure the instance or clean it up. + // + if (Ip6ConfigData != NULL) { + Status = Ip6ConfigProtocol (IpInstance, Ip6ConfigData); + } else { + Status = Ip6CleanProtocol (IpInstance); + + // + // Don't change the state if it is DESTROY, 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 == IP6_STATE_CONFIGED) { + IpInstance->State = IP6_STATE_UNCONFIGED; + } + } + + // + // Update the MNP's configure data. Ip6ServiceConfigMnp will check + // whether it is necessary to reconfigure the MNP. + // + Ip6ServiceConfigMnp (IpInstance->Service, FALSE); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + 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 reception of matching + multicast packets. Source-Specific Multicast isn't required to be supported. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session, and FALSE to leave. + @param[in] GroupAddress Pointer to the IPv6 multicast address. + This is an optional parameter that may be NULL. + + @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 IPv6 address. + - GroupAddress is not NULL and *GroupAddress is in the + range of SSM destination address. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_OUT_OF_RESOURCES System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv6 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 +EfiIp6Groups ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (JoinFlag && GroupAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (GroupAddress != NULL && !IP6_IS_MULTICAST (GroupAddress)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto ON_EXIT; + } + + Status = Ip6Groups (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 leftmost PrefixLength bits of Destination with + the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the + configured station address. + + The default route is added with Destination and PrefixLength both set to all zeros. The + default route matches all destination IPv6 addresses that do not match any other routes. + + All EFI IPv6 Protocol instances share a routing table. + + @param[in] This Pointer to the EFI_IP6_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. Destination, + PrefixLength and Gateway are used as the key to each + route entry. + @param[in] Destination The address prefix of the subnet that needs to be routed. + This is an optional parameter that may be NULL. + @param[in] PrefixLength The prefix length of Destination. Ignored if Destination + is NULL. + @param[in] GatewayAddress The unicast gateway IPv6 address for this route. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - When DeleteRoute is TRUE, both Destination and + GatewayAddress are NULL. + - When DeleteRoute is FALSE, either Destination or + GatewayAddress is NULL. + - *GatewayAddress is not a valid unicast IPv6 address. + - *GatewayAddress is one of the local configured IPv6 + addresses. + @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 +EfiIp6Routes ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP6_SERVICE *IpSb; + + if ((This == NULL) || (PrefixLength > IP6_PREFIX_MAX)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (IpInstance->State != IP6_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (DeleteRoute && (Destination == NULL) && (GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!DeleteRoute && (Destination == NULL || GatewayAddress == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (GatewayAddress != NULL) { + if (!Ip6IsValidAddress (IpSb, GatewayAddress, FALSE)) { + return EFI_INVALID_PARAMETER; + } + + if (!NetIp6IsUnspecifiedAddr (GatewayAddress) && + !NetIp6IsNetEqual (GatewayAddress, &IpInstance->ConfigData.StationAddress, PrefixLength) + ) { + return EFI_INVALID_PARAMETER; + } + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // Update the route table + // + if (DeleteRoute) { + Status = Ip6DelRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress); + } else { + Status = Ip6AddRoute (IpSb->RouteTable, Destination, PrefixLength, GatewayAddress); + } + + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Add or delete Neighbor cache entries. + + The Neighbors() function is used to add, update, or delete an entry from neighbor cache. + IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as + network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network + traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not + timeout) or dynamic (will timeout). + + The implementation should follow the neighbor cache timeout mechanism which is defined in + RFC4861. The default neighbor cache timeout value should be tuned for the expected network + environment + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteFlag Set to TRUE to delete the specified cache entry, set to FALSE to + add (or update, if it already exists and Override is TRUE) the + specified cache entry. TargetIp6Address is used as the key + to find the requested cache entry. + @param[in] TargetIp6Address Pointer to the Target IPv6 address. + @param[in] TargetLinkAddress Pointer to the link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache, it will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, EFI_ACCESS_DENIED + will be returned if a corresponding cache entry already existed. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - TargetIpAddress is NULL. + - *TargetLinkAddress is invalid when not NULL. + - *TargetIpAddress is not a valid unicast IPv6 address. + - *TargetIpAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is + TRUE or when DeleteFlag is FALSE while + TargetLinkAddress is NULL.). + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when Override + is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Neighbors ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + EFI_TPL OldTpl; + EFI_STATUS Status; + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + + if (This == NULL || TargetIp6Address == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (NetIp6IsUnspecifiedAddr (TargetIp6Address)) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (!Ip6IsValidAddress (IpSb, TargetIp6Address, FALSE)) { + return EFI_INVALID_PARAMETER; + } + + if (TargetLinkAddress != NULL) { + if (!Ip6IsValidLinkAddress (IpSb, TargetLinkAddress)) { + return EFI_INVALID_PARAMETER; + } + } + + if (Ip6IsOneOfSetAddress (IpSb, TargetIp6Address, NULL, NULL)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + if (DeleteFlag) { + Status = Ip6DelNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override); + } else { + Status = Ip6AddNeighbor (IpInstance->Service, TargetIp6Address, TargetLinkAddress, Timeout, Override); + } + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + +/** + Check whether the user's token or event has already + been enqueue on IP6'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 +Ip6TokenExist ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_COMPLETION_TOKEN *TokenInItem; + + Token = (EFI_IP6_COMPLETION_TOKEN *) Context; + TokenInItem = (EFI_IP6_COMPLETION_TOKEN *) Item->Key; + + if (Token == TokenInItem || Token->Event == TokenInItem->Event) { + return EFI_ACCESS_DENIED; + } + + return EFI_SUCCESS; +} + +/** + Validate the user's token against the current station address. + + @param[in] Token User's token to validate. + + @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 OK. + +**/ +EFI_STATUS +Ip6TxTokenValid ( + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + EFI_IP6_TRANSMIT_DATA *TxData; + UINT32 Index; + UINT32 DataLength; + + if (Token == NULL || Token->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + TxData = Token->Packet.TxData; + + if (TxData == NULL || (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (TxData->FragmentCount == 0 || TxData->DataLength == 0) { + return EFI_INVALID_PARAMETER; + } + + for (DataLength = 0, Index = 0; Index < TxData->FragmentCount; Index++) { + if (TxData->FragmentTable[Index].FragmentLength == 0 || TxData->FragmentTable[Index].FragmentBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + DataLength += TxData->FragmentTable[Index].FragmentLength; + } + + if (TxData->DataLength != DataLength) { + return EFI_INVALID_PARAMETER; + } + + // + // TODO: Token.Packet.TxData.DataLength is too short to transmit. + // return EFI_BUFFER_TOO_SMALL; + // + + // + // If Token.Packet.TxData.DataLength is beyond the maximum that which can be + // described through the Fragment Offset field in Fragment header when performing + // fragmentation. + // + if (TxData->DataLength > 64 * 1024) { + return EFI_BAD_BUFFER_SIZE; + } + + return EFI_SUCCESS; +} + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although this function seems simple, there + are some subtle aspects. + When user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data + is wrapped in an net buffer. The net buffer's Free function is + set to Ip6FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip6Output for + transmission. If an error happened before that, the buffer + is freed, which in turn frees 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 EfiIp6Transmit. If + the buffer has been sent by Ip6Output, 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 Ip6Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip6FreeTxToken ( + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + NET_MAP_ITEM *Item; + + Wrap = (IP6_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. EfiIp6Transmit 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 Ip6Output to update the transmit status. + + @param[in] Packet The user's transmit packet. + @param[in] IoStatus The result of the transmission. + @param[in] Flag Not used during transmission. + @param[in] Context The token's wrap. + +**/ +VOID +Ip6OnPacketSent ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + + // + // This is the transmission request from upper layer, + // not the IP6 driver itself. + // + Wrap = (IP6_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 IPv6 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_IP6_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 The IPv6 driver was responsible for choosing + a source address for this transmission, + but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + - Token.Packet.TxData is NULL. + - Token.Packet.ExtHdrsLength is not zero and + Token.Packet.ExtHdrs is NULL. + - Token.Packet.FragmentCount is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentLength fields is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentBuffer fields is NULL. + - Token.Packet.TxData.DataLength is zero or not + equal to the sum of fragment lengths. + - Token.Packet.TxData.DestinationAddress is non + zero when DestinationAddress is configured as + non-zero when doing Configure() for this + EFI IPv6 protocol instance. + - Token.Packet.TxData.DestinationAddress is + unspecified when DestinationAddress is unspecified + when doing Configure() for this EFI IPv6 protocol + instance. + @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 If Token.Packet.TxData.DataLength is beyond the + maximum that which can be described through the + Fragment Offset field in Fragment header when + performing fragmentation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Transmit ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + IP6_SERVICE *IpSb; + IP6_PROTOCOL *IpInstance; + EFI_IP6_CONFIG_DATA *Config; + EFI_STATUS Status; + EFI_TPL OldTpl; + EFI_IP6_HEADER Head; + EFI_IP6_TRANSMIT_DATA *TxData; + EFI_IP6_OVERRIDE_DATA *Override; + IP6_TXTOKEN_WRAP *Wrap; + UINT8 *ExtHdrs; + + // + // Check input parameters. + // + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + ExtHdrs = NULL; + + Status = Ip6TxTokenValid (Token); + if (EFI_ERROR (Status)) { + return Status; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Config = &IpInstance->ConfigData; + + // + // Check whether the token or signal already existed. + // + if (EFI_ERROR (NetMapIterate (&IpInstance->TxTokens, Ip6TokenExist, Token))) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // Build the IP header, fill in the information from ConfigData or OverrideData + // + ZeroMem (&Head, sizeof(EFI_IP6_HEADER)); + TxData = Token->Packet.TxData; + IP6_COPY_ADDRESS (&Head.SourceAddress, &Config->StationAddress); + IP6_COPY_ADDRESS (&Head.DestinationAddress, &Config->DestinationAddress); + + Status = EFI_INVALID_PARAMETER; + + if (NetIp6IsUnspecifiedAddr (&TxData->DestinationAddress)) { + if (NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + goto Exit; + } + + ASSERT (!NetIp6IsUnspecifiedAddr (&Config->StationAddress)); + + } else { + // + // StationAddress is unspecified only when ConfigData.Dest is unspecified. + // Use TxData.Dest to override the DestinationAddress. + // + if (!NetIp6IsUnspecifiedAddr (&Config->DestinationAddress)) { + goto Exit; + } + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress)) { + Status = Ip6SelectSourceAddress ( + IpSb, + &TxData->DestinationAddress, + &Head.SourceAddress + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, &TxData->DestinationAddress); + } + + // + // Fill in Head infos. + // + Head.NextHeader = Config->DefaultProtocol; + if (TxData->ExtHdrsLength != 0) { + Head.NextHeader = TxData->NextHeader; + } + + if (TxData->OverrideData != NULL) { + Override = TxData->OverrideData; + Head.NextHeader = Override->Protocol; + Head.HopLimit = Override->HopLimit; + Head.FlowLabelL = HTONS ((UINT16) Override->FlowLabel); + Head.FlowLabelH = (UINT8) ((Override->FlowLabel >> 16) & 0x0F); + } else { + Head.HopLimit = Config->HopLimit; + Head.FlowLabelL = HTONS ((UINT16) Config->FlowLabel); + Head.FlowLabelH = (UINT8) ((Config->FlowLabel >> 16) & 0x0F); + } + + Head.PayloadLength = HTONS ((UINT16) (TxData->ExtHdrsLength + TxData->DataLength)); + + // + // OK, it survives all the validation check. Wrap the token in + // a IP6_TXTOKEN_WRAP and the data in a netbuf + // + Status = EFI_OUT_OF_RESOURCES; + Wrap = AllocateZeroPool (sizeof (IP6_TXTOKEN_WRAP)); + if (Wrap == NULL) { + goto Exit; + } + + Wrap->IpInstance = IpInstance; + Wrap->Token = Token; + Wrap->Sent = FALSE; + Wrap->Life = IP6_US_TO_SEC (Config->TransmitTimeout); + Wrap->Packet = NetbufFromExt ( + (NET_FRAGMENT *) TxData->FragmentTable, + TxData->FragmentCount, + IP6_MAX_HEADLEN, + 0, + Ip6FreeTxToken, + Wrap + ); + + if (Wrap->Packet == NULL) { + FreePool (Wrap); + goto Exit; + } + + Token->Status = EFI_NOT_READY; + + Status = NetMapInsertTail (&IpInstance->TxTokens, Token, Wrap); + if (EFI_ERROR (Status)) { + // + // NetbufFree will call Ip6FreeTxToken, which in turn will + // free the IP6_TXTOKEN_WRAP. Now, the token wrap hasn't been + // enqueued. + // + NetbufFree (Wrap->Packet); + goto Exit; + } + + // + // Allocate a new buffer to store IPv6 extension headers to avoid updating + // the original data in EFI_IP6_COMPLETION_TOKEN. + // + if (TxData->ExtHdrsLength != 0 && TxData->ExtHdrs != NULL) { + ExtHdrs = (UINT8 *) AllocateCopyPool (TxData->ExtHdrsLength, TxData->ExtHdrs); + if (ExtHdrs == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto 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 = Ip6Output ( + IpSb, + NULL, + IpInstance, + Wrap->Packet, + &Head, + ExtHdrs, + TxData->ExtHdrsLength, + Ip6OnPacketSent, + Wrap + ); + if (EFI_ERROR (Status)) { + Wrap->Sent = FALSE; + NetbufFree (Wrap->Packet); + } + +Exit: + gBS->RestoreTPL (OldTpl); + + if (ExtHdrs != NULL) { + FreePool (ExtHdrs); + } + + 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 IPv6 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + Current Udp implementation creates an IP child for each Udp child. + It initiates a asynchronous receive immediately no matter whether + there is no mapping or not. Therefore, disable the returning EFI_NO_MAPPING for now. + To enable it, the following check must be performed: + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + @param[in] This Pointer to the EFI_IP6_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 IPv6 Protocol instance has not been started. + @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance, + while no source address is available for use. + @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 IPv6 Protocol instance has been reset to startup defaults. + @retval 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. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Receive ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + IP6_SERVICE *IpSb; + + if (This == NULL || Token == NULL || Token->Event == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + // + // Check whether the toke is already on the receive queue. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip6TokenExist, Token); + + if (EFI_ERROR (Status)) { + Status = EFI_ACCESS_DENIED; + goto Exit; + } + + // + // Queue the token then check whether there is pending received packet. + // + Status = NetMapInsertTail (&IpInstance->RxTokens, Token, NULL); + + if (EFI_ERROR (Status)) { + goto Exit; + } + + Status = Ip6InstanceDeliverPacket (IpInstance); + + // + // Dispatch the DPC queued by the NotifyFunction of this instane's receive + // event. + // + DispatchDpc (); + +Exit: + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Cancel the transmitted but not recycled packet. If a matching + token is found, it will call Ip6CancelPacket to cancel the + packet. Ip6CancelPacket cancels all the fragments of the + packet. When all the fragments are freed, the IP6_TXTOKEN_WRAP + is deleted from the Map, and user's event is signalled. + Because Ip6CancelPacket and other functions are all called in + line, after Ip6CancelPacket returns, the Item has been freed. + + @param[in] Map The IP6 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 +Ip6CancelTxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + IP6_TXTOKEN_WRAP *Wrap; + + Token = (EFI_IP6_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 = (IP6_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. + // + Ip6CancelPacket (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 simple, because + it is only enqueued in our local receive map. + + @param[in] Map The IP6 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 +Ip6CancelRxTokens ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_IP6_COMPLETION_TOKEN *This; + + Token = (EFI_IP6_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. It is the worker function of + EfiIp6Cancel API. + + @param[in] IpInstance The IP6 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 tokens are cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip6Cancel ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + EFI_STATUS Status; + + // + // First check the transmitted packet. Ip6CancelTxTokens 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, Ip6CancelTxTokens, Token); + if (EFI_ERROR (Status)) { + if ((Token != NULL) && (Status == EFI_ABORTED)) { + return EFI_SUCCESS; + } + + return Status; + } + + // + // Check the receive queue. Ip6CancelRxTokens also returns EFI_ABORT + // for Token!=NULL and it is cancelled. + // + Status = NetMapIterate (&IpInstance->RxTokens, Ip6CancelRxTokens, 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 not + // 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_IP6_PROTOCOL instance. + @param[in] Token Pointer to a token that has been issued by + EFI_IP6_PROTOCOL.Transmit() or + EFI_IP6_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is + defined in EFI_IP6_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_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(). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Cancel ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ) +{ + IP6_PROTOCOL *IpInstance; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + if (IpInstance->State != IP6_STATE_CONFIGED) { + Status = EFI_NOT_STARTED; + goto Exit; + } + + Status = Ip6Cancel (IpInstance, Token); + +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_IP6_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_IP6_PROTOCOL.Poll() function + more often. + + @param[in] This Pointer to the EFI_IP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Poll ( + IN EFI_IP6_PROTOCOL *This + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_SERVICE *IpSb; + EFI_MANAGED_NETWORK_PROTOCOL *Mnp; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + IpInstance = IP6_INSTANCE_FROM_PROTOCOL (This); + IpSb = IpInstance->Service; + + if (IpSb->LinkLocalDadFail) { + return EFI_DEVICE_ERROR; + } + + if (IpInstance->State == IP6_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); + +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Impl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Impl.h new file mode 100644 index 00000000..3fa1defa --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Impl.h @@ -0,0 +1,748 @@ +/** @file + Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions. + + Copyright (c) 2009 - 2016, 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_IP6_IMPL_H__ +#define __EFI_IP6_IMPL_H__ + +#include <Uefi.h> + +#include <IndustryStandard/Dhcp.h> + +#include <Protocol/ServiceBinding.h> +#include <Protocol/ManagedNetwork.h> +#include <Protocol/IpSec.h> +#include <Protocol/Ip6.h> +#include <Protocol/Ip6Config.h> +#include <Protocol/Dhcp6.h> +#include <Protocol/DevicePath.h> +#include <Protocol/HiiConfigRouting.h> +#include <Protocol/HiiConfigAccess.h> + +#include <Library/DebugLib.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiRuntimeServicesTableLib.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/HiiLib.h> +#include <Library/UefiHiiServicesLib.h> +#include <Library/DevicePathLib.h> +#include <Library/PrintLib.h> + +#include <Guid/MdeModuleHii.h> + +#include "Ip6Common.h" +#include "Ip6Driver.h" +#include "Ip6Icmp.h" +#include "Ip6If.h" +#include "Ip6Input.h" +#include "Ip6Mld.h" +#include "Ip6Nd.h" +#include "Ip6Option.h" +#include "Ip6Output.h" +#include "Ip6Route.h" +#include "Ip6ConfigNv.h" +#include "Ip6ConfigImpl.h" + +#define IP6_PROTOCOL_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'P') +#define IP6_SERVICE_SIGNATURE SIGNATURE_32 ('I', 'P', '6', 'S') + +// +// The state of IP6 protocol. It starts from UNCONFIGED. if it is +// successfully configured, it goes to CONFIGED. if configure NULL +// is called, it becomes UNCONFIGED again. If (partly) destroyed, it +// becomes DESTROY. +// +#define IP6_STATE_UNCONFIGED 0 +#define IP6_STATE_CONFIGED 1 + +// +// The state of IP6 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 IP6_SERVICE_UNSTARTED 0 +#define IP6_SERVICE_STARTED 1 +#define IP6_SERVICE_CONFIGED 2 +#define IP6_SERVICE_DESTROY 3 + +#define IP6_INSTANCE_FROM_PROTOCOL(Ip6) \ + CR ((Ip6), IP6_PROTOCOL, Ip6Proto, IP6_PROTOCOL_SIGNATURE) + +#define IP6_SERVICE_FROM_PROTOCOL(Sb) \ + CR ((Sb), IP6_SERVICE, ServiceBinding, IP6_SERVICE_SIGNATURE) + +#define IP6_NO_MAPPING(IpInstance) (!(IpInstance)->Interface->Configured) + +extern EFI_IPSEC2_PROTOCOL *mIpSec; +extern BOOLEAN mIpSec2Installed; + +// +// IP6_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 IP6_TXTOKEN_WRAP will be released, and +// user's event signalled. +// +typedef struct { + IP6_PROTOCOL *IpInstance; + EFI_IP6_COMPLETION_TOKEN *Token; + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; + BOOLEAN Sent; + INTN Life; +} IP6_TXTOKEN_WRAP; + +typedef struct { + EFI_EVENT IpSecRecycleSignal; + NET_BUF *Packet; +} IP6_IPSEC_WRAP; + +// +// IP6_RXDATA_WRAP wraps the data IP6 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 +// IP6_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; + IP6_PROTOCOL *IpInstance; + NET_BUF *Packet; + EFI_IP6_RECEIVE_DATA RxData; +} IP6_RXDATA_WRAP; + +struct _IP6_PROTOCOL { + UINT32 Signature; + + EFI_IP6_PROTOCOL Ip6Proto; + EFI_HANDLE Handle; + INTN State; + + IP6_SERVICE *Service; + LIST_ENTRY Link; // Link to all the IP protocol from the service + + UINT8 PrefixLength; // PrefixLength of the configured station address. + // + // User's transmit/receive tokens, and received/delivered packets + // + NET_MAP RxTokens; + NET_MAP TxTokens; // map between (User's Token, IP6_TXTOKE_WRAP) + LIST_ENTRY Received; // Received but not delivered packet + LIST_ENTRY Delivered; // Delivered and to be recycled packets + EFI_LOCK RecycleLock; + + IP6_INTERFACE *Interface; + LIST_ENTRY AddrLink; // Ip instances with the same IP address. + + EFI_IPv6_ADDRESS *GroupList; // stored in network order. + UINT32 GroupCount; + + EFI_IP6_CONFIG_DATA ConfigData; + BOOLEAN InDestroy; +}; + +struct _IP6_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; + + IP6_INTERFACE *DefaultInterface; + IP6_ROUTE_TABLE *RouteTable; + + IP6_LINK_RX_TOKEN RecvRequest; + + // + // Ip reassemble utilities and MLD data + // + IP6_ASSEMBLE_TABLE Assemble; + IP6_MLD_SERVICE_DATA MldCtrl; + + EFI_IPv6_ADDRESS LinkLocalAddr; + BOOLEAN LinkLocalOk; + BOOLEAN LinkLocalDadFail; + BOOLEAN Dhcp6NeedStart; + BOOLEAN Dhcp6NeedInfoRequest; + + // + // ND data + // + UINT8 CurHopLimit; + UINT32 LinkMTU; + UINT32 BaseReachableTime; + UINT32 ReachableTime; + UINT32 RetransTimer; + LIST_ENTRY NeighborTable; + + LIST_ENTRY OnlinkPrefix; + LIST_ENTRY AutonomousPrefix; + + LIST_ENTRY DefaultRouterList; + UINT32 RoundRobin; + + UINT8 InterfaceIdLen; + UINT8 *InterfaceId; + + BOOLEAN RouterAdvertiseReceived; + UINT8 SolicitTimer; + UINT32 Ticks; + + // + // 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 FasterTimer; + + // + // IPv6 Configuration Protocol instance + // + IP6_CONFIG_INSTANCE Ip6ConfigInstance; + + // + // The string representation of the current mac address of the + // NIC this IP6_SERVICE works on. + // + CHAR16 *MacString; + UINT32 MaxPacketSize; + UINT32 OldMaxPacketSize; +}; + +/** + The callback function for the net buffer which wraps the user's + transmit token. Although this function seems simple, + there are some subtle aspects. + When a user requests the IP to transmit a packet by passing it a + token, the token is wrapped in an IP6_TXTOKEN_WRAP and the data + is wrapped in a net buffer. The net buffer's Free function is + set to Ip6FreeTxToken. The Token and token wrap are added to the + IP child's TxToken map. Then the buffer is passed to Ip6Output for + transmission. If an error occurs before that, the buffer + is freed, which in turn frees the token wrap. The wrap may + have been added to the TxToken map or not, and the user's event + shouldn't be signaled because we are still in the EfiIp6Transmit. If + the buffer has been sent by Ip6Output, it should be removed from + the TxToken map and the user's event signaled. The token wrap and buffer + are bound together. Refer to the comments in Ip6Output for information + about IP fragmentation. + + @param[in] Context The token's wrap. + +**/ +VOID +EFIAPI +Ip6FreeTxToken ( + IN VOID *Context + ); + +/** + 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 promiscuous 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 + whether that is changed or not. + + @param[in] IpSb The IP6 service instance that is to be changed. + @param[in] Force Force the configuration or not. + + @retval EFI_SUCCESS The MNP successfully configured/reconfigured. + @retval Others The configuration failed. + +**/ +EFI_STATUS +Ip6ServiceConfigMnp ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN Force + ); + +/** + Cancel the user's receive/transmit request. It is the worker function of + EfiIp6Cancel API. + + @param[in] IpInstance The IP6 child. + @param[in] Token The token to cancel. If NULL, all tokens will be + cancelled. + + @retval EFI_SUCCESS The token was cancelled. + @retval EFI_NOT_FOUND The token isn't found on either the + transmit or receive queue. + @retval EFI_DEVICE_ERROR Not all tokens are cancelled when Token is NULL. + +**/ +EFI_STATUS +Ip6Cancel ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_COMPLETION_TOKEN *Token OPTIONAL + ); + +/** + Initialize the IP6_PROTOCOL structure to the unconfigured states. + + @param[in] IpSb The IP6 service instance. + @param[in, out] IpInstance The IP6 child instance. + +**/ +VOID +Ip6InitProtocol ( + IN IP6_SERVICE *IpSb, + IN OUT IP6_PROTOCOL *IpInstance + ); + +/** + Clean up the IP6 child, release all the resources used by it. + + @param[in, out] IpInstance The IP6 child to clean up. + + @retval EFI_SUCCESS The IP6 child was cleaned up + @retval EFI_DEVICE_ERROR Some resources failed to be released. + +**/ +EFI_STATUS +Ip6CleanProtocol ( + IN OUT IP6_PROTOCOL *IpInstance + ); + +// +// EFI_IP6_PROTOCOL interface prototypes +// + +/** + Gets the current operational settings for this instance of the EFI IPv6 Protocol driver. + + The GetModeData() function returns the current operational mode data for this driver instance. + The data fields in EFI_IP6_MODE_DATA are read only. This function is used optionally to + retrieve the operational mode data of underlying networks or drivers. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[out] Ip6ModeData The pointer to the EFI IPv6 Protocol mode data structure. + @param[out] MnpConfigData The pointer to the managed network configuration data structure. + @param[out] SnpModeData The 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 +EfiIp6GetModeData ( + IN EFI_IP6_PROTOCOL *This, + OUT EFI_IP6_MODE_DATA *Ip6ModeData OPTIONAL, + OUT EFI_MANAGED_NETWORK_CONFIG_DATA *MnpConfigData OPTIONAL, + OUT EFI_SIMPLE_NETWORK_MODE *SnpModeData OPTIONAL + ); + +/** + Assigns an IPv6 address and subnet mask to this EFI IPv6 Protocol driver instance. + + The Configure() function is used to set, change, or reset the operational parameters and filter + settings for this EFI IPv6 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 Ip6ConfigData set to NULL), no more traffic can be sent or received until these + parameters have been set again. Each EFI IPv6 Protocol instance can be started and stopped + independently of each other by enabling or disabling their receive filter settings with the + Configure() function. + + If Ip6ConfigData.StationAddress is a valid non-zero IPv6 unicast address, it is required + to be one of the currently configured IPv6 addresses list in the EFI IPv6 drivers, or else + EFI_INVALID_PARAMETER will be returned. If Ip6ConfigData.StationAddress is + unspecified, the IPv6 driver will bind a source address according to the source address selection + algorithm. Clients could frequently call GetModeData() to check get a currently configured IPv6. + If both Ip6ConfigData.StationAddress and Ip6ConfigData.Destination are unspecified, when + transmitting the packet afterwards, the source address filled in each outgoing IPv6 packet + is decided based on the destination of this packet. + + 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 The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Ip6ConfigData The pointer to the EFI IPv6 Protocol configuration data structure. + If NULL, reset the configuration data. + + @retval EFI_SUCCESS The driver instance was successfully opened. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Ip6ConfigData.StationAddress is neither zero nor + a unicast IPv6 address. + - Ip6ConfigData.StationAddress is neither zero nor + one of the configured IP addresses in the EFI IPv6 driver. + - Ip6ConfigData.DefaultProtocol is illegal. + @retval EFI_OUT_OF_RESOURCES The EFI IPv6 Protocol driver instance data could not be allocated. + @retval EFI_NO_MAPPING The IPv6 driver was responsible for choosing a source address for + this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED The interface is already open and must be stopped before the IPv6 + address or prefix length can be changed. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI IPv6 + Protocol driver instance was not opened. + @retval EFI_UNSUPPORTED Default protocol specified through + Ip6ConfigData.DefaultProtocol isn't supported. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Configure ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_CONFIG_DATA *Ip6ConfigData 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 reception of matching + multicast packets. Source-Specific Multicast isn't required to be supported. + + If JoinFlag is FALSE and GroupAddress is NULL, all joined groups will be left. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] JoinFlag Set to TRUE to join the multicast group session and FALSE to leave. + @param[in] GroupAddress The pointer to the IPv6 multicast address. + This is an optional parameter that may be NULL. + + @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 IPv6 address. + - GroupAddress is not NULL and *GroupAddress is in the + range of SSM destination address. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_OUT_OF_RESOURCES System resources could not be allocated. + @retval EFI_UNSUPPORTED This EFI IPv6 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 +EfiIp6Groups ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_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 leftmost PrefixLength bits of Destination with + the destination IPv6 address arithmetically. The gateway address must be on the same subnet as the + configured station address. + + The default route is added with Destination and PrefixLength both set to all zeros. The + default route matches all destination IPv6 addresses that do not match any other routes. + + All EFI IPv6 Protocol instances share a routing table. + + @param[in] This The pointer to the EFI_IP6_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. Destination, + PrefixLength and Gateway are used as the key to each + route entry. + @param[in] Destination The address prefix of the subnet that needs to be routed. + This is an optional parameter that may be NULL. + @param[in] PrefixLength The prefix length of Destination. Ignored if Destination + is NULL. + @param[in] GatewayAddress The unicast gateway IPv6 address for this route. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_NOT_STARTED The driver instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - When DeleteRoute is TRUE, both Destination and + GatewayAddress are NULL. + - When DeleteRoute is FALSE, either Destination or + GatewayAddress is NULL. + - *GatewayAddress is not a valid unicast IPv6 address. + - *GatewayAddress is one of the local configured IPv6 + addresses. + @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 +EfiIp6Routes ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteRoute, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ); + +/** + Add or delete Neighbor cache entries. + + The Neighbors() function is used to add, update, or delete an entry from a neighbor cache. + IPv6 neighbor cache entries are typically inserted and updated by the network protocol driver as + network traffic is processed. Most neighbor cache entries will timeout and be deleted if the network + traffic stops. Neighbor cache entries that were inserted by Neighbors() may be static (will not + timeout) or dynamic (will timeout). + + The implementation should follow the neighbor cache timeout mechanism defined in + RFC4861. The default neighbor cache timeout value should be tuned for the expected network + environment. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] DeleteFlag Set to TRUE to delete the specified cache entry. Set to FALSE to + add (or update, if it already exists and Override is TRUE) the + specified cache entry. TargetIp6Address is used as the key + to find the requested cache entry. + @param[in] TargetIp6Address The pointer to the Target IPv6 address. + @param[in] TargetLinkAddress The pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache, it will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, EFI_ACCESS_DENIED + will be returned if a corresponding cache entry already exists. + + @retval EFI_SUCCESS The data has been queued for transmission. + @retval EFI_NOT_STARTED This instance has not been started. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - TargetIpAddress is NULL. + - *TargetLinkAddress is invalid when not NULL. + - *TargetIpAddress is not a valid unicast IPv6 address. + - *TargetIpAddress is one of the local configured IPv6 + addresses. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache (when DeleteFlag is + TRUE or when DeleteFlag is FALSE while + TargetLinkAddress is NULL.). + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when Override + is FALSE). + +**/ +EFI_STATUS +EFIAPI +EfiIp6Neighbors ( + IN EFI_IP6_PROTOCOL *This, + IN BOOLEAN DeleteFlag, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Places outgoing data packets into the transmit queue. + + The Transmit() function places a sending request in the transmit queue of this + EFI IPv6 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 The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The 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 The IPv6 driver was responsible for choosing + a source address for this transmission, + but no source address was available for use. + @retval EFI_INVALID_PARAMETER One or more of the following is TRUE: + - This is NULL. + - Token is NULL. + - Token.Event is NULL. + - Token.Packet.TxData is NULL. + - Token.Packet.ExtHdrsLength is not zero and + Token.Packet.ExtHdrs is NULL. + - Token.Packet.FragmentCount is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentLength fields is zero. + - One or more of the Token.Packet.TxData. + FragmentTable[].FragmentBuffer fields is NULL. + - Token.Packet.TxData.DataLength is zero or not + equal to the sum of fragment lengths. + - Token.Packet.TxData.DestinationAddress is non- + zero when DestinationAddress is configured as + non-zero when doing Configure() for this + EFI IPv6 protocol instance. + - Token.Packet.TxData.DestinationAddress is + unspecified when DestinationAddress is unspecified + when doing Configure() for this EFI IPv6 protocol + instance. + @retval EFI_ACCESS_DENIED The transmit completion token with the same Token. + The 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 the 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 If Token.Packet.TxData.DataLength is beyond the + maximum that which can be described through the + Fragment Offset field in Fragment header when + performing fragmentation. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Transmit ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_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 IPv6 Protocol + driver updates the Token.Status and Token.Packet.RxData fields and the Token.Event + is signaled. + + Current Udp implementation creates an IP child for each Udp child. + It initiates a asynchronous receive immediately whether or not + there is no mapping. Therefore, disable the returning EFI_NO_MAPPING for now. + To enable it, the following check must be performed: + + if (NetIp6IsUnspecifiedAddr (&Config->StationAddress) && IP6_NO_MAPPING (IpInstance)) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The 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 IPv6 Protocol instance has not been started. + @retval EFI_NO_MAPPING When IP6 driver responsible for binding source address to this instance, + while no source address is available for use. + @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 IPv6 Protocol instance has been reset to startup defaults. + @retval 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. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Receive ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_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 The pointer to the EFI_IP6_PROTOCOL instance. + @param[in] Token The pointer to a token that has been issued by + EFI_IP6_PROTOCOL.Transmit() or + EFI_IP6_PROTOCOL.Receive(). If NULL, all pending + tokens are aborted. Type EFI_IP6_COMPLETION_TOKEN is + defined in EFI_IP6_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_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(). + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Cancel ( + IN EFI_IP6_PROTOCOL *This, + IN EFI_IP6_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_IP6_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_IP6_PROTOCOL.Poll() function + more often. + + @param[in] This The pointer to the EFI_IP6_PROTOCOL instance. + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI IPv6 Protocol instance has not been started. + @retval EFI_INVALID_PARAMETER This is NULL. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. + @retval EFI_NOT_READY No incoming or outgoing data was processed. + @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive queue. + Consider increasing the polling rate. + +**/ +EFI_STATUS +EFIAPI +EfiIp6Poll ( + IN EFI_IP6_PROTOCOL *This + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c new file mode 100644 index 00000000..91ff89a5 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.c @@ -0,0 +1,1815 @@ +/** @file + IP6 internal functions to process the incoming packets. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +/** + Create an empty assemble entry for the packet identified by + (Dst, Src, Id). The default life for the packet is 60 seconds. + + @param[in] Dst The destination address. + @param[in] Src The source address. + @param[in] Id The ID field in the IP header. + + @return NULL if failed to allocate memory for the entry. Otherwise, + the pointer to the just created reassemble entry. + +**/ +IP6_ASSEMBLE_ENTRY * +Ip6CreateAssembleEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN UINT32 Id + ) +{ + IP6_ASSEMBLE_ENTRY *Assemble; + + Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY)); + if (Assemble == NULL) { + return NULL; + } + + IP6_COPY_ADDRESS (&Assemble->Dst, Dst); + IP6_COPY_ADDRESS (&Assemble->Src, Src); + InitializeListHead (&Assemble->Fragments); + + Assemble->Id = Id; + Assemble->Life = IP6_FRAGMENT_LIFE + 1; + + Assemble->TotalLen = 0; + Assemble->CurLen = 0; + Assemble->Head = NULL; + Assemble->Info = NULL; + Assemble->Packet = NULL; + + return Assemble; +} + +/** + Release all the fragments of a packet, then free the assemble entry. + + @param[in] Assemble The assemble entry to free. + +**/ +VOID +Ip6FreeAssembleEntry ( + IN IP6_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); + } + + if (Assemble->Packet != NULL) { + NetbufFree (Assemble->Packet); + } + + FreePool (Assemble); +} + +/** + 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 frees all the fragments of the packet. + + @param[in] Arg The assemble entry to free. + +**/ +VOID +EFIAPI +Ip6OnFreeFragments ( + IN VOID *Arg + ) +{ + Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg); +} + +/** + Trim the packet to fit in [Start, End), and update per the + packet information. + + @param[in, out] Packet Packet to trim. + @param[in] Start The sequence of the first byte to fit in. + @param[in] End One beyond the sequence of last byte to fit in. + +**/ +VOID +Ip6TrimPacket ( + IN OUT NET_BUF *Packet, + IN INTN Start, + IN INTN End + ) +{ + IP6_CLIP_INFO *Info; + INTN Len; + + Info = IP6_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 = (UINT32) Start; + Info->Length -= (UINT32) Len; + } + + if (End < Info->End) { + Len = End - Info->End; + + NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL); + Info->End = (UINT32) End; + Info->Length -= (UINT32) Len; + } +} + +/** + 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 + returned. + + @param[in, out] Table The assemble table used. A new assemble entry will be created + if the Packet is from a new chain of fragments. + @param[in] 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 reassembled. The pointer to the just assembled + packet if all the fragments of the packet have arrived. + +**/ +NET_BUF * +Ip6Reassemble ( + IN OUT IP6_ASSEMBLE_TABLE *Table, + IN NET_BUF *Packet + ) +{ + EFI_IP6_HEADER *Head; + IP6_CLIP_INFO *This; + IP6_CLIP_INFO *Node; + IP6_ASSEMBLE_ENTRY *Assemble; + IP6_ASSEMBLE_ENTRY *Entry; + LIST_ENTRY *ListHead; + LIST_ENTRY *Prev; + LIST_ENTRY *Cur; + NET_BUF *Fragment; + NET_BUF *TmpPacket; + NET_BUF *NewPacket; + NET_BUF *Duplicate; + UINT8 *DupHead; + INTN Index; + UINT16 UnFragmentLen; + UINT8 *NextHeader; + + Head = Packet->Ip.Ip6; + This = IP6_GET_CLIP_INFO (Packet); + + ASSERT (Head != NULL); + + // + // Find the corresponding assemble entry by (Dst, Src, Id) + // + Assemble = NULL; + Index = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id); + + NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) { + Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link); + + if (Entry->Id == This->Id && + EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) && + EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress) + ) { + Assemble = Entry; + break; + } + } + + // + // Create a new entry if can not find an existing one, insert it to assemble table + // + if (Assemble == NULL) { + Assemble = Ip6CreateAssembleEntry ( + &Head->DestinationAddress, + &Head->SourceAddress, + This->Id + ); + + if (Assemble == NULL) { + goto Error; + } + + InsertHeadList (&Table->Bucket[Index], &Assemble->Link); + } + + // + // 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. + // + ListHead = &Assemble->Fragments; + + NET_LIST_FOR_EACH (Cur, ListHead) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + + if (This->Start < IP6_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) != ListHead) { + Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List); + Node = IP6_GET_CLIP_INFO (Fragment); + + if (This->Start < Node->End) { + if (This->End <= Node->End) { + goto Error; + } + + // + // Trim the previous fragment from tail. + // + Ip6TrimPacket (Fragment, Node->Start, This->Start); + } + } + + // + // 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 != ListHead) { + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + Node = IP6_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 Error; + } + + Ip6TrimPacket (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. + // + if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) { + goto Error; + } + + // + // Backup the first fragment in case the reassembly of that packet fail. + // + Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER)); + if (Duplicate == NULL) { + goto Error; + } + + // + // Revert IP head to network order. + // + DupHead = NetbufGetByte (Duplicate, 0, NULL); + ASSERT (DupHead != NULL); + Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead); + Assemble->Packet = Duplicate; + + // + // Adjust the unfragmentable part in first fragment + // + UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER)); + if (UnFragmentLen == 0) { + // + // There is not any unfragmentable extension header. + // + ASSERT (Head->NextHeader == IP6_FRAGMENT); + Head->NextHeader = This->NextHeader; + } else { + NextHeader = NetbufGetByte ( + Packet, + This->FormerNextHeader + sizeof (EFI_IP6_HEADER), + 0 + ); + if (NextHeader == NULL) { + goto Error; + } + + *NextHeader = This->NextHeader; + } + + Assemble->Head = Head; + Assemble->Info = IP6_GET_CLIP_INFO (Packet); + } + + // + // Don't update the length more than once. + // + if ((This->LastFrag != 0) && (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 known + // 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 (ListHead->BackLink, NET_BUF, List); + if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) { + Ip6FreeAssembleEntry (Assemble); + goto Error; + } + + Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List); + This = Assemble->Info; + + // + // This TmpPacket is used to hold the unfragmentable part, i.e., + // the IPv6 header and the unfragmentable extension headers. Be noted that + // the Fragment Header is excluded. + // + TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0); + ASSERT (TmpPacket != NULL); + + NET_LIST_FOR_EACH (Cur, ListHead) { + // + // Trim off the unfragment part plus the fragment header from all fragments. + // + Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List); + NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE); + } + + InsertHeadList (ListHead, &TmpPacket->List); + + // + // Wrap the packet in a net buffer then deliver it up + // + NewPacket = NetbufFromBufList ( + &Assemble->Fragments, + 0, + 0, + Ip6OnFreeFragments, + Assemble + ); + + if (NewPacket == NULL) { + Ip6FreeAssembleEntry (Assemble); + goto Error; + } + + NewPacket->Ip.Ip6 = Assemble->Head; + + CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO)); + + return NewPacket; + } + + return NULL; + +Error: + NetbufFree (Packet); + return NULL; +} + + +/** + The callback function for the net buffer that 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 +Ip6IpSecFree ( + IN VOID *Arg + ) +{ + IP6_IPSEC_WRAP *Wrap; + + Wrap = (IP6_IPSEC_WRAP *) Arg; + + if (Wrap->IpSecRecycleSignal != NULL) { + gBS->SignalEvent (Wrap->IpSecRecycleSignal); + } + + NetbufFree (Wrap->Packet); + + FreePool (Wrap); + + return; +} + +/** + The work function to locate the IPsec protocol to process the inbound or + outbound IP packets. The process routine handles the packet with the following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP6 service instance. + @param[in, out] Head The caller-supplied IP6 header. + @param[in, out] LastHead The next header field of last IP header. + @param[in, out] Netbuf The IP6 packet to be processed by IPsec. + @param[in, out] ExtHdrs The caller-supplied options. + @param[in, out] ExtHdrsLen 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 are not sufficient resources to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the + number of input data blocks when building a fragment table. + +**/ +EFI_STATUS +Ip6IpSecProcessPacket ( + IN IP6_SERVICE *IpSb, + IN OUT EFI_IP6_HEADER **Head, + IN OUT UINT8 *LastHead, + IN OUT NET_BUF **Netbuf, + IN OUT UINT8 **ExtHdrs, + IN OUT UINT32 *ExtHdrsLen, + 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; + IP6_TXTOKEN_WRAP *TxWrap; + IP6_IPSEC_WRAP *IpSecWrap; + EFI_STATUS Status; + EFI_IP6_HEADER *PacketHead; + UINT8 *Buf; + EFI_IP6_HEADER ZeroHead; + + Status = EFI_SUCCESS; + + if (!mIpSec2Installed) { + goto ON_EXIT; + } + ASSERT (mIpSec != NULL); + + Packet = *Netbuf; + RecycleEvent = NULL; + IpSecWrap = NULL; + FragmentTable = NULL; + PacketHead = NULL; + Buf = NULL; + TxWrap = (IP6_TXTOKEN_WRAP *) Context; + FragmentCount = Packet->BlockOpNum; + ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER)); + + // + // 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 - IP6_MAX_IPSEC_HEADLEN; + } + + + // + // Bypass all multicast inbound or outbound traffic. + // + if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress) || IP6_IS_MULTICAST (&(*Head)->SourceAddress)) { + goto ON_EXIT; + } + + // + // 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); + OriginalFragmentTable = FragmentTable; + OriginalFragmentCount = FragmentCount; + + if (EFI_ERROR(Status)) { + FreePool (FragmentTable); + goto ON_EXIT; + } + + // + // Convert host byte order to network byte order + // + Ip6NtohHead (*Head); + + Status = mIpSec->ProcessExt ( + mIpSec, + IpSb->Controller, + IP_VERSION_6, + (VOID *) (*Head), + LastHead, + (VOID **) ExtHdrs, + ExtHdrsLen, + (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable), + &FragmentCount, + Direction, + &RecycleEvent + ); + // + // Convert back to host byte order + // + Ip6NtohHead (*Head); + + if (EFI_ERROR (Status)) { + FreePool (OriginalFragmentTable); + goto ON_EXIT; + } + + if (OriginalFragmentCount == FragmentCount && OriginalFragmentTable == FragmentTable) { + // + // 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, + IP6_MAX_HEADLEN, + 0, + Ip6FreeTxToken, + TxWrap + ); + if (TxWrap->Packet == NULL) { + TxWrap->Packet = *Netbuf; + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem ( + IP6_GET_CLIP_INFO (TxWrap->Packet), + IP6_GET_CLIP_INFO (Packet), + sizeof (IP6_CLIP_INFO) + ); + + NetIpSecNetbufFree(Packet); + *Netbuf = TxWrap->Packet; + + } else { + + IpSecWrap = AllocateZeroPool (sizeof (IP6_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, + IP6_MAX_HEADLEN, + 0, + Ip6IpSecFree, + 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 (&ZeroHead, *Head, sizeof (EFI_IP6_HEADER))) { + + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace ( + Packet, + sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, + NET_BUF_HEAD + ); + if (PacketHead == NULL) { + *Netbuf = Packet; + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (PacketHead, *Head, sizeof (EFI_IP6_HEADER)); + *Head = PacketHead; + Packet->Ip.Ip6 = PacketHead; + + if (*ExtHdrs != NULL) { + Buf = (UINT8 *) (PacketHead + 1); + CopyMem (Buf, *ExtHdrs, *ExtHdrsLen); + } + + NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE); + CopyMem ( + IP6_GET_CLIP_INFO (Packet), + IP6_GET_CLIP_INFO (IpSecWrap->Packet), + sizeof (IP6_CLIP_INFO) + ); + } + *Netbuf = Packet; + } + +ON_EXIT: + return Status; +} + +/** + Pre-process the IPv6 packet. First validates the IPv6 packet, and + then reassembles packet if it is necessary. + + @param[in] IpSb The IP6 service instance. + @param[in, out] Packet The received IP6 packet to be processed. + @param[in] Flag The link layer flag for the packet received, such + as multicast. + @param[out] Payload The pointer to the payload of the received packet. + it starts from the first byte of the extension header. + @param[out] LastHead The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] ExtHdrsLen The length of the whole option. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + @param[out] Fragmented Indicate whether the packet is fragmented. + @param[out] Head The pointer to the EFI_IP6_Header. + + @retval EFI_SUCCESS The received packet is well format. + @retval EFI_INVALID_PARAMETER The received packet is malformed. + +**/ +EFI_STATUS +Ip6PreProcessPacket ( + IN IP6_SERVICE *IpSb, + IN OUT NET_BUF **Packet, + IN UINT32 Flag, + OUT UINT8 **Payload, + OUT UINT8 **LastHead, + OUT UINT32 *ExtHdrsLen, + OUT UINT32 *UnFragmentLen, + OUT BOOLEAN *Fragmented, + OUT EFI_IP6_HEADER **Head + ) +{ + UINT16 PayloadLen; + UINT16 TotalLen; + UINT32 FormerHeadOffset; + UINT32 HeadLen; + IP6_FRAGMENT_HEADER *FragmentHead; + UINT16 FragmentOffset; + IP6_CLIP_INFO *Info; + EFI_IPv6_ADDRESS Loopback; + + HeadLen = 0; + PayloadLen = 0; + // + // Check whether the input packet is a valid packet + // + if ((*Packet)->TotalSize < IP6_MIN_HEADLEN) { + return EFI_INVALID_PARAMETER; + } + + // + // Get header information of the packet. + // + *Head = (EFI_IP6_HEADER *) NetbufGetByte (*Packet, 0, NULL); + if (*Head == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Multicast addresses must not be used as source addresses in IPv6 packets. + // + if (((*Head)->Version != 6) || (IP6_IS_MULTICAST (&(*Head)->SourceAddress))) { + return EFI_INVALID_PARAMETER; + } + + // + // A packet with a destination address of loopback ::1/128 or unspecified must be dropped. + // + ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS)); + Loopback.Addr[15] = 0x1; + if ((CompareMem (&Loopback, &(*Head)->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) || + (NetIp6IsUnspecifiedAddr (&(*Head)->DestinationAddress))) { + return EFI_INVALID_PARAMETER; + } + + // + // Convert the IP header to host byte order. + // + (*Packet)->Ip.Ip6 = Ip6NtohHead (*Head); + + // + // Get the per packet info. + // + Info = IP6_GET_CLIP_INFO (*Packet); + Info->LinkFlag = Flag; + Info->CastType = 0; + + if (IpSb->MnpConfigData.EnablePromiscuousReceive) { + Info->CastType = Ip6Promiscuous; + } + + if (Ip6IsOneOfSetAddress (IpSb, &(*Head)->DestinationAddress, NULL, NULL)) { + Info->CastType = Ip6Unicast; + } else if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress)) { + if (Ip6FindMldEntry (IpSb, &(*Head)->DestinationAddress) != NULL) { + Info->CastType = Ip6Multicast; + } + } + + // + // Drop the packet that is not delivered to us. + // + if (Info->CastType == 0) { + return EFI_INVALID_PARAMETER; + } + + + PayloadLen = (*Head)->PayloadLength; + + Info->Start = 0; + Info->Length = PayloadLen; + Info->End = Info->Start + Info->Length; + Info->HeadLen = (UINT16) sizeof (EFI_IP6_HEADER); + Info->Status = EFI_SUCCESS; + Info->LastFrag = FALSE; + + TotalLen = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER)); + + // + // Mnp may deliver frame trailer sequence up, trim it off. + // + if (TotalLen < (*Packet)->TotalSize) { + NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE); + } + + if (TotalLen != (*Packet)->TotalSize) { + return EFI_INVALID_PARAMETER; + } + + // + // Check the extension headers, if exist validate them + // + if (PayloadLen != 0) { + *Payload = AllocatePool ((UINTN) PayloadLen); + if (*Payload == NULL) { + return EFI_INVALID_PARAMETER; + } + + NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload); + } + + if (!Ip6IsExtsValid ( + IpSb, + *Packet, + &(*Head)->NextHeader, + *Payload, + (UINT32) PayloadLen, + TRUE, + &FormerHeadOffset, + LastHead, + ExtHdrsLen, + UnFragmentLen, + Fragmented + )) { + return EFI_INVALID_PARAMETER; + } + + HeadLen = sizeof (EFI_IP6_HEADER) + *UnFragmentLen; + + if (*Fragmented) { + // + // Get the fragment offset from the Fragment header + // + FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (*Packet, HeadLen, NULL); + if (FragmentHead == NULL) { + return EFI_INVALID_PARAMETER; + } + + FragmentOffset = NTOHS (FragmentHead->FragmentOffset); + + if ((FragmentOffset & 0x1) == 0) { + Info->LastFrag = TRUE; + } + + FragmentOffset &= (~0x1); + + // + // This is the first fragment of the packet + // + if (FragmentOffset == 0) { + Info->NextHeader = FragmentHead->NextHeader; + } + + Info->HeadLen = (UINT16) HeadLen; + HeadLen += sizeof (IP6_FRAGMENT_HEADER); + Info->Start = FragmentOffset; + Info->Length = TotalLen - (UINT16) HeadLen; + Info->End = Info->Start + Info->Length; + Info->Id = FragmentHead->Identification; + Info->FormerNextHeader = FormerHeadOffset; + + // + // Fragments should in the unit of 8 octets long except the last one. + // + if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) { + return EFI_INVALID_PARAMETER; + } + + // + // Reassemble the packet. + // + *Packet = Ip6Reassemble (&IpSb->Assemble, *Packet); + if (*Packet == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Re-check the assembled packet to get the right values. + // + *Head = (*Packet)->Ip.Ip6; + PayloadLen = (*Head)->PayloadLength; + if (PayloadLen != 0) { + if (*Payload != NULL) { + FreePool (*Payload); + } + + *Payload = AllocatePool ((UINTN) PayloadLen); + if (*Payload == NULL) { + return EFI_INVALID_PARAMETER; + } + + NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload); + } + + if (!Ip6IsExtsValid ( + IpSb, + *Packet, + &(*Head)->NextHeader, + *Payload, + (UINT32) PayloadLen, + TRUE, + NULL, + LastHead, + ExtHdrsLen, + UnFragmentLen, + Fragmented + )) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Trim the head off, after this point, the packet is headless. + // and Packet->TotalLen == Info->Length. + // + NetbufTrim (*Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE); + + return EFI_SUCCESS; +} + +/** + The IP6 input routine. It is called by the IP6_INTERFACE when an + IP6 fragment is received from MNP. + + @param[in] Packet The IP6 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 IP6 service instance that owns the MNP. + +**/ +VOID +Ip6AcceptFrame ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ) +{ + IP6_SERVICE *IpSb; + EFI_IP6_HEADER *Head; + UINT8 *Payload; + UINT8 *LastHead; + UINT32 UnFragmentLen; + UINT32 ExtHdrsLen; + BOOLEAN Fragmented; + EFI_STATUS Status; + EFI_IP6_HEADER ZeroHead; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Payload = NULL; + LastHead = NULL; + + // + // Check input parameters + // + if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) { + goto Drop; + } + + // + // Pre-Process the Ipv6 Packet and then reassemble if it is necessary. + // + Status = Ip6PreProcessPacket ( + IpSb, + &Packet, + Flag, + &Payload, + &LastHead, + &ExtHdrsLen, + &UnFragmentLen, + &Fragmented, + &Head + ); + 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 = Ip6IpSecProcessPacket ( + IpSb, + &Head, + LastHead, // need get the lasthead value for input + &Packet, + &Payload, + &ExtHdrsLen, + EfiIPsecInBound, + NULL + ); + + if (EFI_ERROR (Status)) { + goto Restart; + } + + // + // If the packet is protected by IPsec Tunnel Mode, Check the Inner Ip Packet. + // + ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER)); + if (0 == CompareMem (Head, &ZeroHead, sizeof (EFI_IP6_HEADER))) { + Status = Ip6PreProcessPacket ( + IpSb, + &Packet, + Flag, + &Payload, + &LastHead, + &ExtHdrsLen, + &UnFragmentLen, + &Fragmented, + &Head + ); + if (EFI_ERROR (Status)) { + goto Restart; + } + } + + // + // Check the Packet again. + // + if (Packet == NULL) { + goto Restart; + } + + // + // Packet may have been changed. The ownership of the packet + // is transferred to the packet process logic. + // + Head = Packet->Ip.Ip6; + IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS; + + switch (*LastHead) { + case IP6_ICMP: + Ip6IcmpHandle (IpSb, Head, Packet); + break; + default: + Ip6Demultiplex (IpSb, Head, Packet); + } + + Packet = NULL; + + // + // Dispatch the DPCs queued by the NotifyFunction of the rx token's events + // which are signaled with received data. + // + DispatchDpc (); + +Restart: + if (Payload != NULL) { + FreePool (Payload); + } + + Ip6ReceiveFrame (Ip6AcceptFrame, IpSb); + +Drop: + if (Packet != NULL) { + NetbufFree (Packet); + } + + return ; +} + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP6 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip6CreateAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ) +{ + UINT32 Index; + + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + InitializeListHead (&Table->Bucket[Index]); + } +} + +/** + Clean up the assemble table by removing all of the fragments + and assemble entries. + + @param[in, out] Table The assemble table to clean up. + +**/ +VOID +Ip6CleanAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ASSEMBLE_ENTRY *Assemble; + UINT32 Index; + + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link); + + RemoveEntryList (Entry); + Ip6FreeAssembleEntry (Assemble); + } + } +} + + +/** + The signal handle of IP6's recycle event. It is called back + when the upper layer releases the packet. + + @param[in] Event The IP6's recycle event. + @param[in] Context The context of the handle, which is a IP6_RXDATA_WRAP. + +**/ +VOID +EFIAPI +Ip6OnRecyclePacket ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + IP6_RXDATA_WRAP *Wrap; + + Wrap = (IP6_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 IP6_RXDATA_WRAP, which will be + delivered to the upper layer. Each IP6 child that accepts the + packet will get a not-shared copy of the packet which is wrapped + in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed + to the upper layer. The upper layer will signal the recycle event in + it when it is done with the packet. + + @param[in] IpInstance The IP6 child to receive the packet. + @param[in] Packet The packet to deliver up. + + @return NULL if it failed to wrap the packet; otherwise, the wrapper. + +**/ +IP6_RXDATA_WRAP * +Ip6WrapRxData ( + IN IP6_PROTOCOL *IpInstance, + IN NET_BUF *Packet + ) +{ + IP6_RXDATA_WRAP *Wrap; + EFI_IP6_RECEIVE_DATA *RxData; + EFI_STATUS Status; + + Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum)); + + if (Wrap == NULL) { + return NULL; + } + + InitializeListHead (&Wrap->Link); + + Wrap->IpInstance = IpInstance; + Wrap->Packet = Packet; + RxData = &Wrap->RxData; + + ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME)); + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + Ip6OnRecyclePacket, + Wrap, + &RxData->RecycleSignal + ); + + if (EFI_ERROR (Status)) { + FreePool (Wrap); + return NULL; + } + + ASSERT (Packet->Ip.Ip6 != NULL); + + // + // The application expects a network byte order header. + // + RxData->HeaderLength = sizeof (EFI_IP6_HEADER); + RxData->Header = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6); + 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; +} + +/** + 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 The child wants to receive the packet. + @retval FALSE The child does not want to receive the packet. + +**/ +BOOLEAN +Ip6InstanceFrameAcceptable ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_ERROR_HEAD Icmp; + EFI_IP6_CONFIG_DATA *Config; + IP6_CLIP_INFO *Info; + UINT8 *Proto; + UINT32 Index; + UINT8 *ExtHdrs; + UINT16 ErrMsgPayloadLen; + UINT8 *ErrMsgPayload; + + Config = &IpInstance->ConfigData; + Proto = NULL; + + // + // 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 captibility. 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; + } + + // + // Check whether the protocol is acceptable. + // + ExtHdrs = NetbufGetByte (Packet, 0, NULL); + + if (!Ip6IsExtsValid ( + IpInstance->Service, + Packet, + &Head->NextHeader, + ExtHdrs, + (UINT32) Head->PayloadLength, + TRUE, + NULL, + &Proto, + NULL, + NULL, + NULL + )) { + return FALSE; + } + + // + // The upper layer driver may want to receive the ICMPv6 error packet + // invoked by its packet, like UDP. + // + if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) { + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) { + if (!Config->AcceptIcmpErrors) { + return FALSE; + } + + // + // Get the protocol of the invoking packet of ICMPv6 error packet. + // + ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength); + ErrMsgPayload = NetbufGetByte (Packet, sizeof (Icmp), NULL); + + if (!Ip6IsExtsValid ( + NULL, + NULL, + &Icmp.IpHead.NextHeader, + ErrMsgPayload, + ErrMsgPayloadLen, + TRUE, + NULL, + &Proto, + NULL, + NULL, + NULL + )) { + return FALSE; + } + } + } + + // + // 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 = IP6_GET_CLIP_INFO (Packet); + + // + // If it is a multicast packet, check whether we are in the group. + // + if (Info->CastType == Ip6Multicast) { + // + // Receive the multicast if the instance wants to receive all packets. + // + if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) { + return TRUE; + } + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) { + break; + } + } + + return (BOOLEAN)(Index < IpInstance->GroupCount); + } + + return TRUE; +} + +/** + Enqueue a shared copy of the packet to the IP6 child if the + packet is acceptable to it. Here the data of the packet is + shared, but the net buffer isn't. + + @param IpInstance The IP6 child to enqueue the packet to. + @param Head The IP header of the received packet. + @param 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 resources + @retval EFI_SUCCESS A shared copy the packet is enqueued to the child. + +**/ +EFI_STATUS +Ip6InstanceEnquePacket ( + IN IP6_PROTOCOL *IpInstance, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_CLIP_INFO *Info; + NET_BUF *Clone; + + // + // Check whether the packet is acceptable to this instance. + // + if (IpInstance->State != IP6_STATE_CONFIGED) { + return EFI_NOT_STARTED; + } + + if (!Ip6InstanceFrameAcceptable (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 = IP6_GET_CLIP_INFO (Clone); + Info->Life = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout); + + InsertTailList (&IpInstance->Received, &Clone->List); + return EFI_SUCCESS; +} + +/** + Deliver the received packets to the 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 +Ip6InstanceDeliverPacket ( + IN IP6_PROTOCOL *IpInstance + ) +{ + EFI_IP6_COMPLETION_TOKEN *Token; + IP6_RXDATA_WRAP *Wrap; + NET_BUF *Packet; + NET_BUF *Dup; + UINT8 *Head; + + // + // 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 = Ip6WrapRxData (IpInstance, Packet); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RemoveEntryList (&Packet->List); + + } else { + // + // Create a duplicated packet if this packet is shared + // + Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER)); + + if (Dup == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // 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, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD); + ASSERT (Head != NULL); + Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head; + + CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER)); + NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE); + + Wrap = Ip6WrapRxData (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 = IP6_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 IP6 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] IpIf The interface to enqueue the packet to. + + @return The number of the IP6 children that accepts the packet. + +**/ +INTN +Ip6InterfaceEnquePacket ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet, + IN IP6_INTERFACE *IpIf + ) +{ + IP6_PROTOCOL *IpInstance; + IP6_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. + // + LocalType = 0; + Info = IP6_GET_CLIP_INFO (Packet); + + if (IpIf->PromiscRecv) { + LocalType = Ip6Promiscuous; + } else { + LocalType = Info->CastType; + } + + // + // 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 = (UINT32) LocalType; + + Enqueued = 0; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink); + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + + if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) { + Enqueued++; + } + } + + Info->CastType = (UINT32) SavedType; + return Enqueued; +} + +/** + Deliver the packet for each IP6 child on the interface. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] IpIf The IP6 interface to deliver the packet. + +**/ +VOID +Ip6InterfaceDeliverPacket ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *IpIf + ) +{ + IP6_PROTOCOL *IpInstance; + LIST_ENTRY *Entry; + + NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) { + IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink); + Ip6InstanceDeliverPacket (IpInstance); + } +} + +/** + De-multiplex the packet. the packet delivery is processed in two + passes. The first pass will enqueue a shared copy of the packet + to each IP6 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP6 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 IP6 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + + @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 +Ip6Demultiplex ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + + LIST_ENTRY *Entry; + IP6_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, IP6_INTERFACE, Link); + + if (IpIf->Configured) { + Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, 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); + Packet = NULL; + + if (Enqueued == 0) { + return EFI_NOT_FOUND; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + if (IpIf->Configured) { + Ip6InterfaceDeliverPacket (IpSb, IpIf); + } + } + + return EFI_SUCCESS; +} + +/** + Decrease the life of the transmitted packets. If it is + decreased to zero, cancel the packet. This function is + called by Ip6packetTimerTicking that provides timeout for both the + received-but-not-delivered and transmitted-but-not-recycle + packets. + + @param[in] Map The IP6 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 +Ip6SentPacketTicking ( + IN NET_MAP *Map, + IN NET_MAP_ITEM *Item, + IN VOID *Context + ) +{ + IP6_TXTOKEN_WRAP *Wrap; + + Wrap = (IP6_TXTOKEN_WRAP *) Item->Value; + ASSERT (Wrap != NULL); + + if ((Wrap->Life > 0) && (--Wrap->Life == 0)) { + Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED); + } + + return EFI_SUCCESS; +} + +/** + Timeout the fragments, and the enqueued, and transmitted packets. + + @param[in] IpSb The IP6 service instance to timeout. + +**/ +VOID +Ip6PacketTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + LIST_ENTRY *InstanceEntry; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_PROTOCOL *IpInstance; + IP6_ASSEMBLE_ENTRY *Assemble; + NET_BUF *Packet; + IP6_CLIP_INFO *Info; + UINT32 Index; + + // + // First, time out the fragments. The packet's life is counting down + // once the first-arriving fragment of that packet was received. + // + for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) { + Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link); + + if ((Assemble->Life > 0) && (--Assemble->Life == 0)) { + // + // If the first fragment (the one with a Fragment Offset of zero) + // has been received, an ICMP Time Exceeded - Fragment Reassembly + // Time Exceeded message should be sent to the source of that fragment. + // + if ((Assemble->Packet != NULL) && + !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Assemble->Packet, + NULL, + &Assemble->Head->SourceAddress, + ICMP_V6_TIME_EXCEEDED, + ICMP_V6_TIMEOUT_REASSEMBLE, + NULL + ); + } + + // + // If reassembly of a packet is not completed within 60 seconds of + // the reception of the first-arriving fragment of that packet, the + // reassembly must be abandoned and all the fragments that have been + // received for that packet must be discarded. + // + RemoveEntryList (Entry); + Ip6FreeAssembleEntry (Assemble); + } + } + } + + NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_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 = IP6_GET_CLIP_INFO (Packet); + + if ((Info->Life > 0) && (--Info->Life == 0)) { + RemoveEntryList (Entry); + NetbufFree (Packet); + } + } + + // + // Third: time out the transmitted packets. + // + NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL); + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.h new file mode 100644 index 00000000..be6e5d01 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Input.h @@ -0,0 +1,229 @@ +/** @file + IP6 internal functions and definitions to process the incoming packets. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_INPUT_H__ +#define __EFI_IP6_INPUT_H__ + +#define IP6_MIN_HEADLEN 40 +#define IP6_MAX_HEADLEN 120 +/// +/// 8(ESP header) + 16(max IV) + 16(max padding) + 2(ESP tail) + 12(max ICV) = 54 +/// +#define IP6_MAX_IPSEC_HEADLEN 54 + + +#define IP6_ASSEMLE_HASH_SIZE 127 +/// +/// Lift time in seconds. +/// +#define IP6_FRAGMENT_LIFE 60 +#define IP6_MAX_PACKET_SIZE 65535 + + +#define IP6_GET_CLIP_INFO(Packet) ((IP6_CLIP_INFO *) ((Packet)->ProtoData)) + +#define IP6_ASSEMBLE_HASH(Dst, Src, Id) \ + ((*((UINT32 *) (Dst)) + *((UINT32 *) (Src)) + (Id)) % IP6_ASSEMLE_HASH_SIZE) + +#define IP6_RXDATA_WRAP_SIZE(NumFrag) \ + (sizeof (IP6_RXDATA_WRAP) + sizeof (EFI_IP6_FRAGMENT_DATA) * ((NumFrag) - 1)) + +// +// 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 { + UINT32 LinkFlag; + INT32 CastType; + INT32 Start; + INT32 End; + INT32 Length; + UINT32 Life; + EFI_STATUS Status; + UINT32 Id; + UINT16 HeadLen; + UINT8 NextHeader; + UINT8 LastFrag; + UINT32 FormerNextHeader; +} IP6_CLIP_INFO; + +// +// Structure used to assemble IP packets. +// +typedef struct { + LIST_ENTRY Link; + LIST_ENTRY Fragments; // List of all the fragments of this packet + + // + // Identity of one IP6 packet. Each fragment of a packet has + // the same (Dst, Src, Id). + // + EFI_IPv6_ADDRESS Dst; + EFI_IPv6_ADDRESS Src; + UINT32 Id; + + UINT32 TotalLen; + UINT32 CurLen; + UINT32 Life; // Count down life for the packet. + + EFI_IP6_HEADER *Head; // IP head of the first fragment + IP6_CLIP_INFO *Info; // Per packet information of the first fragment + NET_BUF *Packet; // The first fragment of the packet +} IP6_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[IP6_ASSEMLE_HASH_SIZE]; +} IP6_ASSEMBLE_TABLE; + +/** + The IP6 input routine. It is called by the IP6_INTERFACE when an + IP6 fragment is received from MNP. + + @param[in] Packet The IP6 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 IP6 service instance that own the MNP. + +**/ +VOID +Ip6AcceptFrame ( + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus, + IN UINT32 Flag, + IN VOID *Context + ); + +/** + 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 +Ip6InstanceDeliverPacket ( + IN IP6_PROTOCOL *IpInstance + ); + +/** + The work function to locate the IPsec protocol to process the inbound or + outbound IP packets. The process routine handles the packet with the following + actions: bypass the packet, discard the packet, or protect the packet. + + @param[in] IpSb The IP6 service instance. + @param[in, out] Head The caller-supplied IP6 header. + @param[in, out] LastHead The next header field of last IP header. + @param[in, out] Netbuf The IP6 packet to be processed by IPsec. + @param[in, out] ExtHdrs The caller-supplied options. + @param[in, out] ExtHdrsLen 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 are not sufficient resources to complete the operation. + @retval EFI_BUFFER_TOO_SMALL The number of non-empty blocks is bigger than the + number of input data blocks when building a fragment table. + +**/ +EFI_STATUS +Ip6IpSecProcessPacket ( + IN IP6_SERVICE *IpSb, + IN OUT EFI_IP6_HEADER **Head, + IN OUT UINT8 *LastHead, + IN OUT NET_BUF **Netbuf, + IN OUT UINT8 **ExtHdrs, + IN OUT UINT32 *ExtHdrsLen, + IN EFI_IPSEC_TRAFFIC_DIR Direction, + IN VOID *Context + ); + +/** + Initialize an already allocated assemble table. This is generally + the assemble table embedded in the IP6 service instance. + + @param[in, out] Table The assemble table to initialize. + +**/ +VOID +Ip6CreateAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ); + +/** + Clean up the assemble table: remove all the fragments + and assemble entries. + + @param[in, out] Table The assemble table to clean up. + +**/ +VOID +Ip6CleanAssembleTable ( + IN OUT IP6_ASSEMBLE_TABLE *Table + ); + +/** + Demultiple the packet. the packet delivery is processed in two + passes. The first pass will enqueue a shared copy of the packet + to each IP6 child that accepts the packet. The second pass will + deliver a non-shared copy of the packet to each IP6 child that + has pending receive requests. Data is copied if more than one + child wants to consume the packet because each IP child need + its own copy of the packet to make changes. + + @param[in] IpSb The IP6 service instance that received the packet. + @param[in] Head The header of the received packet. + @param[in] Packet The data of the received packet. + + @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 +Ip6Demultiplex ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Timeout the fragmented, enqueued, and transmitted packets. + + @param[in] IpSb The IP6 service instance to timeout. + +**/ +VOID +Ip6PacketTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Mld.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Mld.c new file mode 100644 index 00000000..c59e3d89 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Mld.c @@ -0,0 +1,902 @@ +/** @file + Multicast Listener Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +/** + Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data. + + @param[in, out] IpSb Points to IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be recorded. + @param[in] DelayTimer The maximum allowed delay before sending a responding + report, in units of milliseconds. + @return The created IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6CreateMldEntry ( + IN OUT IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr, + IN UINT32 DelayTimer + ) +{ + IP6_MLD_GROUP *Entry; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + Entry = AllocatePool (sizeof (IP6_MLD_GROUP)); + if (Entry != NULL) { + Entry->RefCnt = 1; + Entry->DelayTimer = DelayTimer; + Entry->SendByUs = FALSE; + IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr); + InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link); + } + + return Entry; +} + +/** + Search a IP6_MLD_GROUP list entry node from a list array. + + @param[in] IpSb Points to IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be searched. + + @return The found IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6FindMldEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + LIST_ENTRY *Entry; + IP6_MLD_GROUP *Group; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) { + return Group; + } + } + + return NULL; +} + +/** + Count the number of IP6 multicast groups that are mapped to the + same MAC address. Several IP6 multicast address may be mapped to + the same MAC address. + + @param[in] MldCtrl The MLD control block to search in. + @param[in] Mac The MAC address to search. + + @return The number of the IP6 multicast group that mapped to the same + multicast group Mac. + +**/ +INTN +Ip6FindMac ( + IN IP6_MLD_SERVICE_DATA *MldCtrl, + IN EFI_MAC_ADDRESS *Mac + ) +{ + LIST_ENTRY *Entry; + IP6_MLD_GROUP *Group; + INTN Count; + + Count = 0; + + NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + + if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) { + Count++; + } + } + + return Count; +} + +/** + Generate MLD report message and send it out to MulticastAddr. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface The IP interface to send the packet. + If NULL, a system interface will be selected. + @param[in] MulticastAddr The specific IPv6 multicast address to which + the message sender is listening. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD report message was successfully sent out. + +**/ +EFI_STATUS +Ip6SendMldReport ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + IP6_MLD_HEAD *MldHead; + NET_BUF *Packet; + EFI_IP6_HEADER Head; + UINT16 PayloadLen; + UINTN OptionLen; + UINT8 *Options; + EFI_STATUS Status; + UINT16 HeadChecksum; + UINT16 PseudoChecksum; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + // + // Generate the packet to be sent + // IPv6 basic header + Hop by Hop option + MLD message + // + + OptionLen = 0; + Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // RFC3590: Use link-local address as source address if it is available, + // otherwise use the unspecified address. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_HOP_BY_HOP; + Head.HopLimit = 1; + IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr); + + // + // If Link-Local address is not ready, we use unspecified address. + // + IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header + // + Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); + ASSERT (Options != NULL); + Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); + if (EFI_ERROR (Status)) { + NetbufFree (Packet); + Packet = NULL; + return Status; + } + + // + // Fill in MLD message - Report + // + MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); + ASSERT (MldHead != NULL); + ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); + MldHead->Head.Type = ICMP_V6_LISTENER_REPORT; + MldHead->Head.Code = 0; + IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); + + HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head.SourceAddress, + &Head.DestinationAddress, + IP6_ICMP, + sizeof (IP6_MLD_HEAD) + ); + + MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); + + // + // Transmit the packet + // + return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate MLD Done message and send it out to MulticastAddr. + + @param[in] IpSb The IP service to send the packet. + @param[in] MulticastAddr The specific IPv6 multicast address to which + the message sender is ceasing to listen. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD report message was successfully sent out. + +**/ +EFI_STATUS +Ip6SendMldDone ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ) +{ + IP6_MLD_HEAD *MldHead; + NET_BUF *Packet; + EFI_IP6_HEADER Head; + UINT16 PayloadLen; + UINTN OptionLen; + UINT8 *Options; + EFI_STATUS Status; + EFI_IPv6_ADDRESS Destination; + UINT16 HeadChecksum; + UINT16 PseudoChecksum; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr)); + + // + // Generate the packet to be sent + // IPv6 basic header + Hop by Hop option + MLD message + // + + OptionLen = 0; + Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP); + ASSERT (Status == EFI_BUFFER_TOO_SMALL); + + PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD)); + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_HOP_BY_HOP; + Head.HopLimit = 1; + + // + // If Link-Local address is not ready, we use unspecified address. + // + IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr); + + Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination); + IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header + // + Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE); + ASSERT (Options != NULL); + Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP); + if (EFI_ERROR (Status)) { + NetbufFree (Packet); + Packet = NULL; + return Status; + } + + // + // Fill in MLD message - Done + // + MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE); + ASSERT (MldHead != NULL); + ZeroMem (MldHead, sizeof (IP6_MLD_HEAD)); + MldHead->Head.Type = ICMP_V6_LISTENER_DONE; + MldHead->Head.Code = 0; + IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr); + + HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD)); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head.SourceAddress, + &Head.DestinationAddress, + IP6_ICMP, + sizeof (IP6_MLD_HEAD) + ); + + MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum); + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Init the MLD data of the IP6 service instance. Configure + MNP to receive ALL SYSTEM multicast. + + @param[in] IpSb The IP6 service whose MLD is to be initialized. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the + operation. + @retval EFI_SUCCESS The MLD module successfully initialized. + +**/ +EFI_STATUS +Ip6InitMld ( + IN IP6_SERVICE *IpSb + ) +{ + EFI_IPv6_ADDRESS AllNodes; + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + // + // Join the link-scope all-nodes multicast address (FF02::1). + // This address is started in Idle Listener state and never transitions to + // another state, and never sends a Report or Done for that address. + // + + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + + Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME); + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + // + // Configure MNP to receive all-nodes multicast + // + Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ERROR; + } + + return EFI_SUCCESS; + +ERROR: + RemoveEntryList (&Group->Link); + FreePool (Group); + return Status; +} + +/** + Add a group address to the array of group addresses. + The caller should make sure that no duplicated address + existed in the array. + + @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. + @param[in] Group The IP6 multicast address to add. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete + the operation. + @retval EFI_SUCCESS The address is added to the group address array. + +**/ +EFI_STATUS +Ip6CombineGroups ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IPv6_ADDRESS *Group + ) +{ + EFI_IPv6_ADDRESS *GroupList; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (Group != NULL && IP6_IS_MULTICAST (Group)); + + IpInstance->GroupCount++; + + GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS)); + if (GroupList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (IpInstance->GroupCount > 1) { + ASSERT (IpInstance->GroupList != NULL); + + CopyMem ( + GroupList, + IpInstance->GroupList, + (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS) + ); + + FreePool (IpInstance->GroupList); + } + + IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group); + + IpInstance->GroupList = GroupList; + + return EFI_SUCCESS; +} + +/** + Remove a group address from the array of group addresses. + Although the function doesn't assume the byte order of Group, + the network byte order is used by the caller. + + @param[in, out] IpInstance Points to an IP6_PROTOCOL instance. + @param[in] Group The IP6 multicast address to remove. + + @retval EFI_NOT_FOUND Cannot find the to be removed group address. + @retval EFI_SUCCESS The group address was successfully removed. + +**/ +EFI_STATUS +Ip6RemoveGroup ( + IN OUT IP6_PROTOCOL *IpInstance, + IN EFI_IPv6_ADDRESS *Group + ) +{ + UINT32 Index; + UINT32 Count; + + Count = IpInstance->GroupCount; + + for (Index = 0; Index < Count; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) { + break; + } + } + + if (Index == Count) { + return EFI_NOT_FOUND; + } + + while (Index < Count - 1) { + IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1); + Index++; + } + + ASSERT (IpInstance->GroupCount > 0); + IpInstance->GroupCount--; + + return EFI_SUCCESS; +} + +/** + Join the multicast group on behalf of this IP6 service binding instance. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Interface Points to an IP6_INTERFACE structure. + @param[in] Address The group address 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 +Ip6JoinGroup ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Address + ) +{ + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Group = Ip6FindMldEntry (IpSb, Address); + if (Group != NULL) { + Group->RefCnt++; + return EFI_SUCCESS; + } + + // + // Repeat the report once or twice after short delays [Unsolicited Report Interval] (default:10s) + // Simulate this operation as a Multicast-Address-Specific Query was received for that address. + // + Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL); + if (Group == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Group->SendByUs = TRUE; + + Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { + goto ERROR; + } + + // + // Send unsolicited report when a node starts listening to a multicast address + // + Status = Ip6SendMldReport (IpSb, Interface, Address); + if (EFI_ERROR (Status)) { + goto ERROR; + } + + return EFI_SUCCESS; + +ERROR: + RemoveEntryList (&Group->Link); + FreePool (Group); + return Status; +} + +/** + Leave the IP6 multicast group. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP6 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 +Ip6LeaveGroup ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ) +{ + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Group = Ip6FindMldEntry (IpSb, 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) && (--Group->RefCnt > 0)) { + return EFI_SUCCESS; + } + + // + // If multiple IP6 group addresses are mapped to the same + // multicast MAC address, don't configure the MNP to leave + // the MAC. + // + if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) { + Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac); + if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { + return Status; + } + } + + // + // Send a leave report if we are the last node to report + // + if (Group->SendByUs) { + Status = Ip6SendMldDone (IpSb, Address); + if (EFI_ERROR (Status)) { + return Status; + } + } + + RemoveEntryList (&Group->Link); + FreePool (Group); + + return EFI_SUCCESS; +} + +/** + Worker function for EfiIp6Groups(). The caller + should make sure that the parameters are valid. + + @param[in] IpInstance The IP6 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it. + @param[in] GroupAddress The target group address. If NULL, leave all + the group addresses. + + @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it + @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient 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 +Ip6Groups ( + IN IP6_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ) +{ + EFI_STATUS Status; + IP6_SERVICE *IpSb; + UINT32 Index; + EFI_IPv6_ADDRESS *Group; + + IpSb = IpInstance->Service; + + if (JoinFlag) { + ASSERT (GroupAddress != NULL); + + for (Index = 0; Index < IpInstance->GroupCount; Index++) { + if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) { + return EFI_ALREADY_STARTED; + } + } + + Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress); + if (!EFI_ERROR (Status)) { + return Ip6CombineGroups (IpInstance, GroupAddress); + } + + return Status; + } + + // + // Leave the group. Leave all the groups if GroupAddress is NULL. + // + for (Index = IpInstance->GroupCount; Index > 0; Index--) { + Group = IpInstance->GroupList + (Index - 1); + + if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) { + Status = Ip6LeaveGroup (IpInstance->Service, Group); + if (EFI_ERROR (Status)) { + return Status; + } + + Ip6RemoveGroup (IpInstance, Group); + + if (IpInstance->GroupCount == 0) { + ASSERT (Index == 1); + FreePool (IpInstance->GroupList); + IpInstance->GroupList = NULL; + } + + if (GroupAddress != NULL) { + return EFI_SUCCESS; + } + } + } + + return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS); +} + +/** + Set a random value of the delay timer for the multicast address from the range + [0, Maximum Response Delay]. If a timer for any address is already + running, it is reset to the new random value only if the requested + Maximum Response Delay is less than the remaining value of the + running timer. If the Query packet specifies a Maximum Response + Delay of zero, each timer is effectively set to zero, and the action + specified below for timer expiration is performed immediately. + + @param[in] IpSb The IP6 service binding instance. + @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds. + @param[in] MulticastAddr The multicast address. + @param[in, out] Group Points to a IP6_MLD_GROUP list entry node. + + @retval EFI_SUCCESS The delay timer is successfully updated or + timer expiration is performed immediately. + @retval Others Failed to send out MLD report message. + +**/ +EFI_STATUS +Ip6UpdateDelayTimer ( + IN IP6_SERVICE *IpSb, + IN UINT16 MaxRespDelay, + IN EFI_IPv6_ADDRESS *MulticastAddr, + IN OUT IP6_MLD_GROUP *Group + ) +{ + UINT32 Delay; + + // + // If the Query packet specifies a Maximum Response Delay of zero, perform timer + // expiration immediately. + // + if (MaxRespDelay == 0) { + Group->DelayTimer = 0; + return Ip6SendMldReport (IpSb, NULL, MulticastAddr); + } + + Delay = (UINT32) (MaxRespDelay / 1000); + + // + // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay] + // If a timer is already running, resets it if the request Maximum Response Delay + // is less than the remaining value of the running timer. + // + if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) { + Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ()); + } + + return EFI_SUCCESS; +} + +/** + Process the Multicast Listener Query message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD query packet. + @param[in] Packet The content of the MLD query packet with IP head + removed. + + @retval EFI_SUCCESS The MLD query packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessMldQuery ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + EFI_IPv6_ADDRESS AllNodes; + IP6_MLD_GROUP *Group; + IP6_MLD_HEAD MldPacket; + LIST_ENTRY *Entry; + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + // + // Check the validity of the packet, generic query or specific query + // + if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { + goto Exit; + } + + // + // The Packet points to MLD report raw data without Hop-By-Hop option. + // + NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); + MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay); + + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) { + // + // Receives a Multicast-Address-Specific Query, check it firstly + // + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { + goto Exit; + } + // + // The node is not listening but it receives the specific query. Just return. + // + Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); + if (Group == NULL) { + Status = EFI_SUCCESS; + goto Exit; + } + + Status = Ip6UpdateDelayTimer ( + IpSb, + MldPacket.MaxRespDelay, + &MldPacket.Group, + Group + ); + goto Exit; + } + + // + // Receives a General Query, sets a delay timer for each multicast address it is listening + // + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Multicast Listener Report message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD report packet. + @param[in] Packet The content of the MLD report packet with IP head + removed. + + @retval EFI_SUCCESS The MLD report packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + +**/ +EFI_STATUS +Ip6ProcessMldReport ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_MLD_HEAD MldPacket; + IP6_MLD_GROUP *Group; + EFI_STATUS Status; + + Status = EFI_INVALID_PARAMETER; + + // + // Validate the incoming message, if invalid, drop it. + // + if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) { + goto Exit; + } + + // + // The Packet points to MLD report raw data without Hop-By-Hop option. + // + NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket); + if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) { + goto Exit; + } + + Group = Ip6FindMldEntry (IpSb, &MldPacket.Group); + if (Group == NULL) { + goto Exit; + } + + // + // The report is sent by another node, stop its own timer relates to the multicast address and clear + // + + if (!Group->SendByUs) { + Group->DelayTimer = 0; + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + The heartbeat timer of MLD module. It sends out a solicited MLD report when + DelayTimer expires. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6MldTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + IP6_MLD_GROUP *Group; + LIST_ENTRY *Entry; + + // + // Send solicited report when timer expires + // + NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) { + Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link); + if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) { + Ip6SendMldReport (IpSb, NULL, &Group->Address); + } + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Mld.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Mld.h new file mode 100644 index 00000000..d4102180 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Mld.h @@ -0,0 +1,192 @@ +/** @file + Multicast Listener Discovery support routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_MLD_H__ +#define __EFI_IP6_MLD_H__ + +#define IP6_UNSOLICITED_REPORT_INTERVAL 10 + +#pragma pack(1) +typedef struct { + IP6_ICMP_HEAD Head; + UINT16 MaxRespDelay; + UINT16 Reserved; + EFI_IPv6_ADDRESS Group; +} IP6_MLD_HEAD; +#pragma pack() + +// +// The status of multicast group. It isn't necessary to maintain +// explicit state of host state diagram. A group with finity +// DelayTime (less than 0xffffffff) is in "delaying listener" state. otherwise, it is in +// "idle listener" state. +// +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + EFI_IPv6_ADDRESS Address; + UINT32 DelayTimer; + BOOLEAN SendByUs; + EFI_MAC_ADDRESS Mac; +} IP6_MLD_GROUP; + +// +// The MLD status. Each IP6 service instance has a MLD_SERVICE_DATA +// attached. The Mldv1QuerySeen remember whether the server on this +// connected network is v1 or v2. +// +typedef struct { + INTN Mldv1QuerySeen; + LIST_ENTRY Groups; +} IP6_MLD_SERVICE_DATA; + +/** + Search a IP6_MLD_GROUP list entry node from a list array. + + @param[in] IpSb Points to an IP6 service binding instance. + @param[in] MulticastAddr The IPv6 multicast address to be searched. + + @return The found IP6_ML_GROUP list entry or NULL. + +**/ +IP6_MLD_GROUP * +Ip6FindMldEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *MulticastAddr + ); + +/** + Init the MLD data of the IP6 service instance, configure + MNP to receive ALL SYSTEM multicasts. + + @param[in] IpSb The IP6 service whose MLD is to be initialized. + + @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the + operation. + @retval EFI_SUCCESS The MLD module successfully initialized. + +**/ +EFI_STATUS +Ip6InitMld ( + IN IP6_SERVICE *IpSb + ); + +/** + Join the multicast group on behalf of this IP6 service binding instance. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Interface Points to an IP6_INTERFACE structure. + @param[in] Address The group address to join. + + @retval EFI_SUCCESS Successfully joined the multicast group. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval Others Failed to join the multicast group. + +**/ +EFI_STATUS +Ip6JoinGroup ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Address + ); + +/** + Leave the IP6 multicast group. + + @param[in] IpSb The IP6 service binding instance. + @param[in] Address The group address to leave. + + @retval EFI_NOT_FOUND The IP6 service instance isn't in the group. + @retval EFI_SUCCESS Successfully left the multicast group. + @retval Others Failed to leave the multicast group. + +**/ +EFI_STATUS +Ip6LeaveGroup ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Address + ); + +/** + Worker function for EfiIp6Groups(). The caller + should verify that the parameters are valid. + + @param[in] IpInstance The IP6 child to change the setting. + @param[in] JoinFlag TRUE to join the group, otherwise leave it. + @param[in] GroupAddress The target group address. If NULL, leave all + the group addresses. + + @retval EFI_ALREADY_STARTED Wants to join the group, but is 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 Tried to leave a group of whom it isn't a member. + +**/ +EFI_STATUS +Ip6Groups ( + IN IP6_PROTOCOL *IpInstance, + IN BOOLEAN JoinFlag, + IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL + ); + +/** + Process the Multicast Listener Query message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD query packet. + @param[in] Packet The content of the MLD query packet with IP head + removed. + + @retval EFI_SUCCESS The MLD query packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessMldQuery ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Multicast Listener Report message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the MLD report packet. + @param[in] Packet The content of the MLD report packet with IP head + removed. + + @retval EFI_SUCCESS The MLD report packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + +**/ +EFI_STATUS +Ip6ProcessMldReport ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + + +/** + The heartbeat timer of the MLD module. It sends out solicited MLD report when + DelayTimer expires. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6MldTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Nd.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Nd.c new file mode 100644 index 00000000..5e461bb8 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Nd.c @@ -0,0 +1,3159 @@ +/** @file + Implementation of Neighbor Discovery support routines. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +EFI_MAC_ADDRESS mZeroMacAddress; + +/** + Update the ReachableTime in IP6 service binding instance data, in milliseconds. + + @param[in, out] IpSb Points to the IP6_SERVICE. + +**/ +VOID +Ip6UpdateReachableTime ( + IN OUT IP6_SERVICE *IpSb + ) +{ + UINT32 Random; + + Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE; + Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED; + IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE; +} + +/** + Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number + of EFI_IP6_NEIGHBOR_CACHE is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] NeighborCount The number of returned neighbor cache entries. + @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE. + + @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiNeighborCache ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *NeighborCount, + OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + LIST_ENTRY *Entry; + IP6_SERVICE *IpSb; + UINT32 Count; + EFI_IP6_NEIGHBOR_CACHE *EfiNeighborCache; + EFI_IP6_NEIGHBOR_CACHE *NeighborCacheTmp; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (NeighborCount != NULL && NeighborCache != NULL); + + IpSb = IpInstance->Service; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { + Count++; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE)); + if (NeighborCacheTmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *NeighborCount = Count; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { + Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + + EfiNeighborCache = NeighborCacheTmp + Count; + + EfiNeighborCache->State = Neighbor->State; + IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor); + IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress); + + Count++; + } + + ASSERT (*NeighborCount == Count); + *NeighborCache = NeighborCacheTmp; + + return EFI_SUCCESS; +} + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of prefix entries is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] PrefixCount The number of returned prefix entries. + @param[out] PrefixTable The pointer to the array of PrefixTable. + + @retval EFI_SUCCESS The prefix table successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table. + +**/ +EFI_STATUS +Ip6BuildPrefixTable ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *PrefixCount, + OUT EFI_IP6_ADDRESS_INFO **PrefixTable + ) +{ + LIST_ENTRY *Entry; + IP6_SERVICE *IpSb; + UINT32 Count; + IP6_PREFIX_LIST_ENTRY *PrefixList; + EFI_IP6_ADDRESS_INFO *EfiPrefix; + EFI_IP6_ADDRESS_INFO *PrefixTableTmp; + + NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); + ASSERT (PrefixCount != NULL && PrefixTable != NULL); + + IpSb = IpInstance->Service; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + Count++; + } + + if (Count == 0) { + return EFI_SUCCESS; + } + + PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO)); + if (PrefixTableTmp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + *PrefixCount = Count; + Count = 0; + + NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { + PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + EfiPrefix = PrefixTableTmp + Count; + IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix); + EfiPrefix->PrefixLength = PrefixList->PrefixLength; + + Count++; + } + + ASSERT (*PrefixCount == Count); + *PrefixTable = PrefixTableTmp; + + return EFI_SUCCESS; +} + +/** + Allocate and initialize a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list. + Otherwise, it is created for the autoconfiguration prefix list. + @param[in] ValidLifetime The length of time in seconds that the prefix + is valid for the purpose of on-link determination. + @param[in] PreferredLifetime The length of time in seconds that addresses + generated from the prefix via stateless address + autoconfiguration remain preferred. + @param[in] PrefixLength The prefix length of the Prefix. + @param[in] Prefix The prefix address. + + @return NULL if it failed to allocate memory for the prefix node. Otherwise, point + to the created or existing prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6CreatePrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixEntry; + IP6_ROUTE_ENTRY *RtEntry; + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + IP6_PREFIX_LIST_ENTRY *TmpPrefixEntry; + + if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength > IP6_PREFIX_MAX) { + return NULL; + } + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + PrefixEntry = Ip6FindPrefixListEntry ( + IpSb, + OnLinkOrAuto, + PrefixLength, + Prefix + ); + if (PrefixEntry != NULL) { + PrefixEntry->RefCnt ++; + return PrefixEntry; + } + + PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY)); + if (PrefixEntry == NULL) { + return NULL; + } + + PrefixEntry->RefCnt = 1; + PrefixEntry->ValidLifetime = ValidLifetime; + PrefixEntry->PreferredLifetime = PreferredLifetime; + PrefixEntry->PrefixLength = PrefixLength; + IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix); + + ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix; + + // + // Create a direct route entry for on-link prefix and insert to route area. + // + if (OnLinkOrAuto) { + RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL); + if (RtEntry == NULL) { + FreePool (PrefixEntry); + return NULL; + } + + RtEntry->Flag = IP6_DIRECT_ROUTE; + InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link); + IpSb->RouteTable->TotalNum++; + } + + // + // Insert the prefix entry in the order that a prefix with longer prefix length + // is put ahead in the list. + // + NET_LIST_FOR_EACH (Entry, ListHead) { + TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link); + + if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) { + break; + } + } + + NetListInsertBefore (Entry, &PrefixEntry->Link); + + return PrefixEntry; +} + +/** + Destroy a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] PrefixEntry The to be destroyed prefix list entry. + @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list. + Otherwise remove from autoconfiguration prefix list. + @param[in] ImmediateDelete If TRUE, remove the entry directly. + Otherwise, check the reference count to see whether + it should be removed. + +**/ +VOID +Ip6DestroyPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_PREFIX_LIST_ENTRY *PrefixEntry, + IN BOOLEAN OnLinkOrAuto, + IN BOOLEAN ImmediateDelete + ) +{ + LIST_ENTRY *Entry; + IP6_INTERFACE *IpIf; + EFI_STATUS Status; + + if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) { + return ; + } + + if (OnLinkOrAuto) { + // + // Remove the direct route for onlink prefix from route table. + // + do { + Status = Ip6DelRoute ( + IpSb->RouteTable, + &PrefixEntry->Prefix, + PrefixEntry->PrefixLength, + NULL + ); + } while (Status != EFI_NOT_FOUND); + } else { + // + // Remove the corresponding addresses generated from this autonomous prefix. + // + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE); + + Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength); + } + } + + RemoveEntryList (&PrefixEntry->Link); + FreePool (PrefixEntry); +} + +/** + Search the list array to find an IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the search the link prefix list, + Otherwise search the autoconfiguration prefix list. + @param[in] PrefixLength The prefix length of the Prefix + @param[in] Prefix The prefix address. + + @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the + pointer to the IP6 prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6FindPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixList; + LIST_ENTRY *Entry; + LIST_ENTRY *ListHead; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Prefix != NULL); + + if (OnLinkOrAuto) { + ListHead = &IpSb->OnlinkPrefix; + } else { + ListHead = &IpSb->AutonomousPrefix; + } + + NET_LIST_FOR_EACH (Entry, ListHead) { + PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixLength != 255) { + // + // Perform exactly prefix match. + // + if (PrefixList->PrefixLength == PrefixLength && + NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) { + return PrefixList; + } + } else { + // + // Perform the longest prefix match. The list is already sorted with + // the longest length prefix put at the head of the list. + // + if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) { + return PrefixList; + } + } + } + + return NULL; +} + +/** + Release the resource in the prefix list table, and destroy the list entry and + corresponding addresses or route entries. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] ListHead The list entry head of the prefix list table. + +**/ +VOID +Ip6CleanPrefixListTable ( + IN IP6_SERVICE *IpSb, + IN LIST_ENTRY *ListHead + ) +{ + IP6_PREFIX_LIST_ENTRY *PrefixList; + BOOLEAN OnLink; + + OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix); + + while (!IsListEmpty (ListHead)) { + PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link); + Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE); + } +} + +/** + Callback function when address resolution is finished. It will cancel + all the queued frames if the address resolution failed, or transmit them + if the request succeeded. + + @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY. + +**/ +VOID +Ip6OnArpResolved ( + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_NEIGHBOR_ENTRY *ArpQue; + IP6_SERVICE *IpSb; + IP6_LINK_TX_TOKEN *Token; + EFI_STATUS Status; + BOOLEAN Sent; + + ArpQue = (IP6_NEIGHBOR_ENTRY *) Context; + if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) { + return ; + } + + IpSb = ArpQue->Interface->Service; + if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) { + return ; + } + + // + // ARP resolve failed for some reason. Release all the frame + // and ARP queue itself. Ip6FreeArpQue will call the frame's + // owner back. + // + if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) { + Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL); + return ; + } + + // + // ARP resolve succeeded, Transmit all the frame. + // + Sent = FALSE; + NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) { + RemoveEntryList (Entry); + + Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress); + + // + // 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 (&ArpQue->Interface->SentFrames, &Token->Link); + + Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken); + if (EFI_ERROR (Status)) { + RemoveEntryList (&Token->Link); + Token->CallBack (Token->Packet, Status, 0, Token->Context); + + Ip6FreeLinkTxToken (Token); + continue; + } else { + Sent = TRUE; + } + } + + // + // Free the ArpQue only but not the whole neighbor entry. + // + Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL); + + if (Sent && (ArpQue->State == EfiNeighborStale)) { + ArpQue->State = EfiNeighborDelay; + ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME); + } +} + +/** + Allocate and initialize an IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] CallBack The callback function to be called when + address resolution is finished. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + @param[in] LinkAddress Points to the MAC address of the neighbor. + Ignored if NULL. + + @return NULL if failed to allocate memory for the neighbor cache entry. + Otherwise, point to the created neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6CreateNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_ARP_CALLBACK CallBack, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL + ) +{ + IP6_NEIGHBOR_ENTRY *Entry; + IP6_DEFAULT_ROUTER *DefaultRouter; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address!= NULL); + + Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY)); + if (Entry == NULL) { + return NULL; + } + + Entry->RefCnt = 1; + Entry->IsRouter = FALSE; + Entry->ArpFree = FALSE; + Entry->Dynamic = FALSE; + Entry->State = EfiNeighborInComplete; + Entry->Transmit = IP6_MAX_MULTICAST_SOLICIT + 1; + Entry->CallBack = CallBack; + Entry->Interface = NULL; + + InitializeListHead (&Entry->Frames); + + IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address); + + if (LinkAddress != NULL) { + IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress); + } else { + IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress); + } + + InsertHeadList (&IpSb->NeighborTable, &Entry->Link); + + // + // If corresponding default router entry exists, establish the relationship. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address); + if (DefaultRouter != NULL) { + DefaultRouter->NeighborCache = Entry; + } + + return Entry; +} + +/** + Search a IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + + @return NULL if it failed to find the matching neighbor cache entry. + Otherwise, point to the found neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6FindNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_NEIGHBOR_ENTRY *Neighbor; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) { + Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) { + RemoveEntryList (Entry); + InsertHeadList (&IpSb->NeighborTable, Entry); + + return Neighbor; + } + } + + return NULL; +} + +/** + Free a IP6 neighbor cache entry and remove all the frames on the address + resolution queue that pass the FrameToCancel. That is, either FrameToCancel + is NULL, or it returns true for the frame. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] NeighborCache The to be free neighbor cache entry. + @param[in] SendIcmpError If TRUE, send out ICMP error. + @param[in] FullFree If TRUE, remove the neighbor cache entry. + Otherwise remove the pending frames. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + This is an optional parameter that may be NULL. + @param[in] Context Opaque parameter to the FrameToCancel. + Ignored if FrameToCancel is NULL. + + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + @retval EFI_SUCCESS The operation finished successfully. + +**/ +EFI_STATUS +Ip6FreeNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_NEIGHBOR_ENTRY *NeighborCache, + IN BOOLEAN SendIcmpError, + IN BOOLEAN FullFree, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_LINK_TX_TOKEN *TxToken; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_DEFAULT_ROUTER *DefaultRouter; + + // + // If FrameToCancel fails, the token will not be released. + // To avoid the memory leak, stop this usage model. + // + if (FullFree && FrameToCancel != NULL) { + return EFI_INVALID_PARAMETER; + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) { + TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link); + + if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + TxToken->Packet, + NULL, + &TxToken->Packet->Ip.Ip6->SourceAddress, + ICMP_V6_DEST_UNREACHABLE, + ICMP_V6_ADDR_UNREACHABLE, + NULL + ); + } + + if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) { + RemoveEntryList (Entry); + TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context); + Ip6FreeLinkTxToken (TxToken); + } + } + + if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) { + RemoveEntryList (&NeighborCache->ArpList); + NeighborCache->ArpFree = FALSE; + } + + if (FullFree) { + if (NeighborCache->IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + RemoveEntryList (&NeighborCache->Link); + FreePool (NeighborCache); + } + + return EFI_SUCCESS; +} + +/** + Allocate and initialize an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the default router. + @param[in] RouterLifetime The lifetime associated with the default + router, in units of seconds. + + @return NULL if it failed to allocate memory for the default router node. + Otherwise, point to the created default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6CreateDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN UINT16 RouterLifetime + ) +{ + IP6_DEFAULT_ROUTER *Entry; + IP6_ROUTE_ENTRY *RtEntry; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER)); + if (Entry == NULL) { + return NULL; + } + + Entry->RefCnt = 1; + Entry->Lifetime = RouterLifetime; + Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address); + IP6_COPY_ADDRESS (&Entry->Router, Ip6Address); + + // + // Add a default route into route table with both Destination and PrefixLength set to zero. + // + RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address); + if (RtEntry == NULL) { + FreePool (Entry); + return NULL; + } + + InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link); + IpSb->RouteTable->TotalNum++; + + InsertTailList (&IpSb->DefaultRouterList, &Entry->Link); + + return Entry; +} + +/** + Destroy an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6DestroyDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN IP6_DEFAULT_ROUTER *DefaultRouter + ) +{ + EFI_STATUS Status; + + RemoveEntryList (&DefaultRouter->Link); + + // + // Update the Destination Cache - all entries using the time-out router as next-hop + // should perform next-hop determination again. + // + do { + Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router); + } while (Status != EFI_NOT_FOUND); + + FreePool (DefaultRouter); +} + +/** + Clean an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + +**/ +VOID +Ip6CleanDefaultRouterList ( + IN IP6_SERVICE *IpSb + ) +{ + IP6_DEFAULT_ROUTER *DefaultRouter; + + while (!IsListEmpty (&IpSb->DefaultRouterList)) { + DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link); + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } +} + +/** + Search a default router node from an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the to be searched default router node. + + @return NULL if it failed to find the matching default router node. + Otherwise, point to the found default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6FindDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ) +{ + LIST_ENTRY *Entry; + IP6_DEFAULT_ROUTER *DefaultRouter; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Ip6Address != NULL); + + NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) { + DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link); + if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) { + return DefaultRouter; + } + } + + return NULL; +} + +/** + The function to be called after DAD (Duplicate Address Detection) is performed. + + @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed. + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] DadEntry The DAD entry which already performed DAD. + +**/ +VOID +Ip6OnDADFinished ( + IN BOOLEAN IsDadPassed, + IN IP6_INTERFACE *IpIf, + IN IP6_DAD_ENTRY *DadEntry + ) +{ + IP6_SERVICE *IpSb; + IP6_ADDRESS_INFO *AddrInfo; + EFI_DHCP6_PROTOCOL *Dhcp6; + UINT16 OptBuf[4]; + EFI_DHCP6_PACKET_OPTION *Oro; + EFI_DHCP6_RETRANSMISSION InfoReqReXmit; + EFI_IPv6_ADDRESS AllNodes; + + IpSb = IpIf->Service; + AddrInfo = DadEntry->AddressInfo; + + if (IsDadPassed) { + // + // DAD succeed. + // + if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + ASSERT (!IpSb->LinkLocalOk); + + IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address); + IpSb->LinkLocalOk = TRUE; + IpIf->Configured = TRUE; + + // + // Check whether DHCP6 need to be started. + // + Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6; + + if (IpSb->Dhcp6NeedStart) { + Dhcp6->Start (Dhcp6); + IpSb->Dhcp6NeedStart = FALSE; + } + + if (IpSb->Dhcp6NeedInfoRequest) { + // + // Set the exta options to send. Here we only want the option request option + // with DNS SERVERS. + // + Oro = (EFI_DHCP6_PACKET_OPTION *) OptBuf; + Oro->OpCode = HTONS (DHCP6_OPT_ORO); + Oro->OpLen = HTONS (2); + *((UINT16 *) &Oro->Data[0]) = HTONS (DHCP6_OPT_DNS_SERVERS); + + InfoReqReXmit.Irt = 4; + InfoReqReXmit.Mrc = 64; + InfoReqReXmit.Mrt = 60; + InfoReqReXmit.Mrd = 0; + + Dhcp6->InfoRequest ( + Dhcp6, + TRUE, + Oro, + 0, + NULL, + &InfoReqReXmit, + IpSb->Ip6ConfigInstance.Dhcp6Event, + Ip6ConfigOnDhcp6Reply, + &IpSb->Ip6ConfigInstance + ); + } + + // + // Add an on-link prefix for link-local address. + // + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + (UINT32) IP6_INFINIT_LIFETIME, + (UINT32) IP6_INFINIT_LIFETIME, + IP6_LINK_LOCAL_PREFIX_LENGTH, + &IpSb->LinkLocalAddr + ); + + } else { + // + // Global scope unicast address. + // + Ip6AddAddr (IpIf, AddrInfo); + + // + // Add an on-link prefix for this address. + // + Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + AddrInfo->ValidLifetime, + AddrInfo->PreferredLifetime, + AddrInfo->PrefixLength, + &AddrInfo->Address + ); + + IpIf->Configured = TRUE; + } + } else { + // + // Leave the group we joined before. + // + Ip6LeaveGroup (IpSb, &DadEntry->Destination); + } + + if (DadEntry->Callback != NULL) { + DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context); + } + + if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + FreePool (AddrInfo); + RemoveEntryList (&DadEntry->Link); + FreePool (DadEntry); + // + // Leave link-scope all-nodes multicast address (FF02::1) + // + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes); + Ip6LeaveGroup (IpSb, &AllNodes); + // + // Disable IP operation since link-local address is a duplicate address. + // + IpSb->LinkLocalDadFail = TRUE; + IpSb->Mnp->Configure (IpSb->Mnp, NULL); + gBS->SetTimer (IpSb->Timer, TimerCancel, 0); + gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0); + return ; + } + + if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) { + // + // Free the AddressInfo we hold if DAD fails or it is a link-local address. + // + FreePool (AddrInfo); + } + + RemoveEntryList (&DadEntry->Link); + FreePool (DadEntry); +} + +/** + Create a DAD (Duplicate Address Detection) entry and queue it to be performed. + + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] AddressInfo The address information which needs DAD performed. + @param[in] Callback The callback routine that will be called after DAD + is performed. This is an optional parameter that + may be NULL. + @param[in] Context The opaque parameter for a DAD callback routine. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The DAD entry was created and queued. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the + operation. + + +**/ +EFI_STATUS +Ip6InitDADProcess ( + IN IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddressInfo, + IN IP6_DAD_CALLBACK Callback OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + IP6_DAD_ENTRY *Entry; + EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits; + IP6_SERVICE *IpSb; + EFI_STATUS Status; + UINT32 MaxDelayTick; + + NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE); + ASSERT (AddressInfo != NULL); + + // + // Do nothing if we have already started DAD on the address. + // + if (Ip6FindDADEntry (IpIf->Service, &AddressInfo->Address, NULL) != NULL) { + return EFI_SUCCESS; + } + + Status = EFI_SUCCESS; + IpSb = IpIf->Service; + DadXmits = &IpSb->Ip6ConfigInstance.DadXmits; + + // + // Allocate the resources and insert info + // + Entry = AllocatePool (sizeof (IP6_DAD_ENTRY)); + if (Entry == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Map the incoming unicast address to solicited-node multicast address + // + Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination); + + // + // Join in the solicited-node multicast address. + // + Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination); + if (EFI_ERROR (Status)) { + FreePool (Entry); + return Status; + } + + Entry->Signature = IP6_DAD_ENTRY_SIGNATURE; + Entry->MaxTransmit = DadXmits->DupAddrDetectTransmits; + Entry->Transmit = 0; + Entry->Receive = 0; + MaxDelayTick = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS; + Entry->RetransTick = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5; + Entry->AddressInfo = AddressInfo; + Entry->Callback = Callback; + Entry->Context = Context; + InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link); + + if (Entry->MaxTransmit == 0) { + // + // DAD is disabled on this interface, immediately mark this DAD successful. + // + Ip6OnDADFinished (TRUE, IpIf, Entry); + } + + return EFI_SUCCESS; +} + +/** + Search IP6_DAD_ENTRY from the Duplicate Address Detection List. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Target The address information which needs DAD performed . + @param[out] Interface If not NULL, output the IP6 interface that configures + the tentative address. + + @return NULL if failed to find the matching DAD entry. + Otherwise, point to the found DAD entry. + +**/ +IP6_DAD_ENTRY * +Ip6FindDADEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Target, + OUT IP6_INTERFACE **Interface OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_ADDRESS_INFO *AddrInfo; + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) { + DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE); + AddrInfo = DupAddrDetect->AddressInfo; + if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) { + if (Interface != NULL) { + *Interface = IpIf; + } + return DupAddrDetect; + } + } + } + + return NULL; +} + +/** + Generate router solicit message and send it out to Destination Address or + All Router Link Local scope multicast address. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface If not NULL, points to the IP6 interface to send + the packet. + @param[in] SourceAddress If not NULL, the source address of the message. + @param[in] DestinationAddress If not NULL, the destination address of the message. + @param[in] SourceLinkAddress If not NULL, the MAC address of the source. + A source link-layer address option will be appended + to the message. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The router solicit message was successfully sent. + +**/ +EFI_STATUS +Ip6SendRouterSolicit ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + UINT16 PayloadLen; + IP6_INTERFACE *IpIf; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + IpIf = Interface; + if (IpIf == NULL && IpSb->DefaultInterface != NULL) { + IpIf = IpSb->DefaultInterface; + } + + // + // Generate the packet to be sent + // + + PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD); + if (SourceLinkAddress != NULL) { + PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION); + } + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + + if (DestinationAddress != NULL) { + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + } else { + Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress); + } + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, and Source link-layer address if contained. + // + + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT; + IcmpHead->Head.Code = 0; + + LinkLayerOption = NULL; + if (SourceLinkAddress != NULL) { + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherSource; + LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION); + CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6); + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate a Neighbor Advertisement message and send it out to Destination Address. + + @param[in] IpSb The IP service to send the packet. + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The target address field in the Neighbor Solicitation + message that prompted this advertisement. + @param[in] TargetLinkAddress The MAC address for the target, i.e. the sender + of the advertisement. + @param[in] IsRouter If TRUE, indicates the sender is a router. + @param[in] Override If TRUE, indicates the advertisement should override + an existing cache entry and update the MAC address. + @param[in] Solicited If TRUE, indicates the advertisement was sent + in response to a Neighbor Solicitation from + the Destination address. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress, + IN BOOLEAN IsRouter, + IN BOOLEAN Override, + IN BOOLEAN Solicited + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_IPv6_ADDRESS *Target; + UINT16 PayloadLen; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // The Neighbor Advertisement message must include a Target link-layer address option + // when responding to multicast solicitation and should include such option when + // responding to unicast solicitation. It also must include such option as unsolicited + // advertisement. + // + ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL); + + PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION)); + + // + // Generate the packet to be sent + // + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header. + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, Target address, and Target link-layer address. + // Set the Router flag, Solicited flag and Override flag. + // + + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE; + IcmpHead->Head.Code = 0; + + if (IsRouter) { + IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG; + } + + if (Solicited) { + IcmpHead->Fourth |= IP6_SOLICITED_FLAG; + } + + if (Override) { + IcmpHead->Fourth |= IP6_OVERRIDE_FLAG; + } + + Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE); + ASSERT (Target != NULL); + IP6_COPY_ADDRESS (Target, TargetIp6Address); + + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherTarget; + LinkLayerOption->Length = 1; + CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6); + + // + // Transmit the packet + // + return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Generate the Neighbor Solicitation message and send it to the Destination Address. + + @param[in] IpSb The IP service to send the packet + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The IP address of the target of the solicitation. + It must not be a multicast address. + @param[in] SourceLinkAddress The MAC address for the sender. If not NULL, + a source link-layer address option will be appended + to the message. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ) +{ + NET_BUF *Packet; + EFI_IP6_HEADER Head; + IP6_ICMP_INFORMATION_HEAD *IcmpHead; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_IPv6_ADDRESS *Target; + BOOLEAN IsDAD; + UINT16 PayloadLen; + IP6_NEIGHBOR_ENTRY *Neighbor; + + // + // Check input parameters + // + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + if (DestinationAddress == NULL || TargetIp6Address == NULL) { + return EFI_INVALID_PARAMETER; + } + + IsDAD = FALSE; + + if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) { + IsDAD = TRUE; + } + + // + // The Neighbor Solicitation message should include a source link-layer address option + // if the solicitation is not sent by performing DAD - Duplicate Address Detection. + // Otherwise must not include it. + // + PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS)); + + if (!IsDAD) { + if (SourceLinkAddress == NULL) { + return EFI_INVALID_PARAMETER; + } + + PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION)); + } + + // + // Generate the packet to be sent + // + + Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Create the basic IPv6 header + // + Head.FlowLabelL = 0; + Head.FlowLabelH = 0; + Head.PayloadLength = HTONS (PayloadLen); + Head.NextHeader = IP6_ICMP; + Head.HopLimit = IP6_HOP_LIMIT; + + if (SourceAddress != NULL) { + IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress); + } else { + ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS)); + } + + IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress); + + NetbufReserve (Packet, sizeof (EFI_IP6_HEADER)); + + // + // Fill in the ICMP header, Target address, and Source link-layer address. + // + IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE); + ASSERT (IcmpHead != NULL); + ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD)); + IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT; + IcmpHead->Head.Code = 0; + + Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE); + ASSERT (Target != NULL); + IP6_COPY_ADDRESS (Target, TargetIp6Address); + + LinkLayerOption = NULL; + if (!IsDAD) { + + // + // Fill in the source link-layer address option + // + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace ( + Packet, + sizeof (IP6_ETHER_ADDR_OPTION), + FALSE + ); + ASSERT (LinkLayerOption != NULL); + LinkLayerOption->Type = Ip6OptionEtherSource; + LinkLayerOption->Length = 1; + CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6); + } + + // + // Create a Neighbor Cache entry in the INCOMPLETE state when performing + // address resolution. + // + if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) { + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor == NULL) { + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL); + ASSERT (Neighbor != NULL); + } + } + + // + // Transmit the packet + // + return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL); +} + +/** + Process the Neighbor Solicitation message. The message may be sent for Duplicate + Address Detection or Address Resolution. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_IPv6_ADDRESS Target; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + BOOLEAN IsDAD; + BOOLEAN IsUnicast; + BOOLEAN IsMaintained; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_INTERFACE *IpIf; + IP6_NEIGHBOR_ENTRY *Neighbor; + BOOLEAN Solicited; + BOOLEAN UpdateCache; + EFI_IPv6_ADDRESS Dest; + UINT16 OptionLen; + UINT8 *Option; + BOOLEAN Provided; + EFI_STATUS Status; + VOID *MacAddress; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr); + + // + // Perform Message Validation: + // The IP Hop Limit field has a value of 255, i.e., the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // Target Address is not a multicast address. + // + Status = EFI_INVALID_PARAMETER; + + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) { + goto Exit; + } + + // + // ICMP length is 24 or more octets. + // + OptionLen = 0; + if (Head->PayloadLength < IP6_ND_LENGTH) { + goto Exit; + } else { + OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL); + ASSERT (Option != NULL); + + // + // All included options should have a length that is greater than zero. + // + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + } + + IsDAD = NetIp6IsUnspecifiedAddr (&Head->SourceAddress); + IsUnicast = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress); + IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL); + + Provided = FALSE; + if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) { + NetbufCopy ( + Packet, + IP6_ND_LENGTH, + sizeof (IP6_ETHER_ADDR_OPTION), + (UINT8 *) &LinkLayerOption + ); + // + // The solicitation for neighbor discovery should include a source link-layer + // address option. If the option is not recognized, silently ignore it. + // + if (LinkLayerOption.Type == Ip6OptionEtherSource) { + if (IsDAD) { + // + // If the IP source address is the unspecified address, the source + // link-layer address option must not be included in the message. + // + goto Exit; + } + + Provided = TRUE; + } + } + + // + // If the IP source address is the unspecified address, the IP + // destination address is a solicited-node multicast address. + // + if (IsDAD && IsUnicast) { + goto Exit; + } + + // + // If the target address is tentative, and the source address is a unicast address, + // the solicitation's sender is performing address resolution on the target; + // the solicitation should be silently ignored. + // + if (!IsDAD && !IsMaintained) { + goto Exit; + } + + // + // If received unicast neighbor solicitation but destination is not this node, + // drop the packet. + // + if (IsUnicast && !IsMaintained) { + goto Exit; + } + + // + // In DAD, when target address is a tentative address, + // process the received neighbor solicitation message but not send out response. + // + if (IsDAD && !IsMaintained) { + DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf); + if (DupAddrDetect != NULL) { + // + // Check the MAC address of the incoming packet. + // + if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) { + goto Exit; + } + + MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress; + if (MacAddress != NULL) { + if (CompareMem ( + MacAddress, + &IpSb->SnpMode.CurrentAddress, + IpSb->SnpMode.HwAddressSize + ) != 0) { + // + // The NS is from another node to performing DAD on the same address. + // Fail DAD for the tentative address. + // + Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect); + Status = EFI_ICMP_ERROR; + } else { + // + // The below layer loopback the NS we sent. Record it and wait for more. + // + DupAddrDetect->Receive++; + Status = EFI_SUCCESS; + } + } + } + goto Exit; + } + + // + // If the solicitation does not contain a link-layer address, DO NOT create or + // update the neighbor cache entries. + // + if (Provided) { + Neighbor = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + UpdateCache = FALSE; + + if (Neighbor == NULL) { + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL); + if (Neighbor == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + UpdateCache = TRUE; + } else { + if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) { + UpdateCache = TRUE; + } + } + + if (UpdateCache) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + // + // Send queued packets if exist. + // + Neighbor->CallBack ((VOID *) Neighbor); + } + } + + // + // Sends a Neighbor Advertisement as response. + // Set the Router flag to zero since the node is a host. + // If the source address of the solicitation is unspecified, and target address + // is one of the maintained address, reply a unsolicited multicast advertisement. + // + if (IsDAD && IsMaintained) { + Solicited = FALSE; + Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest); + } else { + Solicited = TRUE; + IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress); + } + + Status = Ip6SendNeighborAdvertise ( + IpSb, + &Target, + &Dest, + &Target, + &IpSb->SnpMode.CurrentAddress, + FALSE, + TRUE, + Solicited + ); +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Neighbor Advertisement message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + EFI_IPv6_ADDRESS Target; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + BOOLEAN Provided; + INTN Compare; + IP6_NEIGHBOR_ENTRY *Neighbor; + IP6_DEFAULT_ROUTER *DefaultRouter; + BOOLEAN Solicited; + BOOLEAN IsRouter; + BOOLEAN Override; + IP6_DAD_ENTRY *DupAddrDetect; + IP6_INTERFACE *IpIf; + UINT16 OptionLen; + UINT8 *Option; + EFI_STATUS Status; + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr); + + // + // Validate the incoming Neighbor Advertisement + // + Status = EFI_INVALID_PARAMETER; + // + // The IP Hop Limit field has a value of 255, i.e., the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // Target Address is not a multicast address. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) { + goto Exit; + } + + // + // ICMP length is 24 or more octets. + // + Provided = FALSE; + OptionLen = 0; + if (Head->PayloadLength < IP6_ND_LENGTH) { + goto Exit; + } else { + OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL); + ASSERT (Option != NULL); + + // + // All included options should have a length that is greater than zero. + // + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + } + + // + // If the IP destination address is a multicast address, Solicited Flag is ZERO. + // + Solicited = FALSE; + if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) { + Solicited = TRUE; + } + if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) { + goto Exit; + } + + // + // DAD - Check whether the Target is one of our tentative address. + // + DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf); + if (DupAddrDetect != NULL) { + // + // DAD fails, some other node is using this address. + // + NetbufFree (Packet); + Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect); + return EFI_ICMP_ERROR; + } + + // + // Search the Neighbor Cache for the target's entry. If no entry exists, + // the advertisement should be silently discarded. + // + Neighbor = Ip6FindNeighborEntry (IpSb, &Target); + if (Neighbor == NULL) { + goto Exit; + } + + // + // Get IsRouter Flag and Override Flag + // + IsRouter = FALSE; + Override = FALSE; + if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) { + IsRouter = TRUE; + } + if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) { + Override = TRUE; + } + + // + // Check whether link layer option is included. + // + if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) { + NetbufCopy ( + Packet, + IP6_ND_LENGTH, + sizeof (IP6_ETHER_ADDR_OPTION), + (UINT8 *) &LinkLayerOption + ); + + if (LinkLayerOption.Type == Ip6OptionEtherTarget) { + Provided = TRUE; + } + } + + Compare = 0; + if (Provided) { + Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + } + + if (!Neighbor->IsRouter && IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target); + if (DefaultRouter != NULL) { + DefaultRouter->NeighborCache = Neighbor; + } + } + + if (Neighbor->State == EfiNeighborInComplete) { + // + // If the target's Neighbor Cache entry is in INCOMPLETE state and no + // Target Link-Layer address option is included while link layer has + // address, the message should be silently discarded. + // + if (!Provided) { + goto Exit; + } + // + // Update the Neighbor Cache + // + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + if (Solicited) { + Neighbor->State = EfiNeighborReachable; + Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime); + } else { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + // + // Send any packets queued for the neighbor awaiting address resolution. + // + Neighbor->CallBack ((VOID *) Neighbor); + } + + Neighbor->IsRouter = IsRouter; + + } else { + if (!Override && Compare != 0) { + // + // When the Override Flag is clear and supplied link-layer address differs from + // that in the cache, if the state of the entry is not REACHABLE, ignore the + // message. Otherwise set it to STALE but do not update the entry in any + // other way. + // + if (Neighbor->State == EfiNeighborReachable) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } else { + if (Compare != 0) { + CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6); + } + // + // Update the entry's state + // + if (Solicited) { + Neighbor->State = EfiNeighborReachable; + Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime); + } else { + if (Compare != 0) { + Neighbor->State = EfiNeighborStale; + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } + + // + // When IsRouter is changed from TRUE to FALSE, remove the router from the + // Default Router List and remove the Destination Cache entries for all destinations + // using the neighbor as a router. + // + if (Neighbor->IsRouter && !IsRouter) { + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target); + if (DefaultRouter != NULL) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + Neighbor->IsRouter = IsRouter; + } + } + + if (Neighbor->State == EfiNeighborReachable) { + Neighbor->CallBack ((VOID *) Neighbor); + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the Router Advertisement message according to RFC4861. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with the IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessRouterAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD Icmp; + UINT32 ReachableTime; + UINT32 RetransTimer; + UINT16 RouterLifetime; + UINT32 Offset; + UINT8 Type; + UINT8 Length; + IP6_ETHER_ADDR_OPTION LinkLayerOption; + UINT32 Fourth; + UINT8 CurHopLimit; + BOOLEAN Mflag; + BOOLEAN Oflag; + IP6_DEFAULT_ROUTER *DefaultRouter; + IP6_NEIGHBOR_ENTRY *NeighborCache; + EFI_MAC_ADDRESS LinkLayerAddress; + IP6_MTU_OPTION MTUOption; + IP6_PREFIX_INFO_OPTION PrefixOption; + IP6_PREFIX_LIST_ENTRY *PrefixList; + BOOLEAN OnLink; + BOOLEAN Autonomous; + EFI_IPv6_ADDRESS StatelessAddress; + EFI_STATUS Status; + UINT16 OptionLen; + UINT8 *Option; + INTN Result; + + Status = EFI_INVALID_PARAMETER; + + if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) { + // + // Skip the process below as it's not required under the current policy. + // + goto Exit; + } + + NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); + + // + // Validate the incoming Router Advertisement + // + + // + // The IP source address must be a link-local address + // + if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + // + // The IP Hop Limit field has a value of 255, i.e. the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // ICMP length (derived from the IP length) is 16 or more octets. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || + Head->PayloadLength < IP6_RA_LENGTH) { + goto Exit; + } + + // + // All included options have a length that is greater than zero. + // + OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL); + ASSERT (Option != NULL); + + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + + // + // Process Fourth field. + // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag, + // and Router Lifetime (16 bit). + // + + Fourth = NTOHL (Icmp.Fourth); + CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16)); + + // + // If the source address already in the default router list, update it. + // Otherwise create a new entry. + // A Lifetime of zero indicates that the router is not a default router. + // + DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress); + if (DefaultRouter == NULL) { + if (RouterLifetime != 0) { + DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime); + if (DefaultRouter == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + } else { + if (RouterLifetime != 0) { + DefaultRouter->Lifetime = RouterLifetime; + // + // Check the corresponding neighbor cache entry here. + // + if (DefaultRouter->NeighborCache == NULL) { + DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + } + } else { + // + // If the address is in the host's default router list and the router lifetime is zero, + // immediately time-out the entry. + // + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + + CurHopLimit = *((UINT8 *) &Fourth + 3); + if (CurHopLimit != 0) { + IpSb->CurHopLimit = CurHopLimit; + } + + Mflag = FALSE; + Oflag = FALSE; + if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) { + Mflag = TRUE; + } else { + if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) { + Oflag = TRUE; + } + } + + if (Mflag || Oflag) { + // + // Use Ip6Config to get available addresses or other configuration from DHCP. + // + Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag); + } + + // + // Process Reachable Time and Retrans Timer fields. + // + NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime); + NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer); + ReachableTime = NTOHL (ReachableTime); + RetransTimer = NTOHL (RetransTimer); + + if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) { + // + // If new value is not unspecified and differs from the previous one, record it + // in BaseReachableTime and recompute a ReachableTime. + // + IpSb->BaseReachableTime = ReachableTime; + Ip6UpdateReachableTime (IpSb); + } + + if (RetransTimer != 0) { + IpSb->RetransTimer = RetransTimer; + } + + // + // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists. + // + NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress); + if (NeighborCache != NULL) { + NeighborCache->IsRouter = TRUE; + } + + // + // If an valid router advertisement is received, stops router solicitation. + // + IpSb->RouterAdvertiseReceived = TRUE; + + // + // The only defined options that may appear are the Source + // Link-Layer Address, Prefix information and MTU options. + // All included options have a length that is greater than zero and + // fit within the input packet. + // + Offset = 16; + while (Offset < (UINT32) Head->PayloadLength) { + NetbufCopy (Packet, Offset, sizeof (UINT8), &Type); + switch (Type) { + case Ip6OptionEtherSource: + // + // Update the neighbor cache + // + NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption); + + // + // Option size validity ensured by Ip6IsNDOptionValid(). + // + ASSERT (LinkLayerOption.Length != 0); + ASSERT (Offset + (UINT32) LinkLayerOption.Length * 8 <= (UINT32) Head->PayloadLength); + + ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6); + + if (NeighborCache == NULL) { + NeighborCache = Ip6CreateNeighborEntry ( + IpSb, + Ip6OnArpResolved, + &Head->SourceAddress, + &LinkLayerAddress + ); + if (NeighborCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + NeighborCache->IsRouter = TRUE; + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } else { + Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6); + + // + // If the link-local address is the same as that already in the cache, + // the cache entry's state remains unchanged. Otherwise update the + // reachability state to STALE. + // + if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) { + CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6); + + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + + if (NeighborCache->State == EfiNeighborInComplete) { + // + // Send queued packets if exist. + // + NeighborCache->State = EfiNeighborStale; + NeighborCache->CallBack ((VOID *) NeighborCache); + } else { + NeighborCache->State = EfiNeighborStale; + } + } + } + + Offset += (UINT32) LinkLayerOption.Length * 8; + break; + case Ip6OptionPrefixInfo: + NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption); + + // + // Option size validity ensured by Ip6IsNDOptionValid(). + // + ASSERT (PrefixOption.Length == 4); + ASSERT (Offset + (UINT32) PrefixOption.Length * 8 <= (UINT32) Head->PayloadLength); + + PrefixOption.ValidLifetime = NTOHL (PrefixOption.ValidLifetime); + PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime); + + // + // Get L and A flag, recorded in the lower 2 bits of Reserved1 + // + OnLink = FALSE; + if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) { + OnLink = TRUE; + } + Autonomous = FALSE; + if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) { + Autonomous = TRUE; + } + + // + // If the prefix is the link-local prefix, silently ignore the prefix option. + // + if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH && + NetIp6IsLinkLocalAddr (&PrefixOption.Prefix) + ) { + Offset += sizeof (IP6_PREFIX_INFO_OPTION); + break; + } + // + // Do following if on-link flag is set according to RFC4861. + // + if (OnLink) { + PrefixList = Ip6FindPrefixListEntry ( + IpSb, + TRUE, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + // + // Create a new entry for the prefix, if the ValidLifetime is zero, + // silently ignore the prefix option. + // + if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) { + PrefixList = Ip6CreatePrefixListEntry ( + IpSb, + TRUE, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + if (PrefixList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } else if (PrefixList != NULL) { + if (PrefixOption.ValidLifetime != 0) { + PrefixList->ValidLifetime = PrefixOption.ValidLifetime; + } else { + // + // If the prefix exists and incoming ValidLifetime is zero, immediately + // remove the prefix. + Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE); + } + } + } + + // + // Do following if Autonomous flag is set according to RFC4862. + // + if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) { + PrefixList = Ip6FindPrefixListEntry ( + IpSb, + FALSE, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + // + // Create a new entry for the prefix, and form an address by prefix + interface id + // If the sum of the prefix length and interface identifier length + // does not equal 128 bits, the Prefix Information option MUST be ignored. + // + if (PrefixList == NULL && + PrefixOption.ValidLifetime != 0 && + PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128 + ) { + // + // Form the address in network order. + // + CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64)); + CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64)); + + // + // If the address is not yet in the assigned address list, adds it into. + // + if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) { + // + // And also not in the DAD process, check its uniqueness firstly. + // + if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) { + Status = Ip6SetAddress ( + IpSb->DefaultInterface, + &StatelessAddress, + FALSE, + PrefixOption.PrefixLength, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + goto Exit; + } + } + } + + // + // Adds the prefix option to stateless prefix option list. + // + PrefixList = Ip6CreatePrefixListEntry ( + IpSb, + FALSE, + PrefixOption.ValidLifetime, + PrefixOption.PreferredLifetime, + PrefixOption.PrefixLength, + &PrefixOption.Prefix + ); + if (PrefixList == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } else if (PrefixList != NULL) { + + // + // Reset the preferred lifetime of the address if the advertised prefix exists. + // Perform specific action to valid lifetime together. + // + PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime; + if ((PrefixOption.ValidLifetime > 7200) || + (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) { + // + // If the received Valid Lifetime is greater than 2 hours or + // greater than RemainingLifetime, set the valid lifetime of the + // corresponding address to the advertised Valid Lifetime. + // + PrefixList->ValidLifetime = PrefixOption.ValidLifetime; + + } else if (PrefixList->ValidLifetime <= 7200) { + // + // If RemainingLifetime is less than or equals to 2 hours, ignore the + // Prefix Information option with regards to the valid lifetime. + // TODO: If this option has been authenticated, set the valid lifetime. + // + } else { + // + // Otherwise, reset the valid lifetime of the corresponding + // address to 2 hours. + // + PrefixList->ValidLifetime = 7200; + } + } + } + + Offset += sizeof (IP6_PREFIX_INFO_OPTION); + break; + case Ip6OptionMtu: + NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption); + + // + // Option size validity ensured by Ip6IsNDOptionValid(). + // + ASSERT (MTUOption.Length == 1); + ASSERT (Offset + (UINT32) MTUOption.Length * 8 <= (UINT32) Head->PayloadLength); + + // + // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order + // to omit implementation of Path MTU Discovery. Thus ignore the MTU option + // in Router Advertisement. + // + + Offset += sizeof (IP6_MTU_OPTION); + break; + default: + // + // Silently ignore unrecognized options + // + NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length); + + ASSERT (Length != 0); + + Offset += (UINT32) Length * 8; + break; + } + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Process the ICMPv6 redirect message. Find the instance, then update + its route cache. + + @param[in] IpSb The IP6 service binding instance that received + the packet. + @param[in] Head The IP head of the received ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 redirect packet with + the IP head removed. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS Successfully updated the route caches. + +**/ +EFI_STATUS +Ip6ProcessRedirect ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ) +{ + IP6_ICMP_INFORMATION_HEAD *Icmp; + EFI_IPv6_ADDRESS *Target; + EFI_IPv6_ADDRESS *IcmpDest; + UINT8 *Option; + UINT16 OptionLen; + IP6_ROUTE_ENTRY *RouteEntry; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + IP6_NEIGHBOR_ENTRY *NeighborCache; + INT32 Length; + UINT8 OptLen; + IP6_ETHER_ADDR_OPTION *LinkLayerOption; + EFI_MAC_ADDRESS Mac; + UINT32 Index; + BOOLEAN IsRouter; + EFI_STATUS Status; + INTN Result; + + Status = EFI_INVALID_PARAMETER; + + Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL); + if (Icmp == NULL) { + goto Exit; + } + + // + // Validate the incoming Redirect message + // + + // + // The IP Hop Limit field has a value of 255, i.e. the packet + // could not possibly have been forwarded by a router. + // ICMP Code is 0. + // ICMP length (derived from the IP length) is 40 or more octets. + // + if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 || + Head->PayloadLength < IP6_REDITECT_LENGTH) { + goto Exit; + } + + // + // The IP source address must be a link-local address + // + if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { + goto Exit; + } + + // + // The dest of this ICMP redirect message is not us. + // + if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { + goto Exit; + } + + // + // All included options have a length that is greater than zero. + // + OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH); + if (OptionLen != 0) { + Option = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL); + ASSERT (Option != NULL); + + if (!Ip6IsNDOptionValid (Option, OptionLen)) { + goto Exit; + } + } + + Target = (EFI_IPv6_ADDRESS *) (Icmp + 1); + IcmpDest = Target + 1; + + // + // The ICMP Destination Address field in the redirect message does not contain + // a multicast address. + // + if (IP6_IS_MULTICAST (IcmpDest)) { + goto Exit; + } + + // + // The ICMP Target Address is either a link-local address (when redirected to + // a router) or the same as the ICMP Destination Address (when redirected to + // the on-link destination). + // + IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest); + if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) { + goto Exit; + } + + // + // Check the options. The only interested option here is the target-link layer + // address option. + // + Length = Packet->TotalSize - 40; + Option = (UINT8 *) (IcmpDest + 1); + LinkLayerOption = NULL; + while (Length > 0) { + switch (*Option) { + case Ip6OptionEtherTarget: + + LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option; + OptLen = LinkLayerOption->Length; + if (OptLen != 1) { + // + // For ethernet, the length must be 1. + // + goto Exit; + } + break; + + default: + + OptLen = *(Option + 1); + if (OptLen == 0) { + // + // A length of 0 is invalid. + // + goto Exit; + } + break; + } + + Length -= 8 * OptLen; + Option += 8 * OptLen; + } + + if (Length != 0) { + goto Exit; + } + + // + // The IP source address of the Redirect is the same as the current + // first-hop router for the specified ICMP Destination Address. + // + RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress); + if (RouteCache != NULL) { + if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) { + // + // The source of this Redirect message must match the NextHop of the + // corresponding route cache entry. + // + goto Exit; + } + + // + // Update the NextHop. + // + IP6_COPY_ADDRESS (&RouteCache->NextHop, Target); + + if (!IsRouter) { + RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag; + RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE; + } + + } else { + // + // Get the Route Entry. + // + RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL); + if (RouteEntry == NULL) { + RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL); + if (RouteEntry == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + } + + if (!IsRouter) { + RouteEntry->Flag = IP6_DIRECT_ROUTE; + } + + // + // Create a route cache for this. + // + RouteCache = Ip6CreateRouteCacheEntry ( + IcmpDest, + &Head->DestinationAddress, + Target, + (UINTN) RouteEntry + ); + if (RouteCache == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Exit; + } + + // + // Insert the newly created route cache entry. + // + Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress); + InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link); + } + + // + // Try to locate the neighbor cache for the Target. + // + NeighborCache = Ip6FindNeighborEntry (IpSb, Target); + + if (LinkLayerOption != NULL) { + if (NeighborCache == NULL) { + // + // Create a neighbor cache for the Target. + // + ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS)); + CopyMem (&Mac, LinkLayerOption->EtherAddr, 6); + NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac); + if (NeighborCache == NULL) { + // + // Just report a success here. The neighbor cache can be created in + // some other place. + // + Status = EFI_SUCCESS; + goto Exit; + } + + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } else { + Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6); + + // + // If the link-local address is the same as that already in the cache, + // the cache entry's state remains unchanged. Otherwise update the + // reachability state to STALE. + // + if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) { + CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6); + + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + + if (NeighborCache->State == EfiNeighborInComplete) { + // + // Send queued packets if exist. + // + NeighborCache->State = EfiNeighborStale; + NeighborCache->CallBack ((VOID *) NeighborCache); + } else { + NeighborCache->State = EfiNeighborStale; + } + } + } + } + + if (NeighborCache != NULL && IsRouter) { + // + // The Target is a router, set IsRouter to TRUE. + // + NeighborCache->IsRouter = TRUE; + } + + Status = EFI_SUCCESS; + +Exit: + NetbufFree (Packet); + return Status; +} + +/** + Add Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been added. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache + due to insufficient resources. + @retval EFI_NOT_FOUND TargetLinkAddress is NULL. + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when DeleteFlag + is FALSE). + +**/ +EFI_STATUS +Ip6AddNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor != NULL) { + if (!Override) { + return EFI_ACCESS_DENIED; + } else { + if (TargetLinkAddress != NULL) { + IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress); + } + } + } else { + if (TargetLinkAddress == NULL) { + return EFI_NOT_FOUND; + } + + Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress); + if (Neighbor == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + Neighbor->State = EfiNeighborReachable; + + if (Timeout != 0) { + Neighbor->Ticks = IP6_GET_TICKS (Timeout / TICKS_PER_MS); + Neighbor->Dynamic = TRUE; + } else { + Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + + return EFI_SUCCESS; +} + +/** + Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache. + +**/ +EFI_STATUS +Ip6DelNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ) +{ + IP6_NEIGHBOR_ENTRY *Neighbor; + + Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address); + if (Neighbor == NULL) { + return EFI_NOT_FOUND; + } + + RemoveEntryList (&Neighbor->Link); + FreePool (Neighbor); + + return EFI_SUCCESS; +} + +/** + The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds. + This time routine handles DAD module and neighbor state transition. + It is also responsible for sending out router solicitations. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6NdFasterTimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + LIST_ENTRY *Entry2; + IP6_INTERFACE *IpIf; + IP6_DELAY_JOIN_LIST *DelayNode; + EFI_IPv6_ADDRESS Source; + IP6_DAD_ENTRY *DupAddrDetect; + EFI_STATUS Status; + IP6_NEIGHBOR_ENTRY *NeighborCache; + EFI_IPv6_ADDRESS Destination; + IP6_SERVICE *IpSb; + BOOLEAN Flag; + + IpSb = (IP6_SERVICE *) Context; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS)); + + // + // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router + // Solicitation messages, each separated by at least + // RTR_SOLICITATION_INTERVAL (4) seconds. + // + if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) && + !IpSb->RouterAdvertiseReceived && + IpSb->SolicitTimer > 0 + ) { + if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) { + Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL); + if (!EFI_ERROR (Status)) { + IpSb->SolicitTimer--; + IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL); + } + } + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + // + // Process the delay list to join the solicited-node multicast address. + // + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) { + DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link); + if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) { + // + // The timer expires, init the duplicate address detection. + // + Ip6InitDADProcess ( + DelayNode->Interface, + DelayNode->AddressInfo, + DelayNode->DadCallback, + DelayNode->Context + ); + + // + // Remove the delay node + // + RemoveEntryList (&DelayNode->Link); + FreePool (DelayNode); + } + } + + // + // Process the duplicate address detection list. + // + NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) { + DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link); + + if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) { + // + // The timer expires, check the remaining transmit counts. + // + if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) { + // + // Send the Neighbor Solicitation message with + // Source - unspecified address, destination - solicited-node multicast address + // Target - the address to be validated + // + Status = Ip6SendNeighborSolicit ( + IpSb, + NULL, + &DupAddrDetect->Destination, + &DupAddrDetect->AddressInfo->Address, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + + DupAddrDetect->Transmit++; + DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer); + } else { + // + // All required solicitation has been sent out, and the RetransTime after the last + // Neighbor Solicit is elapsed, finish the DAD process. + // + Flag = FALSE; + if ((DupAddrDetect->Receive == 0) || + (DupAddrDetect->Transmit <= DupAddrDetect->Receive)) { + Flag = TRUE; + } + + Ip6OnDADFinished (Flag, IpIf, DupAddrDetect); + } + } + } + } + + // + // Polling the state of Neighbor cache + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) { + NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); + + switch (NeighborCache->State) { + case EfiNeighborInComplete: + if (NeighborCache->Ticks > 0) { + --NeighborCache->Ticks; + } + + // + // Retransmit Neighbor Solicitation messages approximately every + // RetransTimer milliseconds while awaiting a response. + // + if (NeighborCache->Ticks == 0) { + if (NeighborCache->Transmit > 1) { + // + // Send out multicast neighbor solicitation for address resolution. + // After last neighbor solicitation message has been sent out, wait + // for RetransTimer and then remove entry if no response is received. + // + Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination); + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &Destination, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + } + + // + // Update the retransmit times. + // + if (NeighborCache->Transmit > 0) { + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + } + } + + if (NeighborCache->Transmit == 0) { + // + // Timeout, send ICMP destination unreachable packet and then remove entry + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + TRUE, + TRUE, + EFI_ICMP_ERROR, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } + + break; + + case EfiNeighborReachable: + // + // This entry is inserted by EfiIp6Neighbors() as static entry + // and will not timeout. + // + if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) { + break; + } + + if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) { + if (NeighborCache->Dynamic) { + // + // This entry is inserted by EfiIp6Neighbors() as dynamic entry + // and will be deleted after timeout. + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + FALSE, + TRUE, + EFI_TIMEOUT, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } else { + NeighborCache->State = EfiNeighborStale; + NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; + } + } + + break; + + case EfiNeighborDelay: + if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) { + + NeighborCache->State = EfiNeighborProbe; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1; + // + // Send out unicast neighbor solicitation for Neighbor Unreachability Detection + // + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &NeighborCache->Neighbor, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + + NeighborCache->Transmit--; + } + + break; + + case EfiNeighborProbe: + if (NeighborCache->Ticks > 0) { + --NeighborCache->Ticks; + } + + // + // Retransmit Neighbor Solicitation messages approximately every + // RetransTimer milliseconds while awaiting a response. + // + if (NeighborCache->Ticks == 0) { + if (NeighborCache->Transmit > 1) { + // + // Send out unicast neighbor solicitation for Neighbor Unreachability + // Detection. After last neighbor solicitation message has been sent out, + // wait for RetransTimer and then remove entry if no response is received. + // + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &NeighborCache->Neighbor, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return; + } + } + + // + // Update the retransmit times. + // + if (NeighborCache->Transmit > 0) { + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer); + } + } + + if (NeighborCache->Transmit == 0) { + // + // Delete the neighbor entry. + // + Status = Ip6FreeNeighborEntry ( + IpSb, + NeighborCache, + FALSE, + TRUE, + EFI_TIMEOUT, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + return; + } + } + + break; + + default: + break; + } + } +} + +/** + The heartbeat timer of ND module in 1 second. This time routine handles following + things: 1) maintain default router list; 2) maintain prefix options; + 3) maintain route caches. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6NdTimerTicking ( + IN IP6_SERVICE *IpSb + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_DEFAULT_ROUTER *DefaultRouter; + IP6_PREFIX_LIST_ENTRY *PrefixOption; + UINT8 Index; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + + // + // Decrease the lifetime of default router, if expires remove it from default router list. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) { + DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link); + if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) { + if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) { + Ip6DestroyDefaultRouter (IpSb, DefaultRouter); + } + } + } + + // + // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) { + PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { + if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) { + if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) && + (PrefixOption->PreferredLifetime > 0) + ) { + --PrefixOption->PreferredLifetime; + } + } else { + Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE); + } + } + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) { + PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { + if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) { + Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE); + } + } + } + + // + // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries. + // Remove the entries at the tail of the bucket. These entries + // are likely to be used least. + // Reclaim frequency is set to 1 second. + // + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) { + Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]); + if (Entry == NULL) { + break; + } + + RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + Ip6FreeRouteCacheEntry (RouteCache); + ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0); + IpSb->RouteTable->Cache.CacheNum[Index]--; + } + } +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Nd.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Nd.h new file mode 100644 index 00000000..cdfe0d6d --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Nd.h @@ -0,0 +1,756 @@ +/** @file + Definition of Neighbor Discovery support routines. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_ND_H__ +#define __EFI_IP6_ND_H__ + +#define IP6_GET_TICKS(Ms) (((Ms) + IP6_TIMER_INTERVAL_IN_MS - 1) / IP6_TIMER_INTERVAL_IN_MS) + +enum { + IP6_INF_ROUTER_LIFETIME = 0xFFFF, + + IP6_MAX_RTR_SOLICITATION_DELAY = 1000, ///< 1000 milliseconds + IP6_MAX_RTR_SOLICITATIONS = 3, + IP6_RTR_SOLICITATION_INTERVAL = 4000, + + IP6_MIN_RANDOM_FACTOR_SCALED = 1, + IP6_MAX_RANDOM_FACTOR_SCALED = 3, + IP6_RANDOM_FACTOR_SCALE = 2, + + IP6_MAX_MULTICAST_SOLICIT = 3, + IP6_MAX_UNICAST_SOLICIT = 3, + IP6_MAX_ANYCAST_DELAY_TIME = 1, + IP6_MAX_NEIGHBOR_ADV = 3, + IP6_REACHABLE_TIME = 30000, + IP6_RETRANS_TIMER = 1000, + IP6_DELAY_FIRST_PROBE_TIME = 5000, + + IP6_MIN_LINK_MTU = 1280, + IP6_MAX_LINK_MTU = 1500, + + IP6_IS_ROUTER_FLAG = 0x80, + IP6_SOLICITED_FLAG = 0x40, + IP6_OVERRIDE_FLAG = 0x20, + + IP6_M_ADDR_CONFIG_FLAG = 0x80, + IP6_O_CONFIG_FLAG = 0x40, + + IP6_ON_LINK_FLAG = 0x80, + IP6_AUTO_CONFIG_FLAG = 0x40, + + IP6_ND_LENGTH = 24, + IP6_RA_LENGTH = 16, + IP6_REDITECT_LENGTH = 40, + IP6_DAD_ENTRY_SIGNATURE = SIGNATURE_32 ('I', 'P', 'D', 'E') +}; + +typedef +VOID +(*IP6_ARP_CALLBACK) ( + VOID *Context + ); + +typedef struct _IP6_OPTION_HEADER { + UINT8 Type; + UINT8 Length; +} IP6_OPTION_HEADER; + +STATIC_ASSERT (sizeof (IP6_OPTION_HEADER) == 2, "IP6_OPTION_HEADER is expected to be exactly 2 bytes long."); + +typedef struct _IP6_ETHE_ADDR_OPTION { + UINT8 Type; + UINT8 Length; + UINT8 EtherAddr[6]; +} IP6_ETHER_ADDR_OPTION; + +STATIC_ASSERT (sizeof (IP6_ETHER_ADDR_OPTION) == 8, "IP6_ETHER_ADDR_OPTION is expected to be exactly 8 bytes long."); + +typedef struct _IP6_MTU_OPTION { + UINT8 Type; + UINT8 Length; + UINT16 Reserved; + UINT32 Mtu; +} IP6_MTU_OPTION; + +STATIC_ASSERT (sizeof (IP6_MTU_OPTION) == 8, "IP6_MTU_OPTION is expected to be exactly 8 bytes long."); + +typedef struct _IP6_PREFIX_INFO_OPTION { + UINT8 Type; + UINT8 Length; + UINT8 PrefixLength; + UINT8 Reserved1; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; + UINT32 Reserved2; + EFI_IPv6_ADDRESS Prefix; +} IP6_PREFIX_INFO_OPTION; + +STATIC_ASSERT (sizeof (IP6_PREFIX_INFO_OPTION) == 32, "IP6_PREFIX_INFO_OPTION is expected to be exactly 32 bytes long."); + +typedef +VOID +(*IP6_DAD_CALLBACK) ( + IN BOOLEAN IsDadPassed, + IN EFI_IPv6_ADDRESS *TargetAddress, + IN VOID *Context + ); + +typedef struct _IP6_DAD_ENTRY { + UINT32 Signature; + LIST_ENTRY Link; + UINT32 MaxTransmit; + UINT32 Transmit; + UINT32 Receive; + UINT32 RetransTick; + IP6_ADDRESS_INFO *AddressInfo; + EFI_IPv6_ADDRESS Destination; + IP6_DAD_CALLBACK Callback; + VOID *Context; +} IP6_DAD_ENTRY; + +typedef struct _IP6_DELAY_JOIN_LIST { + LIST_ENTRY Link; + UINT32 DelayTime; ///< in tick per 50 milliseconds + IP6_INTERFACE *Interface; + IP6_ADDRESS_INFO *AddressInfo; + IP6_DAD_CALLBACK DadCallback; + VOID *Context; +} IP6_DELAY_JOIN_LIST; + +typedef struct _IP6_NEIGHBOR_ENTRY { + LIST_ENTRY Link; + LIST_ENTRY ArpList; + INTN RefCnt; + BOOLEAN IsRouter; + BOOLEAN ArpFree; + BOOLEAN Dynamic; + EFI_IPv6_ADDRESS Neighbor; + EFI_MAC_ADDRESS LinkAddress; + EFI_IP6_NEIGHBOR_STATE State; + UINT32 Transmit; + UINT32 Ticks; + + LIST_ENTRY Frames; + IP6_INTERFACE *Interface; + IP6_ARP_CALLBACK CallBack; +} IP6_NEIGHBOR_ENTRY; + +typedef struct _IP6_DEFAULT_ROUTER { + LIST_ENTRY Link; + INTN RefCnt; + UINT16 Lifetime; + EFI_IPv6_ADDRESS Router; + IP6_NEIGHBOR_ENTRY *NeighborCache; +} IP6_DEFAULT_ROUTER; + +typedef struct _IP6_PREFIX_LIST_ENTRY { + LIST_ENTRY Link; + INTN RefCnt; + UINT32 ValidLifetime; + UINT32 PreferredLifetime; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS Prefix; +} IP6_PREFIX_LIST_ENTRY; + +/** + Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number + of EFI_IP6_NEIGHBOR_CACHE is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] NeighborCount The number of returned neighbor cache entries. + @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE. + + @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiNeighborCache ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *NeighborCount, + OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache + ); + +/** + Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number + of prefix entries is also returned. + + @param[in] IpInstance The pointer to IP6_PROTOCOL instance. + @param[out] PrefixCount The number of returned prefix entries. + @param[out] PrefixTable The pointer to the array of PrefixTable. + + @retval EFI_SUCCESS The prefix table successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table. + +**/ +EFI_STATUS +Ip6BuildPrefixTable ( + IN IP6_PROTOCOL *IpInstance, + OUT UINT32 *PrefixCount, + OUT EFI_IP6_ADDRESS_INFO **PrefixTable + ); + +/** + Allocate and initialize an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the default router. + @param[in] RouterLifetime The lifetime associated with the default + router, in units of seconds. + + @return NULL if it failed to allocate memory for the default router node. + Otherwise, point to the created default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6CreateDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN UINT16 RouterLifetime + ); + +/** + Destroy an IP6 default router entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER. + +**/ +VOID +Ip6DestroyDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN IP6_DEFAULT_ROUTER *DefaultRouter + ); + +/** + Clean an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + +**/ +VOID +Ip6CleanDefaultRouterList ( + IN IP6_SERVICE *IpSb + ); + +/** + Search a default router node from an IP6 default router list. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address The IPv6 address of the to be searched default router node. + + @return NULL if it failed to find the matching default router node. + Otherwise, point to the found default router node. + +**/ +IP6_DEFAULT_ROUTER * +Ip6FindDefaultRouter ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ); + +/** + The function to be called after DAD (Duplicate Address Detection) is performed. + + @param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed. + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] DadEntry The DAD entry which already performed DAD. + +**/ +VOID +Ip6OnDADFinished ( + IN BOOLEAN IsDadPassed, + IN IP6_INTERFACE *IpIf, + IN IP6_DAD_ENTRY *DadEntry + ); + +/** + Create a DAD (Duplicate Address Detection) entry and queue it to be performed. + + @param[in] IpIf Points to the IP6_INTERFACE. + @param[in] AddressInfo The address information which needs DAD performed. + @param[in] Callback The callback routine that will be called after DAD + is performed. This is an optional parameter that + may be NULL. + @param[in] Context The opaque parameter for a DAD callback routine. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The DAD entry was created and queued. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the + operation. + + +**/ +EFI_STATUS +Ip6InitDADProcess ( + IN IP6_INTERFACE *IpIf, + IN IP6_ADDRESS_INFO *AddressInfo, + IN IP6_DAD_CALLBACK Callback OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Search IP6_DAD_ENTRY from the Duplicate Address Detection List. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Target The address information which needs DAD performed . + @param[out] Interface If not NULL, output the IP6 interface that configures + the tentative address. + + @return NULL if failed to find the matching DAD entry. + Otherwise, point to the found DAD entry. + +**/ +IP6_DAD_ENTRY * +Ip6FindDADEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Target, + OUT IP6_INTERFACE **Interface OPTIONAL + ); + +/** + Allocate and initialize a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list. + Otherwise, it is created for the autoconfiguration prefix list. + @param[in] ValidLifetime The length of time in seconds that the prefix + is valid for the purpose of on-link determination. + @param[in] PreferredLifetime The length of time in seconds that addresses + generated from the prefix via stateless address + autoconfiguration remain preferred. + @param[in] PrefixLength The prefix length of the Prefix. + @param[in] Prefix The prefix address. + + @return NULL if it failed to allocate memory for the prefix node. Otherwise, point + to the created or existing prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6CreatePrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ); + +/** + Destroy a IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] PrefixEntry The to be destroyed prefix list entry. + @param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list. + Otherwise remove from autoconfiguration prefix list. + @param[in] ImmediateDelete If TRUE, remove the entry directly. + Otherwise, check the reference count to see whether + it should be removed. + +**/ +VOID +Ip6DestroyPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_PREFIX_LIST_ENTRY *PrefixEntry, + IN BOOLEAN OnLinkOrAuto, + IN BOOLEAN ImmediateDelete + ); + +/** + Search the list array to find an IP6 prefix list entry. + + @param[in] IpSb The pointer to IP6_SERVICE instance. + @param[in] OnLinkOrAuto If TRUE, the search the link prefix list, + Otherwise search the autoconfiguration prefix list. + @param[in] PrefixLength The prefix length of the Prefix + @param[in] Prefix The prefix address. + + @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the + pointer to the IP6 prefix list entry. + +**/ +IP6_PREFIX_LIST_ENTRY * +Ip6FindPrefixListEntry ( + IN IP6_SERVICE *IpSb, + IN BOOLEAN OnLinkOrAuto, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *Prefix + ); + +/** + Release the resource in prefix list table, and destroy the list entry and + corresponding addresses or route entries. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] ListHead The list entry head of the prefix list table. + +**/ +VOID +Ip6CleanPrefixListTable ( + IN IP6_SERVICE *IpSb, + IN LIST_ENTRY *ListHead + ); + +/** + Allocate and initialize an IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] CallBack The callback function to be called when + address resolution is finished. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + @param[in] LinkAddress Points to the MAC address of the neighbor. + Ignored if NULL. + + @return NULL if failed to allocate memory for the neighbor cache entry. + Otherwise, point to the created neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6CreateNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_ARP_CALLBACK CallBack, + IN EFI_IPv6_ADDRESS *Ip6Address, + IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL + ); + +/** + Search a IP6 neighbor cache entry. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] Ip6Address Points to the IPv6 address of the neighbor. + + @return NULL if it failed to find the matching neighbor cache entry. + Otherwise, point to the found neighbor cache entry. + +**/ +IP6_NEIGHBOR_ENTRY * +Ip6FindNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Ip6Address + ); + +/** + Free a IP6 neighbor cache entry and remove all the frames on the address + resolution queue that pass the FrameToCancel. That is, either FrameToCancel + is NULL, or it returns true for the frame. + + @param[in] IpSb The pointer to the IP6_SERVICE instance. + @param[in] NeighborCache The to be free neighbor cache entry. + @param[in] SendIcmpError If TRUE, send out ICMP error. + @param[in] FullFree If TRUE, remove the neighbor cache entry. + Otherwise remove the pending frames. + @param[in] IoStatus The status returned to the cancelled frames' + callback function. + @param[in] FrameToCancel Function to select which frame to cancel. + This is an optional parameter that may be NULL. + @param[in] Context Opaque parameter to the FrameToCancel. + Ignored if FrameToCancel is NULL. + + @retval EFI_INVALID_PARAMETER The input parameter is invalid. + @retval EFI_SUCCESS The operation finished successfully. + +**/ +EFI_STATUS +Ip6FreeNeighborEntry ( + IN IP6_SERVICE *IpSb, + IN IP6_NEIGHBOR_ENTRY *NeighborCache, + IN BOOLEAN SendIcmpError, + IN BOOLEAN FullFree, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Add Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been added. + @retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache + due to insufficient resources. + @retval EFI_NOT_FOUND TargetLinkAddress is NULL. + @retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache, + and that entry is tagged as un-overridden (when DeleteFlag + is FALSE). + +**/ +EFI_STATUS +Ip6AddNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors(). + + @param[in] IpSb The IP6 service binding instance. + @param[in] TargetIp6Address Pointer to Target IPv6 address. + @param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL. + @param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor + cache. It will be deleted after Timeout. A value of zero means that + the entry is permanent. A non-zero value means that the entry is + dynamic. + @param[in] Override If TRUE, the cached link-layer address of the matching entry will + be overridden and updated; if FALSE, and if a + corresponding cache entry already existed, EFI_ACCESS_DENIED + will be returned. + + @retval EFI_SUCCESS The neighbor cache entry has been updated or deleted. + @retval EFI_NOT_FOUND This entry is not in the neighbor cache. + +**/ +EFI_STATUS +Ip6DelNeighbor ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL, + IN UINT32 Timeout, + IN BOOLEAN Override + ); + +/** + Process the Neighbor Solicitation message. The message may be sent for Duplicate + Address Detection or Address Resolution. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Neighbor Advertisement message. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessNeighborAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the Router Advertisement message according to RFC4861. + + @param[in] IpSb The IP service that received the packet. + @param[in] Head The IP head of the message. + @param[in] Packet The content of the message with the IP head removed. + + @retval EFI_SUCCESS The packet processed successfully. + @retval EFI_INVALID_PARAMETER The packet is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation. + @retval Others Failed to process the packet. + +**/ +EFI_STATUS +Ip6ProcessRouterAdvertise ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Process the ICMPv6 redirect message. Find the instance, then update + its route cache. + + @param[in] IpSb The IP6 service binding instance that received + the packet. + @param[in] Head The IP head of the received ICMPv6 packet. + @param[in] Packet The content of the ICMPv6 redirect packet with + the IP head removed. + + @retval EFI_INVALID_PARAMETER The parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS Successfully updated the route caches. + +**/ +EFI_STATUS +Ip6ProcessRedirect ( + IN IP6_SERVICE *IpSb, + IN EFI_IP6_HEADER *Head, + IN NET_BUF *Packet + ); + +/** + Generate router solicit message and send it out to Destination Address or + All Router Link Local scope multicast address. + + @param[in] IpSb The IP service to send the packet. + @param[in] Interface If not NULL, points to the IP6 interface to send + the packet. + @param[in] SourceAddress If not NULL, the source address of the message. + @param[in] DestinationAddress If not NULL, the destination address of the message. + @param[in] SourceLinkAddress If not NULL, the MAC address of the source. + A source link-layer address option will be appended + to the message. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the operation. + @retval EFI_SUCCESS The router solicit message was successfully sent. + +**/ +EFI_STATUS +Ip6SendRouterSolicit ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL, + IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ); + +/** + Generate the Neighbor Solicitation message and send it to the Destination Address. + + @param[in] IpSb The IP service to send the packet + @param[in] SourceAddress The source address of the message. + @param[in] DestinationAddress The destination address of the message. + @param[in] TargetIp6Address The IP address of the target of the solicitation. + It must not be a multicast address. + @param[in] SourceLinkAddress The MAC address for the sender. If not NULL, + a source link-layer address option will be appended + to the message. + + @retval EFI_INVALID_PARAMETER Any input parameter is invalid. + @retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the + operation. + @retval EFI_SUCCESS The Neighbor Advertise message was successfully sent. + +**/ +EFI_STATUS +Ip6SendNeighborSolicit ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *SourceAddress, + IN EFI_IPv6_ADDRESS *DestinationAddress, + IN EFI_IPv6_ADDRESS *TargetIp6Address, + IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL + ); + +/** + Set the interface's address. This will trigger the DAD process for the + address to set. To set an already set address, the lifetimes wil be + updated to the new value passed in. + + @param[in] Interface The interface to set the address. + @param[in] Ip6Addr The interface's to be assigned IPv6 address. + @param[in] IsAnycast If TRUE, the unicast IPv6 address is anycast. + Otherwise, it is not anycast. + @param[in] PrefixLength The prefix length of the Ip6Addr. + @param[in] ValidLifetime The valid lifetime for this address. + @param[in] PreferredLifetime The preferred lifetime for this address. + @param[in] DadCallback The caller's callback to trigger when DAD finishes. + This is an optional parameter that may be NULL. + @param[in] Context The context that will be passed to DadCallback. + This is an optional parameter that may be NULL. + + @retval EFI_SUCCESS The interface is scheduled to be configured with + the specified address. + @retval EFI_OUT_OF_RESOURCES Failed to set the interface's address due to + lack of resources. + +**/ +EFI_STATUS +Ip6SetAddress ( + IN IP6_INTERFACE *Interface, + IN EFI_IPv6_ADDRESS *Ip6Addr, + IN BOOLEAN IsAnycast, + IN UINT8 PrefixLength, + IN UINT32 ValidLifetime, + IN UINT32 PreferredLifetime, + IN IP6_DAD_CALLBACK DadCallback OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds. + This time routine handles DAD module and neighbor state transition. + It is also responsible for sending out router solicitations. + + @param[in] Event The IP6 service instance's heartbeat timer. + @param[in] Context The IP6 service instance. + +**/ +VOID +EFIAPI +Ip6NdFasterTimerTicking ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + The heartbeat timer of ND module in 1 second. This time routine handles following + things: 1) maintain default router list; 2) maintain prefix options; + 3) maintain route caches. + + @param[in] IpSb The IP6 service binding instance. + +**/ +VOID +Ip6NdTimerTicking ( + IN IP6_SERVICE *IpSb + ); + +/** + Callback function when address resolution is finished. It will cancel + all the queued frames if the address resolution failed, or transmit them + if the request succeeded. + + @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY. + +**/ +VOID +Ip6OnArpResolved ( + IN VOID *Context + ); + +/** + Update the ReachableTime in IP6 service binding instance data, in milliseconds. + + @param[in, out] IpSb Points to the IP6_SERVICE. + +**/ +VOID +Ip6UpdateReachableTime ( + IN OUT IP6_SERVICE *IpSb + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6NvData.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6NvData.h new file mode 100644 index 00000000..be9572e1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6NvData.h @@ -0,0 +1,63 @@ +/** @file + NVData structure used by the IP6 configuration component. + + Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef _IP6_NV_DATA_H_ +#define _IP6_NV_DATA_H_ + +#include <Guid/Ip6ConfigHii.h> + +#define FORMID_MAIN_FORM 1 +#define FORMID_MANUAL_CONFIG_FORM 2 +#define FORMID_HEAD_FORM 3 + +#define IP6_POLICY_AUTO 0 +#define IP6_POLICY_MANUAL 1 +#define DAD_MAX_TRANSMIT_COUNT 10 + +#define KEY_INTERFACE_ID 0x101 +#define KEY_MANUAL_ADDRESS 0x102 +#define KEY_GATEWAY_ADDRESS 0x103 +#define KEY_DNS_ADDRESS 0x104 +#define KEY_SAVE_CHANGES 0x105 +#define KEY_SAVE_CONFIG_CHANGES 0x106 +#define KEY_IGNORE_CONFIG_CHANGES 0x107 +#define KEY_GET_CURRENT_SETTING 0x108 + +#define HOST_ADDRESS_LABEL 0x9000 +#define ROUTE_TABLE_LABEL 0xa000 +#define GATEWAY_ADDRESS_LABEL 0xb000 +#define DNS_ADDRESS_LABEL 0xc000 +#define LABEL_END 0xffff + +#define INTERFACE_ID_STR_MIN_SIZE 1 +#define INTERFACE_ID_STR_MAX_SIZE 23 +#define INTERFACE_ID_STR_STORAGE 25 +#define IP6_STR_MAX_SIZE 40 +#define ADDRESS_STR_MIN_SIZE 2 +#define ADDRESS_STR_MAX_SIZE 255 + +/// +/// IP6_CONFIG_IFR_NVDATA contains the IP6 configure +/// parameters for that NIC. +/// +#pragma pack(1) +typedef struct { + UINT8 IfType; ///< interface type + UINT8 Padding[3]; + UINT32 Policy; ///< manual or automatic + UINT32 DadTransmitCount; ///< dad transmits count + CHAR16 InterfaceId[INTERFACE_ID_STR_STORAGE]; ///< alternative interface id + CHAR16 ManualAddress[ADDRESS_STR_MAX_SIZE]; ///< IP addresses + CHAR16 GatewayAddress[ADDRESS_STR_MAX_SIZE]; ///< Gateway address + CHAR16 DnsAddress[ADDRESS_STR_MAX_SIZE]; ///< DNS server address +} IP6_CONFIG_IFR_NVDATA; +#pragma pack() + +#endif + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Option.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Option.c new file mode 100644 index 00000000..10d311c1 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Option.c @@ -0,0 +1,781 @@ +/** @file + IP6 option support functions and routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is malformatted. + + @param[in] IpSb The IP6 service data. + @param[in] Packet The to be validated packet. + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + @param[in] Pointer Identifies the octet offset within + the invoking packet where the error was detected. + + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformatted. + +**/ +BOOLEAN +Ip6IsOptionValid ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN UINT8 *Option, + IN UINT8 OptionLen, + IN UINT32 Pointer + ) +{ + UINT8 Offset; + UINT8 OptionType; + + Offset = 0; + + while (Offset < OptionLen) { + OptionType = *(Option + Offset); + + switch (OptionType) { + case Ip6OptionPad1: + // + // It is a Pad1 option + // + Offset++; + break; + case Ip6OptionPadN: + // + // It is a PadN option + // + Offset = (UINT8) (Offset + *(Option + Offset + 1) + 2); + break; + case Ip6OptionRouterAlert: + // + // It is a Router Alert Option + // + Offset += 4; + break; + default: + // + // The highest-order two bits specify the action must be taken if + // the processing IPv6 node does not recognize the option type. + // + switch (OptionType & Ip6OptionMask) { + case Ip6OptionSkip: + Offset = (UINT8) (Offset + *(Option + Offset + 1)); + break; + case Ip6OptionDiscard: + return FALSE; + case Ip6OptionParameterProblem: + Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 2, + &Pointer + ); + return FALSE; + case Ip6OptionMask: + if (!IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Pointer = Pointer + Offset + sizeof (EFI_IP6_HEADER); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 2, + &Pointer + ); + } + + return FALSE; + break; + } + + break; + } + + } + + return TRUE; +} + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It supports the defined options in Neighbor + Discovery messages. + + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformatted. + +**/ +BOOLEAN +Ip6IsNDOptionValid ( + IN UINT8 *Option, + IN UINT16 OptionLen + ) +{ + UINT32 Offset; + UINT16 Length; + IP6_OPTION_HEADER *OptionHeader; + + if (Option == NULL) { + ASSERT (Option != NULL); + return FALSE; + } + + Offset = 0; + + // + // RFC 4861 states that Neighbor Discovery packet can contain zero or more + // options. Start processing the options if at least Type + Length fields + // fit within the input buffer. + // + while (Offset + sizeof (IP6_OPTION_HEADER) - 1 < OptionLen) { + OptionHeader = (IP6_OPTION_HEADER*) (Option + Offset); + Length = (UINT16) OptionHeader->Length * 8; + + switch (OptionHeader->Type) { + case Ip6OptionPrefixInfo: + if (Length != 32) { + return FALSE; + } + break; + + case Ip6OptionMtu: + if (Length != 8) { + return FALSE; + } + break; + + default: + // RFC 4861 states that Length field cannot be 0. + if (Length == 0) { + return FALSE; + } + break; + } + + // + // Check whether recognized options are within the input buffer's scope. + // + switch (OptionHeader->Type) { + case Ip6OptionEtherSource: + case Ip6OptionEtherTarget: + case Ip6OptionPrefixInfo: + case Ip6OptionRedirected: + case Ip6OptionMtu: + if (Offset + Length > (UINT32) OptionLen) { + return FALSE; + } + break; + + default: + // + // Unrecognized options can be either valid (but unused) or invalid + // (garbage in between or right after valid options). Silently ignore. + // + break; + } + + // + // Advance to the next option. + // Length already considers option header's Type + Length. + // + Offset += Length; + } + + return TRUE; +} + + +/** + Validate whether the NextHeader is a known valid protocol or one of the user configured + protocols from the upper layer. + + @param[in] IpSb The IP6 service instance. + @param[in] NextHeader The next header field. + + @retval TRUE The NextHeader is a known valid protocol or user configured. + @retval FALSE The NextHeader is not a known valid protocol. + +**/ +BOOLEAN +Ip6IsValidProtocol ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader + ) +{ + LIST_ENTRY *Entry; + IP6_PROTOCOL *IpInstance; + + if (NextHeader == EFI_IP_PROTO_TCP || + NextHeader == EFI_IP_PROTO_UDP || + NextHeader == IP6_ICMP || + NextHeader == IP6_ESP + ) { + return TRUE; + } + + if (IpSb == NULL) { + return FALSE; + } + + if (IpSb->Signature != IP6_SERVICE_SIGNATURE) { + return FALSE; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Children) { + IpInstance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); + if (IpInstance->State == IP6_STATE_CONFIGED) { + if (IpInstance->ConfigData.DefaultProtocol == NextHeader) { + return TRUE; + } + } + } + + return FALSE; +} + +/** + Validate the IP6 extension header format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is mal-formatted. + + @param[in] IpSb The IP6 service instance. This is an optional parameter. + @param[in] Packet The data of the packet. Ignored if NULL. + @param[in] NextHeader The next header field in IPv6 basic header. + @param[in] ExtHdrs The first byte of the option. + @param[in] ExtHdrsLen The length of the whole option. + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise, the option we want to transmit. + @param[out] FormerHeader The offset of NextHeader which points to Fragment + Header when we received, of the ExtHdrs. + Ignored if we transmit. + @param[out] LastHeader The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] RealExtsLen The length of extension headers processed by IP6 layer. + This is an optional parameter that may be NULL. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + This is an optional parameter that may be NULL. + @param[out] Fragmented Indicate whether the packet is fragmented. + This is an optional parameter that may be NULL. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformatted. + +**/ +BOOLEAN +Ip6IsExtsValid ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN NET_BUF *Packet OPTIONAL, + IN UINT8 *NextHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN BOOLEAN Rcvd, + OUT UINT32 *FormerHeader OPTIONAL, + OUT UINT8 **LastHeader, + OUT UINT32 *RealExtsLen OPTIONAL, + OUT UINT32 *UnFragmentLen OPTIONAL, + OUT BOOLEAN *Fragmented OPTIONAL + ) +{ + UINT32 Pointer; + UINT32 Offset; + UINT8 *Option; + UINT8 OptionLen; + BOOLEAN Flag; + UINT8 CountD; + UINT8 CountA; + IP6_FRAGMENT_HEADER *FragmentHead; + UINT16 FragmentOffset; + IP6_ROUTING_HEADER *RoutingHead; + + if (RealExtsLen != NULL) { + *RealExtsLen = 0; + } + + if (UnFragmentLen != NULL) { + *UnFragmentLen = 0; + } + + if (Fragmented != NULL) { + *Fragmented = FALSE; + } + + *LastHeader = NextHeader; + + if (ExtHdrs == NULL && ExtHdrsLen == 0) { + return TRUE; + } + + if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) { + return FALSE; + } + + Pointer = 0; + Offset = 0; + Flag = FALSE; + CountD = 0; + CountA = 0; + + while (Offset <= ExtHdrsLen) { + + switch (*NextHeader) { + case IP6_HOP_BY_HOP: + if (Offset != 0) { + if (!Rcvd) { + return FALSE; + } + // + // Hop-by-Hop Options header is restricted to appear immediately after an IPv6 header only. + // If not, generate a ICMP parameter problem message with code value of 1. + // + if (Pointer == 0) { + Pointer = sizeof (EFI_IP6_HEADER); + } else { + Pointer = Offset + sizeof (EFI_IP6_HEADER); + } + + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 1, + &Pointer + ); + } + return FALSE; + } + + Flag = TRUE; + + // + // Fall through + // + case IP6_DESTINATION: + if (*NextHeader == IP6_DESTINATION) { + CountD++; + } + + if (CountD > 2) { + return FALSE; + } + + NextHeader = ExtHdrs + Offset; + Pointer = Offset; + + Offset++; + Option = ExtHdrs + Offset; + OptionLen = (UINT8) ((*Option + 1) * 8 - 2); + Option++; + Offset++; + + if (IpSb != NULL && Packet != NULL && !Ip6IsOptionValid (IpSb, Packet, Option, OptionLen, Offset)) { + return FALSE; + } + + Offset = Offset + OptionLen; + + if (Flag) { + if (UnFragmentLen != NULL) { + *UnFragmentLen = Offset; + } + + Flag = FALSE; + } + + break; + + case IP6_ROUTING: + NextHeader = ExtHdrs + Offset; + RoutingHead = (IP6_ROUTING_HEADER *) NextHeader; + + // + // Type 0 routing header is defined in RFC2460 and deprecated in RFC5095. + // Thus all routing types are processed as unrecognized. + // + if (RoutingHead->SegmentsLeft == 0) { + // + // Ignore the routing header and proceed to process the next header. + // + Offset = Offset + (RoutingHead->HeaderLen + 1) * 8; + + if (UnFragmentLen != NULL) { + *UnFragmentLen = Offset; + } + + } else { + // + // Discard the packet and send an ICMP Parameter Problem, Code 0, message + // to the packet's source address, pointing to the unrecognized routing + // type. + // + Pointer = Offset + 2 + sizeof (EFI_IP6_HEADER); + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 0, + &Pointer + ); + } + + return FALSE; + } + + break; + + case IP6_FRAGMENT: + + // + // RFC2402, AH header should after fragment header. + // + if (CountA > 1) { + return FALSE; + } + + // + // RFC2460, ICMP Parameter Problem message with code 0 should be sent + // if the length of a fragment is not a multiple of 8 octets and the M + // flag of that fragment is 1, pointing to the Payload length field of the + // fragment packet. + // + if (IpSb != NULL && Packet != NULL && (ExtHdrsLen % 8) != 0) { + // + // Check whether it is the last fragment. + // + FragmentHead = (IP6_FRAGMENT_HEADER *) (ExtHdrs + Offset); + if (FragmentHead == NULL) { + return FALSE; + } + + FragmentOffset = NTOHS (FragmentHead->FragmentOffset); + + if (((FragmentOffset & 0x1) == 0x1) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Pointer = sizeof (UINT32); + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 0, + &Pointer + ); + return FALSE; + } + } + + if (Fragmented != NULL) { + *Fragmented = TRUE; + } + + if (Rcvd && FormerHeader != NULL) { + *FormerHeader = (UINT32) (NextHeader - ExtHdrs); + } + + NextHeader = ExtHdrs + Offset; + Offset = Offset + 8; + break; + + case IP6_AH: + if (++CountA > 1) { + return FALSE; + } + + Option = ExtHdrs + Offset; + NextHeader = Option; + Option++; + // + // RFC2402, Payload length is specified in 32-bit words, minus "2". + // + OptionLen = (UINT8) ((*Option + 2) * 4); + Offset = Offset + OptionLen; + break; + + case IP6_NO_NEXT_HEADER: + *LastHeader = NextHeader; + return FALSE; + break; + + default: + if (Ip6IsValidProtocol (IpSb, *NextHeader)) { + + *LastHeader = NextHeader; + + if (RealExtsLen != NULL) { + *RealExtsLen = Offset; + } + + return TRUE; + } + + // + // The Next Header value is unrecognized by the node, discard the packet and + // send an ICMP parameter problem message with code value of 1. + // + if (Offset == 0) { + // + // The Next Header directly follows IPv6 basic header. + // + Pointer = 6; + } else { + if (Pointer == 0) { + Pointer = sizeof (EFI_IP6_HEADER); + } else { + Pointer = Offset + sizeof (EFI_IP6_HEADER); + } + } + + if ((IpSb != NULL) && (Packet != NULL) && + !IP6_IS_MULTICAST (&Packet->Ip.Ip6->DestinationAddress)) { + Ip6SendIcmpError ( + IpSb, + Packet, + NULL, + &Packet->Ip.Ip6->SourceAddress, + ICMP_V6_PARAMETER_PROBLEM, + 1, + &Pointer + ); + } + return FALSE; + } + } + + *LastHeader = NextHeader; + + if (RealExtsLen != NULL) { + *RealExtsLen = Offset; + } + + return TRUE; +} + +/** + Generate an IPv6 router alert option in network order and output it through Buffer. + + @param[out] Buffer Points to a buffer to record the generated option. + @param[in, out] BufferLen The length of Buffer, in bytes. + @param[in] NextHeader The 8-bit selector indicates the type of header + immediately following the Hop-by-Hop Options header. + + @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated + option. BufferLen is updated for the required size. + + @retval EFI_SUCCESS The option is generated and filled in to Buffer. + +**/ +EFI_STATUS +Ip6FillHopByHop ( + OUT UINT8 *Buffer, + IN OUT UINTN *BufferLen, + IN UINT8 NextHeader + ) +{ + UINT8 BufferArray[8]; + + if (*BufferLen < 8) { + *BufferLen = 8; + return EFI_BUFFER_TOO_SMALL; + } + + // + // Form the Hop-By-Hop option in network order. + // NextHeader (1 octet) + HdrExtLen (1 octet) + RouterAlertOption(4 octets) + PadN + // The Hdr Ext Len is the length in 8-octet units, and does not including the first 8 octets. + // + ZeroMem (BufferArray, sizeof (BufferArray)); + BufferArray[0] = NextHeader; + BufferArray[2] = 0x5; + BufferArray[3] = 0x2; + BufferArray[6] = 1; + + CopyMem (Buffer, BufferArray, sizeof (BufferArray)); + return EFI_SUCCESS; +} + +/** + Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] NextHeader The extension header type of first extension header. + @param[in] LastHeader The extension header type of last extension header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted. + It's caller's responsibility to free this buffer. + + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of + resource. + @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not + supported currently. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6FillFragmentHeader ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader, + IN UINT8 LastHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT16 FragmentOffset, + OUT UINT8 **UpdatedExtHdrs + ) +{ + UINT32 Length; + UINT8 *Buffer; + UINT32 FormerHeader; + UINT32 Offset; + UINT32 Part1Len; + UINT32 HeaderLen; + UINT8 Current; + IP6_FRAGMENT_HEADER FragmentHead; + + if (UpdatedExtHdrs == NULL) { + return EFI_INVALID_PARAMETER; + } + + Length = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + Buffer = AllocatePool (Length); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Offset = 0; + Part1Len = 0; + FormerHeader = 0; + Current = NextHeader; + + while ((ExtHdrs != NULL) && (Offset <= ExtHdrsLen)) { + switch (NextHeader) { + case IP6_ROUTING: + case IP6_HOP_BY_HOP: + case IP6_DESTINATION: + Current = NextHeader; + NextHeader = *(ExtHdrs + Offset); + + if ((Current == IP6_DESTINATION) && (NextHeader != IP6_ROUTING)) { + // + // Destination Options header should occur at most twice, once before + // a Routing header and once before the upper-layer header. Here we + // find the one before the upper-layer header. Insert the Fragment + // Header before it. + // + CopyMem (Buffer, ExtHdrs, Part1Len); + *(Buffer + FormerHeader) = IP6_FRAGMENT; + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + } + + + FormerHeader = Offset; + HeaderLen = (*(ExtHdrs + Offset + 1) + 1) * 8; + Part1Len = Part1Len + HeaderLen; + Offset = Offset + HeaderLen; + break; + + case IP6_FRAGMENT: + Current = NextHeader; + + if (Part1Len != 0) { + CopyMem (Buffer, ExtHdrs, Part1Len); + } + + *(Buffer + FormerHeader) = IP6_FRAGMENT; + + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + + case IP6_AH: + Current = NextHeader; + NextHeader = *(ExtHdrs + Offset); + // + // RFC2402, Payload length is specified in 32-bit words, minus "2". + // + HeaderLen = (*(ExtHdrs + Offset + 1) + 2) * 4; + Part1Len = Part1Len + HeaderLen; + Offset = Offset + HeaderLen; + break; + + default: + if (Ip6IsValidProtocol (IpSb, NextHeader)) { + Current = NextHeader; + CopyMem (Buffer, ExtHdrs, Part1Len); + *(Buffer + FormerHeader) = IP6_FRAGMENT; + // + // Exit the loop. + // + Offset = ExtHdrsLen + 1; + break; + } + + FreePool (Buffer); + return EFI_UNSUPPORTED; + } + } + + // + // Append the Fragment header. If the fragment offset indicates the fragment + // is the first fragment. + // + if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) { + FragmentHead.NextHeader = Current; + } else { + FragmentHead.NextHeader = LastHeader; + } + + FragmentHead.Reserved = 0; + FragmentHead.FragmentOffset = HTONS (FragmentOffset); + FragmentHead.Identification = mIp6Id; + + CopyMem (Buffer + Part1Len, &FragmentHead, sizeof (IP6_FRAGMENT_HEADER)); + + if ((ExtHdrs != NULL) && (Part1Len < ExtHdrsLen)) { + // + // Append the part2 (fragmentable part) of Extension headers + // + CopyMem ( + Buffer + Part1Len + sizeof (IP6_FRAGMENT_HEADER), + ExtHdrs + Part1Len, + ExtHdrsLen - Part1Len + ); + } + + *UpdatedExtHdrs = Buffer; + + return EFI_SUCCESS; +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Option.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Option.h new file mode 100644 index 00000000..6affc46f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Option.h @@ -0,0 +1,185 @@ +/** @file + Definition of IP6 option process routines. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_OPTION_H__ +#define __EFI_IP6_OPTION_H__ + +#define IP6_FRAGMENT_OFFSET_MASK (~0x3) + +typedef struct _IP6_FRAGMENT_HEADER { + UINT8 NextHeader; + UINT8 Reserved; + UINT16 FragmentOffset; + UINT32 Identification; +} IP6_FRAGMENT_HEADER; + +typedef struct _IP6_ROUTING_HEADER { + UINT8 NextHeader; + UINT8 HeaderLen; + UINT8 RoutingType; + UINT8 SegmentsLeft; +} IP6_ROUTING_HEADER; + +typedef enum { + Ip6OptionPad1 = 0, + Ip6OptionPadN = 1, + Ip6OptionRouterAlert = 5, + Ip6OptionSkip = 0, + Ip6OptionDiscard = 0x40, + Ip6OptionParameterProblem = 0x80, + Ip6OptionMask = 0xc0, + + Ip6OptionEtherSource = 1, + Ip6OptionEtherTarget = 2, + Ip6OptionPrefixInfo = 3, + Ip6OptionRedirected = 4, + Ip6OptionMtu = 5 +} IP6_OPTION_TYPE; + +/** + Validate the IP6 extension header format for both the packets we received + and that we will transmit. It will compute the ICMPv6 error message fields + if the option is mal-formatted. + + @param[in] IpSb The IP6 service instance. This is an optional parameter. + @param[in] Packet The data of the packet. Ignored if NULL. + @param[in] NextHeader The next header field in IPv6 basic header. + @param[in] ExtHdrs The first byte of the option. + @param[in] ExtHdrsLen The length of the whole option. + @param[in] Rcvd The option is from the packet we received if TRUE, + otherwise, the option we want to transmit. + @param[out] FormerHeader The offset of NextHeader which points to Fragment + Header when we received, of the ExtHdrs. + Ignored if we transmit. + @param[out] LastHeader The pointer of NextHeader of the last extension + header processed by IP6. + @param[out] RealExtsLen The length of extension headers processed by IP6 layer. + This is an optional parameter that may be NULL. + @param[out] UnFragmentLen The length of unfragmented length of extension headers. + This is an optional parameter that may be NULL. + @param[out] Fragmented Indicate whether the packet is fragmented. + This is an optional parameter that may be NULL. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformatted. + +**/ +BOOLEAN +Ip6IsExtsValid ( + IN IP6_SERVICE *IpSb OPTIONAL, + IN NET_BUF *Packet OPTIONAL, + IN UINT8 *NextHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN BOOLEAN Rcvd, + OUT UINT32 *FormerHeader OPTIONAL, + OUT UINT8 **LastHeader, + OUT UINT32 *RealExtsLen OPTIONAL, + OUT UINT32 *UnFragmentLen OPTIONAL, + OUT BOOLEAN *Fragmented OPTIONAL + ); + +/** + Generate an IPv6 router alert option in network order and output it through Buffer. + + @param[out] Buffer Points to a buffer to record the generated option. + @param[in, out] BufferLen The length of Buffer, in bytes. + @param[in] NextHeader The 8-bit selector indicates the type of header + immediately following the Hop-by-Hop Options header. + + @retval EFI_BUFFER_TOO_SMALL The Buffer is too small to contain the generated + option. BufferLen is updated for the required size. + + @retval EFI_SUCCESS The option is generated and filled in to Buffer. + +**/ +EFI_STATUS +Ip6FillHopByHop ( + OUT UINT8 *Buffer, + IN OUT UINTN *BufferLen, + IN UINT8 NextHeader + ); + +/** + Insert a Fragment Header to the Extension headers and output it in UpdatedExtHdrs. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] NextHeader The extension header type of first extension header. + @param[in] LastHeader The extension header type of last extension header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[out] UpdatedExtHdrs The updated ExtHdrs with Fragment header inserted. + It's caller's responsibility to free this buffer. + + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lake of + resource. + @retval EFI_UNSUPPORTED The extension header specified in ExtHdrs is not + supported currently. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6FillFragmentHeader ( + IN IP6_SERVICE *IpSb, + IN UINT8 NextHeader, + IN UINT8 LastHeader, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT16 FragmentOffset, + OUT UINT8 **UpdatedExtHdrs + ); + +/** + Copy the extension headers from the original to buffer. A Fragment header is + appended to the end. + + @param[in] NextHeader The 8-bit selector indicates the type of + the fragment header's next header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] LastHeader The pointer of next header of last extension header. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[in] UnFragmentHdrLen The length of unfragmented length of extension headers. + @param[in, out] Buf The buffer to copy options to. + @param[in, out] BufLen The length of the buffer. + + @retval EFI_SUCCESS The options are copied over. + @retval EFI_BUFFER_TOO_SMALL The buffer caller provided is too small. + +**/ +EFI_STATUS +Ip6CopyExts ( + IN UINT8 NextHeader, + IN UINT8 *ExtHdrs, + IN UINT8 *LastHeader, + IN UINT16 FragmentOffset, + IN UINT32 UnFragmentHdrLen, + IN OUT UINT8 *Buf, + IN OUT UINT32 *BufLen + ); + +/** + Validate the IP6 option format for both the packets we received + and that we will transmit. It supports the defined options in Neighbor + Discovery messages. + + @param[in] Option The first byte of the option. + @param[in] OptionLen The length of the whole option. + + @retval TRUE The option is properly formatted. + @retval FALSE The option is malformatted. + +**/ +BOOLEAN +Ip6IsNDOptionValid ( + IN UINT8 *Option, + IN UINT16 OptionLen + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Output.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Output.c new file mode 100644 index 00000000..7d2337ae --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Output.c @@ -0,0 +1,1085 @@ +/** @file + The internal functions and routines to transmit the IP6 packet. + + Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +UINT32 mIp6Id; + +/** + Output all the available source addresses to a list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to an IP6 service binding instance. + @param[out] SourceList The list entry head of all source addresses. + It is the caller's responsibility to free the + resources. + @param[out] SourceCount The number of source addresses. + + @retval EFI_SUCCESS The source addresses were copied to a list entry head + SourceList. + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources to complete the operation. + +**/ +EFI_STATUS +Ip6CandidateSource ( + IN IP6_SERVICE *IpSb, + OUT LIST_ENTRY *SourceList, + OUT UINT32 *SourceCount + ) +{ + IP6_INTERFACE *IpIf; + LIST_ENTRY *Entry; + LIST_ENTRY *Entry2; + IP6_ADDRESS_INFO *AddrInfo; + IP6_ADDRESS_INFO *Copy; + + *SourceCount = 0; + + if (IpSb->LinkLocalOk) { + Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO)); + if (Copy == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Copy->Signature = IP6_ADDR_INFO_SIGNATURE; + IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr); + Copy->IsAnycast = FALSE; + Copy->PrefixLength = IP6_LINK_LOCAL_PREFIX_LENGTH; + Copy->ValidLifetime = (UINT32) IP6_INFINIT_LIFETIME; + Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME; + + InsertTailList (SourceList, &Copy->Link); + (*SourceCount)++; + } + + NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) { + IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link); + + NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + if (AddrInfo->IsAnycast) { + // + // Never use an anycast address. + // + continue; + } + + Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo); + if (Copy == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + InsertTailList (SourceList, &Copy->Link); + (*SourceCount)++; + } + } + + return EFI_SUCCESS; +} + +/** + Calculate how many bits are the same between two IPv6 addresses. + + @param[in] AddressA Points to an IPv6 address. + @param[in] AddressB Points to another IPv6 address. + + @return The common bits of the AddressA and AddressB. + +**/ +UINT8 +Ip6CommonPrefixLen ( + IN EFI_IPv6_ADDRESS *AddressA, + IN EFI_IPv6_ADDRESS *AddressB + ) +{ + UINT8 Count; + UINT8 Index; + UINT8 ByteA; + UINT8 ByteB; + UINT8 NumBits; + + Count = 0; + Index = 0; + + while (Index < 16) { + ByteA = AddressA->Addr[Index]; + ByteB = AddressB->Addr[Index]; + + if (ByteA == ByteB) { + Count += 8; + Index++; + continue; + } + + // + // Check how many bits are common between the two bytes. + // + NumBits = 8; + ByteA = (UINT8) (ByteA ^ ByteB); + + while (ByteA != 0) { + NumBits--; + ByteA = (UINT8) (ByteA >> 1); + } + + return (UINT8) (Count + NumBits); + } + + return Count; +} + +/** + Output all the available source addresses to a list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Destination The IPv6 destination address. + @param[out] Source The selected IPv6 source address according to + the Destination. + + @retval EFI_SUCCESS The source addresses were copied to a list entry + head SourceList. + @retval EFI_NO_MAPPING The IPv6 stack is not auto configured. + +**/ +EFI_STATUS +Ip6SelectSourceAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + OUT EFI_IPv6_ADDRESS *Source + ) +{ + EFI_STATUS Status; + LIST_ENTRY SourceList; + UINT32 SourceCount; + UINT8 ScopeD; + LIST_ENTRY *Entry; + IP6_ADDRESS_INFO *AddrInfo; + IP6_PREFIX_LIST_ENTRY *Prefix; + UINT8 LastCommonLength; + UINT8 CurrentCommonLength; + EFI_IPv6_ADDRESS *TmpAddress; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + Status = EFI_SUCCESS; + InitializeListHead (&SourceList); + + if (!IpSb->LinkLocalOk) { + return EFI_NO_MAPPING; + } + + // + // Rule 1: Prefer same address. + // + if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) { + IP6_COPY_ADDRESS (Source, Destination); + goto Exit; + } + + // + // Rule 2: Prefer appropriate scope. + // + if (IP6_IS_MULTICAST (Destination)) { + ScopeD = (UINT8) (Destination->Addr[1] >> 4); + } else if (NetIp6IsLinkLocalAddr (Destination)) { + ScopeD = 0x2; + } else { + ScopeD = 0xE; + } + + if (ScopeD <= 0x2) { + // + // Return the link-local address if it exists + // One IP6_SERVICE only has one link-local address. + // + IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); + goto Exit; + } + + // + // All candidate source addresses are global unicast address. + // + Ip6CandidateSource (IpSb, &SourceList, &SourceCount); + + if (SourceCount == 0) { + Status = EFI_NO_MAPPING; + goto Exit; + } + + IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr); + + if (SourceCount == 1) { + goto Exit; + } + + // + // Rule 3: Avoid deprecated addresses. + // TODO: check the "deprecated" state of the stateful configured address + // + NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) { + Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); + if (Prefix->PreferredLifetime == 0) { + Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength); + + if (SourceCount == 1) { + goto Exit; + } + } + } + + // + // TODO: Rule 4: Prefer home addresses. + // TODO: Rule 5: Prefer outgoing interface. + // TODO: Rule 6: Prefer matching label. + // TODO: Rule 7: Prefer public addresses. + // + + // + // Rule 8: Use longest matching prefix. + // + LastCommonLength = Ip6CommonPrefixLen (Source, Destination); + TmpAddress = NULL; + + for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) { + AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE); + + CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination); + if (CurrentCommonLength > LastCommonLength) { + LastCommonLength = CurrentCommonLength; + TmpAddress = &AddrInfo->Address; + } + } + + if (TmpAddress != NULL) { + IP6_COPY_ADDRESS (Source, TmpAddress); + } + +Exit: + + Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0); + + return Status; +} + +/** + Select an interface to send the packet generated in the IP6 driver + itself: that is, not by the requests of the IP6 child's consumer. Such + packets include the ICMPv6 echo replies and other ICMPv6 error packets. + + @param[in] IpSb The IP4 service that wants to send the packets. + @param[in] Destination The destination of the packet. + @param[in, out] Source 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. + +**/ +IP6_INTERFACE * +Ip6SelectInterface ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + IN OUT EFI_IPv6_ADDRESS *Source + ) +{ + EFI_STATUS Status; + EFI_IPv6_ADDRESS SelectedSource; + IP6_INTERFACE *IpIf; + BOOLEAN Exist; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + ASSERT (Destination != NULL && Source != NULL); + + if (NetIp6IsUnspecifiedAddr (Destination)) { + return NULL; + } + + if (!NetIp6IsUnspecifiedAddr (Source)) { + Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL); + ASSERT (Exist); + + return IpIf; + } + + // + // If source is unspecified, select a source according to the destination. + // + Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource); + if (EFI_ERROR (Status)) { + return IpSb->DefaultInterface; + } + + Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL); + IP6_COPY_ADDRESS (Source, &SelectedSource); + + return IpIf; +} + +/** + The default callback function for the system generated packet. + It will free the packet. + + @param[in] Packet The packet that transmitted. + @param[in] IoStatus The result of the transmission, succeeded or failed. + @param[in] LinkFlag Not used when transmitted. Check IP6_FRAME_CALLBACK + for reference. + @param[in] Context The context provided by us. + +**/ +VOID +Ip6SysPacketSent ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ) +{ + NetbufFree (Packet); + Packet = NULL; +} + +/** + Prefix an IP6 basic head and unfragmentable extension headers and a fragment header + to the Packet. Used for IP6 fragmentation. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Packet The packet to prefix the IP6 header to. + @param[in] Head The caller supplied header. + @param[in] FragmentOffset The fragment offset of the data following the header. + @param[in] ExtHdrs The length of the original extension header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] LastHeader The pointer of next header of last extension header. + @param[in] HeadLen The length of the unfragmented part of the IP6 header. + + @retval EFI_BAD_BUFFER_SIZE There is no enough room in the head space of + Packet. + @retval EFI_SUCCESS The operation performed successfully. + +**/ +EFI_STATUS +Ip6PrependHead ( + IN IP6_SERVICE *IpSb, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT16 FragmentOffset, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN UINT8 LastHeader, + IN UINT32 HeadLen + ) +{ + UINT32 Len; + UINT32 UnFragExtHdrsLen; + EFI_IP6_HEADER *PacketHead; + UINT8 *UpdatedExtHdrs; + EFI_STATUS Status; + UINT8 NextHeader; + + UpdatedExtHdrs = NULL; + + // + // HeadLen is the length of the fixed part of the sequences of fragments, i.e. + // the unfragment part. + // + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + if (PacketHead == NULL) { + return EFI_BAD_BUFFER_SIZE; + } + + // + // Set the head up, convert the host byte order to network byte order + // + CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); + PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER))); + Packet->Ip.Ip6 = PacketHead; + + Len = HeadLen - sizeof (EFI_IP6_HEADER); + UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER); + + if (UnFragExtHdrsLen == 0) { + PacketHead->NextHeader = IP6_FRAGMENT; + } + + // + // Append the extension headers: firstly copy the unfragmentable headers, then append + // fragmentation header. + // + if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) { + NextHeader = Head->NextHeader; + } else { + NextHeader = PacketHead->NextHeader; + } + + Status = Ip6FillFragmentHeader ( + IpSb, + NextHeader, + LastHeader, + ExtHdrs, + ExtHdrsLen, + FragmentOffset, + &UpdatedExtHdrs + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem ( + (UINT8 *) (PacketHead + 1), + UpdatedExtHdrs, + UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER) + ); + + FreePool (UpdatedExtHdrs); + return EFI_SUCCESS; +} + +/** + Transmit an IP6 packet. The packet comes either from the IP6 + child's consumer (IpInstance != NULL) or the IP6 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through an interface. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Interface The IP6 interface to transmit the packet. Ignored + if NULL. + @param[in] IpInstance The IP6 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: NextHeader, HopLimit, + Src, Dest, FlowLabel, PayloadLength. This function + will fill in the Ver, TrafficClass. + @param[in] ExtHdrs The extension headers to append to the IPv6 basic + header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback. + + @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid. + @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 successfully transmitted. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resources. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip6Output ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN IP6_FRAME_CALLBACK Callback, + IN VOID *Context + ) +{ + IP6_INTERFACE *IpIf; + EFI_IPv6_ADDRESS NextHop; + IP6_NEIGHBOR_ENTRY *NeighborCache; + IP6_ROUTE_CACHE_ENTRY *RouteCache; + EFI_STATUS Status; + UINT32 Mtu; + UINT32 HeadLen; + UINT16 FragmentOffset; + UINT8 *LastHeader; + UINT32 UnFragmentLen; + UINT32 UnFragmentHdrsLen; + UINT32 FragmentHdrsLen; + UINT16 *Checksum; + UINT16 PacketChecksum; + UINT16 PseudoChecksum; + UINT32 Index; + UINT32 PacketLen; + UINT32 RealExtLen; + UINT32 Offset; + NET_BUF *TmpPacket; + NET_BUF *Fragment; + UINT32 Num; + UINT8 *Buf; + EFI_IP6_HEADER *PacketHead; + IP6_ICMP_HEAD *IcmpHead; + IP6_TXTOKEN_WRAP *Wrap; + IP6_ROUTE_ENTRY *RouteEntry; + UINT8 *UpdatedExtHdrs; + UINT8 NextHeader; + UINT8 LastHeaderBackup; + BOOLEAN FragmentHeadInserted; + UINT8 *ExtHdrsBackup; + UINT8 NextHeaderBackup; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS Destination; + + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // RFC2460: Each extension header is an integer multiple of 8 octets long, + // in order to retain 8-octet alignment for subsequent headers. + // + if ((ExtHdrsLen & 0x7) != 0) { + return EFI_INVALID_PARAMETER; + } + + LastHeader = NULL; + + Ip6IsExtsValid ( + NULL, + NULL, + &Head->NextHeader, + ExtHdrs, + ExtHdrsLen, + FALSE, + NULL, + &LastHeader, + NULL, + NULL, + NULL + ); + + // + // Select an interface/source for system packet, application + // should select them itself. + // + IpIf = Interface; + if (IpIf == NULL) { + // + // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress + // and destinationaddress is unspecified. + // + if (IpInstance == NULL || IpInstance->Interface == NULL) { + IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress); + if (IpInstance != NULL) { + IpInstance->Interface = IpIf; + } + } else { + IpIf = IpInstance->Interface; + } + } + + if (IpIf == NULL) { + return EFI_NO_MAPPING; + } + + // + // Update the common field in Head here. + // + Head->Version = 6; + Head->TrafficClassL = 0; + Head->TrafficClassH = 0; + + Checksum = NULL; + NextHeader = *LastHeader; + + switch (NextHeader) { + case EFI_IP_PROTO_UDP: + Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Packet->Udp != NULL); + if (Packet->Udp->Checksum == 0) { + Checksum = &Packet->Udp->Checksum; + } + break; + + case EFI_IP_PROTO_TCP: + Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Packet->Tcp != NULL); + if (Packet->Tcp->Checksum == 0) { + Checksum = &Packet->Tcp->Checksum; + } + break; + + case IP6_ICMP: + // + // Don't send ICMP packet to an IPv6 anycast address. + // + if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) { + return EFI_INVALID_PARAMETER; + } + + IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL); + ASSERT (IcmpHead != NULL); + if (IcmpHead->Checksum == 0) { + Checksum = &IcmpHead->Checksum; + } + break; + + default: + break; + } + + if (Checksum != NULL) { + // + // Calculate the checksum for upper layer protocol if it is not calculated due to lack of + // IPv6 source address. + // + PacketChecksum = NetbufChecksum (Packet); + PseudoChecksum = NetIp6PseudoHeadChecksum ( + &Head->SourceAddress, + &Head->DestinationAddress, + NextHeader, + Packet->TotalSize + ); + *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum); + } + + Status = Ip6IpSecProcessPacket ( + IpSb, + &Head, + LastHeader, // no need get the lasthead value for output + &Packet, + &ExtHdrs, + &ExtHdrsLen, + EfiIPsecOutBound, + Context + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + LastHeader = NULL; + // + // Check incoming parameters. + // + if (!Ip6IsExtsValid ( + IpSb, + Packet, + &Head->NextHeader, + ExtHdrs, + ExtHdrsLen, + FALSE, + NULL, + &LastHeader, + &RealExtLen, + &UnFragmentHdrsLen, + NULL + )) { + return EFI_INVALID_PARAMETER; + } + + if ((RealExtLen & 0x7) != 0) { + return EFI_INVALID_PARAMETER; + } + + LastHeaderBackup = *LastHeader; + + // + // Perform next hop determination: + // For multicast packets, the next-hop is always the destination address and + // is considered to be on-link. + // + if (IP6_IS_MULTICAST (&Head->DestinationAddress)) { + IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress); + } else { + // + // For unicast packets, use a combination of the Destination Cache, the Prefix List + // and the Default Router List to determine the IP address of the appropriate next hop. + // + + NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->DestinationAddress); + if (NeighborCache != NULL) { + // + // Hit Neighbor Cache. + // + IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress); + } else { + // + // Not in Neighbor Cache, check Router cache + // + RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress); + if (RouteCache == NULL) { + return EFI_NOT_FOUND; + } + + IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop); + Ip6FreeRouteCacheEntry (RouteCache); + } + } + + // + // Examines the Neighbor Cache for link-layer information about that neighbor. + // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error. + // + if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) { + NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop); + if (NeighborCache == NULL) { + NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL); + + if (NeighborCache == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Send out multicast neighbor solicitation for address resolution immediately. + // + Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination); + Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Ip6SendNeighborSolicit ( + IpSb, + &Source, + &Destination, + &NeighborCache->Neighbor, + &IpSb->SnpMode.CurrentAddress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + --NeighborCache->Transmit; + NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1; + } + + NeighborCache->Interface = IpIf; + } + + UpdatedExtHdrs = NULL; + ExtHdrsBackup = NULL; + NextHeaderBackup = 0; + FragmentHeadInserted = FALSE; + + // + // Check whether we received Packet Too Big message for the packet sent to the + // Destination. If yes include a Fragment Header in the subsequent packets. + // + RouteEntry = Ip6FindRouteEntry ( + IpSb->RouteTable, + &Head->DestinationAddress, + NULL + ); + if (RouteEntry != NULL) { + if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) { + + // + // FragmentHead is inserted after Hop-by-Hop Options header, Destination + // Options header (first occur), Routing header, and before Fragment header, + // Authentication header, Encapsulating Security Payload header, and + // Destination Options header (last occur), and upper-layer header. + // + Status = Ip6FillFragmentHeader ( + IpSb, + Head->NextHeader, + LastHeaderBackup, + ExtHdrs, + ExtHdrsLen, + 0, + &UpdatedExtHdrs + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { + NextHeaderBackup = Head->NextHeader; + Head->NextHeader = IP6_FRAGMENT; + } + + ExtHdrsBackup = ExtHdrs; + ExtHdrs = UpdatedExtHdrs; + ExtHdrsLen = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + RealExtLen = RealExtLen + sizeof (IP6_FRAGMENT_HEADER); + + mIp6Id++; + + FragmentHeadInserted = TRUE; + } + + Ip6FreeRouteEntry (RouteEntry); + } + + // + // OK, selected the source and route, fragment the packet then send + // them. Tag each fragment other than the first one as spawn from it. + // Each extension header is an integer multiple of 8 octets long, in + // order to retain 8-octet alignment for subsequent headers. + // + Mtu = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER); + HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen; + + if (Packet->TotalSize + HeadLen > Mtu) { + // + // Remove the inserted Fragment Header since we need fragment the packet. + // + if (FragmentHeadInserted) { + ExtHdrs = ExtHdrsBackup; + ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER); + + if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) { + Head->NextHeader = NextHeaderBackup; + } + } + + FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen; + + // + // The packet is beyond the maximum which can be described through the + // fragment offset field in Fragment header. + // + if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + + if (FragmentHdrsLen != 0) { + // + // Append the fragmentable extension hdrs before the upper layer payload + // to form a new NET_BUF. This NET_BUF contains all the buffer which will + // be fragmented below. + // + TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen); + ASSERT (TmpPacket != NULL); + + // + // Allocate the space to contain the fragmentable hdrs and copy the data. + // + Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE); + ASSERT (Buf != NULL); + CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen); + + // + // Free the old Packet. + // + NetbufFree (Packet); + Packet = TmpPacket; + } + + // + // The unfragment part which appears in every fragmented IPv6 packet includes + // the IPv6 header, the unfragmentable extension hdrs and the fragment header. + // + UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER); + + // + // Mtu now is the length of the fragment part in a full-length fragment. + // + Mtu = (Mtu - UnFragmentLen) & (~0x07); + Num = (Packet->TotalSize + Mtu - 1) / Mtu; + + for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) { + // + // Get fragment from the Packet, append UnFragmentLen spare buffer + // before the fragmented data, the corresponding data is filled in later. + // + Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen); + if (Fragment == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + FragmentOffset = (UINT16) ((UINT16) Offset | 0x1); + if (Index == Num - 1){ + // + // The last fragment, clear the M flag. + // + FragmentOffset &= (~0x1); + } + + Status = Ip6PrependHead ( + IpSb, + Fragment, + Head, + FragmentOffset, + ExtHdrs, + ExtHdrsLen, + LastHeaderBackup, + UnFragmentLen + ); + ASSERT (Status == EFI_SUCCESS); + + Status = Ip6SendFrame ( + IpIf, + IpInstance, + Fragment, + &NextHop, + Ip6SysPacketSent, + Packet + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // The last fragment of upper layer packet, update the IP6 token status. + // + if ((Index == Num -1) && (Context != NULL)) { + Wrap = (IP6_TXTOKEN_WRAP *) Context; + Wrap->Token->Status = Status; + } + + Offset += PacketLen; + PacketLen = Packet->TotalSize - Offset; + if (PacketLen > Mtu) { + PacketLen = Mtu; + } + } + + NetbufFree (Packet); + mIp6Id++; + + if (UpdatedExtHdrs != NULL) { + FreePool (UpdatedExtHdrs); + } + + return EFI_SUCCESS; + } + + // + // Need not fragment the packet, send it in one frame. + // + PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD); + if (PacketHead == NULL) { + Status = EFI_BAD_BUFFER_SIZE; + goto Error; + } + + CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER)); + Packet->Ip.Ip6 = PacketHead; + + if (ExtHdrs != NULL) { + Buf = (UINT8 *) (PacketHead + 1); + CopyMem (Buf, ExtHdrs, ExtHdrsLen); + } + + if (UpdatedExtHdrs != NULL) { + // + // A Fragment Header is inserted to the packet, update the payload length. + // + PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) + + sizeof (IP6_FRAGMENT_HEADER)); + PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength); + FreePool (UpdatedExtHdrs); + } + + return Ip6SendFrame ( + IpIf, + IpInstance, + Packet, + &NextHop, + Callback, + Context + ); + +Error: + if (UpdatedExtHdrs != NULL) { + FreePool (UpdatedExtHdrs); + } + Ip6CancelPacket (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 an unrelated packet. + +**/ +BOOLEAN +Ip6CancelPacketFragments ( + IN IP6_LINK_TX_TOKEN *Frame, + IN VOID *Context + ) +{ + if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) { + return TRUE; + } + + return FALSE; +} + +/** + 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. Ignored if + FrameToCancel is NULL. + +**/ +VOID +Ip6CancelFrames ( + IN IP6_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_LINK_TX_TOKEN *Token; + IP6_SERVICE *IpSb; + IP6_NEIGHBOR_ENTRY *ArpQue; + EFI_STATUS Status; + + IpSb = Interface->Service; + NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); + + // + // Cancel all the pending frames on ARP requests + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) { + ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList); + + Status = Ip6FreeNeighborEntry ( + IpSb, + ArpQue, + FALSE, + FALSE, + IoStatus, + FrameToCancel, + Context + ); + ASSERT_EFI_ERROR (Status); + } + + // + // 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, IP6_LINK_TX_TOKEN, Link); + + if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) { + IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken); + } + } +} + +/** + Cancel the Packet and all its fragments. + + @param[in] IpIf The interface from which the Packet is sent. + @param[in] Packet The Packet to cancel. + @param[in] IoStatus The status returns to the sender. + +**/ +VOID +Ip6CancelPacket ( + IN IP6_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ) +{ + Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet); +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Output.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Output.h new file mode 100644 index 00000000..7a42bd07 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Output.h @@ -0,0 +1,135 @@ +/** @file + The internal functions and routines to transmit the IP6 packet. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_OUTPUT_H__ +#define __EFI_IP6_OUTPUT_H__ + +extern UINT32 mIp6Id; + +/** + Output all the available source addresses to the list entry head SourceList. The + number of source addresses are also returned. + + @param[in] IpSb Points to a IP6 service binding instance. + @param[in] Destination The IPv6 destination address. + @param[out] Source The selected IPv6 source address according to + the Destination. + + @retval EFI_SUCCESS The source addresses were copied to the list entry + head SourceList. + @retval EFI_NO_MAPPING The IPv6 stack is not auto configured. + +**/ +EFI_STATUS +Ip6SelectSourceAddress ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Destination, + OUT EFI_IPv6_ADDRESS *Source + ); + +/** + The default callback function for system generated packet. + It will free the packet. + + @param[in] Packet The packet that transmitted. + @param[in] IoStatus The result of the transmission: succeeded or failed. + @param[in] LinkFlag Not used when transmission. Check IP6_FRAME_CALLBACK + for reference. + @param[in] Context The context provided by us. + +**/ +VOID +Ip6SysPacketSent ( + NET_BUF *Packet, + EFI_STATUS IoStatus, + UINT32 LinkFlag, + VOID *Context + ); + +/** + Transmit an IP6 packet. The packet comes either from the IP6 + child's consumer (IpInstance != NULL) or the IP6 driver itself + (IpInstance == NULL). It will route the packet, fragment it, + then transmit all the fragments through an interface. + + @param[in] IpSb The IP6 service instance to transmit the packet. + @param[in] Interface The IP6 interface to transmit the packet. Ignored + if NULL. + @param[in] IpInstance The IP6 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: NextHeader, HopLimit, + Src, Dest, FlowLabel, PayloadLength. This function + will fill in the Ver, TrafficClass. + @param[in] ExtHdrs The extension headers to append to the IPv6 basic + header. + @param[in] ExtHdrsLen The length of the extension headers. + @param[in] Callback The callback function to issue when transmission + completed. + @param[in] Context The opaque context for the callback. + + @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid. + @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 successfully transmitted. + @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of + resources. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Ip6Output ( + IN IP6_SERVICE *IpSb, + IN IP6_INTERFACE *Interface OPTIONAL, + IN IP6_PROTOCOL *IpInstance OPTIONAL, + IN NET_BUF *Packet, + IN EFI_IP6_HEADER *Head, + IN UINT8 *ExtHdrs, + IN UINT32 ExtHdrsLen, + IN IP6_FRAME_CALLBACK Callback, + IN VOID *Context + ); + +/** + 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. Ignored if + FrameToCancel is NULL. + +**/ +VOID +Ip6CancelFrames ( + IN IP6_INTERFACE *Interface, + IN EFI_STATUS IoStatus, + IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL, + IN VOID *Context OPTIONAL + ); + +/** + Cancel the Packet and all its fragments. + + @param[in] IpIf The interface from which the Packet is sent. + @param[in] Packet The Packet to cancel. + @param[in] IoStatus The status returns to the sender. + +**/ +VOID +Ip6CancelPacket ( + IN IP6_INTERFACE *IpIf, + IN NET_BUF *Packet, + IN EFI_STATUS IoStatus + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Route.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Route.c new file mode 100644 index 00000000..c7889058 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Route.c @@ -0,0 +1,629 @@ +/** @file + The functions and routines to handle the route caches and route table. + + Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Ip6Impl.h" + +/** + This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value + as the index of the route cache bucket according to the prefix of two IPv6 addresses. + + @param[in] Ip1 The IPv6 address. + @param[in] Ip2 The IPv6 address. + + @return The hash value of the prefix of two IPv6 addresses. + +**/ +UINT32 +Ip6RouteCacheHash ( + IN EFI_IPv6_ADDRESS *Ip1, + IN EFI_IPv6_ADDRESS *Ip2 + ) +{ + UINT32 Prefix1; + UINT32 Prefix2; + + Prefix1 = *((UINT32 *) ((UINTN *) (Ip1))); + Prefix2 = *((UINT32 *) ((UINTN *) (Ip2))); + + return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE); +} + +/** + Allocate a route entry then initialize it with the Destination/PrefixLength + and Gateway. + + @param[in] Destination The IPv6 destination address. This is an optional + parameter that may be NULL. + @param[in] PrefixLength The destination network's prefix length. + @param[in] GatewayAddress The next hop address. This is an optional parameter + that may be NULL. + + @return NULL if failed to allocate memory; otherwise, the newly created route entry. + +**/ +IP6_ROUTE_ENTRY * +Ip6CreateRouteEntry ( + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ) +{ + IP6_ROUTE_ENTRY *RtEntry; + + RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY)); + + if (RtEntry == NULL) { + return NULL; + } + + RtEntry->RefCnt = 1; + RtEntry->Flag = 0; + RtEntry->PrefixLength = PrefixLength; + + if (Destination != NULL) { + IP6_COPY_ADDRESS (&RtEntry->Destination, Destination); + } + + if (GatewayAddress != NULL) { + IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress); + } + + return RtEntry; +} + +/** + Free the route table entry. It is reference counted. + + @param[in, out] RtEntry The route entry to free. + +**/ +VOID +Ip6FreeRouteEntry ( + IN OUT IP6_ROUTE_ENTRY *RtEntry + ) +{ + ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0)); + + if (--RtEntry->RefCnt == 0) { + FreePool (RtEntry); + } +} + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (prefix length == 128) 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 per 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] Destination The destination address to search. If NULL, search + the route table by NextHop. + @param[in] NextHop The next hop address. If NULL, search the route table + by Destination. + + @return NULL if no route matches the Dst. Otherwise, the point to the + @return most specific route to the Dst. + +**/ +IP6_ROUTE_ENTRY * +Ip6FindRouteEntry ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN EFI_IPv6_ADDRESS *NextHop OPTIONAL + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *RtEntry; + INTN Index; + + ASSERT (Destination != NULL || NextHop != NULL); + + RtEntry = NULL; + + for (Index = IP6_PREFIX_MAX; Index >= 0; Index--) { + NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (Destination != NULL) { + if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } else if (NextHop != NULL) { + if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) { + NET_GET_REF (RtEntry); + return RtEntry; + } + } + + } + } + + return NULL; +} + +/** + Allocate and initialize a IP6 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. Otherwise, point + to the created route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6CreateRouteCacheEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN EFI_IPv6_ADDRESS *GateWay, + IN UINTN Tag + ) +{ + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + + RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY)); + + if (RtCacheEntry == NULL) { + return NULL; + } + + RtCacheEntry->RefCnt = 1; + RtCacheEntry->Tag = Tag; + + IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst); + IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src); + IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay); + + return RtCacheEntry; +} + +/** + Free the route cache entry. It is reference counted. + + @param[in, out] RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip6FreeRouteCacheEntry ( + IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry + ) +{ + ASSERT (RtCacheEntry->RefCnt > 0); + + if (--RtCacheEntry->RefCnt == 0) { + FreePool (RtCacheEntry); + } +} + +/** + Find a route cache with the destination and source address. This is + used by the ICMPv6 redirect message process. + + @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 pointer + to the correct route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6FindRouteCache ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + Index = IP6_ROUTE_CACHE_HASH (Dest, Src); + + NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + + if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) { + NET_GET_REF (RtCacheEntry); + return RtCacheEntry; + } + } + + return NULL; +} + +/** + Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number + of EFI_IP6_ROUTE_TABLE is also returned. + + @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used. + @param[out] EfiRouteCount The number of returned route entries. + @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE. + If NULL, only the route entry count is returned. + + @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiRouteTable ( + IN IP6_ROUTE_TABLE *RouteTable, + OUT UINT32 *EfiRouteCount, + OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL + ) +{ + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *RtEntry; + EFI_IP6_ROUTE_TABLE *EfiTable; + UINT32 Count; + INT32 Index; + + ASSERT (EfiRouteCount != NULL); + + Count = RouteTable->TotalNum; + *EfiRouteCount = Count; + + if ((EfiRouteTable == NULL) || (Count == 0)) { + return EFI_SUCCESS; + } + + if (*EfiRouteTable == NULL) { + *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count); + if (*EfiRouteTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + EfiTable = *EfiRouteTable; + + // + // Copy the route entry to EFI route table. + // + Count = 0; + + for (Index = IP6_PREFIX_MAX; Index >= 0; Index--) { + + NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + Ip6CopyAddressByPrefix ( + &EfiTable[Count].Destination, + &RtEntry->Destination, + RtEntry->PrefixLength + ); + + IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop); + EfiTable[Count].PrefixLength = RtEntry->PrefixLength; + + Count++; + } + } + + ASSERT (Count == RouteTable->TotalNum); + + return EFI_SUCCESS; +} + +/** + Create an empty route table. This includes its internal route cache. + + @return NULL if failed to allocate memory for the route table. Otherwise, + the point to newly created route table. + +**/ +IP6_ROUTE_TABLE * +Ip6CreateRouteTable ( + VOID + ) +{ + IP6_ROUTE_TABLE *RtTable; + UINT32 Index; + + RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE)); + if (RtTable == NULL) { + return NULL; + } + + RtTable->RefCnt = 1; + RtTable->TotalNum = 0; + + for (Index = 0; Index <= IP6_PREFIX_MAX; Index++) { + InitializeListHead (&RtTable->RouteArea[Index]); + } + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + InitializeListHead (&RtTable->Cache.CacheBucket[Index]); + RtTable->Cache.CacheNum[Index] = 0; + } + + return RtTable; +} + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in, out] RtTable The route table to free. + +**/ +VOID +Ip6CleanRouteTable ( + IN OUT IP6_ROUTE_TABLE *RtTable + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_ENTRY *RtEntry; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + 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 <= IP6_PREFIX_MAX; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) { + RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + RemoveEntryList (Entry); + Ip6FreeRouteEntry (RtEntry); + } + } + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) { + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + RemoveEntryList (Entry); + Ip6FreeRouteCacheEntry (RtCacheEntry); + } + } + + 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[in] RtCache Route cache to remove the entries from. + @param[in] Tag The Tag of the entries to remove. + +**/ +VOID +Ip6PurgeRouteCache ( + IN IP6_ROUTE_CACHE *RtCache, + IN UINTN Tag + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + UINT32 Index; + + for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { + NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) { + + RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); + + if (RtCacheEntry->Tag == Tag) { + RemoveEntryList (Entry); + Ip6FreeRouteCacheEntry (RtCacheEntry); + } + } + } +} + +/** + Add a route entry to the route table. It is the help function for EfiIp6Routes. + + @param[in, out] RtTable Route table to add route to. + @param[in] Destination The destination of the network. + @param[in] PrefixLength The PrefixLength of the destination. + @param[in] GatewayAddress 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 was added successfully. + +**/ +EFI_STATUS +Ip6AddRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ) +{ + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + IP6_ROUTE_ENTRY *Route; + + ListHead = &RtTable->RouteArea[PrefixLength]; + + // + // First check whether the route exists + // + NET_LIST_FOR_EACH (Entry, ListHead) { + Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) && + EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) { + return EFI_ACCESS_DENIED; + } + } + + // + // Create a route entry and insert it to the route area. + // + Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress); + + if (Route == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (NetIp6IsUnspecifiedAddr (GatewayAddress)) { + Route->Flag = IP6_DIRECT_ROUTE; + } + + InsertHeadList (ListHead, &Route->Link); + RtTable->TotalNum++; + + return EFI_SUCCESS; +} + +/** + Remove a route entry and all the route caches spawn from it. + It is the help function for EfiIp6Routes. + + @param[in, out] RtTable The route table to remove the route from. + @param[in] Destination The destination network. + @param[in] PrefixLength The PrefixLength of the Destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_SUCCESS The route entry was successfully removed. + @retval EFI_NOT_FOUND There is no route entry in the table with that + property. + +**/ +EFI_STATUS +Ip6DelRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ) +{ + LIST_ENTRY *ListHead; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + IP6_ROUTE_ENTRY *Route; + UINT32 TotalNum; + + ListHead = &RtTable->RouteArea[PrefixLength]; + TotalNum = RtTable->TotalNum; + + NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) { + Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link); + + if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) { + continue; + } + if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) { + continue; + } + + Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route); + RemoveEntryList (Entry); + Ip6FreeRouteEntry (Route); + + ASSERT (RtTable->TotalNum > 0); + RtTable->TotalNum--; + } + + return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS; +} + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] IpSb The IP6 service data. + @param[in] Dest The destination address to search for. + @param[in] Src The source address to search for. + + @return NULL if it failed to route the packet. Otherwise, a route cache + entry that can be used to route packets. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6Route ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ) +{ + IP6_ROUTE_TABLE *RtTable; + LIST_ENTRY *ListHead; + IP6_ROUTE_CACHE_ENTRY *RtCacheEntry; + IP6_ROUTE_ENTRY *RtEntry; + EFI_IPv6_ADDRESS NextHop; + UINT32 Index; + + RtTable = IpSb->RouteTable; + + ASSERT (RtTable != NULL); + + // + // Search the destination cache in IP6_ROUTE_TABLE. + // + Index = IP6_ROUTE_CACHE_HASH (Dest, Src); + ListHead = &RtTable->Cache.CacheBucket[Index]; + + RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src); + + // + // If found, promote the cache entry to the head of the hash bucket. + // + if (RtCacheEntry != NULL) { + RemoveEntryList (&RtCacheEntry->Link); + InsertHeadList (ListHead, &RtCacheEntry->Link); + return RtCacheEntry; + } + + // + // Search the route table for the most specific route + // + RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL); + if (RtEntry == NULL) { + return NULL; + } + + // + // Found a route to the Dest, if it is a direct route, the packet + // will be send directly to the destination, such as for connected + // network. Otherwise, it is an indirect route, the packet will be + // send the next hop router. + // + if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) { + IP6_COPY_ADDRESS (&NextHop, Dest); + } else { + IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop); + } + + Ip6FreeRouteEntry (RtEntry); + + // + // Create a route cache entry, and tag it as spawned from this route entry + // + RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry); + + if (RtCacheEntry == NULL) { + return NULL; + } + + InsertHeadList (ListHead, &RtCacheEntry->Link); + NET_GET_REF (RtCacheEntry); + RtTable->Cache.CacheNum[Index]++; + + return RtCacheEntry; +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Route.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Route.h new file mode 100644 index 00000000..428bb926 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Ip6Dxe/Ip6Route.h @@ -0,0 +1,293 @@ +/** @file + EFI IP6 route table and route cache table definitions. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_IP6_ROUTE_H__ +#define __EFI_IP6_ROUTE_H__ + +#define IP6_DIRECT_ROUTE 0x00000001 +#define IP6_PACKET_TOO_BIG 0x00000010 + +#define IP6_ROUTE_CACHE_HASH_SIZE 31 +/// +/// Max NO. of cache entry per hash bucket +/// +#define IP6_ROUTE_CACHE_MAX 32 + +#define IP6_ROUTE_CACHE_HASH(Ip1, Ip2) Ip6RouteCacheHash ((Ip1), (Ip2)) + +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + UINT32 Flag; + UINT8 PrefixLength; + EFI_IPv6_ADDRESS Destination; + EFI_IPv6_ADDRESS NextHop; +} IP6_ROUTE_ENTRY; + +typedef struct { + LIST_ENTRY Link; + INTN RefCnt; + UINTN Tag; + EFI_IPv6_ADDRESS Destination; + EFI_IPv6_ADDRESS Source; + EFI_IPv6_ADDRESS NextHop; +} IP6_ROUTE_CACHE_ENTRY; + +typedef struct { + LIST_ENTRY CacheBucket[IP6_ROUTE_CACHE_HASH_SIZE]; + UINT8 CacheNum[IP6_ROUTE_CACHE_HASH_SIZE]; +} IP6_ROUTE_CACHE; + +// +// Each IP6 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 prefix length 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 _IP6_ROUTE_TABLE { + INTN RefCnt; + UINT32 TotalNum; + LIST_ENTRY RouteArea[IP6_PREFIX_NUM]; + IP6_ROUTE_CACHE Cache; +} IP6_ROUTE_TABLE; + +/** + This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value + as the index of the route cache bucket according to the prefix of two IPv6 addresses. + + @param[in] Ip1 The IPv6 address. + @param[in] Ip2 The IPv6 address. + + @return The hash value of the prefix of two IPv6 addresses. + +**/ +UINT32 +Ip6RouteCacheHash ( + IN EFI_IPv6_ADDRESS *Ip1, + IN EFI_IPv6_ADDRESS *Ip2 + ); + +/** + Allocate and initialize an IP6 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 it failed to allocate memory for the cache. Otherwise, point + to the created route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6CreateRouteCacheEntry ( + IN EFI_IPv6_ADDRESS *Dst, + IN EFI_IPv6_ADDRESS *Src, + IN EFI_IPv6_ADDRESS *GateWay, + IN UINTN Tag + ); + +/** + Free the route cache entry. It is reference counted. + + @param[in, out] RtCacheEntry The route cache entry to free. + +**/ +VOID +Ip6FreeRouteCacheEntry ( + IN OUT IP6_ROUTE_CACHE_ENTRY *RtCacheEntry + ); + +/** + Find a route cache with the destination and source address. This is + used by the ICMPv6 redirect message process. + + @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, point + to the correct route cache entry. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6FindRouteCache ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ); + +/** + Build a array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number + of EFI_IP6_ROUTE_TABLE is also returned. + + @param[in] RouteTable The pointer of IP6_ROUTE_TABLE internal used. + @param[out] EfiRouteCount The number of returned route entries. + @param[out] EfiRouteTable The pointer to the array of EFI_IP6_ROUTE_TABLE. + If NULL, only the route entry count is returned. + + @retval EFI_SUCCESS The EFI_IP6_ROUTE_TABLE successfully built. + @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. + +**/ +EFI_STATUS +Ip6BuildEfiRouteTable ( + IN IP6_ROUTE_TABLE *RouteTable, + OUT UINT32 *EfiRouteCount, + OUT EFI_IP6_ROUTE_TABLE **EfiRouteTable OPTIONAL + ); + +/** + 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. + +**/ +IP6_ROUTE_TABLE * +Ip6CreateRouteTable ( + VOID + ); + +/** + Free the route table and its associated route cache. Route + table is reference counted. + + @param[in, out] RtTable The route table to free. + +**/ +VOID +Ip6CleanRouteTable ( + IN OUT IP6_ROUTE_TABLE *RtTable + ); + +/** + Allocate a route entry then initialize it with the Destination/PrefixLength + and Gateway. + + @param[in] Destination The IPv6 destination address. This is an optional + parameter that may be NULL. + @param[in] PrefixLength The destination network's prefix length. + @param[in] GatewayAddress The next hop address. This is optional parameter + that may be NULL. + + @return NULL if it failed to allocate memory. Otherwise, the newly created route entry. + +**/ +IP6_ROUTE_ENTRY * +Ip6CreateRouteEntry ( + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress OPTIONAL + ); + +/** + Search the route table for a most specific match to the Dst. It searches + from the longest route area (prefix length == 128) 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 per 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] Destination The destination address to search. If NULL, search + the route table by NextHop. + @param[in] NextHop The next hop address. If NULL, search the route table + by Destination. + + @return NULL if no route matches the Dst. Otherwise the point to the + most specific route to the Dst. + +**/ +IP6_ROUTE_ENTRY * +Ip6FindRouteEntry ( + IN IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination OPTIONAL, + IN EFI_IPv6_ADDRESS *NextHop OPTIONAL + ); + +/** + Free the route table entry. It is reference counted. + + @param[in, out] RtEntry The route entry to free. + +**/ +VOID +Ip6FreeRouteEntry ( + IN OUT IP6_ROUTE_ENTRY *RtEntry + ); + +/** + Add a route entry to the route table. It is the help function for EfiIp6Routes. + + @param[in, out] RtTable Route table to add route to. + @param[in] Destination The destination of the network. + @param[in] PrefixLength The PrefixLength of the destination. + @param[in] GatewayAddress 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 was added successfully. + +**/ +EFI_STATUS +Ip6AddRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ); + +/** + Remove a route entry and all the route caches spawn from it. + It is the help function for EfiIp6Routes. + + @param[in, out] RtTable The route table to remove the route from. + @param[in] Destination The destination network. + @param[in] PrefixLength The PrefixLength of the Destination. + @param[in] GatewayAddress The next hop address. + + @retval EFI_SUCCESS Successfully removed the route entry. + @retval EFI_NOT_FOUND There is no route entry in the table with that + property. + +**/ +EFI_STATUS +Ip6DelRoute ( + IN OUT IP6_ROUTE_TABLE *RtTable, + IN EFI_IPv6_ADDRESS *Destination, + IN UINT8 PrefixLength, + IN EFI_IPv6_ADDRESS *GatewayAddress + ); + +/** + Search the route table to route the packet. Return/create a route + cache if there is a route to the destination. + + @param[in] IpSb The IP6 service data. + @param[in] Dest The destination address to search for. + @param[in] Src The source address to search for. + + @return NULL if failed to route packet. Otherwise, a route cache + entry that can be used to route packet. + +**/ +IP6_ROUTE_CACHE_ENTRY * +Ip6Route ( + IN IP6_SERVICE *IpSb, + IN EFI_IPv6_ADDRESS *Dest, + IN EFI_IPv6_ADDRESS *Src + ); + +#endif |