| /** @file | |
| Implementation of Neighbor Discovery support routines. | |
| Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php. | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "Ip6Impl.h" | |
| EFI_MAC_ADDRESS mZeroMacAddress; | |
| /** | |
| Update the ReachableTime in IP6 service binding instance data, in milliseconds. | |
| @param[in, out] IpSb Points to the IP6_SERVICE. | |
| **/ | |
| VOID | |
| Ip6UpdateReachableTime ( | |
| IN OUT IP6_SERVICE *IpSb | |
| ) | |
| { | |
| UINT32 Random; | |
| Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE; | |
| Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED; | |
| IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE; | |
| } | |
| /** | |
| Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number | |
| of EFI_IP6_NEIGHBOR_CACHE is also returned. | |
| @param[in] IpInstance The pointer to IP6_PROTOCOL instance. | |
| @param[out] NeighborCount The number of returned neighbor cache entries. | |
| @param[out] NeighborCache The pointer to the array of EFI_IP6_NEIGHBOR_CACHE. | |
| @retval EFI_SUCCESS The EFI_IP6_NEIGHBOR_CACHE successfully built. | |
| @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the route table. | |
| **/ | |
| EFI_STATUS | |
| Ip6BuildEfiNeighborCache ( | |
| IN IP6_PROTOCOL *IpInstance, | |
| OUT UINT32 *NeighborCount, | |
| OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache | |
| ) | |
| { | |
| IP6_NEIGHBOR_ENTRY *Neighbor; | |
| LIST_ENTRY *Entry; | |
| IP6_SERVICE *IpSb; | |
| UINT32 Count; | |
| EFI_IP6_NEIGHBOR_CACHE *EfiNeighborCache; | |
| EFI_IP6_NEIGHBOR_CACHE *NeighborCacheTmp; | |
| NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); | |
| ASSERT (NeighborCount != NULL && NeighborCache != NULL); | |
| IpSb = IpInstance->Service; | |
| Count = 0; | |
| NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { | |
| Count++; | |
| } | |
| if (Count == 0) { | |
| return EFI_SUCCESS; | |
| } | |
| NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE)); | |
| if (NeighborCacheTmp == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| *NeighborCount = Count; | |
| Count = 0; | |
| NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) { | |
| Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link); | |
| EfiNeighborCache = NeighborCacheTmp + Count; | |
| EfiNeighborCache->State = Neighbor->State; | |
| IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor); | |
| IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress); | |
| Count++; | |
| } | |
| ASSERT (*NeighborCount == Count); | |
| *NeighborCache = NeighborCacheTmp; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number | |
| of prefix entries is also returned. | |
| @param[in] IpInstance The pointer to IP6_PROTOCOL instance. | |
| @param[out] PrefixCount The number of returned prefix entries. | |
| @param[out] PrefixTable The pointer to the array of PrefixTable. | |
| @retval EFI_SUCCESS The prefix table successfully built. | |
| @retval EFI_OUT_OF_RESOURCES Failed to allocate the memory for the prefix table. | |
| **/ | |
| EFI_STATUS | |
| Ip6BuildPrefixTable ( | |
| IN IP6_PROTOCOL *IpInstance, | |
| OUT UINT32 *PrefixCount, | |
| OUT EFI_IP6_ADDRESS_INFO **PrefixTable | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| IP6_SERVICE *IpSb; | |
| UINT32 Count; | |
| IP6_PREFIX_LIST_ENTRY *PrefixList; | |
| EFI_IP6_ADDRESS_INFO *EfiPrefix; | |
| EFI_IP6_ADDRESS_INFO *PrefixTableTmp; | |
| NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE); | |
| ASSERT (PrefixCount != NULL && PrefixTable != NULL); | |
| IpSb = IpInstance->Service; | |
| Count = 0; | |
| NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { | |
| Count++; | |
| } | |
| if (Count == 0) { | |
| return EFI_SUCCESS; | |
| } | |
| PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO)); | |
| if (PrefixTableTmp == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| *PrefixCount = Count; | |
| Count = 0; | |
| NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) { | |
| PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); | |
| EfiPrefix = PrefixTableTmp + Count; | |
| IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix); | |
| EfiPrefix->PrefixLength = PrefixList->PrefixLength; | |
| Count++; | |
| } | |
| ASSERT (*PrefixCount == Count); | |
| *PrefixTable = PrefixTableTmp; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Allocate and initialize a IP6 prefix list entry. | |
| @param[in] IpSb The pointer to IP6_SERVICE instance. | |
| @param[in] OnLinkOrAuto If TRUE, the entry is created for the on link prefix list. | |
| Otherwise, it is created for the autoconfiguration prefix list. | |
| @param[in] ValidLifetime The length of time in seconds that the prefix | |
| is valid for the purpose of on-link determination. | |
| @param[in] PreferredLifetime The length of time in seconds that addresses | |
| generated from the prefix via stateless address | |
| autoconfiguration remain preferred. | |
| @param[in] PrefixLength The prefix length of the Prefix. | |
| @param[in] Prefix The prefix address. | |
| @return NULL if it failed to allocate memory for the prefix node. Otherwise, point | |
| to the created or existing prefix list entry. | |
| **/ | |
| IP6_PREFIX_LIST_ENTRY * | |
| Ip6CreatePrefixListEntry ( | |
| IN IP6_SERVICE *IpSb, | |
| IN BOOLEAN OnLinkOrAuto, | |
| IN UINT32 ValidLifetime, | |
| IN UINT32 PreferredLifetime, | |
| IN UINT8 PrefixLength, | |
| IN EFI_IPv6_ADDRESS *Prefix | |
| ) | |
| { | |
| IP6_PREFIX_LIST_ENTRY *PrefixEntry; | |
| IP6_ROUTE_ENTRY *RtEntry; | |
| LIST_ENTRY *ListHead; | |
| LIST_ENTRY *Entry; | |
| IP6_PREFIX_LIST_ENTRY *TmpPrefixEntry; | |
| if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength > IP6_PREFIX_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 unspeicifed, and target address | |
| // is one of the maintained address, reply a unsolicited multicast advertisement. | |
| // | |
| if (IsDAD && IsMaintained) { | |
| Solicited = FALSE; | |
| Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest); | |
| } else { | |
| Solicited = TRUE; | |
| IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress); | |
| } | |
| Status = Ip6SendNeighborAdvertise ( | |
| IpSb, | |
| &Target, | |
| &Dest, | |
| &Target, | |
| &IpSb->SnpMode.CurrentAddress, | |
| FALSE, | |
| TRUE, | |
| Solicited | |
| ); | |
| Exit: | |
| NetbufFree (Packet); | |
| return Status; | |
| } | |
| /** | |
| Process the Neighbor Advertisement message. | |
| @param[in] IpSb The IP service that received the packet. | |
| @param[in] Head The IP head of the message. | |
| @param[in] Packet The content of the message with IP head removed. | |
| @retval EFI_SUCCESS The packet processed successfully. | |
| @retval EFI_INVALID_PARAMETER The packet is invalid. | |
| @retval EFI_ICMP_ERROR The packet indicates that DAD is failed. | |
| @retval Others Failed to process the packet. | |
| **/ | |
| EFI_STATUS | |
| Ip6ProcessNeighborAdvertise ( | |
| IN IP6_SERVICE *IpSb, | |
| IN EFI_IP6_HEADER *Head, | |
| IN NET_BUF *Packet | |
| ) | |
| { | |
| IP6_ICMP_INFORMATION_HEAD Icmp; | |
| EFI_IPv6_ADDRESS Target; | |
| IP6_ETHER_ADDR_OPTION LinkLayerOption; | |
| BOOLEAN Provided; | |
| INTN Compare; | |
| IP6_NEIGHBOR_ENTRY *Neighbor; | |
| IP6_DEFAULT_ROUTER *DefaultRouter; | |
| BOOLEAN Solicited; | |
| BOOLEAN IsRouter; | |
| BOOLEAN Override; | |
| IP6_DAD_ENTRY *DupAddrDetect; | |
| IP6_INTERFACE *IpIf; | |
| UINT16 OptionLen; | |
| UINT8 *Option; | |
| EFI_STATUS Status; | |
| NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); | |
| NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr); | |
| // | |
| // Validate the incoming Neighbor Advertisement | |
| // | |
| Status = EFI_INVALID_PARAMETER; | |
| // | |
| // The IP Hop Limit field has a value of 255, i.e., the packet | |
| // could not possibly have been forwarded by a router. | |
| // ICMP Code is 0. | |
| // Target Address is not a multicast address. | |
| // | |
| if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) { | |
| goto Exit; | |
| } | |
| // | |
| // ICMP length is 24 or more octets. | |
| // | |
| Provided = FALSE; | |
| OptionLen = 0; | |
| if (Head->PayloadLength < IP6_ND_LENGTH) { | |
| goto Exit; | |
| } else { | |
| OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH); | |
| 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; | |
| UINT16 Offset; | |
| UINT8 Type; | |
| UINT8 Length; | |
| IP6_ETHER_ADDR_OPTION LinkLayerOption; | |
| UINT32 Fourth; | |
| UINT8 CurHopLimit; | |
| BOOLEAN Mflag; | |
| BOOLEAN Oflag; | |
| IP6_DEFAULT_ROUTER *DefaultRouter; | |
| IP6_NEIGHBOR_ENTRY *NeighborCache; | |
| EFI_MAC_ADDRESS LinkLayerAddress; | |
| IP6_MTU_OPTION MTUOption; | |
| IP6_PREFIX_INFO_OPTION PrefixOption; | |
| IP6_PREFIX_LIST_ENTRY *PrefixList; | |
| BOOLEAN OnLink; | |
| BOOLEAN Autonomous; | |
| EFI_IPv6_ADDRESS StatelessAddress; | |
| EFI_STATUS Status; | |
| UINT16 OptionLen; | |
| UINT8 *Option; | |
| INTN Result; | |
| Status = EFI_INVALID_PARAMETER; | |
| if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) { | |
| // | |
| // Skip the process below as it's not required under the current policy. | |
| // | |
| goto Exit; | |
| } | |
| NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); | |
| // | |
| // Validate the incoming Router Advertisement | |
| // | |
| // | |
| // The IP source address must be a link-local address | |
| // | |
| if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { | |
| goto Exit; | |
| } | |
| // | |
| // The IP Hop Limit field has a value of 255, i.e. the packet | |
| // could not possibly have been forwarded by a router. | |
| // ICMP Code is 0. | |
| // ICMP length (derived from the IP length) is 16 or more octets. | |
| // | |
| if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || | |
| Head->PayloadLength < IP6_RA_LENGTH) { | |
| goto Exit; | |
| } | |
| // | |
| // All included options have a length that is greater than zero. | |
| // | |
| OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH); | |
| 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 advertisment is received, stops router solicitation. | |
| // | |
| IpSb->RouterAdvertiseReceived = TRUE; | |
| // | |
| // The only defined options that may appear are the Source | |
| // Link-Layer Address, Prefix information and MTU options. | |
| // All included options have a length that is greater than zero. | |
| // | |
| Offset = 16; | |
| while (Offset < Head->PayloadLength) { | |
| NetbufCopy (Packet, Offset, sizeof (UINT8), &Type); | |
| switch (Type) { | |
| case Ip6OptionEtherSource: | |
| // | |
| // Update the neighbor cache | |
| // | |
| NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption); | |
| if (LinkLayerOption.Length <= 0) { | |
| goto Exit; | |
| } | |
| ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS)); | |
| CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6); | |
| if (NeighborCache == NULL) { | |
| NeighborCache = Ip6CreateNeighborEntry ( | |
| IpSb, | |
| Ip6OnArpResolved, | |
| &Head->SourceAddress, | |
| &LinkLayerAddress | |
| ); | |
| if (NeighborCache == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Exit; | |
| } | |
| NeighborCache->IsRouter = TRUE; | |
| NeighborCache->State = EfiNeighborStale; | |
| NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; | |
| } else { | |
| Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6); | |
| // | |
| // If the link-local address is the same as that already in the cache, | |
| // the cache entry's state remains unchanged. Otherwise update the | |
| // reachability state to STALE. | |
| // | |
| if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) { | |
| CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6); | |
| NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME; | |
| if (NeighborCache->State == EfiNeighborInComplete) { | |
| // | |
| // Send queued packets if exist. | |
| // | |
| NeighborCache->State = EfiNeighborStale; | |
| NeighborCache->CallBack ((VOID *) NeighborCache); | |
| } else { | |
| NeighborCache->State = EfiNeighborStale; | |
| } | |
| } | |
| } | |
| Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8); | |
| break; | |
| case Ip6OptionPrefixInfo: | |
| NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption); | |
| if (PrefixOption.Length != 4) { | |
| goto Exit; | |
| } | |
| PrefixOption.ValidLifetime = NTOHL (PrefixOption.ValidLifetime); | |
| PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime); | |
| // | |
| // Get L and A flag, recorded in the lower 2 bits of Reserved1 | |
| // | |
| OnLink = FALSE; | |
| if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) { | |
| OnLink = TRUE; | |
| } | |
| Autonomous = FALSE; | |
| if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) { | |
| Autonomous = TRUE; | |
| } | |
| // | |
| // If the prefix is the link-local prefix, silently ignore the prefix option. | |
| // | |
| if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH && | |
| NetIp6IsLinkLocalAddr (&PrefixOption.Prefix) | |
| ) { | |
| Offset += sizeof (IP6_PREFIX_INFO_OPTION); | |
| break; | |
| } | |
| // | |
| // Do following if on-link flag is set according to RFC4861. | |
| // | |
| if (OnLink) { | |
| PrefixList = Ip6FindPrefixListEntry ( | |
| IpSb, | |
| TRUE, | |
| PrefixOption.PrefixLength, | |
| &PrefixOption.Prefix | |
| ); | |
| // | |
| // Create a new entry for the prefix, if the ValidLifetime is zero, | |
| // silently ignore the prefix option. | |
| // | |
| if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) { | |
| PrefixList = Ip6CreatePrefixListEntry ( | |
| IpSb, | |
| TRUE, | |
| PrefixOption.ValidLifetime, | |
| PrefixOption.PreferredLifetime, | |
| PrefixOption.PrefixLength, | |
| &PrefixOption.Prefix | |
| ); | |
| if (PrefixList == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Exit; | |
| } | |
| } else if (PrefixList != NULL) { | |
| if (PrefixOption.ValidLifetime != 0) { | |
| PrefixList->ValidLifetime = PrefixOption.ValidLifetime; | |
| } else { | |
| // | |
| // If the prefix exists and incoming ValidLifetime is zero, immediately | |
| // remove the prefix. | |
| Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE); | |
| } | |
| } | |
| } | |
| // | |
| // Do following if Autonomous flag is set according to RFC4862. | |
| // | |
| if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) { | |
| PrefixList = Ip6FindPrefixListEntry ( | |
| IpSb, | |
| FALSE, | |
| PrefixOption.PrefixLength, | |
| &PrefixOption.Prefix | |
| ); | |
| // | |
| // Create a new entry for the prefix, and form an address by prefix + interface id | |
| // If the sum of the prefix length and interface identifier length | |
| // does not equal 128 bits, the Prefix Information option MUST be ignored. | |
| // | |
| if (PrefixList == NULL && | |
| PrefixOption.ValidLifetime != 0 && | |
| PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128 | |
| ) { | |
| // | |
| // Form the address in network order. | |
| // | |
| CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64)); | |
| CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64)); | |
| // | |
| // If the address is not yet in the assigned address list, adds it into. | |
| // | |
| if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) { | |
| // | |
| // And also not in the DAD process, check its uniqeness firstly. | |
| // | |
| if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) { | |
| Status = Ip6SetAddress ( | |
| IpSb->DefaultInterface, | |
| &StatelessAddress, | |
| FALSE, | |
| PrefixOption.PrefixLength, | |
| PrefixOption.ValidLifetime, | |
| PrefixOption.PreferredLifetime, | |
| NULL, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto Exit; | |
| } | |
| } | |
| } | |
| // | |
| // Adds the prefix option to stateless prefix option list. | |
| // | |
| PrefixList = Ip6CreatePrefixListEntry ( | |
| IpSb, | |
| FALSE, | |
| PrefixOption.ValidLifetime, | |
| PrefixOption.PreferredLifetime, | |
| PrefixOption.PrefixLength, | |
| &PrefixOption.Prefix | |
| ); | |
| if (PrefixList == NULL) { | |
| Status = EFI_OUT_OF_RESOURCES; | |
| goto Exit; | |
| } | |
| } else if (PrefixList != NULL) { | |
| // | |
| // Reset the preferred lifetime of the address if the advertised prefix exists. | |
| // Perform specific action to valid lifetime together. | |
| // | |
| PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime; | |
| if ((PrefixOption.ValidLifetime > 7200) || | |
| (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) { | |
| // | |
| // If the received Valid Lifetime is greater than 2 hours or | |
| // greater than RemainingLifetime, set the valid lifetime of the | |
| // corresponding address to the advertised Valid Lifetime. | |
| // | |
| PrefixList->ValidLifetime = PrefixOption.ValidLifetime; | |
| } else if (PrefixList->ValidLifetime <= 7200) { | |
| // | |
| // If RemainingLifetime is less than or equls to 2 hours, ignore the | |
| // Prefix Information option with regards to the valid lifetime. | |
| // TODO: If this option has been authenticated, set the valid lifetime. | |
| // | |
| } else { | |
| // | |
| // Otherwise, reset the valid lifetime of the corresponding | |
| // address to 2 hours. | |
| // | |
| PrefixList->ValidLifetime = 7200; | |
| } | |
| } | |
| } | |
| Offset += sizeof (IP6_PREFIX_INFO_OPTION); | |
| break; | |
| case Ip6OptionMtu: | |
| NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption); | |
| if (MTUOption.Length != 1) { | |
| goto Exit; | |
| } | |
| // | |
| // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order | |
| // to omit implementation of Path MTU Discovery. Thus ignore the MTU option | |
| // in Router Advertisement. | |
| // | |
| Offset += sizeof (IP6_MTU_OPTION); | |
| break; | |
| default: | |
| // | |
| // Silently ignore unrecognized options | |
| // | |
| NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length); | |
| if (Length <= 0) { | |
| goto Exit; | |
| } | |
| Offset = (UINT16) (Offset + (UINT16) Length * 8); | |
| break; | |
| } | |
| } | |
| Status = EFI_SUCCESS; | |
| Exit: | |
| NetbufFree (Packet); | |
| return Status; | |
| } | |
| /** | |
| Process the ICMPv6 redirect message. Find the instance, then update | |
| its route cache. | |
| @param[in] IpSb The IP6 service binding instance that received | |
| the packet. | |
| @param[in] Head The IP head of the received ICMPv6 packet. | |
| @param[in] Packet The content of the ICMPv6 redirect packet with | |
| the IP head removed. | |
| @retval EFI_INVALID_PARAMETER The parameter is invalid. | |
| @retval EFI_OUT_OF_RESOURCES Insuffcient resources to complete the | |
| operation. | |
| @retval EFI_SUCCESS Successfully updated the route caches. | |
| **/ | |
| EFI_STATUS | |
| Ip6ProcessRedirect ( | |
| IN IP6_SERVICE *IpSb, | |
| IN EFI_IP6_HEADER *Head, | |
| IN NET_BUF *Packet | |
| ) | |
| { | |
| IP6_ICMP_INFORMATION_HEAD *Icmp; | |
| EFI_IPv6_ADDRESS *Target; | |
| EFI_IPv6_ADDRESS *IcmpDest; | |
| UINT8 *Option; | |
| UINT16 OptionLen; | |
| IP6_ROUTE_ENTRY *RouteEntry; | |
| IP6_ROUTE_CACHE_ENTRY *RouteCache; | |
| IP6_NEIGHBOR_ENTRY *NeighborCache; | |
| INT32 Length; | |
| UINT8 OptLen; | |
| IP6_ETHER_ADDR_OPTION *LinkLayerOption; | |
| EFI_MAC_ADDRESS Mac; | |
| UINT32 Index; | |
| BOOLEAN IsRouter; | |
| EFI_STATUS Status; | |
| INTN Result; | |
| Status = EFI_INVALID_PARAMETER; | |
| Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL); | |
| if (Icmp == NULL) { | |
| goto Exit; | |
| } | |
| // | |
| // Validate the incoming Redirect message | |
| // | |
| // | |
| // The IP Hop Limit field has a value of 255, i.e. the packet | |
| // could not possibly have been forwarded by a router. | |
| // ICMP Code is 0. | |
| // ICMP length (derived from the IP length) is 40 or more octets. | |
| // | |
| if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 || | |
| Head->PayloadLength < IP6_REDITECT_LENGTH) { | |
| goto Exit; | |
| } | |
| // | |
| // The IP source address must be a link-local address | |
| // | |
| if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) { | |
| goto Exit; | |
| } | |
| // | |
| // The dest of this ICMP redirect message is not us. | |
| // | |
| if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { | |
| goto Exit; | |
| } | |
| // | |
| // All included options have a length that is greater than zero. | |
| // | |
| OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH); | |
| 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) maitain default router list; 2) maintain prefix options; | |
| 3) maintain route caches. | |
| @param[in] IpSb The IP6 service binding instance. | |
| **/ | |
| VOID | |
| Ip6NdTimerTicking ( | |
| IN IP6_SERVICE *IpSb | |
| ) | |
| { | |
| LIST_ENTRY *Entry; | |
| LIST_ENTRY *Next; | |
| IP6_DEFAULT_ROUTER *DefaultRouter; | |
| IP6_PREFIX_LIST_ENTRY *PrefixOption; | |
| UINT8 Index; | |
| IP6_ROUTE_CACHE_ENTRY *RouteCache; | |
| // | |
| // Decrease the lifetime of default router, if expires remove it from default router list. | |
| // | |
| NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) { | |
| DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link); | |
| if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) { | |
| if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) { | |
| Ip6DestroyDefaultRouter (IpSb, DefaultRouter); | |
| } | |
| } | |
| } | |
| // | |
| // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses. | |
| // | |
| NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) { | |
| PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); | |
| if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { | |
| if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) { | |
| if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) && | |
| (PrefixOption->PreferredLifetime > 0) | |
| ) { | |
| --PrefixOption->PreferredLifetime; | |
| } | |
| } else { | |
| Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE); | |
| } | |
| } | |
| } | |
| NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) { | |
| PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link); | |
| if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) { | |
| if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) { | |
| Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE); | |
| } | |
| } | |
| } | |
| // | |
| // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries. | |
| // Remove the entries at the tail of the bucket. These entries | |
| // are likely to be used least. | |
| // Reclaim frequency is set to 1 second. | |
| // | |
| for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) { | |
| while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) { | |
| Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]); | |
| if (Entry == NULL) { | |
| break; | |
| } | |
| RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link); | |
| Ip6FreeRouteCacheEntry (RouteCache); | |
| ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0); | |
| IpSb->RouteTable->Cache.CacheNum[Index]--; | |
| } | |
| } | |
| } | |