/** @file | |
The implementation of common functions shared by IP6 driver. | |
Copyright (c) 2009 - 2014, 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++; | |
} | |
/** | |
Callback function which provided by user to remove one node in NetDestroyLinkList process. | |
@param[in] Entry The entry to be removed. | |
@param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. | |
@retval EFI_SUCCESS The entry has been removed successfully. | |
@retval Others Fail to remove the entry. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Ip6DestroyChildEntryByAddr ( | |
IN LIST_ENTRY *Entry, | |
IN VOID *Context | |
) | |
{ | |
IP6_PROTOCOL *Instance; | |
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; | |
EFI_IPv6_ADDRESS *Address; | |
Instance = NET_LIST_USER_STRUCT_S (Entry, IP6_PROTOCOL, Link, IP6_PROTOCOL_SIGNATURE); | |
ServiceBinding = ((IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT*) Context)->ServiceBinding; | |
Address = ((IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT*) Context)->Address; | |
if ((Instance->State == IP6_STATE_CONFIGED) && EFI_IP6_EQUAL (&Instance->ConfigData.StationAddress, Address)) { | |
return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Destroy the IP instance if its StationAddress is removed. It is the help function | |
for Ip6RemoveAddr(). | |
@param[in, out] IpSb Points to an IP6 service binding instance. | |
@param[in] Address The to be removed address | |
**/ | |
VOID | |
Ip6DestroyInstanceByAddress ( | |
IN OUT IP6_SERVICE *IpSb, | |
IN EFI_IPv6_ADDRESS *Address | |
) | |
{ | |
LIST_ENTRY *List; | |
IP6_DESTROY_CHILD_BY_ADDR_CALLBACK_CONTEXT Context; | |
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE); | |
List = &IpSb->Children; | |
Context.ServiceBinding = &IpSb->ServiceBinding; | |
Context.Address = Address; | |
NetDestroyLinkList ( | |
List, | |
Ip6DestroyChildEntryByAddr, | |
&Context, | |
NULL | |
); | |
} | |
/** | |
Remove the IPv6 address from the address list node points to IP6_ADDRESS_INFO. | |
This function removes the matching IPv6 addresses from the address list and | |
adjusts the address count of the address list. If IpSb is not NULL, this function | |
calls Ip6LeaveGroup to see whether it should call Mnp->Groups() to remove the | |
its solicited-node multicast MAC address from the filter list and sends out | |
a Multicast Listener Done. If Prefix is NULL, all address in the address list | |
will be removed. If Prefix is not NULL, the address that matching the Prefix | |
with PrefixLength in the address list will be removed. | |
@param[in] IpSb NULL or points to IP6 service binding instance. | |
@param[in, out] AddressList Address list array. | |
@param[in, out] AddressCount The count of addresses in address list array. | |
@param[in] Prefix NULL or an IPv6 address prefix. | |
@param[in] PrefixLength The length of Prefix. | |
@retval EFI_SUCCESS The operation completed successfully. | |
@retval EFI_NOT_FOUND The address matching the Prefix with PrefixLength | |
cannot be found in the address list. | |
@retval EFI_INVALID_PARAMETER Any input parameter is invalid. | |
**/ | |
EFI_STATUS | |
Ip6RemoveAddr ( | |
IN IP6_SERVICE *IpSb OPTIONAL, | |
IN OUT LIST_ENTRY *AddressList, | |
IN OUT UINT32 *AddressCount, | |
IN EFI_IPv6_ADDRESS *Prefix OPTIONAL, | |
IN UINT8 PrefixLength | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *Next; | |
IP6_ADDRESS_INFO *AddrInfo; | |
EFI_IPv6_ADDRESS SnMCastAddr; | |
if (IsListEmpty (AddressList) || *AddressCount < 1 || PrefixLength > IP6_PREFIX_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); | |
} | |
/** | |
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; | |
} | |