v2: *refine some codes Add 2 macros in NetLib.h #define IP4_MASK_MAX 32 #define IP6_PREFIX_MAX 128 we will use these two macros to check the max mask/prefix length, instead of #define IP4_MASK_NUM 33 #define IP6_PREFIX_NUM 129 which means a valid number This will make the code readability and maintainability. Cc: Subramanian Sriram <sriram-s@hpe.com> Cc: Fu Siyuan <siyuan.fu@intel.com> Cc: Ye Ting <ting.ye@intel.com> Cc: Wu Jiaxin <jiaxin.wu@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Zhang Lubo <lubo.zhang@intel.com> Reviewed-by: Sriram Subramanian <sriram-s@@hpe.com>
		
			
				
	
	
		
			3156 lines
		
	
	
		
			99 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3156 lines
		
	
	
		
			99 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @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 (IP6_CONFIG_DHCP6_OPTION_ORO);
 | |
|         Oro->OpLen  = HTONS (2);
 | |
|         *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);
 | |
| 
 | |
|         InfoReqReXmit.Irt = 4;
 | |
|         InfoReqReXmit.Mrc = 64;
 | |
|         InfoReqReXmit.Mrt = 60;
 | |
|         InfoReqReXmit.Mrd = 0;
 | |
| 
 | |
|         Dhcp6->InfoRequest (
 | |
|                  Dhcp6,
 | |
|                  TRUE,
 | |
|                  Oro,
 | |
|                  0,
 | |
|                  NULL,
 | |
|                  &InfoReqReXmit,
 | |
|                  IpSb->Ip6ConfigInstance.Dhcp6Event,
 | |
|                  Ip6ConfigOnDhcp6Reply,
 | |
|                  &IpSb->Ip6ConfigInstance
 | |
|                  );
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Add an on-link prefix for link-local address.
 | |
|       //
 | |
|       Ip6CreatePrefixListEntry (
 | |
|         IpSb,
 | |
|         TRUE,
 | |
|         (UINT32) IP6_INFINIT_LIFETIME,
 | |
|         (UINT32) IP6_INFINIT_LIFETIME,
 | |
|         IP6_LINK_LOCAL_PREFIX_LENGTH,
 | |
|         &IpSb->LinkLocalAddr
 | |
|         );
 | |
| 
 | |
|     } else {
 | |
|       //
 | |
|       // Global scope unicast address.
 | |
|       //
 | |
|       Ip6AddAddr (IpIf, AddrInfo);
 | |
| 
 | |
|       //
 | |
|       // Add an on-link prefix for this address.
 | |
|       //
 | |
|       Ip6CreatePrefixListEntry (
 | |
|         IpSb,
 | |
|         TRUE,
 | |
|         AddrInfo->ValidLifetime,
 | |
|         AddrInfo->PreferredLifetime,
 | |
|         AddrInfo->PrefixLength,
 | |
|         &AddrInfo->Address
 | |
|         );
 | |
| 
 | |
|       IpIf->Configured = TRUE;
 | |
|     }
 | |
|   } else {
 | |
|     //
 | |
|     // Leave the group we joined before.
 | |
|     //
 | |
|     Ip6LeaveGroup (IpSb, &DadEntry->Destination);
 | |
|   }
 | |
| 
 | |
|   if (DadEntry->Callback != NULL) {
 | |
|     DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context);
 | |
|   }
 | |
| 
 | |
|   if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
 | |
|     FreePool (AddrInfo);
 | |
|     RemoveEntryList (&DadEntry->Link);
 | |
|     FreePool (DadEntry);
 | |
|     //
 | |
|     // 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]--;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 |