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