summaryrefslogtreecommitdiff
path: root/NetworkPkg/Ip6Dxe
diff options
context:
space:
mode:
authorhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>2010-11-01 06:13:54 +0000
committerhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>2010-11-01 06:13:54 +0000
commita3bcde70e6dc69000f85cc5deee98101d2ae200a (patch)
tree693709a5293f80b320931693b34b2d6983cfcf4b /NetworkPkg/Ip6Dxe
parent12873d57666d0beff41959a1fb8f9062016f0983 (diff)
downloadedk2-platforms-a3bcde70e6dc69000f85cc5deee98101d2ae200a.tar.xz
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'NetworkPkg/Ip6Dxe')
-rw-r--r--NetworkPkg/Ip6Dxe/ComponentName.c313
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Common.c796
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Common.h338
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Config.vfr170
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c2369
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h295
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigNv.c2116
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6ConfigNv.h73
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Driver.c930
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Driver.h185
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Dxe.inf100
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6DxeStrings.unibin0 -> 9574 bytes
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Icmp.c684
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Icmp.h108
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6If.c802
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6If.h267
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Impl.c1857
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Impl.h751
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Input.c1710
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Input.h235
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Mld.c908
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Mld.h198
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Nd.c3141
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Nd.h750
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6NvData.h70
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Option.c758
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Option.h191
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Output.c1077
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Output.h141
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Route.c635
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Route.h299
31 files changed, 22267 insertions, 0 deletions
diff --git a/NetworkPkg/Ip6Dxe/ComponentName.c b/NetworkPkg/Ip6Dxe/ComponentName.c
new file mode 100644
index 0000000000..c8382f74a9
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/ComponentName.c
@@ -0,0 +1,313 @@
+/** @file
+ Implementation of EFI_COMPONENT_NAME_PROTOCOL and
+ EFI_COMPONENT_NAME2_PROTOCOL protocol.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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 not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language 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
+ }
+};
+
+/**
+ 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)
+ );
+
+}
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user-readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user-readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language 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
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/Ip6Dxe/Ip6Common.c b/NetworkPkg/Ip6Dxe/Ip6Common.c
new file mode 100644
index 0000000000..2ae14a952c
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Common.c
@@ -0,0 +1,796 @@
+/** @file
+ The implementation of common functions shared by IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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 manully 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++;
+}
+
+/**
+ 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
+ )
+{
+ BOOLEAN OneDestroyed;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ LIST_ENTRY *Entry;
+ IP6_PROTOCOL *Instance;
+
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ ServiceBinding = &IpSb->ServiceBinding;
+
+ //
+ // Upper layer IP protocol consumers may have tight relationship between several
+ // IP protocol instances, in other words, calling ServiceBinding->DestroyChild to
+ // destroy one IP child may cause other related IP children destroyed too. This
+ // will probably leave hole in the children list when we iterate it. So everytime
+ // we just destroy one child then back to the start point to iterate the list.
+ //
+ do {
+ OneDestroyed = FALSE;
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE);
+
+ if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) {
+ ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+ OneDestroyed = TRUE;
+ break;
+ }
+ }
+ } while (OneDestroyed);
+}
+
+/**
+ 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_NUM) {
+ 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_NUM);
+
+ 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);
+}
+
+/**
+ Set the Ip6 variable data.
+
+ @param[in] IpSb Points to an IP6 service binding instance.
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Ip6SetVariableData (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ UINT32 NumConfiguredInstance;
+ LIST_ENTRY *Entry;
+ UINTN VariableDataSize;
+ EFI_IP6_VARIABLE_DATA *Ip6VariableData;
+ EFI_IP6_ADDRESS_PAIR *Ip6AddressPair;
+ IP6_PROTOCOL *IpInstance;
+ CHAR16 *NewMacString;
+ EFI_STATUS Status;
+
+ NumConfiguredInstance = 0;
+
+ //
+ // Go through the children list to count the configured children.
+ //
+ 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) {
+ NumConfiguredInstance++;
+ }
+ }
+
+ //
+ // Calculate the size of the Ip6VariableData. As there may be no IP child,
+ // we should add extra buffer for the address paris only if the number of configured
+ // children is more than 1.
+ //
+ VariableDataSize = sizeof (EFI_IP6_VARIABLE_DATA);
+
+ if (NumConfiguredInstance > 1) {
+ VariableDataSize += sizeof (EFI_IP6_ADDRESS_PAIR) * (NumConfiguredInstance - 1);
+ }
+
+ Ip6VariableData = AllocatePool (VariableDataSize);
+ if (Ip6VariableData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ip6VariableData->DriverHandle = IpSb->Image;
+ Ip6VariableData->AddressCount = NumConfiguredInstance;
+
+ Ip6AddressPair = &Ip6VariableData->AddressPairs[0];
+
+ //
+ // Go through the children list to fill the configured children's address pairs.
+ //
+ 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) {
+ Ip6AddressPair->InstanceHandle = IpInstance->Handle;
+ Ip6AddressPair->PrefixLength = IpInstance->PrefixLength;
+ IP6_COPY_ADDRESS (&Ip6AddressPair->Ip6Address, &IpInstance->ConfigData.StationAddress);
+
+ Ip6AddressPair++;
+ }
+ }
+
+ //
+ // Get the mac string.
+ //
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &NewMacString);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ if (IpSb->MacString != NULL) {
+ //
+ // The variable is set already, we're going to update it.
+ //
+ if (StrCmp (IpSb->MacString, NewMacString) != 0) {
+ //
+ // The mac address is changed, delete the previous variable first.
+ //
+ gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+ }
+
+ FreePool (IpSb->MacString);
+ }
+
+ IpSb->MacString = NewMacString;
+
+ Status = gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableDataSize,
+ (VOID *) Ip6VariableData
+ );
+
+Exit:
+ FreePool (Ip6VariableData);
+ return Status;
+}
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in] IpSb Ip6 service binding instance.
+
+**/
+VOID
+Ip6ClearVariableData (
+ IN IP6_SERVICE *IpSb
+ )
+{
+ ASSERT (IpSb->MacString != NULL);
+
+ gRT->SetVariable (
+ IpSb->MacString,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0,
+ NULL
+ );
+
+ FreePool (IpSb->MacString);
+ IpSb->MacString = NULL;
+}
+
+/**
+ 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/NetworkPkg/Ip6Dxe/Ip6Common.h b/NetworkPkg/Ip6Dxe/Ip6Common.h
new file mode 100644
index 0000000000..c3755f4859
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Common.h
@@ -0,0 +1,338 @@
+/** @file
+ Common definition and functions for IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __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 _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
+ );
+
+/**
+ Set the Ip6 variable data.
+
+ @param[in] IpSb Points to an IP6 service binding instance
+
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the variable.
+ @retval other Set variable failed.
+
+**/
+EFI_STATUS
+Ip6SetVariableData (
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ Clear the variable and free the resource.
+
+ @param[in] IpSb Ip6 service binding instance.
+
+**/
+VOID
+Ip6ClearVariableData (
+ IN IP6_SERVICE *IpSb
+ );
+
+/**
+ 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/NetworkPkg/Ip6Dxe/Ip6Config.vfr b/NetworkPkg/Ip6Dxe/Ip6Config.vfr
new file mode 100644
index 0000000000..902cef6209
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Config.vfr
@@ -0,0 +1,170 @@
+/** @file
+ VFR file used by the IP6 configuration component.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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),
+ class = EFI_NETWORK_DEVICE_CLASS,
+ subclass = 0x03,
+
+ varstore IP6_CONFIG_IFR_NVDATA,
+ name = IP6_CONFIG_IFR_NVDATA,
+ guid = IP6_CONFIG_NVDATA_GUID;
+
+ 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),
+ text = STRING_TOKEN (STR_NULL),
+ 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;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_CONFIG_CHANGES;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_IGNORE_CONFIG_CHANGES;
+
+ endform;
+
+endformset;
+
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
new file mode 100644
index 0000000000..3cfd1f2104
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.c
@@ -0,0 +1,2369 @@
+/** @file
+ The implementation of EFI IPv6 Configuration Protocol.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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;
+
+ //
+ // 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
+ );
+ }
+
+ //
+ // All IPv6 children that use global unicast address as it's source address
+ // should be destryoed 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
+ );
+
+ NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
+ //
+ // remove all pending 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->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 paramters 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 (IP6_CONFIG_DHCP6_OPTION_ORO);
+ Oro->OpLen = HTONS (2);
+ *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_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. Othewise, 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 still error or the variable is corrupted.
+ // Fall back to the default value.
+ //
+ FreePool (Variable);
+
+ //
+ // Remove the problematic variable and return EFI_NOT_FOUND, a new
+ // variable will be set again.
+ //
+ gRT->SetVariable (
+ VarName,
+ &gEfiIp6ConfigProtocolGuid,
+ IP6_CONFIG_VARIABLE_ATTRIBUTE,
+ 0,
+ NULL
+ );
+
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // 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.
+ //
+ DataItem->Data.Ptr = AllocatePool (DataRecord.DataSize);
+ if (DataItem->Data.Ptr == NULL) {
+ //
+ // no memory resource
+ //
+ continue;
+ }
+ }
+
+ Data = (CHAR8 *) Variable + DataRecord.Offset;
+ CopyMem (DataItem->Data.Ptr, Data, DataRecord.DataSize);
+
+ DataItem->DataSize = DataRecord.DataSize;
+ DataItem->Status = EFI_SUCCESS;
+ }
+
+ FreePool (Variable);
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Write the configuration data from 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 = 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 inteface 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 {
+
+ if (NewPolicy == Ip6ConfigPolicyAutomatic) {
+ //
+ // 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);
+ } else {
+ //
+ // 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] 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 Ip6ConfigSetMaunualAddress.
+
+ @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;
+
+ 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 asynchrous 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
+Ip6ConfigSetMaunualAddress (
+ 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;
+
+ ASSERT (Instance->DataItem[Ip6ConfigDataTypeManualAddress].Status != EFI_NOT_READY);
+
+ if (((DataSize % sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS)) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip6ConfigPolicyManual) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ 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;
+ }
+ }
+ }
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+
+ //
+ // 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.
+ //
+ DataItem = &Instance->DataItem[Ip6ConfigDataTypeManualAddress];
+ 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
+ );
+
+ //
+ // 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;
+ }
+
+ 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;
+
+ if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip6ConfigPolicyManual) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ 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;
+ }
+ }
+ }
+
+ IpSb = IP6_SERVICE_FROM_IP6_CONFIG_INSTANCE (Instance);
+ Item = &Instance->DataItem[Ip6ConfigDataTypeGateway];
+ OldGateway = Item->Data.Gateway;
+ OldGatewayCount = Item->DataSize / sizeof (EFI_IPv6_ADDRESS);
+ OneRemoved = FALSE;
+ OneAdded = FALSE;
+
+ if (NewGatewayCount != OldGatewayCount) {
+ Tmp = AllocatePool (DataSize);
+ if (Tmp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ } else {
+ Tmp = NULL;
+ }
+
+ for (Index1 = 0; Index1 < OldGatewayCount; Index1++) {
+ //
+ // Find the gateways that are no long in the new setting and remove them.
+ //
+ for (Index2 = 0; Index2 < NewGatewayCount; Index2++) {
+ if (EFI_IP6_EQUAL (OldGateway + Index1, NewGateway + Index2)) {
+ OneRemoved = TRUE;
+ break;
+ }
+ }
+
+ if (Index2 == NewGatewayCount) {
+ //
+ // Remove this default router.
+ //
+ DefaultRouter = Ip6FindDefaultRouter (IpSb, OldGateway + Index1);
+ if (DefaultRouter != NULL) {
+ Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
+ }
+ }
+ }
+
+ 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;
+ }
+}
+
+/**
+ 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;
+ UINTN Index1;
+ EFI_IPv6_ADDRESS *OldDns;
+ EFI_IPv6_ADDRESS *NewDns;
+ UINTN OldDnsCount;
+ UINTN NewDnsCount;
+ IP6_CONFIG_DATA_ITEM *Item;
+ BOOLEAN OneAdded;
+ VOID *Tmp;
+
+ if ((DataSize % sizeof (EFI_IPv6_ADDRESS) != 0) || (DataSize == 0)) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (Instance->Policy != Ip6ConfigPolicyManual) {
+ return EFI_WRITE_PROTECTED;
+ }
+
+ Item = &Instance->DataItem[Ip6ConfigDataTypeDnsServer];
+ 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.
+ //
+ FreePool (Tmp);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index1 = NewIndex + 1; Index1 < NewDnsCount; Index1++) {
+ if (EFI_IP6_EQUAL (NewDns + NewIndex, NewDns + Index1)) {
+ 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;
+ 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
+ )
+{
+ IfInfo->Name[0] = L'e';
+ IfInfo->Name[1] = L't';
+ IfInfo->Name[2] = L'h';
+ IfInfo->Name[3] = (CHAR16) (L'0' + IpSb->Ip6ConfigInstance.IfIndex);
+ IfInfo->Name[4] = 0;
+
+ 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 == IP6_CONFIG_DHCP6_OPTION_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.
+ //
+ 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.
+ - Data is NULL.
+ - One or more fields in Data do not match the requirement of the
+ data type indicated by DataType.
+ @retval EFI_WRITE_PROTECTED The specified configuration data is read-only or the specified
+ configuration data cannot be set under the current policy.
+ @retval EFI_ACCESS_DENIED Another set operation on the specified configuration
+ data is already in process.
+ @retval EFI_NOT_READY An asynchronous process was invoked to set the specified
+ configuration data, and the process is not finished yet.
+ @retval EFI_BAD_BUFFER_SIZE The DataSize does not match the size of the type
+ indicated by DataType.
+ @retval EFI_UNSUPPORTED This DataType is not supported.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system error or network error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+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)) {
+ 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 asynchornous 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 = Ip6ConfigPolicyAutomatic;
+ 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 = Ip6ConfigSetMaunualAddress;
+ 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);
+}
+
+/**
+ Destory 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 destory 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/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
new file mode 100644
index 0000000000..5ae483931a
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigImpl.h
@@ -0,0 +1,295 @@
+/** @file
+ Definitions for EFI IPv6 Configuartion Protocol implementation.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __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 IP6_CONFIG_DHCP6_OPTION_ORO 6
+#define IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS 23
+
+#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;
+ UINTN 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;
+};
+
+/**
+ 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
+ );
+
+/**
+ Destory 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 destory the child.
+
+**/
+EFI_STATUS
+Ip6ConfigDestroyDhcp6 (
+ IN OUT IP6_CONFIG_INSTANCE *Instance
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c
new file mode 100644
index 0000000000..9ec4886726
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.c
@@ -0,0 +1,2116 @@
+/** @file
+ Helper functions for configuring or obtaining the parameters relating to IP6.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_GUID mIp6HiiVendorDevicePathGuid = IP6_HII_VENDOR_DEVICE_PATH_GUID;
+EFI_GUID mIp6ConfigNvDataGuid = IP6_CONFIG_NVDATA_GUID;
+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 illustarate 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
+ &mIp6ConfigNvDataGuid, // 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;
+ }
+
+ 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,
+ address info, and route table information. The address information is also used as the
+ content of manual addresses in IP6 UI.
+
+ @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 *LinkLocalStr;
+ CHAR16 PortString[ADDRESS_STR_MAX_SIZE];
+ CHAR16 FormatString[8];
+ EFI_STRING_ID StringId;
+ EFI_STATUS Status;
+
+ 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) {
+ StrCpy (PortString, IP6_ETHERNET);
+ } else if (IfInfo->IfType == Ip6InterfaceTypeExperimentalEthernet) {
+ StrCpy (PortString, 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) {
+ StrCpy (FormatString, L"0%x-");
+ } else {
+ StrCpy (FormatString, 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;
+ }
+
+ //
+ // Print the host address Information.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvHostAddress,
+ IfInfo->AddressInfo,
+ IfInfo->AddressInfoCount
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Copy the Host Address Info to manual address field.
+ // Do not copy the link local address.
+ //
+ LinkLocalStr = StrStr (PortString, IP6_LINK_LOCAL_PREFIX);
+ if (LinkLocalStr != NULL) {
+ Number = LinkLocalStr - PortString;
+ if (Number > 0) {
+ CopyMem (IfrNvData->ManualAddress, PortString, Number * sizeof (CHAR16));
+ }
+
+ while ((*LinkLocalStr != L' ') && (*LinkLocalStr != L'\0')) {
+ LinkLocalStr++;
+ }
+
+ if (*LinkLocalStr != L'\0') {
+ LinkLocalStr++;
+ StrCat (IfrNvData->ManualAddress, LinkLocalStr);
+ }
+ } else {
+ StrCpy (IfrNvData->ManualAddress, PortString);
+ }
+
+ //
+ // Print the route table information.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvRouteTable,
+ IfInfo->RouteTable,
+ IfInfo->RouteCount
+ );
+ return Status;
+}
+
+/**
+ 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
+ )
+{
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ UINTN DataSize;
+ VOID *Data;
+ EFI_STATUS Status;
+ EFI_IP6_CONFIG_INTERFACE_ID InterfaceId;
+ 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;
+ 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 (&InterfaceId, DataSize);
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ Ip6ConfigDataTypeAltInterfaceId,
+ &DataSize,
+ &InterfaceId
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Ip6ConvertInterfaceIdToString (IfrNvData->InterfaceId, &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;
+
+ //
+ // Get DNS server list.
+ //
+ FreePool (Data);
+ Data = NULL;
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDnsServer,
+ &DataSize,
+ (VOID **) &Data
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ if (DataSize > 0) {
+ //
+ // Convert the DNS server address to string and draw it to UI.
+ //
+ Status = Ip6ConvertAddressListToString (
+ IfrNvData->DnsAddress,
+ HiiHandle,
+ Ip6ConfigNvDnsAddress,
+ Data,
+ DataSize / sizeof (EFI_IPv6_ADDRESS)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ FreePool (Data);
+ Data = NULL;
+ }
+
+ //
+ // Get gateway adderss list.
+ //
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeGateway,
+ &DataSize,
+ (VOID **) &Data
+ );
+
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ if (DataSize > 0) {
+ //
+ // Convert the gateway address to string and draw it to UI.
+ //
+ Status = Ip6ConvertAddressListToString (
+ IfrNvData->GatewayAddress,
+ HiiHandle,
+ Ip6ConfigNvGatewayAddress,
+ Data,
+ DataSize / sizeof (EFI_IPv6_ADDRESS)
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+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. If under manual policy, the 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
+Ip6ConvertIfrNvDataToConfigNvData (
+ 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;
+ }
+
+ 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;
+ }
+
+
+ if (Ip6NvData->Policy == Ip6ConfigPolicyAutomatic) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // 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, &mIp6ConfigNvDataGuid, 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 (
+ &mIp6ConfigNvDataGuid,
+ 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
+ beginn ing 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, &mIp6ConfigNvDataGuid, mIp6ConfigStorageName)) {
+ *Progress = Configuration;
+ return EFI_NOT_FOUND;
+ }
+
+ *Progress = Configuration + StrLen (Configuration);
+
+ 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;
+ IP6_CONFIG_IFR_NVDATA OldIfrNvData;
+ EFI_STATUS Status;
+ EFI_INPUT_KEY Key;
+ IP6_CONFIG_INSTANCE *Instance;
+ IP6_CONFIG_NVDATA *Ip6NvData;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ EFI_IP6_CONFIG_INTERFACE_INFO *Data;
+ UINTN DataSize;
+ CHAR16 PortString[ADDRESS_STR_MAX_SIZE];
+ EFI_HII_HANDLE HiiHandle;
+ EFI_IP6_CONFIG_INTERFACE_INFO *IfInfo;
+
+ 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) {
+ //
+ // Update main Form when main Form is opened.
+ // This will be done only in FORM_OPEN CallBack of question with KEY_INTERFACE_ID from main Form.
+ //
+ if (QuestionId != KEY_INTERFACE_ID) {
+ return EFI_SUCCESS;
+ }
+
+ Ip6Config = &Instance->Ip6Config;
+ HiiHandle = Instance->CallbackInfo.RegisteredHandle;
+
+ //
+ // Get the current interface info.
+ //
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeInterfaceInfo,
+ &DataSize,
+ (VOID **) &Data
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Generate the 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)) {
+ goto Exit;
+ }
+
+ //
+ // Generate the dynamic text opcode for route table and draw it.
+ //
+ Status = Ip6ConvertAddressListToString (
+ PortString,
+ HiiHandle,
+ Ip6ConfigNvRouteTable,
+ IfInfo->RouteTable,
+ IfInfo->RouteCount
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Get DNS server list.
+ //
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDnsServer,
+ &DataSize,
+ (VOID **) &Data
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ 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)) {
+ goto Exit;
+ }
+ }
+
+ //
+ // Get gateway adderss list.
+ //
+ DataSize = 0;
+ Status = Ip6ConfigNvGetData (
+ Ip6Config,
+ Ip6ConfigDataTypeGateway,
+ &DataSize,
+ (VOID **) &Data
+ );
+ if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
+ goto Exit;
+ }
+
+ 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)) {
+ goto Exit;
+ }
+ }
+
+Exit:
+ FreePool (Data);
+ return Status;
+ }
+
+ if (Action == EFI_BROWSER_ACTION_FORM_CLOSE) {
+ //
+ // Do nothing for UEFI FORM_CLOSE action
+ //
+ return EFI_SUCCESS;
+ }
+
+ 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;
+
+ ZeroMem (&OldIfrNvData, BufferSize);
+
+ HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);
+
+ CopyMem (&OldIfrNvData, IfrNvData, BufferSize);
+
+ switch (QuestionId) {
+ 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;
+
+ case KEY_SAVE_CONFIG_CHANGES:
+ CopyMem (&OldIfrNvData, IfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_IGNORE_CONFIG_CHANGES:
+ CopyMem (IfrNvData, &OldIfrNvData, sizeof (IP6_CONFIG_IFR_NVDATA));
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_SAVE_CHANGES:
+ Status = Ip6ConvertIfrNvDataToConfigNvData (IfrNvData, Instance);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
+ 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, &mIp6HiiVendorDevicePathGuid);
+
+ 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 (
+ &mIp6ConfigNvDataGuid,
+ CallbackInfo->ChildHandle,
+ Ip6DxeStrings,
+ Ip6ConfigBin,
+ NULL
+ );
+ if (CallbackInfo->RegisteredHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Append MAC string in the menu string and tile string
+ //
+ Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &MacString);
+ if (!EFI_ERROR (Status)) {
+ OldMenuString = HiiGetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),
+ NULL)
+ ;
+ UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP6_CONFIG_FORM_TITLE),
+ MenuString,
+ NULL
+ );
+ UnicodeSPrint (PortString, 128, L"MAC:%s", MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_IP6_DEVICE_FORM_TITLE),
+ 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/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h
new file mode 100644
index 0000000000..d184776707
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6ConfigNv.h
@@ -0,0 +1,73 @@
+/** @file
+ The header file of Ip6ConfigNv.c.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IP6_CONFIGNV_H_
+#define _IP6_CONFIGNV_H_
+
+#include "Ip6NvData.h"
+#include "Ip6ConfigImpl.h"
+
+extern UINT8 Ip6ConfigBin[];
+extern UINT8 Ip6DxeStrings[];
+
+#define IP6_HII_VENDOR_DEVICE_PATH_GUID \
+ { \
+ 0x13288098, 0xb11f, 0x45b9, { 0xbc, 0x4f, 0x91, 0xb5, 0x4b, 0xa3, 0x39, 0xb9 } \
+ }
+
+#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/NetworkPkg/Ip6Dxe/Ip6Driver.c b/NetworkPkg/Ip6Dxe/Ip6Driver.c
new file mode 100644
index 0000000000..388dadef8d
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Driver.c
@@ -0,0 +1,930 @@
+/** @file
+ The driver binding and service binding protocol for IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = {
+ Ip6DriverBindingSupported,
+ Ip6DriverBindingStart,
+ Ip6DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+/**
+ 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
+ )
+{
+ 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 destory 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;
+
+ Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance);
+
+ //
+ // 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);
+ }
+
+ 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;
+ }
+ //
+ // 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;
+ IP6_CONFIG_DATA_ITEM *DataItem;
+
+ 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->InDestroy = FALSE;
+
+ 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;
+ }
+
+ //
+ // 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 ON_ERROR;
+ }
+
+ //
+ // 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 ON_ERROR;
+ }
+
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ Ip6OnFrameReceived,
+ &IpSb->RecvRequest,
+ &MnpToken->Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip6ReceiveFrame (Ip6AcceptFrame, 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;
+ }
+
+ //
+ // If there is any manual address, set it.
+ //
+ DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress];
+ if (DataItem->Data.Ptr != NULL) {
+ DataItem->SetData (
+ &IpSb->Ip6ConfigInstance,
+ DataItem->DataSize,
+ DataItem->Data.Ptr
+ );
+ }
+
+ 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_SUCCES 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;
+
+ //
+ // 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);
+
+ //
+ // Install the Ip6ServiceBinding Protocol onto ControlerHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &ControllerHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ &IpSb->ServiceBinding,
+ &gEfiIp6ConfigProtocolGuid,
+ &IpSb->Ip6ConfigInstance.Ip6Config,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+
+ Ip6CleanService (IpSb);
+ FreePool (IpSb);
+ } else {
+ //
+ // Initialize the IP6 ID
+ //
+ mIp6Id = NET_RANDOM (NetRandomInitSeed ());
+
+ Ip6SetVariableData (IpSb);
+ }
+
+ return Status;
+}
+
+/**
+ 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;
+ IP6_PROTOCOL *IpInstance;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ BOOLEAN IsDhcp6;
+ EFI_TPL OldTpl;
+ INTN State;
+
+ IsDhcp6 = FALSE;
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
+
+ if (NicHandle != NULL) {
+ //
+ // DriverBindingStop is triggered by the uninstallation of the EFI DHCPv6
+ // Protocol used by Ip6Config.
+ //
+ IsDhcp6 = TRUE;
+ } else {
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ 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);
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (IpSb->InDestroy) {
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ if (IsDhcp6) {
+
+ Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance);
+ gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event);
+ IpSb->Ip6ConfigInstance.Dhcp6Event = NULL;
+ } else if (NumberOfChildren == 0) {
+
+ IpSb->InDestroy = TRUE;
+ State = IpSb->State;
+ IpSb->State = IP6_SERVICE_DESTROY;
+
+ //
+ // Clear the variable data.
+ //
+ Ip6ClearVariableData (IpSb);
+
+ 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);
+ } else {
+ //
+ // NumberOfChildren is not zero, destroy all IP6 children instances.
+ //
+ while (!IsListEmpty (&IpSb->Children)) {
+ IpInstance = NET_LIST_HEAD (&IpSb->Children, IP6_PROTOCOL, Link);
+ ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle);
+ }
+
+ if (IpSb->NumChildren != 0) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+Exit:
+
+ gBS->RestoreTPL (OldTpl);
+
+ 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_SUCCES The child handle was created with the I/O services.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+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_SUCCES 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 not a valid EFI Handle.
+ @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;
+ INTN State;
+
+ 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 destory all of its children.
+ // when UDP driver is being stopped, it will destory all
+ // the IP child it opens.
+ //
+ if (IpInstance->State == IP6_STATE_DESTROY) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_SUCCESS;
+ }
+
+ State = IpInstance->State;
+ IpInstance->State = IP6_STATE_DESTROY;
+
+ //
+ // 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 eaxmple, 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 preceeding. If any packets not recycled,
+ // that means there is a resource leak.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiIp6ProtocolGuid,
+ &IpInstance->Ip6Proto
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = Ip6CleanProtocol (IpInstance);
+
+ Ip6SetVariableData (IpSb);
+
+ 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:
+ IpInstance->State = State;
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.h b/NetworkPkg/Ip6Dxe/Ip6Driver.h
new file mode 100644
index 0000000000..fcb92ab946
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Driver.h
@@ -0,0 +1,185 @@
+/** @file
+ The driver binding and service binding protocol for IP6 driver.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __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;
+
+/**
+ 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 destory 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 Drivr 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_SUCCES 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 ptototypes for the ServiceBinding Prococol
+//
+
+/**
+ 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_SUCCES The child handle was created with the I/O services.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
+ the child.
+ @retval other The child handle was not created.
+
+**/
+EFI_STATUS
+EFIAPI
+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_SUCCES 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 not a valid EFI Handle.
+ @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/NetworkPkg/Ip6Dxe/Ip6Dxe.inf b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf
new file mode 100644
index 0000000000..aeb341cdc3
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Dxe.inf
@@ -0,0 +1,100 @@
+## @file
+# Component description file for Ip6 module.
+#
+# Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Ip6Dxe
+ FILE_GUID = 5BEDB5CC-D830-4eb2-8742-2D4CC9B54F2C
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Ip6DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF 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
+
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DevicePathLib
+ HiiLib
+ UefiHiiServicesLib
+ PrintLib
+ MemoryAllocationLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+ UefiLib
+ DebugLib
+ NetLib
+ DpcLib
+
+[Protocols]
+ gEfiManagedNetworkServiceBindingProtocolGuid
+ gEfiManagedNetworkProtocolGuid
+ gEfiIp6ServiceBindingProtocolGuid
+ gEfiIp6ProtocolGuid
+ gEfiIp6ConfigProtocolGuid
+ gEfiDhcp6ServiceBindingProtocolGuid
+ gEfiDhcp6ProtocolGuid
+ gEfiIpSecProtocolGuid
+ gEfiHiiConfigAccessProtocolGuid
+
+[Guids]
+ gEfiIfrTianoGuid ## CONSUMES ## GUID
diff --git a/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
new file mode 100644
index 0000000000..cf85e08795
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6DxeStrings.uni
Binary files differ
diff --git a/NetworkPkg/Ip6Dxe/Ip6Icmp.c b/NetworkPkg/Ip6Dxe/Ip6Icmp.c
new file mode 100644
index 0000000000..db40b81d5e
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.c
@@ -0,0 +1,684 @@
+/** @file
+ The ICMPv6 handle routines to process the ICMPv6 control messages.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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 eror 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 malformated.
+ @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_NUM));
+
+ if (PrefixLength == 0) {
+ ZeroMem (Prefix, sizeof (EFI_IPv6_ADDRESS));
+ return ;
+ }
+
+ if (PrefixLength == IP6_PREFIX_NUM - 1) {
+ return ;
+ }
+
+ Byte = (UINT8) (PrefixLength / 8);
+ Bit = (UINT8) (PrefixLength % 8);
+ Value = Prefix->Addr[Byte];
+
+ if ((Byte > 0) && (Byte < 16)) {
+ 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 malformated.
+ @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/NetworkPkg/Ip6Dxe/Ip6Icmp.h b/NetworkPkg/Ip6Dxe/Ip6Icmp.h
new file mode 100644
index 0000000000..6852ae5490
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Icmp.h
@@ -0,0 +1,108 @@
+/** @file
+ Header file for ICMPv6 protocol.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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 malformated.
+ @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 malformated.
+ @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/NetworkPkg/Ip6Dxe/Ip6If.c b/NetworkPkg/Ip6Dxe/Ip6If.c
new file mode 100644
index 0000000000..198b547ed2
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6If.c
@@ -0,0 +1,802 @@
+/** @file
+ Implement IP6 pesudo interface.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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 mulitcast 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;
+ }
+
+ //
+ // Destory 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 deliever 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 recieve request has been started.
+
+**/
+EFI_STATUS
+Ip6ReceiveFrame (
+ IN IP6_FRAME_CALLBACK CallBack,
+ IN IP6_SERVICE *IpSb
+ )
+{
+ EFI_STATUS Status;
+ IP6_LINK_RX_TOKEN *Token;
+
+ if (IpSb->InDestroy) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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 funtion 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/NetworkPkg/Ip6Dxe/Ip6If.h b/NetworkPkg/Ip6Dxe/Ip6If.h
new file mode 100644
index 0000000000..736035ec39
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6If.h
@@ -0,0 +1,267 @@
+/** @file
+ Definition for IP6 pesudo interface structure.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __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 recieve 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/NetworkPkg/Ip6Dxe/Ip6Impl.c b/NetworkPkg/Ip6Dxe/Ip6Impl.c
new file mode 100644
index 0000000000..9b34eceeb7
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Impl.c
@@ -0,0 +1,1857 @@
+/** @file
+ Implementation of EFI_IP6_PROTOCOL protocol interfaces.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "Ip6Impl.h"
+
+EFI_IPSEC_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;
+}
+
+/**
+ Intiialize 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.DefaulProtocol 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) {
+ 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 DESTORY, 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);
+
+ //
+ // Update the variable data.
+ //
+ Ip6SetVariableData (IpInstance->Service);
+
+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 PrefixLegth 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_NUM)) {
+ 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 againist.
+
+ @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 initates 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;
+ IP6_SERVICE *IpSb;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if (This == 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;
+ }
+
+ 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/NetworkPkg/Ip6Dxe/Ip6Impl.h b/NetworkPkg/Ip6Dxe/Ip6Impl.h
new file mode 100644
index 0000000000..524de5e256
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Impl.h
@@ -0,0 +1,751 @@
+/** @file
+ Implementation of EFI_IP6_PROTOCOL protocol interfaces and type definitions.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_IP6_IMPL_H__
+#define __EFI_IP6_IMPL_H__
+
+#include <Uefi.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
+#define IP6_STATE_DESTROY 2
+
+//
+// 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_IPSEC_PROTOCOL *mIpSec;
+
+//
+// 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/deliverd 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;
+};
+
+struct _IP6_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+ INTN State;
+ BOOLEAN InDestroy;
+
+ //
+ // 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.DefaulProtocol 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 PrefixLegth 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 initates 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/NetworkPkg/Ip6Dxe/Ip6Input.c b/NetworkPkg/Ip6Dxe/Ip6Input.c
new file mode 100644
index 0000000000..c18811b611
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Input.c
@@ -0,0 +1,1710 @@
+/** @file
+ IP6 internal functions to process the incoming packets.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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 ((Cur != ListHead) && ((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 reasembly 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 totoal length is know
+ // 2. received all the data. If the last fragment on the
+ // queue ends at the total length, all data is received.
+ //
+ if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {
+
+ RemoveEntryList (&Assemble->Link);
+
+ //
+ // If the packet is properly formated, 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 exluded.
+ //
+ 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] 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] ExtHdrs The caller-supplied options.
+ @param[in] 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 suffcient 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 EFI_IP6_HEADER *Head,
+ IN OUT UINT8 *LastHead,
+ IN OUT NET_BUF **Netbuf,
+ IN VOID *ExtHdrs,
+ IN UINT32 ExtHdrsLen,
+ IN EFI_IPSEC_TRAFFIC_DIR Direction,
+ IN VOID *Context
+ )
+{
+ NET_FRAGMENT *FragmentTable;
+ UINT32 FragmentCount;
+ EFI_EVENT RecycleEvent;
+ NET_BUF *Packet;
+ IP6_TXTOKEN_WRAP *TxWrap;
+ IP6_IPSEC_WRAP *IpSecWrap;
+ EFI_STATUS Status;
+ EFI_IP6_HEADER *PacketHead;
+ UINT8 *Buf;
+
+ Status = EFI_SUCCESS;
+ Packet = *Netbuf;
+ RecycleEvent = NULL;
+ IpSecWrap = NULL;
+ FragmentTable = NULL;
+ PacketHead = NULL;
+ Buf = NULL;
+ TxWrap = (IP6_TXTOKEN_WRAP *) Context;
+ FragmentCount = Packet->BlockOpNum;
+
+ if (mIpSec == NULL) {
+ gBS->LocateProtocol (&gEfiIpSecProtocolGuid, NULL, (VOID **) &mIpSec);
+
+ //
+ // Check whether the ipsec protocol is available.
+ //
+ if (mIpSec == NULL) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // 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);
+
+ if (EFI_ERROR(Status)) {
+ FreePool (FragmentTable);
+ goto ON_EXIT;
+ }
+
+ //
+ // Convert host byte order to network byte order
+ //
+ Ip6NtohHead (Head);
+
+ Status = mIpSec->Process (
+ mIpSec,
+ IpSb->Controller,
+ IP_VERSION_6,
+ (VOID *) Head,
+ LastHead,
+ NULL,
+ 0,
+ (EFI_IPSEC_FRAGMENT_DATA **) (&FragmentTable),
+ &FragmentCount,
+ Direction,
+ &RecycleEvent
+ );
+ //
+ // Convert back to host byte order
+ //
+ Ip6NtohHead (Head);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (Direction == EfiIPsecOutBound && TxWrap != NULL) {
+
+ TxWrap->IpSecRecycleSignal = RecycleEvent;
+ TxWrap->Packet = NetbufFromExt (
+ FragmentTable,
+ FragmentCount,
+ IP6_MAX_HEADLEN,
+ 0,
+ Ip6FreeTxToken,
+ TxWrap
+ );
+ if (TxWrap->Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ *Netbuf = TxWrap->Packet;
+
+ } else {
+
+ IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP));
+
+ if (IpSecWrap == NULL) {
+ goto ON_EXIT;
+ }
+
+ IpSecWrap->IpSecRecycleSignal = RecycleEvent;
+ IpSecWrap->Packet = Packet;
+ Packet = NetbufFromExt (
+ FragmentTable,
+ FragmentCount,
+ IP6_MAX_HEADLEN,
+ 0,
+ Ip6IpSecFree,
+ IpSecWrap
+ );
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ if (Direction == EfiIPsecInBound) {
+
+ PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (
+ Packet,
+ sizeof (EFI_IP6_HEADER) + ExtHdrsLen,
+ NET_BUF_HEAD
+ );
+ if (PacketHead == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
+ 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;
+}
+
+/**
+ 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;
+ IP6_CLIP_INFO *Info;
+ EFI_IP6_HEADER *Head;
+ UINT16 PayloadLen;
+ UINT8 *Payload;
+ UINT16 TotalLen;
+ UINT8 *LastHead;
+ UINT32 FormerHeadOffset;
+ UINT32 UnFragmentLen;
+ UINT32 ExtHdrsLen;
+ UINT32 HeadLen;
+ BOOLEAN Fragmented;
+ IP6_FRAGMENT_HEADER *FragmentHead;
+ UINT16 FragmentOffset;
+ EFI_STATUS Status;
+ EFI_IPv6_ADDRESS Loopback;
+
+ IpSb = (IP6_SERVICE *) Context;
+ NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
+
+ Payload = NULL;
+
+ //
+ // Check input parameters
+ //
+ if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) {
+ goto Drop;
+ }
+
+ //
+ // Check whether the input packet is a valid packet
+ //
+ if (Packet->TotalSize < IP6_MIN_HEADLEN) {
+ goto Restart;
+ }
+
+ //
+ // Get header information of the packet.
+ //
+ Head = (EFI_IP6_HEADER *) NetbufGetByte (Packet, 0, NULL);
+ if (Head == NULL) {
+ goto Restart;
+ }
+
+ //
+ // Multicast addresses must not be used as source addresses in IPv6 packets.
+ //
+ if ((Head->Version != 6) || (IP6_IS_MULTICAST (&Head->SourceAddress))) {
+ goto Restart;
+ }
+
+ //
+ // 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))) {
+ goto Restart;
+ }
+
+ //
+ // 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) {
+ goto Restart;
+ }
+
+
+ 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) {
+ goto Restart;
+ }
+
+ //
+ // Check the extension headers, if exist validate them
+ //
+ if (PayloadLen != 0) {
+ Payload = AllocatePool ((UINTN) PayloadLen);
+ if (Payload == NULL) {
+ goto Restart;
+ }
+
+ NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);
+ }
+
+ LastHead = NULL;
+ if (!Ip6IsExtsValid (
+ IpSb,
+ Packet,
+ &Head->NextHeader,
+ Payload,
+ (UINT32) PayloadLen,
+ TRUE,
+ &FormerHeadOffset,
+ &LastHead,
+ &ExtHdrsLen,
+ &UnFragmentLen,
+ &Fragmented
+ )) {
+ goto Restart;
+ }
+
+ 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) {
+ goto Restart;
+ }
+
+ 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)) {
+ goto Restart;
+ }
+
+ //
+ // Reassemble the packet.
+ //
+ Packet = Ip6Reassemble (&IpSb->Assemble, Packet);
+ if (Packet == NULL) {
+ goto Restart;
+ }
+
+ //
+ // 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) {
+ goto Restart;
+ }
+
+ NetbufCopy (Packet, sizeof (EFI_IP6_HEADER), PayloadLen, Payload);
+ }
+
+ if (!Ip6IsExtsValid (
+ IpSb,
+ Packet,
+ &Head->NextHeader,
+ Payload,
+ (UINT32) PayloadLen,
+ TRUE,
+ NULL,
+ &LastHead,
+ &ExtHdrsLen,
+ &UnFragmentLen,
+ &Fragmented
+ )) {
+ goto Restart;
+ }
+ }
+
+ //
+ // Trim the head off, after this point, the packet is headless.
+ // and Packet->TotalLen == Info->Length.
+ //
+ NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + ExtHdrsLen, TRUE);
+
+ //
+ // 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,
+ NULL,
+ 0,
+ EfiIPsecInBound,
+ NULL
+ );
+
+ if (EFI_ERROR(Status)) {
+ goto Restart;
+ }
+
+ //
+ // TODO: may check the last head again, the same as the output routine
+ //
+
+ //
+ // Packet may have been changed. The ownership of the packet
+ // is transfered 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 implmentation. 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;
+ }
+
+ //
+ // Enque 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, enque 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/NetworkPkg/Ip6Dxe/Ip6Input.h b/NetworkPkg/Ip6Dxe/Ip6Input.h
new file mode 100644
index 0000000000..8594896521
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Input.h
@@ -0,0 +1,235 @@
+/** @file
+ IP6 internal functions and definitions to process the incoming packets.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __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 IPsec protocol to process the inbound or
+ outbound IP packets. The process routine handls 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] 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] ExtHdrs The caller supplied options.
+ @param[in] 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 suffcient resources to complete the operation.
+ @retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than the
+ number of input data blocks when building a fragment table.
+
+**/
+EFI_STATUS
+Ip6IpSecProcessPacket (
+ IN IP6_SERVICE *IpSb,
+ IN EFI_IP6_HEADER *Head,
+ IN OUT UINT8 *LastHead,
+ IN OUT NET_BUF **Netbuf,
+ IN VOID *ExtHdrs,
+ IN 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 enque 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 bacause 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/NetworkPkg/Ip6Dxe/Ip6Mld.c b/NetworkPkg/Ip6Dxe/Ip6Mld.c
new file mode 100644
index 0000000000..4a418fade5
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Mld.c
@@ -0,0 +1,908 @@
+/** @file
+ Multicast Listener Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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_SUCESS 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 twcie after short delays [Unsolicited Report Interval] (default:10s)
+ // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss.
+ //
+ 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 configuraton.
+ @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/NetworkPkg/Ip6Dxe/Ip6Mld.h b/NetworkPkg/Ip6Dxe/Ip6Mld.h
new file mode 100644
index 0000000000..e69f5d56b5
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Mld.h
@@ -0,0 +1,198 @@
+/** @file
+ Multicast Listener Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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 configuraton.
+ @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/NetworkPkg/Ip6Dxe/Ip6Nd.c b/NetworkPkg/Ip6Dxe/Ip6Nd.c
new file mode 100644
index 0000000000..f2a47a8073
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Nd.c
@@ -0,0 +1,3141 @@
+/** @file
+ Implementation of Neighbor Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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_NUM) {
+ 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;
+}
+
+/**
+ Destory 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.
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.
+
+**/
+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;
+
+ 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 (IP6_CONFIG_DHCP6_OPTION_ORO);
+ Oro->OpLen = HTONS (2);
+ *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_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);
+ //
+ // 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);
+
+ 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);
+ Option = NetbufGetByte (Packet, IP6_ND_LENGTH, 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) {
+ if (DupAddrDetect->Transmit == 0) {
+ //
+ // The NS is from another node to performing DAD on the same address since
+ // we haven't send out any NS yet. Fail DAD for the tentative address.
+ //
+ Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
+ Status = EFI_ICMP_ERROR;
+ goto Exit;
+ }
+
+ //
+ // 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 unspeicifed, 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);
+ Option = NetbufGetByte (Packet, IP6_ND_LENGTH, 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;
+ UINT16 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);
+ Option = NetbufGetByte (Packet, IP6_RA_LENGTH, 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 advertisment 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.
+ //
+ Offset = 16;
+ while (Offset < 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);
+ if (LinkLayerOption.Length <= 0) {
+ goto Exit;
+ }
+
+ 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 = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8);
+ break;
+ case Ip6OptionPrefixInfo:
+ NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption);
+ if (PrefixOption.Length != 4) {
+ goto Exit;
+ }
+ 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 uniqeness 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 equls 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);
+ if (MTUOption.Length != 1) {
+ goto Exit;
+ }
+
+ //
+ // 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);
+ if (Length <= 0) {
+ goto Exit;
+ }
+
+ Offset = (UINT16) (Offset + (UINT16) 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 Insuffcient 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);
+ Option = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, 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) maitain 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/NetworkPkg/Ip6Dxe/Ip6Nd.h b/NetworkPkg/Ip6Dxe/Ip6Nd.h
new file mode 100644
index 0000000000..57fb8feca3
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Nd.h
@@ -0,0 +1,750 @@
+/** @file
+ Definition of Neighbor Discovery support routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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_ETHE_ADDR_OPTION {
+ UINT8 Type;
+ UINT8 Length;
+ UINT8 EtherAddr[6];
+} IP6_ETHER_ADDR_OPTION;
+
+typedef struct _IP6_MTU_OPTION {
+ UINT8 Type;
+ UINT8 Length;
+ UINT16 Reserved;
+ UINT32 Mtu;
+} IP6_MTU_OPTION;
+
+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;
+
+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.
+ @param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.
+
+**/
+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
+ );
+
+/**
+ Destory 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 Insuffcient 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) maitain 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/NetworkPkg/Ip6Dxe/Ip6NvData.h b/NetworkPkg/Ip6Dxe/Ip6NvData.h
new file mode 100644
index 0000000000..715473fde3
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6NvData.h
@@ -0,0 +1,70 @@
+/** @file
+ NVData structure used by the IP6 configuration component.
+
+ Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _IP6_NV_DATA_H_
+#define _IP6_NV_DATA_H_
+
+#define IP6_CONFIG_NVDATA_GUID \
+ { \
+ 0x2eea107, 0x98db, 0x400e, { 0x98, 0x30, 0x46, 0xa, 0x15, 0x42, 0xd7, 0x99 } \
+ }
+
+#define FORMID_MAIN_FORM 1
+#define FORMID_MANUAL_CONFIG_FORM 2
+
+#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 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 24
+#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/NetworkPkg/Ip6Dxe/Ip6Option.c b/NetworkPkg/Ip6Dxe/Ip6Option.c
new file mode 100644
index 0000000000..9a91fd7cd1
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Option.c
@@ -0,0 +1,758 @@
+/** @file
+ IP6 option support functions and routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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 malformated.
+
+ @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 malformated.
+
+**/
+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 malformated.
+
+**/
+BOOLEAN
+Ip6IsNDOptionValid (
+ IN UINT8 *Option,
+ IN UINT16 OptionLen
+ )
+{
+ UINT16 Offset;
+ UINT8 OptionType;
+ UINT16 Length;
+
+ Offset = 0;
+
+ while (Offset < OptionLen) {
+ OptionType = *(Option + Offset);
+ Length = (UINT16) (*(Option + Offset + 1) * 8);
+
+ switch (OptionType) {
+ case Ip6OptionPrefixInfo:
+ if (Length != 32) {
+ return FALSE;
+ }
+
+ break;
+
+ case Ip6OptionMtu:
+ if (Length != 8) {
+ return FALSE;
+ }
+
+ break;
+
+ default:
+ //
+ // Check the length of Ip6OptionEtherSource, Ip6OptionEtherTarget, and
+ // Ip6OptionRedirected here. For unrecognized options, silently ignore
+ // and continue processsing the message.
+ //
+ if (Length == 0) {
+ return FALSE;
+ }
+
+ break;
+ }
+
+ Offset = (UINT16) (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-formated.
+
+ @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 formated.
+ @retval FALSE The option is malformated.
+
+**/
+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 octects 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 responsiblity 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/NetworkPkg/Ip6Dxe/Ip6Option.h b/NetworkPkg/Ip6Dxe/Ip6Option.h
new file mode 100644
index 0000000000..b62a04216e
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Option.h
@@ -0,0 +1,191 @@
+/** @file
+ Definition of IP6 option process routines.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#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-formated.
+
+ @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 formated.
+ @retval FALSE The option is malformated.
+
+**/
+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 responsiblity 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 malformated.
+
+**/
+BOOLEAN
+Ip6IsNDOptionValid (
+ IN UINT8 *Option,
+ IN UINT16 OptionLen
+ );
+
+#endif
diff --git a/NetworkPkg/Ip6Dxe/Ip6Output.c b/NetworkPkg/Ip6Dxe/Ip6Output.c
new file mode 100644
index 0000000000..baa4904fc9
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Output.c
@@ -0,0 +1,1077 @@
+/** @file
+ The internal functions and routines to transmit the IP6 packet.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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 responsiblity 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;
+}
+
+/**
+ Caculate 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 enought 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;
+
+ //
+ // 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.
+ //
+ 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 immediatly.
+ //
+ 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 integar 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 UnFragnmentLen 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/NetworkPkg/Ip6Dxe/Ip6Output.h b/NetworkPkg/Ip6Dxe/Ip6Output.h
new file mode 100644
index 0000000000..80abe858e6
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Output.h
@@ -0,0 +1,141 @@
+/** @file
+ The internal functions and routines to transmit the IP6 packet.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __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/NetworkPkg/Ip6Dxe/Ip6Route.c b/NetworkPkg/Ip6Dxe/Ip6Route.c
new file mode 100644
index 0000000000..bba365c152
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Route.c
@@ -0,0 +1,635 @@
+/** @file
+ The functions and routines to handle the route caches and route table.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "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 memeory; 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 destionation 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_NUM - 1; 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 messasge 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_NUM - 1; 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_NUM; 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_NUM; 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/NetworkPkg/Ip6Dxe/Ip6Route.h b/NetworkPkg/Ip6Dxe/Ip6Route.h
new file mode 100644
index 0000000000..d81e07b19c
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Route.h
@@ -0,0 +1,299 @@
+/** @file
+ EFI IP6 route table and route cache table defintions.
+
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __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 messasge 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 memeory. 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 destionation 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
+ properity.
+
+**/
+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