mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-25 11:55:28 +01:00
3174 lines
100 KiB
C
3174 lines
100 KiB
C
/** @file
|
|
Implementation of Neighbor Discovery support routines.
|
|
|
|
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "Ip6Impl.h"
|
|
|
|
EFI_MAC_ADDRESS mZeroMacAddress;
|
|
|
|
/**
|
|
Update the ReachableTime in IP6 service binding instance data, in milliseconds.
|
|
|
|
@param[in, out] IpSb Points to the IP6_SERVICE.
|
|
|
|
**/
|
|
VOID
|
|
Ip6UpdateReachableTime (
|
|
IN OUT IP6_SERVICE *IpSb
|
|
)
|
|
{
|
|
UINT32 Random;
|
|
|
|
Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE;
|
|
Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED;
|
|
IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE;
|
|
}
|
|
|
|
/**
|
|
Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number
|
|
of EFI_IP6_NEIGHBOR_CACHE is also returned.
|
|
|
|
@param[in] IpInstance The pointer to IP6_PROTOCOL instance.
|
|
@param[out] NeighborCount The number of returned neighbor cache entries.
|
|
@param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.
|
|
|
|
@retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6BuildEfiNeighborCache (
|
|
IN IP6_PROTOCOL *IpInstance,
|
|
OUT UINT32 *NeighborCount,
|
|
OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache
|
|
)
|
|
{
|
|
IP6_NEIGHBOR_ENTRY *Neighbor;
|
|
LIST_ENTRY *Entry;
|
|
IP6_SERVICE *IpSb;
|
|
UINT32 Count;
|
|
EFI_IP6_NEIGHBOR_CACHE *EfiNeighborCache;
|
|
EFI_IP6_NEIGHBOR_CACHE *NeighborCacheTmp;
|
|
|
|
NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
|
|
ASSERT (NeighborCount != NULL && NeighborCache != NULL);
|
|
|
|
IpSb = IpInstance->Service;
|
|
Count = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {
|
|
Count++;
|
|
}
|
|
|
|
if (Count == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE));
|
|
if (NeighborCacheTmp == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
*NeighborCount = Count;
|
|
Count = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {
|
|
Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
|
|
|
|
EfiNeighborCache = NeighborCacheTmp + Count;
|
|
|
|
EfiNeighborCache->State = Neighbor->State;
|
|
IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor);
|
|
IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress);
|
|
|
|
Count++;
|
|
}
|
|
|
|
ASSERT (*NeighborCount == Count);
|
|
*NeighborCache = NeighborCacheTmp;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number
|
|
of prefix entries is also returned.
|
|
|
|
@param[in] IpInstance The pointer to IP6_PROTOCOL instance.
|
|
@param[out] PrefixCount The number of returned prefix entries.
|
|
@param[out] PrefixTable The pointer to the array of PrefixTable.
|
|
|
|
@retval EFI_SUCCESS The prefix table successfully built.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6BuildPrefixTable (
|
|
IN IP6_PROTOCOL *IpInstance,
|
|
OUT UINT32 *PrefixCount,
|
|
OUT EFI_IP6_ADDRESS_INFO **PrefixTable
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
IP6_SERVICE *IpSb;
|
|
UINT32 Count;
|
|
IP6_PREFIX_LIST_ENTRY *PrefixList;
|
|
EFI_IP6_ADDRESS_INFO *EfiPrefix;
|
|
EFI_IP6_ADDRESS_INFO *PrefixTableTmp;
|
|
|
|
NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
|
|
ASSERT (PrefixCount != NULL && PrefixTable != NULL);
|
|
|
|
IpSb = IpInstance->Service;
|
|
Count = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
|
|
Count++;
|
|
}
|
|
|
|
if (Count == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO));
|
|
if (PrefixTableTmp == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
*PrefixCount = Count;
|
|
Count = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
|
|
PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
|
|
EfiPrefix = PrefixTableTmp + Count;
|
|
IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix);
|
|
EfiPrefix->PrefixLength = PrefixList->PrefixLength;
|
|
|
|
Count++;
|
|
}
|
|
|
|
ASSERT (*PrefixCount == Count);
|
|
*PrefixTable = PrefixTableTmp;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Allocate and initialize a IP6 prefix list entry.
|
|
|
|
@param[in] IpSb The pointer to IP6_SERVICE instance.
|
|
@param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list.
|
|
Otherwise, it is created for the autoconfiguration prefix list.
|
|
@param[in] ValidLifetime The length of time in seconds that the prefix
|
|
is valid for the purpose of on-link determination.
|
|
@param[in] PreferredLifetime The length of time in seconds that addresses
|
|
generated from the prefix via stateless address
|
|
autoconfiguration remain preferred.
|
|
@param[in] PrefixLength The prefix length of the Prefix.
|
|
@param[in] Prefix The prefix address.
|
|
|
|
@return NULL if it failed to allocate memory for the prefix node. Otherwise, point
|
|
to the created or existing prefix list entry.
|
|
|
|
**/
|
|
IP6_PREFIX_LIST_ENTRY *
|
|
Ip6CreatePrefixListEntry (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN BOOLEAN OnLinkOrAuto,
|
|
IN UINT32 ValidLifetime,
|
|
IN UINT32 PreferredLifetime,
|
|
IN UINT8 PrefixLength,
|
|
IN EFI_IPv6_ADDRESS *Prefix
|
|
)
|
|
{
|
|
IP6_PREFIX_LIST_ENTRY *PrefixEntry;
|
|
IP6_ROUTE_ENTRY *RtEntry;
|
|
LIST_ENTRY *ListHead;
|
|
LIST_ENTRY *Entry;
|
|
IP6_PREFIX_LIST_ENTRY *TmpPrefixEntry;
|
|
|
|
if ((Prefix == NULL) || (PreferredLifetime > ValidLifetime) || (PrefixLength > IP6_PREFIX_MAX)) {
|
|
return NULL;
|
|
}
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
|
|
PrefixEntry = Ip6FindPrefixListEntry (
|
|
IpSb,
|
|
OnLinkOrAuto,
|
|
PrefixLength,
|
|
Prefix
|
|
);
|
|
if (PrefixEntry != NULL) {
|
|
PrefixEntry->RefCnt++;
|
|
return PrefixEntry;
|
|
}
|
|
|
|
PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY));
|
|
if (PrefixEntry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
PrefixEntry->RefCnt = 1;
|
|
PrefixEntry->ValidLifetime = ValidLifetime;
|
|
PrefixEntry->PreferredLifetime = PreferredLifetime;
|
|
PrefixEntry->PrefixLength = PrefixLength;
|
|
IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix);
|
|
|
|
ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix;
|
|
|
|
//
|
|
// Create a direct route entry for on-link prefix and insert to route area.
|
|
//
|
|
if (OnLinkOrAuto) {
|
|
RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL);
|
|
if (RtEntry == NULL) {
|
|
FreePool (PrefixEntry);
|
|
return NULL;
|
|
}
|
|
|
|
RtEntry->Flag = IP6_DIRECT_ROUTE;
|
|
InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link);
|
|
IpSb->RouteTable->TotalNum++;
|
|
}
|
|
|
|
//
|
|
// Insert the prefix entry in the order that a prefix with longer prefix length
|
|
// is put ahead in the list.
|
|
//
|
|
NET_LIST_FOR_EACH (Entry, ListHead) {
|
|
TmpPrefixEntry = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
|
|
|
|
if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
NetListInsertBefore (Entry, &PrefixEntry->Link);
|
|
|
|
return PrefixEntry;
|
|
}
|
|
|
|
/**
|
|
Destroy a IP6 prefix list entry.
|
|
|
|
@param[in] IpSb The pointer to IP6_SERVICE instance.
|
|
@param[in] PrefixEntry The to be destroyed prefix list entry.
|
|
@param[in] OnLinkOrAuto If TRUE, the entry is removed from on link prefix list.
|
|
Otherwise remove from autoconfiguration prefix list.
|
|
@param[in] ImmediateDelete If TRUE, remove the entry directly.
|
|
Otherwise, check the reference count to see whether
|
|
it should be removed.
|
|
|
|
**/
|
|
VOID
|
|
Ip6DestroyPrefixListEntry (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN IP6_PREFIX_LIST_ENTRY *PrefixEntry,
|
|
IN BOOLEAN OnLinkOrAuto,
|
|
IN BOOLEAN ImmediateDelete
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
IP6_INTERFACE *IpIf;
|
|
EFI_STATUS Status;
|
|
|
|
if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) {
|
|
return;
|
|
}
|
|
|
|
if (OnLinkOrAuto) {
|
|
//
|
|
// Remove the direct route for onlink prefix from route table.
|
|
//
|
|
do {
|
|
Status = Ip6DelRoute (
|
|
IpSb->RouteTable,
|
|
&PrefixEntry->Prefix,
|
|
PrefixEntry->PrefixLength,
|
|
NULL
|
|
);
|
|
} while (Status != EFI_NOT_FOUND);
|
|
} else {
|
|
//
|
|
// Remove the corresponding addresses generated from this autonomous prefix.
|
|
//
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
|
|
IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);
|
|
|
|
Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength);
|
|
}
|
|
}
|
|
|
|
RemoveEntryList (&PrefixEntry->Link);
|
|
FreePool (PrefixEntry);
|
|
}
|
|
|
|
/**
|
|
Search the list array to find an IP6 prefix list entry.
|
|
|
|
@param[in] IpSb The pointer to IP6_SERVICE instance.
|
|
@param[in] OnLinkOrAuto If TRUE, the search the link prefix list,
|
|
Otherwise search the autoconfiguration prefix list.
|
|
@param[in] PrefixLength The prefix length of the Prefix
|
|
@param[in] Prefix The prefix address.
|
|
|
|
@return NULL if cannot find the IP6 prefix list entry. Otherwise, return the
|
|
pointer to the IP6 prefix list entry.
|
|
|
|
**/
|
|
IP6_PREFIX_LIST_ENTRY *
|
|
Ip6FindPrefixListEntry (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN BOOLEAN OnLinkOrAuto,
|
|
IN UINT8 PrefixLength,
|
|
IN EFI_IPv6_ADDRESS *Prefix
|
|
)
|
|
{
|
|
IP6_PREFIX_LIST_ENTRY *PrefixList;
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *ListHead;
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
ASSERT (Prefix != NULL);
|
|
|
|
if (OnLinkOrAuto) {
|
|
ListHead = &IpSb->OnlinkPrefix;
|
|
} else {
|
|
ListHead = &IpSb->AutonomousPrefix;
|
|
}
|
|
|
|
NET_LIST_FOR_EACH (Entry, ListHead) {
|
|
PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
|
|
if (PrefixLength != 255) {
|
|
//
|
|
// Perform exactly prefix match.
|
|
//
|
|
if ((PrefixList->PrefixLength == PrefixLength) &&
|
|
NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength))
|
|
{
|
|
return PrefixList;
|
|
}
|
|
} else {
|
|
//
|
|
// Perform the longest prefix match. The list is already sorted with
|
|
// the longest length prefix put at the head of the list.
|
|
//
|
|
if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) {
|
|
return PrefixList;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Release the resource in the prefix list table, and destroy the list entry and
|
|
corresponding addresses or route entries.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] ListHead The list entry head of the prefix list table.
|
|
|
|
**/
|
|
VOID
|
|
Ip6CleanPrefixListTable (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN LIST_ENTRY *ListHead
|
|
)
|
|
{
|
|
IP6_PREFIX_LIST_ENTRY *PrefixList;
|
|
BOOLEAN OnLink;
|
|
|
|
OnLink = (BOOLEAN)(ListHead == &IpSb->OnlinkPrefix);
|
|
|
|
while (!IsListEmpty (ListHead)) {
|
|
PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link);
|
|
Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Callback function when address resolution is finished. It will cancel
|
|
all the queued frames if the address resolution failed, or transmit them
|
|
if the request succeeded.
|
|
|
|
@param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.
|
|
|
|
**/
|
|
VOID
|
|
Ip6OnArpResolved (
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
IP6_NEIGHBOR_ENTRY *ArpQue;
|
|
IP6_SERVICE *IpSb;
|
|
IP6_LINK_TX_TOKEN *Token;
|
|
EFI_STATUS Status;
|
|
BOOLEAN Sent;
|
|
|
|
ArpQue = (IP6_NEIGHBOR_ENTRY *)Context;
|
|
if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) {
|
|
return;
|
|
}
|
|
|
|
IpSb = ArpQue->Interface->Service;
|
|
if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// ARP resolve failed for some reason. Release all the frame
|
|
// and ARP queue itself. Ip6FreeArpQue will call the frame's
|
|
// owner back.
|
|
//
|
|
if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) {
|
|
Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// ARP resolve succeeded, Transmit all the frame.
|
|
//
|
|
Sent = FALSE;
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
|
|
RemoveEntryList (Entry);
|
|
|
|
Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
|
|
IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress);
|
|
|
|
//
|
|
// Insert the tx token before transmitting it via MNP as the FrameSentDpc
|
|
// may be called before Mnp->Transmit returns which will remove this tx
|
|
// token from the SentFrames list. Remove it from the list if the returned
|
|
// Status of Mnp->Transmit is not EFI_SUCCESS as in this case the
|
|
// FrameSentDpc won't be queued.
|
|
//
|
|
InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link);
|
|
|
|
Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);
|
|
if (EFI_ERROR (Status)) {
|
|
RemoveEntryList (&Token->Link);
|
|
Token->CallBack (Token->Packet, Status, 0, Token->Context);
|
|
|
|
Ip6FreeLinkTxToken (Token);
|
|
continue;
|
|
} else {
|
|
Sent = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the ArpQue only but not the whole neighbor entry.
|
|
//
|
|
Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL);
|
|
|
|
if (Sent && (ArpQue->State == EfiNeighborStale)) {
|
|
ArpQue->State = EfiNeighborDelay;
|
|
ArpQue->Ticks = (UINT32)IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Allocate and initialize an IP6 neighbor cache entry.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] CallBack The callback function to be called when
|
|
address resolution is finished.
|
|
@param[in] Ip6Address Points to the IPv6 address of the neighbor.
|
|
@param[in] LinkAddress Points to the MAC address of the neighbor.
|
|
Ignored if NULL.
|
|
|
|
@return NULL if failed to allocate memory for the neighbor cache entry.
|
|
Otherwise, point to the created neighbor cache entry.
|
|
|
|
**/
|
|
IP6_NEIGHBOR_ENTRY *
|
|
Ip6CreateNeighborEntry (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN IP6_ARP_CALLBACK CallBack,
|
|
IN EFI_IPv6_ADDRESS *Ip6Address,
|
|
IN EFI_MAC_ADDRESS *LinkAddress OPTIONAL
|
|
)
|
|
{
|
|
IP6_NEIGHBOR_ENTRY *Entry;
|
|
IP6_DEFAULT_ROUTER *DefaultRouter;
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
ASSERT (Ip6Address != NULL);
|
|
|
|
Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY));
|
|
if (Entry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Entry->RefCnt = 1;
|
|
Entry->IsRouter = FALSE;
|
|
Entry->ArpFree = FALSE;
|
|
Entry->Dynamic = FALSE;
|
|
Entry->State = EfiNeighborInComplete;
|
|
Entry->Transmit = IP6_MAX_MULTICAST_SOLICIT + 1;
|
|
Entry->CallBack = CallBack;
|
|
Entry->Interface = NULL;
|
|
|
|
InitializeListHead (&Entry->Frames);
|
|
|
|
IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address);
|
|
|
|
if (LinkAddress != NULL) {
|
|
IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress);
|
|
} else {
|
|
IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress);
|
|
}
|
|
|
|
InsertHeadList (&IpSb->NeighborTable, &Entry->Link);
|
|
|
|
//
|
|
// If corresponding default router entry exists, establish the relationship.
|
|
//
|
|
DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address);
|
|
if (DefaultRouter != NULL) {
|
|
DefaultRouter->NeighborCache = Entry;
|
|
}
|
|
|
|
return Entry;
|
|
}
|
|
|
|
/**
|
|
Search a IP6 neighbor cache entry.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] Ip6Address Points to the IPv6 address of the neighbor.
|
|
|
|
@return NULL if it failed to find the matching neighbor cache entry.
|
|
Otherwise, point to the found neighbor cache entry.
|
|
|
|
**/
|
|
IP6_NEIGHBOR_ENTRY *
|
|
Ip6FindNeighborEntry (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *Ip6Address
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
IP6_NEIGHBOR_ENTRY *Neighbor;
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
ASSERT (Ip6Address != NULL);
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {
|
|
Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
|
|
if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) {
|
|
RemoveEntryList (Entry);
|
|
InsertHeadList (&IpSb->NeighborTable, Entry);
|
|
|
|
return Neighbor;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Free a IP6 neighbor cache entry and remove all the frames on the address
|
|
resolution queue that pass the FrameToCancel. That is, either FrameToCancel
|
|
is NULL, or it returns true for the frame.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] NeighborCache The to be free neighbor cache entry.
|
|
@param[in] SendIcmpError If TRUE, send out ICMP error.
|
|
@param[in] FullFree If TRUE, remove the neighbor cache entry.
|
|
Otherwise remove the pending frames.
|
|
@param[in] IoStatus The status returned to the cancelled frames'
|
|
callback function.
|
|
@param[in] FrameToCancel Function to select which frame to cancel.
|
|
This is an optional parameter that may be NULL.
|
|
@param[in] Context Opaque parameter to the FrameToCancel.
|
|
Ignored if FrameToCancel is NULL.
|
|
|
|
@retval EFI_INVALID_PARAMETER The input parameter is invalid.
|
|
@retval EFI_SUCCESS The operation finished successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6FreeNeighborEntry (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN IP6_NEIGHBOR_ENTRY *NeighborCache,
|
|
IN BOOLEAN SendIcmpError,
|
|
IN BOOLEAN FullFree,
|
|
IN EFI_STATUS IoStatus,
|
|
IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,
|
|
IN VOID *Context OPTIONAL
|
|
)
|
|
{
|
|
IP6_LINK_TX_TOKEN *TxToken;
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
IP6_DEFAULT_ROUTER *DefaultRouter;
|
|
|
|
//
|
|
// If FrameToCancel fails, the token will not be released.
|
|
// To avoid the memory leak, stop this usage model.
|
|
//
|
|
if (FullFree && (FrameToCancel != NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) {
|
|
TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
|
|
|
|
if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) {
|
|
Ip6SendIcmpError (
|
|
IpSb,
|
|
TxToken->Packet,
|
|
NULL,
|
|
&TxToken->Packet->Ip.Ip6->SourceAddress,
|
|
ICMP_V6_DEST_UNREACHABLE,
|
|
ICMP_V6_ADDR_UNREACHABLE,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) {
|
|
RemoveEntryList (Entry);
|
|
TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context);
|
|
Ip6FreeLinkTxToken (TxToken);
|
|
}
|
|
}
|
|
|
|
if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) {
|
|
RemoveEntryList (&NeighborCache->ArpList);
|
|
NeighborCache->ArpFree = FALSE;
|
|
}
|
|
|
|
if (FullFree) {
|
|
if (NeighborCache->IsRouter) {
|
|
DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor);
|
|
if (DefaultRouter != NULL) {
|
|
Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
|
|
}
|
|
}
|
|
|
|
RemoveEntryList (&NeighborCache->Link);
|
|
FreePool (NeighborCache);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Allocate and initialize an IP6 default router entry.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] Ip6Address The IPv6 address of the default router.
|
|
@param[in] RouterLifetime The lifetime associated with the default
|
|
router, in units of seconds.
|
|
|
|
@return NULL if it failed to allocate memory for the default router node.
|
|
Otherwise, point to the created default router node.
|
|
|
|
**/
|
|
IP6_DEFAULT_ROUTER *
|
|
Ip6CreateDefaultRouter (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *Ip6Address,
|
|
IN UINT16 RouterLifetime
|
|
)
|
|
{
|
|
IP6_DEFAULT_ROUTER *Entry;
|
|
IP6_ROUTE_ENTRY *RtEntry;
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
ASSERT (Ip6Address != NULL);
|
|
|
|
Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER));
|
|
if (Entry == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Entry->RefCnt = 1;
|
|
Entry->Lifetime = RouterLifetime;
|
|
Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address);
|
|
IP6_COPY_ADDRESS (&Entry->Router, Ip6Address);
|
|
|
|
//
|
|
// Add a default route into route table with both Destination and PrefixLength set to zero.
|
|
//
|
|
RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address);
|
|
if (RtEntry == NULL) {
|
|
FreePool (Entry);
|
|
return NULL;
|
|
}
|
|
|
|
InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link);
|
|
IpSb->RouteTable->TotalNum++;
|
|
|
|
InsertTailList (&IpSb->DefaultRouterList, &Entry->Link);
|
|
|
|
return Entry;
|
|
}
|
|
|
|
/**
|
|
Destroy an IP6 default router entry.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] DefaultRouter The to be destroyed IP6_DEFAULT_ROUTER.
|
|
|
|
**/
|
|
VOID
|
|
Ip6DestroyDefaultRouter (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN IP6_DEFAULT_ROUTER *DefaultRouter
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
RemoveEntryList (&DefaultRouter->Link);
|
|
|
|
//
|
|
// Update the Destination Cache - all entries using the time-out router as next-hop
|
|
// should perform next-hop determination again.
|
|
//
|
|
do {
|
|
Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router);
|
|
} while (Status != EFI_NOT_FOUND);
|
|
|
|
FreePool (DefaultRouter);
|
|
}
|
|
|
|
/**
|
|
Clean an IP6 default router list.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
|
|
**/
|
|
VOID
|
|
Ip6CleanDefaultRouterList (
|
|
IN IP6_SERVICE *IpSb
|
|
)
|
|
{
|
|
IP6_DEFAULT_ROUTER *DefaultRouter;
|
|
|
|
while (!IsListEmpty (&IpSb->DefaultRouterList)) {
|
|
DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link);
|
|
Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Search a default router node from an IP6 default router list.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] Ip6Address The IPv6 address of the to be searched default router node.
|
|
|
|
@return NULL if it failed to find the matching default router node.
|
|
Otherwise, point to the found default router node.
|
|
|
|
**/
|
|
IP6_DEFAULT_ROUTER *
|
|
Ip6FindDefaultRouter (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *Ip6Address
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
IP6_DEFAULT_ROUTER *DefaultRouter;
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
ASSERT (Ip6Address != NULL);
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) {
|
|
DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);
|
|
if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) {
|
|
return DefaultRouter;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
The function to be called after DAD (Duplicate Address Detection) is performed.
|
|
|
|
@param[in] IsDadPassed If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.
|
|
@param[in] IpIf Points to the IP6_INTERFACE.
|
|
@param[in] DadEntry The DAD entry which already performed DAD.
|
|
|
|
**/
|
|
VOID
|
|
Ip6OnDADFinished (
|
|
IN BOOLEAN IsDadPassed,
|
|
IN IP6_INTERFACE *IpIf,
|
|
IN IP6_DAD_ENTRY *DadEntry
|
|
)
|
|
{
|
|
IP6_SERVICE *IpSb;
|
|
IP6_ADDRESS_INFO *AddrInfo;
|
|
EFI_DHCP6_PROTOCOL *Dhcp6;
|
|
UINT16 OptBuf[4];
|
|
EFI_DHCP6_PACKET_OPTION *Oro;
|
|
EFI_DHCP6_RETRANSMISSION InfoReqReXmit;
|
|
EFI_IPv6_ADDRESS AllNodes;
|
|
|
|
IpSb = IpIf->Service;
|
|
AddrInfo = DadEntry->AddressInfo;
|
|
|
|
if (IsDadPassed) {
|
|
//
|
|
// DAD succeed.
|
|
//
|
|
if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
|
|
ASSERT (!IpSb->LinkLocalOk);
|
|
|
|
IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address);
|
|
IpSb->LinkLocalOk = TRUE;
|
|
IpIf->Configured = TRUE;
|
|
|
|
//
|
|
// Check whether DHCP6 need to be started.
|
|
//
|
|
Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6;
|
|
|
|
if (IpSb->Dhcp6NeedStart) {
|
|
Dhcp6->Start (Dhcp6);
|
|
IpSb->Dhcp6NeedStart = FALSE;
|
|
}
|
|
|
|
if (IpSb->Dhcp6NeedInfoRequest) {
|
|
//
|
|
// Set the exta options to send. Here we only want the option request option
|
|
// with DNS SERVERS.
|
|
//
|
|
Oro = (EFI_DHCP6_PACKET_OPTION *)OptBuf;
|
|
Oro->OpCode = HTONS (DHCP6_OPT_ORO);
|
|
Oro->OpLen = HTONS (2);
|
|
*((UINT16 *)&Oro->Data[0]) = HTONS (DHCP6_OPT_DNS_SERVERS);
|
|
|
|
InfoReqReXmit.Irt = 4;
|
|
InfoReqReXmit.Mrc = 64;
|
|
InfoReqReXmit.Mrt = 60;
|
|
InfoReqReXmit.Mrd = 0;
|
|
|
|
Dhcp6->InfoRequest (
|
|
Dhcp6,
|
|
TRUE,
|
|
Oro,
|
|
0,
|
|
NULL,
|
|
&InfoReqReXmit,
|
|
IpSb->Ip6ConfigInstance.Dhcp6Event,
|
|
Ip6ConfigOnDhcp6Reply,
|
|
&IpSb->Ip6ConfigInstance
|
|
);
|
|
}
|
|
|
|
//
|
|
// Add an on-link prefix for link-local address.
|
|
//
|
|
Ip6CreatePrefixListEntry (
|
|
IpSb,
|
|
TRUE,
|
|
(UINT32)IP6_INFINIT_LIFETIME,
|
|
(UINT32)IP6_INFINIT_LIFETIME,
|
|
IP6_LINK_LOCAL_PREFIX_LENGTH,
|
|
&IpSb->LinkLocalAddr
|
|
);
|
|
} else {
|
|
//
|
|
// Global scope unicast address.
|
|
//
|
|
Ip6AddAddr (IpIf, AddrInfo);
|
|
|
|
//
|
|
// Add an on-link prefix for this address.
|
|
//
|
|
Ip6CreatePrefixListEntry (
|
|
IpSb,
|
|
TRUE,
|
|
AddrInfo->ValidLifetime,
|
|
AddrInfo->PreferredLifetime,
|
|
AddrInfo->PrefixLength,
|
|
&AddrInfo->Address
|
|
);
|
|
|
|
IpIf->Configured = TRUE;
|
|
}
|
|
} else {
|
|
//
|
|
// Leave the group we joined before.
|
|
//
|
|
Ip6LeaveGroup (IpSb, &DadEntry->Destination);
|
|
}
|
|
|
|
if (DadEntry->Callback != NULL) {
|
|
DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context);
|
|
}
|
|
|
|
if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
|
|
FreePool (AddrInfo);
|
|
RemoveEntryList (&DadEntry->Link);
|
|
FreePool (DadEntry);
|
|
//
|
|
// Leave link-scope all-nodes multicast address (FF02::1)
|
|
//
|
|
Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
|
|
Ip6LeaveGroup (IpSb, &AllNodes);
|
|
//
|
|
// Disable IP operation since link-local address is a duplicate address.
|
|
//
|
|
IpSb->LinkLocalDadFail = TRUE;
|
|
IpSb->Mnp->Configure (IpSb->Mnp, NULL);
|
|
gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
|
|
gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);
|
|
return;
|
|
}
|
|
|
|
if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
|
|
//
|
|
// Free the AddressInfo we hold if DAD fails or it is a link-local address.
|
|
//
|
|
FreePool (AddrInfo);
|
|
}
|
|
|
|
RemoveEntryList (&DadEntry->Link);
|
|
FreePool (DadEntry);
|
|
}
|
|
|
|
/**
|
|
Create a DAD (Duplicate Address Detection) entry and queue it to be performed.
|
|
|
|
@param[in] IpIf Points to the IP6_INTERFACE.
|
|
@param[in] AddressInfo The address information which needs DAD performed.
|
|
@param[in] Callback The callback routine that will be called after DAD
|
|
is performed. This is an optional parameter that
|
|
may be NULL.
|
|
@param[in] Context The opaque parameter for a DAD callback routine.
|
|
This is an optional parameter that may be NULL.
|
|
|
|
@retval EFI_SUCCESS The DAD entry was created and queued.
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate the memory to complete the
|
|
operation.
|
|
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6InitDADProcess (
|
|
IN IP6_INTERFACE *IpIf,
|
|
IN IP6_ADDRESS_INFO *AddressInfo,
|
|
IN IP6_DAD_CALLBACK Callback OPTIONAL,
|
|
IN VOID *Context OPTIONAL
|
|
)
|
|
{
|
|
IP6_DAD_ENTRY *Entry;
|
|
EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS *DadXmits;
|
|
IP6_SERVICE *IpSb;
|
|
EFI_STATUS Status;
|
|
UINT32 MaxDelayTick;
|
|
|
|
NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE);
|
|
ASSERT (AddressInfo != NULL);
|
|
|
|
//
|
|
// Do nothing if we have already started DAD on the address.
|
|
//
|
|
if (Ip6FindDADEntry (IpIf->Service, &AddressInfo->Address, NULL) != NULL) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
IpSb = IpIf->Service;
|
|
DadXmits = &IpSb->Ip6ConfigInstance.DadXmits;
|
|
|
|
//
|
|
// Allocate the resources and insert info
|
|
//
|
|
Entry = AllocatePool (sizeof (IP6_DAD_ENTRY));
|
|
if (Entry == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Map the incoming unicast address to solicited-node multicast address
|
|
//
|
|
Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination);
|
|
|
|
//
|
|
// Join in the solicited-node multicast address.
|
|
//
|
|
Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (Entry);
|
|
return Status;
|
|
}
|
|
|
|
Entry->Signature = IP6_DAD_ENTRY_SIGNATURE;
|
|
Entry->MaxTransmit = DadXmits->DupAddrDetectTransmits;
|
|
Entry->Transmit = 0;
|
|
Entry->Receive = 0;
|
|
MaxDelayTick = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS;
|
|
Entry->RetransTick = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5;
|
|
Entry->AddressInfo = AddressInfo;
|
|
Entry->Callback = Callback;
|
|
Entry->Context = Context;
|
|
InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link);
|
|
|
|
if (Entry->MaxTransmit == 0) {
|
|
//
|
|
// DAD is disabled on this interface, immediately mark this DAD successful.
|
|
//
|
|
Ip6OnDADFinished (TRUE, IpIf, Entry);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Search IP6_DAD_ENTRY from the Duplicate Address Detection List.
|
|
|
|
@param[in] IpSb The pointer to the IP6_SERVICE instance.
|
|
@param[in] Target The address information which needs DAD performed .
|
|
@param[out] Interface If not NULL, output the IP6 interface that configures
|
|
the tentative address.
|
|
|
|
@return NULL if failed to find the matching DAD entry.
|
|
Otherwise, point to the found DAD entry.
|
|
|
|
**/
|
|
IP6_DAD_ENTRY *
|
|
Ip6FindDADEntry (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *Target,
|
|
OUT IP6_INTERFACE **Interface OPTIONAL
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Entry2;
|
|
IP6_INTERFACE *IpIf;
|
|
IP6_DAD_ENTRY *DupAddrDetect;
|
|
IP6_ADDRESS_INFO *AddrInfo;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
|
|
IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
|
|
|
|
NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) {
|
|
DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);
|
|
AddrInfo = DupAddrDetect->AddressInfo;
|
|
if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) {
|
|
if (Interface != NULL) {
|
|
*Interface = IpIf;
|
|
}
|
|
|
|
return DupAddrDetect;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Generate router solicit message and send it out to Destination Address or
|
|
All Router Link Local scope multicast address.
|
|
|
|
@param[in] IpSb The IP service to send the packet.
|
|
@param[in] Interface If not NULL, points to the IP6 interface to send
|
|
the packet.
|
|
@param[in] SourceAddress If not NULL, the source address of the message.
|
|
@param[in] DestinationAddress If not NULL, the destination address of the message.
|
|
@param[in] SourceLinkAddress If not NULL, the MAC address of the source.
|
|
A source link-layer address option will be appended
|
|
to the message.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
|
|
operation.
|
|
@retval EFI_SUCCESS The router solicit message was successfully sent.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6SendRouterSolicit (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN IP6_INTERFACE *Interface OPTIONAL,
|
|
IN EFI_IPv6_ADDRESS *SourceAddress OPTIONAL,
|
|
IN EFI_IPv6_ADDRESS *DestinationAddress OPTIONAL,
|
|
IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL
|
|
)
|
|
{
|
|
NET_BUF *Packet;
|
|
EFI_IP6_HEADER Head;
|
|
IP6_ICMP_INFORMATION_HEAD *IcmpHead;
|
|
IP6_ETHER_ADDR_OPTION *LinkLayerOption;
|
|
UINT16 PayloadLen;
|
|
IP6_INTERFACE *IpIf;
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
|
|
IpIf = Interface;
|
|
if ((IpIf == NULL) && (IpSb->DefaultInterface != NULL)) {
|
|
IpIf = IpSb->DefaultInterface;
|
|
}
|
|
|
|
//
|
|
// Generate the packet to be sent
|
|
//
|
|
|
|
PayloadLen = (UINT16)sizeof (IP6_ICMP_INFORMATION_HEAD);
|
|
if (SourceLinkAddress != NULL) {
|
|
PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION);
|
|
}
|
|
|
|
Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32)PayloadLen);
|
|
if (Packet == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Create the basic IPv6 header.
|
|
//
|
|
Head.FlowLabelL = 0;
|
|
Head.FlowLabelH = 0;
|
|
Head.PayloadLength = HTONS (PayloadLen);
|
|
Head.NextHeader = IP6_ICMP;
|
|
Head.HopLimit = IP6_HOP_LIMIT;
|
|
|
|
if (SourceAddress != NULL) {
|
|
IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
|
|
} else {
|
|
ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
|
|
}
|
|
|
|
if (DestinationAddress != NULL) {
|
|
IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
|
|
} else {
|
|
Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress);
|
|
}
|
|
|
|
NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
|
|
|
|
//
|
|
// Fill in the ICMP header, and Source link-layer address if contained.
|
|
//
|
|
|
|
IcmpHead = (IP6_ICMP_INFORMATION_HEAD *)NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
|
|
ASSERT (IcmpHead != NULL);
|
|
ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
|
|
IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT;
|
|
IcmpHead->Head.Code = 0;
|
|
|
|
LinkLayerOption = NULL;
|
|
if (SourceLinkAddress != NULL) {
|
|
LinkLayerOption = (IP6_ETHER_ADDR_OPTION *)NetbufAllocSpace (
|
|
Packet,
|
|
sizeof (IP6_ETHER_ADDR_OPTION),
|
|
FALSE
|
|
);
|
|
ASSERT (LinkLayerOption != NULL);
|
|
LinkLayerOption->Type = Ip6OptionEtherSource;
|
|
LinkLayerOption->Length = (UINT8)sizeof (IP6_ETHER_ADDR_OPTION);
|
|
CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
|
|
}
|
|
|
|
//
|
|
// Transmit the packet
|
|
//
|
|
return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
|
|
}
|
|
|
|
/**
|
|
Generate a Neighbor Advertisement message and send it out to Destination Address.
|
|
|
|
@param[in] IpSb The IP service to send the packet.
|
|
@param[in] SourceAddress The source address of the message.
|
|
@param[in] DestinationAddress The destination address of the message.
|
|
@param[in] TargetIp6Address The target address field in the Neighbor Solicitation
|
|
message that prompted this advertisement.
|
|
@param[in] TargetLinkAddress The MAC address for the target, i.e. the sender
|
|
of the advertisement.
|
|
@param[in] IsRouter If TRUE, indicates the sender is a router.
|
|
@param[in] Override If TRUE, indicates the advertisement should override
|
|
an existing cache entry and update the MAC address.
|
|
@param[in] Solicited If TRUE, indicates the advertisement was sent
|
|
in response to a Neighbor Solicitation from
|
|
the Destination address.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
|
|
operation.
|
|
@retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6SendNeighborAdvertise (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *SourceAddress,
|
|
IN EFI_IPv6_ADDRESS *DestinationAddress,
|
|
IN EFI_IPv6_ADDRESS *TargetIp6Address,
|
|
IN EFI_MAC_ADDRESS *TargetLinkAddress,
|
|
IN BOOLEAN IsRouter,
|
|
IN BOOLEAN Override,
|
|
IN BOOLEAN Solicited
|
|
)
|
|
{
|
|
NET_BUF *Packet;
|
|
EFI_IP6_HEADER Head;
|
|
IP6_ICMP_INFORMATION_HEAD *IcmpHead;
|
|
IP6_ETHER_ADDR_OPTION *LinkLayerOption;
|
|
EFI_IPv6_ADDRESS *Target;
|
|
UINT16 PayloadLen;
|
|
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
|
|
//
|
|
// The Neighbor Advertisement message must include a Target link-layer address option
|
|
// when responding to multicast solicitation and should include such option when
|
|
// responding to unicast solicitation. It also must include such option as unsolicited
|
|
// advertisement.
|
|
//
|
|
ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL);
|
|
|
|
PayloadLen = (UINT16)(sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION));
|
|
|
|
//
|
|
// Generate the packet to be sent
|
|
//
|
|
|
|
Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32)PayloadLen);
|
|
if (Packet == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Create the basic IPv6 header.
|
|
//
|
|
Head.FlowLabelL = 0;
|
|
Head.FlowLabelH = 0;
|
|
Head.PayloadLength = HTONS (PayloadLen);
|
|
Head.NextHeader = IP6_ICMP;
|
|
Head.HopLimit = IP6_HOP_LIMIT;
|
|
|
|
IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
|
|
IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
|
|
|
|
NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
|
|
|
|
//
|
|
// Fill in the ICMP header, Target address, and Target link-layer address.
|
|
// Set the Router flag, Solicited flag and Override flag.
|
|
//
|
|
|
|
IcmpHead = (IP6_ICMP_INFORMATION_HEAD *)NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
|
|
ASSERT (IcmpHead != NULL);
|
|
ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
|
|
IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE;
|
|
IcmpHead->Head.Code = 0;
|
|
|
|
if (IsRouter) {
|
|
IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG;
|
|
}
|
|
|
|
if (Solicited) {
|
|
IcmpHead->Fourth |= IP6_SOLICITED_FLAG;
|
|
}
|
|
|
|
if (Override) {
|
|
IcmpHead->Fourth |= IP6_OVERRIDE_FLAG;
|
|
}
|
|
|
|
Target = (EFI_IPv6_ADDRESS *)NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
|
|
ASSERT (Target != NULL);
|
|
IP6_COPY_ADDRESS (Target, TargetIp6Address);
|
|
|
|
LinkLayerOption = (IP6_ETHER_ADDR_OPTION *)NetbufAllocSpace (
|
|
Packet,
|
|
sizeof (IP6_ETHER_ADDR_OPTION),
|
|
FALSE
|
|
);
|
|
ASSERT (LinkLayerOption != NULL);
|
|
LinkLayerOption->Type = Ip6OptionEtherTarget;
|
|
LinkLayerOption->Length = 1;
|
|
CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6);
|
|
|
|
//
|
|
// Transmit the packet
|
|
//
|
|
return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
|
|
}
|
|
|
|
/**
|
|
Generate the Neighbor Solicitation message and send it to the Destination Address.
|
|
|
|
@param[in] IpSb The IP service to send the packet
|
|
@param[in] SourceAddress The source address of the message.
|
|
@param[in] DestinationAddress The destination address of the message.
|
|
@param[in] TargetIp6Address The IP address of the target of the solicitation.
|
|
It must not be a multicast address.
|
|
@param[in] SourceLinkAddress The MAC address for the sender. If not NULL,
|
|
a source link-layer address option will be appended
|
|
to the message.
|
|
|
|
@retval EFI_INVALID_PARAMETER Any input parameter is invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
|
|
operation.
|
|
@retval EFI_SUCCESS The Neighbor Advertise message was successfully sent.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6SendNeighborSolicit (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *SourceAddress,
|
|
IN EFI_IPv6_ADDRESS *DestinationAddress,
|
|
IN EFI_IPv6_ADDRESS *TargetIp6Address,
|
|
IN EFI_MAC_ADDRESS *SourceLinkAddress OPTIONAL
|
|
)
|
|
{
|
|
NET_BUF *Packet;
|
|
EFI_IP6_HEADER Head;
|
|
IP6_ICMP_INFORMATION_HEAD *IcmpHead;
|
|
IP6_ETHER_ADDR_OPTION *LinkLayerOption;
|
|
EFI_IPv6_ADDRESS *Target;
|
|
BOOLEAN IsDAD;
|
|
UINT16 PayloadLen;
|
|
IP6_NEIGHBOR_ENTRY *Neighbor;
|
|
|
|
//
|
|
// Check input parameters
|
|
//
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
if ((DestinationAddress == NULL) || (TargetIp6Address == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
IsDAD = FALSE;
|
|
|
|
if ((SourceAddress == NULL) || ((SourceAddress != NULL) && NetIp6IsUnspecifiedAddr (SourceAddress))) {
|
|
IsDAD = TRUE;
|
|
}
|
|
|
|
//
|
|
// The Neighbor Solicitation message should include a source link-layer address option
|
|
// if the solicitation is not sent by performing DAD - Duplicate Address Detection.
|
|
// Otherwise must not include it.
|
|
//
|
|
PayloadLen = (UINT16)(sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS));
|
|
|
|
if (!IsDAD) {
|
|
if (SourceLinkAddress == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
PayloadLen = (UINT16)(PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION));
|
|
}
|
|
|
|
//
|
|
// Generate the packet to be sent
|
|
//
|
|
|
|
Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32)PayloadLen);
|
|
if (Packet == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Create the basic IPv6 header
|
|
//
|
|
Head.FlowLabelL = 0;
|
|
Head.FlowLabelH = 0;
|
|
Head.PayloadLength = HTONS (PayloadLen);
|
|
Head.NextHeader = IP6_ICMP;
|
|
Head.HopLimit = IP6_HOP_LIMIT;
|
|
|
|
if (SourceAddress != NULL) {
|
|
IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
|
|
} else {
|
|
ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
|
|
}
|
|
|
|
IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
|
|
|
|
NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
|
|
|
|
//
|
|
// Fill in the ICMP header, Target address, and Source link-layer address.
|
|
//
|
|
IcmpHead = (IP6_ICMP_INFORMATION_HEAD *)NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
|
|
ASSERT (IcmpHead != NULL);
|
|
ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
|
|
IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT;
|
|
IcmpHead->Head.Code = 0;
|
|
|
|
Target = (EFI_IPv6_ADDRESS *)NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
|
|
ASSERT (Target != NULL);
|
|
IP6_COPY_ADDRESS (Target, TargetIp6Address);
|
|
|
|
LinkLayerOption = NULL;
|
|
if (!IsDAD) {
|
|
//
|
|
// Fill in the source link-layer address option
|
|
//
|
|
LinkLayerOption = (IP6_ETHER_ADDR_OPTION *)NetbufAllocSpace (
|
|
Packet,
|
|
sizeof (IP6_ETHER_ADDR_OPTION),
|
|
FALSE
|
|
);
|
|
ASSERT (LinkLayerOption != NULL);
|
|
LinkLayerOption->Type = Ip6OptionEtherSource;
|
|
LinkLayerOption->Length = 1;
|
|
CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
|
|
}
|
|
|
|
//
|
|
// Create a Neighbor Cache entry in the INCOMPLETE state when performing
|
|
// address resolution.
|
|
//
|
|
if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) {
|
|
Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
|
|
if (Neighbor == NULL) {
|
|
Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL);
|
|
ASSERT (Neighbor != NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transmit the packet
|
|
//
|
|
return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
|
|
}
|
|
|
|
/**
|
|
Process the Neighbor Solicitation message. The message may be sent for Duplicate
|
|
Address Detection or Address Resolution.
|
|
|
|
@param[in] IpSb The IP service that received the packet.
|
|
@param[in] Head The IP head of the message.
|
|
@param[in] Packet The content of the message with IP head removed.
|
|
|
|
@retval EFI_SUCCESS The packet processed successfully.
|
|
@retval EFI_INVALID_PARAMETER The packet is invalid.
|
|
@retval EFI_ICMP_ERROR The packet indicates that DAD is failed.
|
|
@retval Others Failed to process the packet.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6ProcessNeighborSolicit (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
IP6_ICMP_INFORMATION_HEAD Icmp;
|
|
EFI_IPv6_ADDRESS Target;
|
|
IP6_ETHER_ADDR_OPTION LinkLayerOption;
|
|
BOOLEAN IsDAD;
|
|
BOOLEAN IsUnicast;
|
|
BOOLEAN IsMaintained;
|
|
IP6_DAD_ENTRY *DupAddrDetect;
|
|
IP6_INTERFACE *IpIf;
|
|
IP6_NEIGHBOR_ENTRY *Neighbor;
|
|
BOOLEAN Solicited;
|
|
BOOLEAN UpdateCache;
|
|
EFI_IPv6_ADDRESS Dest;
|
|
UINT16 OptionLen;
|
|
UINT8 *Option;
|
|
BOOLEAN Provided;
|
|
EFI_STATUS Status;
|
|
VOID *MacAddress;
|
|
|
|
NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
|
|
NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
|
|
|
|
//
|
|
// Perform Message Validation:
|
|
// The IP Hop Limit field has a value of 255, i.e., the packet
|
|
// could not possibly have been forwarded by a router.
|
|
// ICMP Code is 0.
|
|
// Target Address is not a multicast address.
|
|
//
|
|
Status = EFI_INVALID_PARAMETER;
|
|
|
|
if ((Head->HopLimit != IP6_HOP_LIMIT) || (Icmp.Head.Code != 0) || !NetIp6IsValidUnicast (&Target)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// ICMP length is 24 or more octets.
|
|
//
|
|
OptionLen = 0;
|
|
if (Head->PayloadLength < IP6_ND_LENGTH) {
|
|
goto Exit;
|
|
} else {
|
|
OptionLen = (UINT16)(Head->PayloadLength - IP6_ND_LENGTH);
|
|
if (OptionLen != 0) {
|
|
Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
|
|
ASSERT (Option != NULL);
|
|
|
|
//
|
|
// All included options should have a length that is greater than zero.
|
|
//
|
|
if (!Ip6IsNDOptionValid (Option, OptionLen)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
IsDAD = NetIp6IsUnspecifiedAddr (&Head->SourceAddress);
|
|
IsUnicast = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress);
|
|
IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL);
|
|
|
|
Provided = FALSE;
|
|
if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
|
|
NetbufCopy (
|
|
Packet,
|
|
IP6_ND_LENGTH,
|
|
sizeof (IP6_ETHER_ADDR_OPTION),
|
|
(UINT8 *)&LinkLayerOption
|
|
);
|
|
//
|
|
// The solicitation for neighbor discovery should include a source link-layer
|
|
// address option. If the option is not recognized, silently ignore it.
|
|
//
|
|
if (LinkLayerOption.Type == Ip6OptionEtherSource) {
|
|
if (IsDAD) {
|
|
//
|
|
// If the IP source address is the unspecified address, the source
|
|
// link-layer address option must not be included in the message.
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
Provided = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the IP source address is the unspecified address, the IP
|
|
// destination address is a solicited-node multicast address.
|
|
//
|
|
if (IsDAD && IsUnicast) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If the target address is tentative, and the source address is a unicast address,
|
|
// the solicitation's sender is performing address resolution on the target;
|
|
// the solicitation should be silently ignored.
|
|
//
|
|
if (!IsDAD && !IsMaintained) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If received unicast neighbor solicitation but destination is not this node,
|
|
// drop the packet.
|
|
//
|
|
if (IsUnicast && !IsMaintained) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// In DAD, when target address is a tentative address,
|
|
// process the received neighbor solicitation message but not send out response.
|
|
//
|
|
if (IsDAD && !IsMaintained) {
|
|
DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
|
|
if (DupAddrDetect != NULL) {
|
|
//
|
|
// Check the MAC address of the incoming packet.
|
|
//
|
|
if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) {
|
|
goto Exit;
|
|
}
|
|
|
|
MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress;
|
|
if (MacAddress != NULL) {
|
|
if (CompareMem (
|
|
MacAddress,
|
|
&IpSb->SnpMode.CurrentAddress,
|
|
IpSb->SnpMode.HwAddressSize
|
|
) != 0)
|
|
{
|
|
//
|
|
// The NS is from another node to performing DAD on the same address.
|
|
// Fail DAD for the tentative address.
|
|
//
|
|
Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
|
|
Status = EFI_ICMP_ERROR;
|
|
} else {
|
|
//
|
|
// The below layer loopback the NS we sent. Record it and wait for more.
|
|
//
|
|
DupAddrDetect->Receive++;
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// If the solicitation does not contain a link-layer address, DO NOT create or
|
|
// update the neighbor cache entries.
|
|
//
|
|
if (Provided) {
|
|
Neighbor = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
|
|
UpdateCache = FALSE;
|
|
|
|
if (Neighbor == NULL) {
|
|
Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL);
|
|
if (Neighbor == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
UpdateCache = TRUE;
|
|
} else {
|
|
if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) {
|
|
UpdateCache = TRUE;
|
|
}
|
|
}
|
|
|
|
if (UpdateCache) {
|
|
Neighbor->State = EfiNeighborStale;
|
|
Neighbor->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
|
|
//
|
|
// Send queued packets if exist.
|
|
//
|
|
Neighbor->CallBack ((VOID *)Neighbor);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sends a Neighbor Advertisement as response.
|
|
// Set the Router flag to zero since the node is a host.
|
|
// If the source address of the solicitation is unspecified, and target address
|
|
// is one of the maintained address, reply a unsolicited multicast advertisement.
|
|
//
|
|
if (IsDAD && IsMaintained) {
|
|
Solicited = FALSE;
|
|
Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest);
|
|
} else {
|
|
Solicited = TRUE;
|
|
IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress);
|
|
}
|
|
|
|
Status = Ip6SendNeighborAdvertise (
|
|
IpSb,
|
|
&Target,
|
|
&Dest,
|
|
&Target,
|
|
&IpSb->SnpMode.CurrentAddress,
|
|
FALSE,
|
|
TRUE,
|
|
Solicited
|
|
);
|
|
Exit:
|
|
NetbufFree (Packet);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Process the Neighbor Advertisement message.
|
|
|
|
@param[in] IpSb The IP service that received the packet.
|
|
@param[in] Head The IP head of the message.
|
|
@param[in] Packet The content of the message with IP head removed.
|
|
|
|
@retval EFI_SUCCESS The packet processed successfully.
|
|
@retval EFI_INVALID_PARAMETER The packet is invalid.
|
|
@retval EFI_ICMP_ERROR The packet indicates that DAD is failed.
|
|
@retval Others Failed to process the packet.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6ProcessNeighborAdvertise (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
IP6_ICMP_INFORMATION_HEAD Icmp;
|
|
EFI_IPv6_ADDRESS Target;
|
|
IP6_ETHER_ADDR_OPTION LinkLayerOption;
|
|
BOOLEAN Provided;
|
|
INTN Compare;
|
|
IP6_NEIGHBOR_ENTRY *Neighbor;
|
|
IP6_DEFAULT_ROUTER *DefaultRouter;
|
|
BOOLEAN Solicited;
|
|
BOOLEAN IsRouter;
|
|
BOOLEAN Override;
|
|
IP6_DAD_ENTRY *DupAddrDetect;
|
|
IP6_INTERFACE *IpIf;
|
|
UINT16 OptionLen;
|
|
UINT8 *Option;
|
|
EFI_STATUS Status;
|
|
|
|
NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
|
|
NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
|
|
|
|
//
|
|
// Validate the incoming Neighbor Advertisement
|
|
//
|
|
Status = EFI_INVALID_PARAMETER;
|
|
//
|
|
// The IP Hop Limit field has a value of 255, i.e., the packet
|
|
// could not possibly have been forwarded by a router.
|
|
// ICMP Code is 0.
|
|
// Target Address is not a multicast address.
|
|
//
|
|
if ((Head->HopLimit != IP6_HOP_LIMIT) || (Icmp.Head.Code != 0) || !NetIp6IsValidUnicast (&Target)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// ICMP length is 24 or more octets.
|
|
//
|
|
Provided = FALSE;
|
|
OptionLen = 0;
|
|
if (Head->PayloadLength < IP6_ND_LENGTH) {
|
|
goto Exit;
|
|
} else {
|
|
OptionLen = (UINT16)(Head->PayloadLength - IP6_ND_LENGTH);
|
|
if (OptionLen != 0) {
|
|
Option = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
|
|
ASSERT (Option != NULL);
|
|
|
|
//
|
|
// All included options should have a length that is greater than zero.
|
|
//
|
|
if (!Ip6IsNDOptionValid (Option, OptionLen)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the IP destination address is a multicast address, Solicited Flag is ZERO.
|
|
//
|
|
Solicited = FALSE;
|
|
if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) {
|
|
Solicited = TRUE;
|
|
}
|
|
|
|
if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// DAD - Check whether the Target is one of our tentative address.
|
|
//
|
|
DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
|
|
if (DupAddrDetect != NULL) {
|
|
//
|
|
// DAD fails, some other node is using this address.
|
|
//
|
|
NetbufFree (Packet);
|
|
Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
|
|
return EFI_ICMP_ERROR;
|
|
}
|
|
|
|
//
|
|
// Search the Neighbor Cache for the target's entry. If no entry exists,
|
|
// the advertisement should be silently discarded.
|
|
//
|
|
Neighbor = Ip6FindNeighborEntry (IpSb, &Target);
|
|
if (Neighbor == NULL) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Get IsRouter Flag and Override Flag
|
|
//
|
|
IsRouter = FALSE;
|
|
Override = FALSE;
|
|
if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) {
|
|
IsRouter = TRUE;
|
|
}
|
|
|
|
if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) {
|
|
Override = TRUE;
|
|
}
|
|
|
|
//
|
|
// Check whether link layer option is included.
|
|
//
|
|
if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
|
|
NetbufCopy (
|
|
Packet,
|
|
IP6_ND_LENGTH,
|
|
sizeof (IP6_ETHER_ADDR_OPTION),
|
|
(UINT8 *)&LinkLayerOption
|
|
);
|
|
|
|
if (LinkLayerOption.Type == Ip6OptionEtherTarget) {
|
|
Provided = TRUE;
|
|
}
|
|
}
|
|
|
|
Compare = 0;
|
|
if (Provided) {
|
|
Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
|
|
}
|
|
|
|
if (!Neighbor->IsRouter && IsRouter) {
|
|
DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
|
|
if (DefaultRouter != NULL) {
|
|
DefaultRouter->NeighborCache = Neighbor;
|
|
}
|
|
}
|
|
|
|
if (Neighbor->State == EfiNeighborInComplete) {
|
|
//
|
|
// If the target's Neighbor Cache entry is in INCOMPLETE state and no
|
|
// Target Link-Layer address option is included while link layer has
|
|
// address, the message should be silently discarded.
|
|
//
|
|
if (!Provided) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Update the Neighbor Cache
|
|
//
|
|
CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
|
|
if (Solicited) {
|
|
Neighbor->State = EfiNeighborReachable;
|
|
Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
|
|
} else {
|
|
Neighbor->State = EfiNeighborStale;
|
|
Neighbor->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
//
|
|
// Send any packets queued for the neighbor awaiting address resolution.
|
|
//
|
|
Neighbor->CallBack ((VOID *)Neighbor);
|
|
}
|
|
|
|
Neighbor->IsRouter = IsRouter;
|
|
} else {
|
|
if (!Override && (Compare != 0)) {
|
|
//
|
|
// When the Override Flag is clear and supplied link-layer address differs from
|
|
// that in the cache, if the state of the entry is not REACHABLE, ignore the
|
|
// message. Otherwise set it to STALE but do not update the entry in any
|
|
// other way.
|
|
//
|
|
if (Neighbor->State == EfiNeighborReachable) {
|
|
Neighbor->State = EfiNeighborStale;
|
|
Neighbor->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
}
|
|
} else {
|
|
if (Compare != 0) {
|
|
CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
|
|
}
|
|
|
|
//
|
|
// Update the entry's state
|
|
//
|
|
if (Solicited) {
|
|
Neighbor->State = EfiNeighborReachable;
|
|
Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
|
|
} else {
|
|
if (Compare != 0) {
|
|
Neighbor->State = EfiNeighborStale;
|
|
Neighbor->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
}
|
|
}
|
|
|
|
//
|
|
// When IsRouter is changed from TRUE to FALSE, remove the router from the
|
|
// Default Router List and remove the Destination Cache entries for all destinations
|
|
// using the neighbor as a router.
|
|
//
|
|
if (Neighbor->IsRouter && !IsRouter) {
|
|
DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
|
|
if (DefaultRouter != NULL) {
|
|
Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
|
|
}
|
|
}
|
|
|
|
Neighbor->IsRouter = IsRouter;
|
|
}
|
|
}
|
|
|
|
if (Neighbor->State == EfiNeighborReachable) {
|
|
Neighbor->CallBack ((VOID *)Neighbor);
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
Exit:
|
|
NetbufFree (Packet);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Process the Router Advertisement message according to RFC4861.
|
|
|
|
@param[in] IpSb The IP service that received the packet.
|
|
@param[in] Head The IP head of the message.
|
|
@param[in] Packet The content of the message with the IP head removed.
|
|
|
|
@retval EFI_SUCCESS The packet processed successfully.
|
|
@retval EFI_INVALID_PARAMETER The packet is invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
|
|
operation.
|
|
@retval Others Failed to process the packet.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6ProcessRouterAdvertise (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
IP6_ICMP_INFORMATION_HEAD Icmp;
|
|
UINT32 ReachableTime;
|
|
UINT32 RetransTimer;
|
|
UINT16 RouterLifetime;
|
|
UINT32 Offset;
|
|
UINT8 Type;
|
|
UINT8 Length;
|
|
IP6_ETHER_ADDR_OPTION LinkLayerOption;
|
|
UINT32 Fourth;
|
|
UINT8 CurHopLimit;
|
|
BOOLEAN Mflag;
|
|
BOOLEAN Oflag;
|
|
IP6_DEFAULT_ROUTER *DefaultRouter;
|
|
IP6_NEIGHBOR_ENTRY *NeighborCache;
|
|
EFI_MAC_ADDRESS LinkLayerAddress;
|
|
IP6_MTU_OPTION MTUOption;
|
|
IP6_PREFIX_INFO_OPTION PrefixOption;
|
|
IP6_PREFIX_LIST_ENTRY *PrefixList;
|
|
BOOLEAN OnLink;
|
|
BOOLEAN Autonomous;
|
|
EFI_IPv6_ADDRESS StatelessAddress;
|
|
EFI_STATUS Status;
|
|
UINT16 OptionLen;
|
|
UINT8 *Option;
|
|
INTN Result;
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
|
|
if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) {
|
|
//
|
|
// Skip the process below as it's not required under the current policy.
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
|
|
|
|
//
|
|
// Validate the incoming Router Advertisement
|
|
//
|
|
|
|
//
|
|
// The IP source address must be a link-local address
|
|
//
|
|
if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// The IP Hop Limit field has a value of 255, i.e. the packet
|
|
// could not possibly have been forwarded by a router.
|
|
// ICMP Code is 0.
|
|
// ICMP length (derived from the IP length) is 16 or more octets.
|
|
//
|
|
if ((Head->HopLimit != IP6_HOP_LIMIT) || (Icmp.Head.Code != 0) ||
|
|
(Head->PayloadLength < IP6_RA_LENGTH))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// All included options have a length that is greater than zero.
|
|
//
|
|
OptionLen = (UINT16)(Head->PayloadLength - IP6_RA_LENGTH);
|
|
if (OptionLen != 0) {
|
|
Option = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL);
|
|
ASSERT (Option != NULL);
|
|
|
|
if (!Ip6IsNDOptionValid (Option, OptionLen)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process Fourth field.
|
|
// In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag,
|
|
// and Router Lifetime (16 bit).
|
|
//
|
|
|
|
Fourth = NTOHL (Icmp.Fourth);
|
|
CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16));
|
|
|
|
//
|
|
// If the source address already in the default router list, update it.
|
|
// Otherwise create a new entry.
|
|
// A Lifetime of zero indicates that the router is not a default router.
|
|
//
|
|
DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress);
|
|
if (DefaultRouter == NULL) {
|
|
if (RouterLifetime != 0) {
|
|
DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime);
|
|
if (DefaultRouter == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
} else {
|
|
if (RouterLifetime != 0) {
|
|
DefaultRouter->Lifetime = RouterLifetime;
|
|
//
|
|
// Check the corresponding neighbor cache entry here.
|
|
//
|
|
if (DefaultRouter->NeighborCache == NULL) {
|
|
DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
|
|
}
|
|
} else {
|
|
//
|
|
// If the address is in the host's default router list and the router lifetime is zero,
|
|
// immediately time-out the entry.
|
|
//
|
|
Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
|
|
}
|
|
}
|
|
|
|
CurHopLimit = *((UINT8 *)&Fourth + 3);
|
|
if (CurHopLimit != 0) {
|
|
IpSb->CurHopLimit = CurHopLimit;
|
|
}
|
|
|
|
Mflag = FALSE;
|
|
Oflag = FALSE;
|
|
if ((*((UINT8 *)&Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) {
|
|
Mflag = TRUE;
|
|
} else {
|
|
if ((*((UINT8 *)&Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) {
|
|
Oflag = TRUE;
|
|
}
|
|
}
|
|
|
|
if (Mflag || Oflag) {
|
|
//
|
|
// Use Ip6Config to get available addresses or other configuration from DHCP.
|
|
//
|
|
Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag);
|
|
}
|
|
|
|
//
|
|
// Process Reachable Time and Retrans Timer fields.
|
|
//
|
|
NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *)&ReachableTime);
|
|
NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *)&RetransTimer);
|
|
ReachableTime = NTOHL (ReachableTime);
|
|
RetransTimer = NTOHL (RetransTimer);
|
|
|
|
if ((ReachableTime != 0) && (ReachableTime != IpSb->BaseReachableTime)) {
|
|
//
|
|
// If new value is not unspecified and differs from the previous one, record it
|
|
// in BaseReachableTime and recompute a ReachableTime.
|
|
//
|
|
IpSb->BaseReachableTime = ReachableTime;
|
|
Ip6UpdateReachableTime (IpSb);
|
|
}
|
|
|
|
if (RetransTimer != 0) {
|
|
IpSb->RetransTimer = RetransTimer;
|
|
}
|
|
|
|
//
|
|
// IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists.
|
|
//
|
|
NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
|
|
if (NeighborCache != NULL) {
|
|
NeighborCache->IsRouter = TRUE;
|
|
}
|
|
|
|
//
|
|
// If an valid router advertisement is received, stops router solicitation.
|
|
//
|
|
IpSb->RouterAdvertiseReceived = TRUE;
|
|
|
|
//
|
|
// The only defined options that may appear are the Source
|
|
// Link-Layer Address, Prefix information and MTU options.
|
|
// All included options have a length that is greater than zero and
|
|
// fit within the input packet.
|
|
//
|
|
Offset = 16;
|
|
while (Offset < (UINT32)Head->PayloadLength) {
|
|
NetbufCopy (Packet, Offset, sizeof (UINT8), &Type);
|
|
switch (Type) {
|
|
case Ip6OptionEtherSource:
|
|
//
|
|
// Update the neighbor cache
|
|
//
|
|
NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *)&LinkLayerOption);
|
|
|
|
//
|
|
// Option size validity ensured by Ip6IsNDOptionValid().
|
|
//
|
|
ASSERT (LinkLayerOption.Length != 0);
|
|
ASSERT (Offset + (UINT32)LinkLayerOption.Length * 8 <= (UINT32)Head->PayloadLength);
|
|
|
|
ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS));
|
|
CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6);
|
|
|
|
if (NeighborCache == NULL) {
|
|
NeighborCache = Ip6CreateNeighborEntry (
|
|
IpSb,
|
|
Ip6OnArpResolved,
|
|
&Head->SourceAddress,
|
|
&LinkLayerAddress
|
|
);
|
|
if (NeighborCache == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
NeighborCache->IsRouter = TRUE;
|
|
NeighborCache->State = EfiNeighborStale;
|
|
NeighborCache->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
} else {
|
|
Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6);
|
|
|
|
//
|
|
// If the link-local address is the same as that already in the cache,
|
|
// the cache entry's state remains unchanged. Otherwise update the
|
|
// reachability state to STALE.
|
|
//
|
|
if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
|
|
CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6);
|
|
|
|
NeighborCache->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
|
|
if (NeighborCache->State == EfiNeighborInComplete) {
|
|
//
|
|
// Send queued packets if exist.
|
|
//
|
|
NeighborCache->State = EfiNeighborStale;
|
|
NeighborCache->CallBack ((VOID *)NeighborCache);
|
|
} else {
|
|
NeighborCache->State = EfiNeighborStale;
|
|
}
|
|
}
|
|
}
|
|
|
|
Offset += (UINT32)LinkLayerOption.Length * 8;
|
|
break;
|
|
case Ip6OptionPrefixInfo:
|
|
NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *)&PrefixOption);
|
|
|
|
//
|
|
// Option size validity ensured by Ip6IsNDOptionValid().
|
|
//
|
|
ASSERT (PrefixOption.Length == 4);
|
|
ASSERT (Offset + (UINT32)PrefixOption.Length * 8 <= (UINT32)Head->PayloadLength);
|
|
|
|
PrefixOption.ValidLifetime = NTOHL (PrefixOption.ValidLifetime);
|
|
PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime);
|
|
|
|
//
|
|
// Get L and A flag, recorded in the lower 2 bits of Reserved1
|
|
//
|
|
OnLink = FALSE;
|
|
if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) {
|
|
OnLink = TRUE;
|
|
}
|
|
|
|
Autonomous = FALSE;
|
|
if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) {
|
|
Autonomous = TRUE;
|
|
}
|
|
|
|
//
|
|
// If the prefix is the link-local prefix, silently ignore the prefix option.
|
|
//
|
|
if ((PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH) &&
|
|
NetIp6IsLinkLocalAddr (&PrefixOption.Prefix)
|
|
)
|
|
{
|
|
Offset += sizeof (IP6_PREFIX_INFO_OPTION);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Do following if on-link flag is set according to RFC4861.
|
|
//
|
|
if (OnLink) {
|
|
PrefixList = Ip6FindPrefixListEntry (
|
|
IpSb,
|
|
TRUE,
|
|
PrefixOption.PrefixLength,
|
|
&PrefixOption.Prefix
|
|
);
|
|
//
|
|
// Create a new entry for the prefix, if the ValidLifetime is zero,
|
|
// silently ignore the prefix option.
|
|
//
|
|
if ((PrefixList == NULL) && (PrefixOption.ValidLifetime != 0)) {
|
|
PrefixList = Ip6CreatePrefixListEntry (
|
|
IpSb,
|
|
TRUE,
|
|
PrefixOption.ValidLifetime,
|
|
PrefixOption.PreferredLifetime,
|
|
PrefixOption.PrefixLength,
|
|
&PrefixOption.Prefix
|
|
);
|
|
if (PrefixList == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
} else if (PrefixList != NULL) {
|
|
if (PrefixOption.ValidLifetime != 0) {
|
|
PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
|
|
} else {
|
|
//
|
|
// If the prefix exists and incoming ValidLifetime is zero, immediately
|
|
// remove the prefix.
|
|
Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do following if Autonomous flag is set according to RFC4862.
|
|
//
|
|
if (Autonomous && (PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime)) {
|
|
PrefixList = Ip6FindPrefixListEntry (
|
|
IpSb,
|
|
FALSE,
|
|
PrefixOption.PrefixLength,
|
|
&PrefixOption.Prefix
|
|
);
|
|
//
|
|
// Create a new entry for the prefix, and form an address by prefix + interface id
|
|
// If the sum of the prefix length and interface identifier length
|
|
// does not equal 128 bits, the Prefix Information option MUST be ignored.
|
|
//
|
|
if ((PrefixList == NULL) &&
|
|
(PrefixOption.ValidLifetime != 0) &&
|
|
(PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128)
|
|
)
|
|
{
|
|
//
|
|
// Form the address in network order.
|
|
//
|
|
CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64));
|
|
CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64));
|
|
|
|
//
|
|
// If the address is not yet in the assigned address list, adds it into.
|
|
//
|
|
if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) {
|
|
//
|
|
// And also not in the DAD process, check its uniqueness firstly.
|
|
//
|
|
if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) {
|
|
Status = Ip6SetAddress (
|
|
IpSb->DefaultInterface,
|
|
&StatelessAddress,
|
|
FALSE,
|
|
PrefixOption.PrefixLength,
|
|
PrefixOption.ValidLifetime,
|
|
PrefixOption.PreferredLifetime,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adds the prefix option to stateless prefix option list.
|
|
//
|
|
PrefixList = Ip6CreatePrefixListEntry (
|
|
IpSb,
|
|
FALSE,
|
|
PrefixOption.ValidLifetime,
|
|
PrefixOption.PreferredLifetime,
|
|
PrefixOption.PrefixLength,
|
|
&PrefixOption.Prefix
|
|
);
|
|
if (PrefixList == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
} else if (PrefixList != NULL) {
|
|
//
|
|
// Reset the preferred lifetime of the address if the advertised prefix exists.
|
|
// Perform specific action to valid lifetime together.
|
|
//
|
|
PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime;
|
|
if ((PrefixOption.ValidLifetime > 7200) ||
|
|
(PrefixOption.ValidLifetime > PrefixList->ValidLifetime))
|
|
{
|
|
//
|
|
// If the received Valid Lifetime is greater than 2 hours or
|
|
// greater than RemainingLifetime, set the valid lifetime of the
|
|
// corresponding address to the advertised Valid Lifetime.
|
|
//
|
|
PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
|
|
} else if (PrefixList->ValidLifetime <= 7200) {
|
|
//
|
|
// If RemainingLifetime is less than or equals to 2 hours, ignore the
|
|
// Prefix Information option with regards to the valid lifetime.
|
|
// TODO: If this option has been authenticated, set the valid lifetime.
|
|
//
|
|
} else {
|
|
//
|
|
// Otherwise, reset the valid lifetime of the corresponding
|
|
// address to 2 hours.
|
|
//
|
|
PrefixList->ValidLifetime = 7200;
|
|
}
|
|
}
|
|
}
|
|
|
|
Offset += sizeof (IP6_PREFIX_INFO_OPTION);
|
|
break;
|
|
case Ip6OptionMtu:
|
|
NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *)&MTUOption);
|
|
|
|
//
|
|
// Option size validity ensured by Ip6IsNDOptionValid().
|
|
//
|
|
ASSERT (MTUOption.Length == 1);
|
|
ASSERT (Offset + (UINT32)MTUOption.Length * 8 <= (UINT32)Head->PayloadLength);
|
|
|
|
//
|
|
// Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order
|
|
// to omit implementation of Path MTU Discovery. Thus ignore the MTU option
|
|
// in Router Advertisement.
|
|
//
|
|
|
|
Offset += sizeof (IP6_MTU_OPTION);
|
|
break;
|
|
default:
|
|
//
|
|
// Silently ignore unrecognized options
|
|
//
|
|
NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length);
|
|
|
|
ASSERT (Length != 0);
|
|
|
|
Offset += (UINT32)Length * 8;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
Exit:
|
|
NetbufFree (Packet);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Process the ICMPv6 redirect message. Find the instance, then update
|
|
its route cache.
|
|
|
|
@param[in] IpSb The IP6 service binding instance that received
|
|
the packet.
|
|
@param[in] Head The IP head of the received ICMPv6 packet.
|
|
@param[in] Packet The content of the ICMPv6 redirect packet with
|
|
the IP head removed.
|
|
|
|
@retval EFI_INVALID_PARAMETER The parameter is invalid.
|
|
@retval EFI_OUT_OF_RESOURCES Insufficient resources to complete the
|
|
operation.
|
|
@retval EFI_SUCCESS Successfully updated the route caches.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6ProcessRedirect (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IP6_HEADER *Head,
|
|
IN NET_BUF *Packet
|
|
)
|
|
{
|
|
IP6_ICMP_INFORMATION_HEAD *Icmp;
|
|
EFI_IPv6_ADDRESS *Target;
|
|
EFI_IPv6_ADDRESS *IcmpDest;
|
|
UINT8 *Option;
|
|
UINT16 OptionLen;
|
|
IP6_ROUTE_ENTRY *RouteEntry;
|
|
IP6_ROUTE_CACHE_ENTRY *RouteCache;
|
|
IP6_NEIGHBOR_ENTRY *NeighborCache;
|
|
INT32 Length;
|
|
UINT8 OptLen;
|
|
IP6_ETHER_ADDR_OPTION *LinkLayerOption;
|
|
EFI_MAC_ADDRESS Mac;
|
|
UINT32 Index;
|
|
BOOLEAN IsRouter;
|
|
EFI_STATUS Status;
|
|
INTN Result;
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
|
|
Icmp = (IP6_ICMP_INFORMATION_HEAD *)NetbufGetByte (Packet, 0, NULL);
|
|
if (Icmp == NULL) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Validate the incoming Redirect message
|
|
//
|
|
|
|
//
|
|
// The IP Hop Limit field has a value of 255, i.e. the packet
|
|
// could not possibly have been forwarded by a router.
|
|
// ICMP Code is 0.
|
|
// ICMP length (derived from the IP length) is 40 or more octets.
|
|
//
|
|
if ((Head->HopLimit != IP6_HOP_LIMIT) || (Icmp->Head.Code != 0) ||
|
|
(Head->PayloadLength < IP6_REDITECT_LENGTH))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// The IP source address must be a link-local address
|
|
//
|
|
if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// The dest of this ICMP redirect message is not us.
|
|
//
|
|
if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// All included options have a length that is greater than zero.
|
|
//
|
|
OptionLen = (UINT16)(Head->PayloadLength - IP6_REDITECT_LENGTH);
|
|
if (OptionLen != 0) {
|
|
Option = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL);
|
|
ASSERT (Option != NULL);
|
|
|
|
if (!Ip6IsNDOptionValid (Option, OptionLen)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Target = (EFI_IPv6_ADDRESS *)(Icmp + 1);
|
|
IcmpDest = Target + 1;
|
|
|
|
//
|
|
// The ICMP Destination Address field in the redirect message does not contain
|
|
// a multicast address.
|
|
//
|
|
if (IP6_IS_MULTICAST (IcmpDest)) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// The ICMP Target Address is either a link-local address (when redirected to
|
|
// a router) or the same as the ICMP Destination Address (when redirected to
|
|
// the on-link destination).
|
|
//
|
|
IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest);
|
|
if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Check the options. The only interested option here is the target-link layer
|
|
// address option.
|
|
//
|
|
Length = Packet->TotalSize - 40;
|
|
Option = (UINT8 *)(IcmpDest + 1);
|
|
LinkLayerOption = NULL;
|
|
while (Length > 0) {
|
|
switch (*Option) {
|
|
case Ip6OptionEtherTarget:
|
|
|
|
LinkLayerOption = (IP6_ETHER_ADDR_OPTION *)Option;
|
|
OptLen = LinkLayerOption->Length;
|
|
if (OptLen != 1) {
|
|
//
|
|
// For ethernet, the length must be 1.
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OptLen = *(Option + 1);
|
|
if (OptLen == 0) {
|
|
//
|
|
// A length of 0 is invalid.
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
Length -= 8 * OptLen;
|
|
Option += 8 * OptLen;
|
|
}
|
|
|
|
if (Length != 0) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// The IP source address of the Redirect is the same as the current
|
|
// first-hop router for the specified ICMP Destination Address.
|
|
//
|
|
RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress);
|
|
if (RouteCache != NULL) {
|
|
if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) {
|
|
//
|
|
// The source of this Redirect message must match the NextHop of the
|
|
// corresponding route cache entry.
|
|
//
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Update the NextHop.
|
|
//
|
|
IP6_COPY_ADDRESS (&RouteCache->NextHop, Target);
|
|
|
|
if (!IsRouter) {
|
|
RouteEntry = (IP6_ROUTE_ENTRY *)RouteCache->Tag;
|
|
RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE;
|
|
}
|
|
} else {
|
|
//
|
|
// Get the Route Entry.
|
|
//
|
|
RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL);
|
|
if (RouteEntry == NULL) {
|
|
RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL);
|
|
if (RouteEntry == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!IsRouter) {
|
|
RouteEntry->Flag = IP6_DIRECT_ROUTE;
|
|
}
|
|
|
|
//
|
|
// Create a route cache for this.
|
|
//
|
|
RouteCache = Ip6CreateRouteCacheEntry (
|
|
IcmpDest,
|
|
&Head->DestinationAddress,
|
|
Target,
|
|
(UINTN)RouteEntry
|
|
);
|
|
if (RouteCache == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Insert the newly created route cache entry.
|
|
//
|
|
Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress);
|
|
InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link);
|
|
}
|
|
|
|
//
|
|
// Try to locate the neighbor cache for the Target.
|
|
//
|
|
NeighborCache = Ip6FindNeighborEntry (IpSb, Target);
|
|
|
|
if (LinkLayerOption != NULL) {
|
|
if (NeighborCache == NULL) {
|
|
//
|
|
// Create a neighbor cache for the Target.
|
|
//
|
|
ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS));
|
|
CopyMem (&Mac, LinkLayerOption->EtherAddr, 6);
|
|
NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac);
|
|
if (NeighborCache == NULL) {
|
|
//
|
|
// Just report a success here. The neighbor cache can be created in
|
|
// some other place.
|
|
//
|
|
Status = EFI_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
NeighborCache->State = EfiNeighborStale;
|
|
NeighborCache->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
} else {
|
|
Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6);
|
|
|
|
//
|
|
// If the link-local address is the same as that already in the cache,
|
|
// the cache entry's state remains unchanged. Otherwise update the
|
|
// reachability state to STALE.
|
|
//
|
|
if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
|
|
CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6);
|
|
|
|
NeighborCache->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
|
|
if (NeighborCache->State == EfiNeighborInComplete) {
|
|
//
|
|
// Send queued packets if exist.
|
|
//
|
|
NeighborCache->State = EfiNeighborStale;
|
|
NeighborCache->CallBack ((VOID *)NeighborCache);
|
|
} else {
|
|
NeighborCache->State = EfiNeighborStale;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((NeighborCache != NULL) && IsRouter) {
|
|
//
|
|
// The Target is a router, set IsRouter to TRUE.
|
|
//
|
|
NeighborCache->IsRouter = TRUE;
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
Exit:
|
|
NetbufFree (Packet);
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().
|
|
|
|
@param[in] IpSb The IP6 service binding instance.
|
|
@param[in] TargetIp6Address Pointer to Target IPv6 address.
|
|
@param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.
|
|
@param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
|
|
cache. It will be deleted after Timeout. A value of zero means that
|
|
the entry is permanent. A non-zero value means that the entry is
|
|
dynamic.
|
|
@param[in] Override If TRUE, the cached link-layer address of the matching entry will
|
|
be overridden and updated; if FALSE, and if a
|
|
corresponding cache entry already existed, EFI_ACCESS_DENIED
|
|
will be returned.
|
|
|
|
@retval EFI_SUCCESS The neighbor cache entry has been added.
|
|
@retval EFI_OUT_OF_RESOURCES Could not add the entry to the neighbor cache
|
|
due to insufficient resources.
|
|
@retval EFI_NOT_FOUND TargetLinkAddress is NULL.
|
|
@retval EFI_ACCESS_DENIED The to-be-added entry is already defined in the neighbor cache,
|
|
and that entry is tagged as un-overridden (when DeleteFlag
|
|
is FALSE).
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6AddNeighbor (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *TargetIp6Address,
|
|
IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
|
|
IN UINT32 Timeout,
|
|
IN BOOLEAN Override
|
|
)
|
|
{
|
|
IP6_NEIGHBOR_ENTRY *Neighbor;
|
|
|
|
Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
|
|
if (Neighbor != NULL) {
|
|
if (!Override) {
|
|
return EFI_ACCESS_DENIED;
|
|
} else {
|
|
if (TargetLinkAddress != NULL) {
|
|
IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress);
|
|
}
|
|
}
|
|
} else {
|
|
if (TargetLinkAddress == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress);
|
|
if (Neighbor == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
|
|
Neighbor->State = EfiNeighborReachable;
|
|
|
|
if (Timeout != 0) {
|
|
Neighbor->Ticks = IP6_GET_TICKS (Timeout / TICKS_PER_MS);
|
|
Neighbor->Dynamic = TRUE;
|
|
} else {
|
|
Neighbor->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().
|
|
|
|
@param[in] IpSb The IP6 service binding instance.
|
|
@param[in] TargetIp6Address Pointer to Target IPv6 address.
|
|
@param[in] TargetLinkAddress Pointer to link-layer address of the target. Ignored if NULL.
|
|
@param[in] Timeout Time in 100-ns units that this entry will remain in the neighbor
|
|
cache. It will be deleted after Timeout. A value of zero means that
|
|
the entry is permanent. A non-zero value means that the entry is
|
|
dynamic.
|
|
@param[in] Override If TRUE, the cached link-layer address of the matching entry will
|
|
be overridden and updated; if FALSE, and if a
|
|
corresponding cache entry already existed, EFI_ACCESS_DENIED
|
|
will be returned.
|
|
|
|
@retval EFI_SUCCESS The neighbor cache entry has been updated or deleted.
|
|
@retval EFI_NOT_FOUND This entry is not in the neighbor cache.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6DelNeighbor (
|
|
IN IP6_SERVICE *IpSb,
|
|
IN EFI_IPv6_ADDRESS *TargetIp6Address,
|
|
IN EFI_MAC_ADDRESS *TargetLinkAddress OPTIONAL,
|
|
IN UINT32 Timeout,
|
|
IN BOOLEAN Override
|
|
)
|
|
{
|
|
IP6_NEIGHBOR_ENTRY *Neighbor;
|
|
|
|
Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
|
|
if (Neighbor == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
RemoveEntryList (&Neighbor->Link);
|
|
FreePool (Neighbor);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.
|
|
This time routine handles DAD module and neighbor state transition.
|
|
It is also responsible for sending out router solicitations.
|
|
|
|
@param[in] Event The IP6 service instance's heartbeat timer.
|
|
@param[in] Context The IP6 service instance.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
Ip6NdFasterTimerTicking (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
LIST_ENTRY *Entry2;
|
|
IP6_INTERFACE *IpIf;
|
|
IP6_DELAY_JOIN_LIST *DelayNode;
|
|
EFI_IPv6_ADDRESS Source;
|
|
IP6_DAD_ENTRY *DupAddrDetect;
|
|
EFI_STATUS Status;
|
|
IP6_NEIGHBOR_ENTRY *NeighborCache;
|
|
EFI_IPv6_ADDRESS Destination;
|
|
IP6_SERVICE *IpSb;
|
|
BOOLEAN Flag;
|
|
|
|
IpSb = (IP6_SERVICE *)Context;
|
|
NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
|
|
|
|
ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS));
|
|
|
|
//
|
|
// A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router
|
|
// Solicitation messages, each separated by at least
|
|
// RTR_SOLICITATION_INTERVAL (4) seconds.
|
|
//
|
|
if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) &&
|
|
!IpSb->RouterAdvertiseReceived &&
|
|
(IpSb->SolicitTimer > 0)
|
|
)
|
|
{
|
|
if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) {
|
|
Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL);
|
|
if (!EFI_ERROR (Status)) {
|
|
IpSb->SolicitTimer--;
|
|
IpSb->Ticks = (UINT32)IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
|
|
IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
|
|
|
|
//
|
|
// Process the delay list to join the solicited-node multicast address.
|
|
//
|
|
NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) {
|
|
DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link);
|
|
if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) {
|
|
//
|
|
// The timer expires, init the duplicate address detection.
|
|
//
|
|
Ip6InitDADProcess (
|
|
DelayNode->Interface,
|
|
DelayNode->AddressInfo,
|
|
DelayNode->DadCallback,
|
|
DelayNode->Context
|
|
);
|
|
|
|
//
|
|
// Remove the delay node
|
|
//
|
|
RemoveEntryList (&DelayNode->Link);
|
|
FreePool (DelayNode);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process the duplicate address detection list.
|
|
//
|
|
NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {
|
|
DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link);
|
|
|
|
if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) {
|
|
//
|
|
// The timer expires, check the remaining transmit counts.
|
|
//
|
|
if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) {
|
|
//
|
|
// Send the Neighbor Solicitation message with
|
|
// Source - unspecified address, destination - solicited-node multicast address
|
|
// Target - the address to be validated
|
|
//
|
|
Status = Ip6SendNeighborSolicit (
|
|
IpSb,
|
|
NULL,
|
|
&DupAddrDetect->Destination,
|
|
&DupAddrDetect->AddressInfo->Address,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
DupAddrDetect->Transmit++;
|
|
DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer);
|
|
} else {
|
|
//
|
|
// All required solicitation has been sent out, and the RetransTime after the last
|
|
// Neighbor Solicit is elapsed, finish the DAD process.
|
|
//
|
|
Flag = FALSE;
|
|
if ((DupAddrDetect->Receive == 0) ||
|
|
(DupAddrDetect->Transmit <= DupAddrDetect->Receive))
|
|
{
|
|
Flag = TRUE;
|
|
}
|
|
|
|
Ip6OnDADFinished (Flag, IpIf, DupAddrDetect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Polling the state of Neighbor cache
|
|
//
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {
|
|
NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
|
|
|
|
switch (NeighborCache->State) {
|
|
case EfiNeighborInComplete:
|
|
if (NeighborCache->Ticks > 0) {
|
|
--NeighborCache->Ticks;
|
|
}
|
|
|
|
//
|
|
// Retransmit Neighbor Solicitation messages approximately every
|
|
// RetransTimer milliseconds while awaiting a response.
|
|
//
|
|
if (NeighborCache->Ticks == 0) {
|
|
if (NeighborCache->Transmit > 1) {
|
|
//
|
|
// Send out multicast neighbor solicitation for address resolution.
|
|
// After last neighbor solicitation message has been sent out, wait
|
|
// for RetransTimer and then remove entry if no response is received.
|
|
//
|
|
Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);
|
|
Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
Status = Ip6SendNeighborSolicit (
|
|
IpSb,
|
|
&Source,
|
|
&Destination,
|
|
&NeighborCache->Neighbor,
|
|
&IpSb->SnpMode.CurrentAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the retransmit times.
|
|
//
|
|
if (NeighborCache->Transmit > 0) {
|
|
--NeighborCache->Transmit;
|
|
NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
|
|
}
|
|
}
|
|
|
|
if (NeighborCache->Transmit == 0) {
|
|
//
|
|
// Timeout, send ICMP destination unreachable packet and then remove entry
|
|
//
|
|
Status = Ip6FreeNeighborEntry (
|
|
IpSb,
|
|
NeighborCache,
|
|
TRUE,
|
|
TRUE,
|
|
EFI_ICMP_ERROR,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case EfiNeighborReachable:
|
|
//
|
|
// This entry is inserted by EfiIp6Neighbors() as static entry
|
|
// and will not timeout.
|
|
//
|
|
if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) {
|
|
break;
|
|
}
|
|
|
|
if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
|
|
if (NeighborCache->Dynamic) {
|
|
//
|
|
// This entry is inserted by EfiIp6Neighbors() as dynamic entry
|
|
// and will be deleted after timeout.
|
|
//
|
|
Status = Ip6FreeNeighborEntry (
|
|
IpSb,
|
|
NeighborCache,
|
|
FALSE,
|
|
TRUE,
|
|
EFI_TIMEOUT,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
} else {
|
|
NeighborCache->State = EfiNeighborStale;
|
|
NeighborCache->Ticks = (UINT32)IP6_INFINIT_LIFETIME;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case EfiNeighborDelay:
|
|
if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
|
|
NeighborCache->State = EfiNeighborProbe;
|
|
NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
|
|
NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1;
|
|
//
|
|
// Send out unicast neighbor solicitation for Neighbor Unreachability Detection
|
|
//
|
|
Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
Status = Ip6SendNeighborSolicit (
|
|
IpSb,
|
|
&Source,
|
|
&NeighborCache->Neighbor,
|
|
&NeighborCache->Neighbor,
|
|
&IpSb->SnpMode.CurrentAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
NeighborCache->Transmit--;
|
|
}
|
|
|
|
break;
|
|
|
|
case EfiNeighborProbe:
|
|
if (NeighborCache->Ticks > 0) {
|
|
--NeighborCache->Ticks;
|
|
}
|
|
|
|
//
|
|
// Retransmit Neighbor Solicitation messages approximately every
|
|
// RetransTimer milliseconds while awaiting a response.
|
|
//
|
|
if (NeighborCache->Ticks == 0) {
|
|
if (NeighborCache->Transmit > 1) {
|
|
//
|
|
// Send out unicast neighbor solicitation for Neighbor Unreachability
|
|
// Detection. After last neighbor solicitation message has been sent out,
|
|
// wait for RetransTimer and then remove entry if no response is received.
|
|
//
|
|
Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
Status = Ip6SendNeighborSolicit (
|
|
IpSb,
|
|
&Source,
|
|
&NeighborCache->Neighbor,
|
|
&NeighborCache->Neighbor,
|
|
&IpSb->SnpMode.CurrentAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the retransmit times.
|
|
//
|
|
if (NeighborCache->Transmit > 0) {
|
|
--NeighborCache->Transmit;
|
|
NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
|
|
}
|
|
}
|
|
|
|
if (NeighborCache->Transmit == 0) {
|
|
//
|
|
// Delete the neighbor entry.
|
|
//
|
|
Status = Ip6FreeNeighborEntry (
|
|
IpSb,
|
|
NeighborCache,
|
|
FALSE,
|
|
TRUE,
|
|
EFI_TIMEOUT,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
The heartbeat timer of ND module in 1 second. This time routine handles following
|
|
things: 1) maintain default router list; 2) maintain prefix options;
|
|
3) maintain route caches.
|
|
|
|
@param[in] IpSb The IP6 service binding instance.
|
|
|
|
**/
|
|
VOID
|
|
Ip6NdTimerTicking (
|
|
IN IP6_SERVICE *IpSb
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
IP6_DEFAULT_ROUTER *DefaultRouter;
|
|
IP6_PREFIX_LIST_ENTRY *PrefixOption;
|
|
UINT8 Index;
|
|
IP6_ROUTE_CACHE_ENTRY *RouteCache;
|
|
|
|
//
|
|
// Decrease the lifetime of default router, if expires remove it from default router list.
|
|
//
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) {
|
|
DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);
|
|
if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) {
|
|
if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) {
|
|
Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses.
|
|
//
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) {
|
|
PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
|
|
if (PrefixOption->ValidLifetime != (UINT32)IP6_INFINIT_LIFETIME) {
|
|
if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) {
|
|
if ((PrefixOption->PreferredLifetime != (UINT32)IP6_INFINIT_LIFETIME) &&
|
|
(PrefixOption->PreferredLifetime > 0)
|
|
)
|
|
{
|
|
--PrefixOption->PreferredLifetime;
|
|
}
|
|
} else {
|
|
Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) {
|
|
PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
|
|
if (PrefixOption->ValidLifetime != (UINT32)IP6_INFINIT_LIFETIME) {
|
|
if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) {
|
|
Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries.
|
|
// Remove the entries at the tail of the bucket. These entries
|
|
// are likely to be used least.
|
|
// Reclaim frequency is set to 1 second.
|
|
//
|
|
for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
|
|
while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) {
|
|
Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]);
|
|
if (Entry == NULL) {
|
|
break;
|
|
}
|
|
|
|
RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
|
|
Ip6FreeRouteCacheEntry (RouteCache);
|
|
ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0);
|
|
IpSb->RouteTable->Cache.CacheNum[Index]--;
|
|
}
|
|
}
|
|
}
|