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>
		
			
				
	
	
		
			636 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			636 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  The functions and routines to handle the route caches and route table.
 | 
						|
 | 
						|
  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"
 | 
						|
 | 
						|
/**
 | 
						|
  This is the worker function for IP6_ROUTE_CACHE_HASH(). It calculates the value
 | 
						|
  as the index of the route cache bucket according to the prefix of two IPv6 addresses.
 | 
						|
 | 
						|
  @param[in]  Ip1     The IPv6 address.
 | 
						|
  @param[in]  Ip2     The IPv6 address.
 | 
						|
 | 
						|
  @return The hash value of the prefix of two IPv6 addresses.
 | 
						|
 | 
						|
**/
 | 
						|
UINT32
 | 
						|
Ip6RouteCacheHash (
 | 
						|
  IN EFI_IPv6_ADDRESS       *Ip1,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Ip2
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32 Prefix1;
 | 
						|
  UINT32 Prefix2;
 | 
						|
 | 
						|
  Prefix1 = *((UINT32 *) ((UINTN *) (Ip1)));
 | 
						|
  Prefix2 = *((UINT32 *) ((UINTN *) (Ip2)));
 | 
						|
 | 
						|
  return ((UINT32) (Prefix1 ^ Prefix2) % IP6_ROUTE_CACHE_HASH_SIZE);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Allocate a route entry then initialize it with the Destination/PrefixLength
 | 
						|
  and Gateway.
 | 
						|
 | 
						|
  @param[in]  Destination     The IPv6 destination address. This is an optional
 | 
						|
                              parameter that may be NULL.
 | 
						|
  @param[in]  PrefixLength    The destination network's prefix length.
 | 
						|
  @param[in]  GatewayAddress  The next hop address. This is an optional parameter
 | 
						|
                              that may be NULL.
 | 
						|
 | 
						|
  @return NULL if failed to allocate memeory; otherwise, the newly created route entry.
 | 
						|
 | 
						|
**/
 | 
						|
IP6_ROUTE_ENTRY *
 | 
						|
Ip6CreateRouteEntry (
 | 
						|
  IN EFI_IPv6_ADDRESS       *Destination    OPTIONAL,
 | 
						|
  IN UINT8                  PrefixLength,
 | 
						|
  IN EFI_IPv6_ADDRESS       *GatewayAddress OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  IP6_ROUTE_ENTRY           *RtEntry;
 | 
						|
 | 
						|
  RtEntry = AllocateZeroPool (sizeof (IP6_ROUTE_ENTRY));
 | 
						|
 | 
						|
  if (RtEntry == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  RtEntry->RefCnt       = 1;
 | 
						|
  RtEntry->Flag         = 0;
 | 
						|
  RtEntry->PrefixLength = PrefixLength;
 | 
						|
 | 
						|
  if (Destination != NULL) {
 | 
						|
    IP6_COPY_ADDRESS (&RtEntry->Destination, Destination);
 | 
						|
  }
 | 
						|
 | 
						|
  if (GatewayAddress != NULL) {
 | 
						|
    IP6_COPY_ADDRESS (&RtEntry->NextHop, GatewayAddress);
 | 
						|
  }
 | 
						|
 | 
						|
  return RtEntry;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free the route table entry. It is reference counted.
 | 
						|
 | 
						|
  @param[in, out]  RtEntry  The route entry to free.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
Ip6FreeRouteEntry (
 | 
						|
  IN OUT IP6_ROUTE_ENTRY    *RtEntry
 | 
						|
  )
 | 
						|
{
 | 
						|
  ASSERT ((RtEntry != NULL) && (RtEntry->RefCnt > 0));
 | 
						|
 | 
						|
  if (--RtEntry->RefCnt == 0) {
 | 
						|
    FreePool (RtEntry);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Search the route table for a most specific match to the Dst. It searches
 | 
						|
  from the longest route area (prefix length == 128) to the shortest route area
 | 
						|
  (default routes). In each route area, it will first search the instance's
 | 
						|
  route table, then the default route table. This is required per the following
 | 
						|
  requirements:
 | 
						|
  1. IP search the route table for a most specific match.
 | 
						|
  2. The local route entries have precedence over the default route entry.
 | 
						|
 | 
						|
  @param[in]  RtTable       The route table to search from.
 | 
						|
  @param[in]  Destination   The destionation address to search. If NULL, search
 | 
						|
                            the route table by NextHop.
 | 
						|
  @param[in]  NextHop       The next hop address. If NULL, search the route table
 | 
						|
                            by Destination.
 | 
						|
 | 
						|
  @return NULL if no route matches the Dst. Otherwise, the point to the
 | 
						|
  @return most specific route to the Dst.
 | 
						|
 | 
						|
**/
 | 
						|
IP6_ROUTE_ENTRY *
 | 
						|
Ip6FindRouteEntry (
 | 
						|
  IN IP6_ROUTE_TABLE        *RtTable,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Destination OPTIONAL,
 | 
						|
  IN EFI_IPv6_ADDRESS       *NextHop     OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  IP6_ROUTE_ENTRY           *RtEntry;
 | 
						|
  INTN                      Index;
 | 
						|
 | 
						|
  ASSERT (Destination != NULL || NextHop != NULL);
 | 
						|
 | 
						|
  RtEntry = NULL;
 | 
						|
 | 
						|
  for (Index = IP6_PREFIX_MAX; Index >= 0; Index--) {
 | 
						|
    NET_LIST_FOR_EACH (Entry, &RtTable->RouteArea[Index]) {
 | 
						|
      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
 | 
						|
 | 
						|
      if (Destination != NULL) {
 | 
						|
        if (NetIp6IsNetEqual (Destination, &RtEntry->Destination, RtEntry->PrefixLength)) {
 | 
						|
          NET_GET_REF (RtEntry);
 | 
						|
          return RtEntry;
 | 
						|
        }
 | 
						|
      } else if (NextHop != NULL) {
 | 
						|
        if (NetIp6IsNetEqual (NextHop, &RtEntry->NextHop, RtEntry->PrefixLength)) {
 | 
						|
          NET_GET_REF (RtEntry);
 | 
						|
          return RtEntry;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Allocate and initialize a IP6 route cache entry.
 | 
						|
 | 
						|
  @param[in]  Dst           The destination address.
 | 
						|
  @param[in]  Src           The source address.
 | 
						|
  @param[in]  GateWay       The next hop address.
 | 
						|
  @param[in]  Tag           The tag from the caller. This marks all the cache entries
 | 
						|
                            spawned from one route table entry.
 | 
						|
 | 
						|
  @return NULL if failed to allocate memory for the cache. Otherwise, point
 | 
						|
          to the created route cache entry.
 | 
						|
 | 
						|
**/
 | 
						|
IP6_ROUTE_CACHE_ENTRY *
 | 
						|
Ip6CreateRouteCacheEntry (
 | 
						|
  IN EFI_IPv6_ADDRESS       *Dst,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Src,
 | 
						|
  IN EFI_IPv6_ADDRESS       *GateWay,
 | 
						|
  IN UINTN                  Tag
 | 
						|
  )
 | 
						|
{
 | 
						|
  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;
 | 
						|
 | 
						|
  RtCacheEntry = AllocatePool (sizeof (IP6_ROUTE_CACHE_ENTRY));
 | 
						|
 | 
						|
  if (RtCacheEntry == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  RtCacheEntry->RefCnt = 1;
 | 
						|
  RtCacheEntry->Tag    = Tag;
 | 
						|
 | 
						|
  IP6_COPY_ADDRESS (&RtCacheEntry->Destination, Dst);
 | 
						|
  IP6_COPY_ADDRESS (&RtCacheEntry->Source, Src);
 | 
						|
  IP6_COPY_ADDRESS (&RtCacheEntry->NextHop, GateWay);
 | 
						|
 | 
						|
  return RtCacheEntry;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free the route cache entry. It is reference counted.
 | 
						|
 | 
						|
  @param[in, out]  RtCacheEntry  The route cache entry to free.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
Ip6FreeRouteCacheEntry (
 | 
						|
  IN OUT IP6_ROUTE_CACHE_ENTRY  *RtCacheEntry
 | 
						|
  )
 | 
						|
{
 | 
						|
  ASSERT (RtCacheEntry->RefCnt > 0);
 | 
						|
 | 
						|
  if (--RtCacheEntry->RefCnt == 0) {
 | 
						|
    FreePool (RtCacheEntry);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Find a route cache with the destination and source address. This is
 | 
						|
  used by the ICMPv6 redirect messasge process.
 | 
						|
 | 
						|
  @param[in]  RtTable       The route table to search the cache for.
 | 
						|
  @param[in]  Dest          The destination address.
 | 
						|
  @param[in]  Src           The source address.
 | 
						|
 | 
						|
  @return NULL if no route entry to the (Dest, Src). Otherwise, the pointer
 | 
						|
          to the correct route cache entry.
 | 
						|
 | 
						|
**/
 | 
						|
IP6_ROUTE_CACHE_ENTRY *
 | 
						|
Ip6FindRouteCache (
 | 
						|
  IN IP6_ROUTE_TABLE        *RtTable,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Dest,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Src
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;
 | 
						|
  UINT32                    Index;
 | 
						|
 | 
						|
  Index = IP6_ROUTE_CACHE_HASH (Dest, Src);
 | 
						|
 | 
						|
  NET_LIST_FOR_EACH (Entry, &RtTable->Cache.CacheBucket[Index]) {
 | 
						|
    RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
 | 
						|
 | 
						|
    if (EFI_IP6_EQUAL (Dest, &RtCacheEntry->Destination)&& EFI_IP6_EQUAL (Src, &RtCacheEntry->Source)) {
 | 
						|
      NET_GET_REF (RtCacheEntry);
 | 
						|
      return RtCacheEntry;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Build an array of EFI_IP6_ROUTE_TABLE to be returned to the caller. The number
 | 
						|
  of EFI_IP6_ROUTE_TABLE is also returned.
 | 
						|
 | 
						|
  @param[in]  RouteTable        The pointer of IP6_ROUTE_TABLE internal used.
 | 
						|
  @param[out] EfiRouteCount     The number of returned route entries.
 | 
						|
  @param[out] EfiRouteTable     The pointer to the array of EFI_IP6_ROUTE_TABLE.
 | 
						|
                                If NULL, only the route entry count is returned.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The EFI_IP6_ROUTE_TABLE successfully built.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Ip6BuildEfiRouteTable (
 | 
						|
  IN IP6_ROUTE_TABLE        *RouteTable,
 | 
						|
  OUT UINT32                *EfiRouteCount,
 | 
						|
  OUT EFI_IP6_ROUTE_TABLE   **EfiRouteTable OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  IP6_ROUTE_ENTRY           *RtEntry;
 | 
						|
  EFI_IP6_ROUTE_TABLE       *EfiTable;
 | 
						|
  UINT32                    Count;
 | 
						|
  INT32                     Index;
 | 
						|
 | 
						|
  ASSERT (EfiRouteCount != NULL);
 | 
						|
 | 
						|
  Count          = RouteTable->TotalNum;
 | 
						|
  *EfiRouteCount = Count;
 | 
						|
 | 
						|
  if ((EfiRouteTable == NULL) || (Count == 0)) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  if (*EfiRouteTable == NULL) {
 | 
						|
    *EfiRouteTable = AllocatePool (sizeof (EFI_IP6_ROUTE_TABLE) * Count);
 | 
						|
    if (*EfiRouteTable == NULL) {
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  EfiTable = *EfiRouteTable;
 | 
						|
 | 
						|
  //
 | 
						|
  // Copy the route entry to EFI route table.
 | 
						|
  //
 | 
						|
  Count = 0;
 | 
						|
 | 
						|
  for (Index = IP6_PREFIX_MAX; Index >= 0; Index--) {
 | 
						|
 | 
						|
    NET_LIST_FOR_EACH (Entry, &(RouteTable->RouteArea[Index])) {
 | 
						|
      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
 | 
						|
 | 
						|
      Ip6CopyAddressByPrefix (
 | 
						|
        &EfiTable[Count].Destination,
 | 
						|
        &RtEntry->Destination,
 | 
						|
        RtEntry->PrefixLength
 | 
						|
        );
 | 
						|
 | 
						|
      IP6_COPY_ADDRESS (&EfiTable[Count].Gateway, &RtEntry->NextHop);
 | 
						|
      EfiTable[Count].PrefixLength = RtEntry->PrefixLength;
 | 
						|
 | 
						|
      Count++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  ASSERT (Count == RouteTable->TotalNum);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Create an empty route table. This includes its internal route cache.
 | 
						|
 | 
						|
  @return NULL if failed to allocate memory for the route table. Otherwise,
 | 
						|
          the point to newly created route table.
 | 
						|
 | 
						|
**/
 | 
						|
IP6_ROUTE_TABLE *
 | 
						|
Ip6CreateRouteTable (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  IP6_ROUTE_TABLE           *RtTable;
 | 
						|
  UINT32                    Index;
 | 
						|
 | 
						|
  RtTable = AllocatePool (sizeof (IP6_ROUTE_TABLE));
 | 
						|
  if (RtTable == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  RtTable->RefCnt   = 1;
 | 
						|
  RtTable->TotalNum = 0;
 | 
						|
 | 
						|
  for (Index = 0; Index <= IP6_PREFIX_MAX; Index++) {
 | 
						|
    InitializeListHead (&RtTable->RouteArea[Index]);
 | 
						|
  }
 | 
						|
 | 
						|
  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
 | 
						|
    InitializeListHead (&RtTable->Cache.CacheBucket[Index]);
 | 
						|
    RtTable->Cache.CacheNum[Index] = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return RtTable;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free the route table and its associated route cache. Route
 | 
						|
  table is reference counted.
 | 
						|
 | 
						|
  @param[in, out]  RtTable      The route table to free.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
Ip6CleanRouteTable (
 | 
						|
  IN OUT IP6_ROUTE_TABLE        *RtTable
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  LIST_ENTRY                *Next;
 | 
						|
  IP6_ROUTE_ENTRY           *RtEntry;
 | 
						|
  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;
 | 
						|
  UINT32                    Index;
 | 
						|
 | 
						|
  ASSERT (RtTable->RefCnt > 0);
 | 
						|
 | 
						|
  if (--RtTable->RefCnt > 0) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Free all the route table entry and its route cache.
 | 
						|
  //
 | 
						|
  for (Index = 0; Index <= IP6_PREFIX_MAX; Index++) {
 | 
						|
    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->RouteArea[Index]) {
 | 
						|
      RtEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
 | 
						|
      RemoveEntryList (Entry);
 | 
						|
      Ip6FreeRouteEntry (RtEntry);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
 | 
						|
    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtTable->Cache.CacheBucket[Index]) {
 | 
						|
      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
 | 
						|
      RemoveEntryList (Entry);
 | 
						|
      Ip6FreeRouteCacheEntry (RtCacheEntry);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  FreePool (RtTable);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Remove all the cache entries bearing the Tag. When a route cache
 | 
						|
  entry is created, it is tagged with the address of route entry
 | 
						|
  from which it is spawned. When a route entry is deleted, the cache
 | 
						|
  entries spawned from it are also deleted.
 | 
						|
 | 
						|
  @param[in]  RtCache       Route cache to remove the entries from.
 | 
						|
  @param[in]  Tag           The Tag of the entries to remove.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
Ip6PurgeRouteCache (
 | 
						|
  IN IP6_ROUTE_CACHE        *RtCache,
 | 
						|
  IN UINTN                  Tag
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  LIST_ENTRY                *Next;
 | 
						|
  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;
 | 
						|
  UINT32                    Index;
 | 
						|
 | 
						|
  for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
 | 
						|
    NET_LIST_FOR_EACH_SAFE (Entry, Next, &RtCache->CacheBucket[Index]) {
 | 
						|
 | 
						|
      RtCacheEntry = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
 | 
						|
 | 
						|
      if (RtCacheEntry->Tag == Tag) {
 | 
						|
        RemoveEntryList (Entry);
 | 
						|
        Ip6FreeRouteCacheEntry (RtCacheEntry);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Add a route entry to the route table. It is the help function for EfiIp6Routes.
 | 
						|
 | 
						|
  @param[in, out]  RtTable        Route table to add route to.
 | 
						|
  @param[in]       Destination    The destination of the network.
 | 
						|
  @param[in]       PrefixLength   The PrefixLength of the destination.
 | 
						|
  @param[in]       GatewayAddress The next hop address.
 | 
						|
 | 
						|
  @retval EFI_ACCESS_DENIED     The same route already exists.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the entry.
 | 
						|
  @retval EFI_SUCCESS           The route was added successfully.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Ip6AddRoute (
 | 
						|
  IN OUT IP6_ROUTE_TABLE    *RtTable,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Destination,
 | 
						|
  IN UINT8                  PrefixLength,
 | 
						|
  IN EFI_IPv6_ADDRESS       *GatewayAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *ListHead;
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  IP6_ROUTE_ENTRY           *Route;
 | 
						|
 | 
						|
  ListHead = &RtTable->RouteArea[PrefixLength];
 | 
						|
 | 
						|
  //
 | 
						|
  // First check whether the route exists
 | 
						|
  //
 | 
						|
  NET_LIST_FOR_EACH (Entry, ListHead) {
 | 
						|
    Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
 | 
						|
 | 
						|
    if (NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength) &&
 | 
						|
        EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {
 | 
						|
      return EFI_ACCESS_DENIED;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Create a route entry and insert it to the route area.
 | 
						|
  //
 | 
						|
  Route = Ip6CreateRouteEntry (Destination, PrefixLength, GatewayAddress);
 | 
						|
 | 
						|
  if (Route == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  if (NetIp6IsUnspecifiedAddr (GatewayAddress)) {
 | 
						|
    Route->Flag = IP6_DIRECT_ROUTE;
 | 
						|
  }
 | 
						|
 | 
						|
  InsertHeadList (ListHead, &Route->Link);
 | 
						|
  RtTable->TotalNum++;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Remove a route entry and all the route caches spawn from it.
 | 
						|
  It is the help function for EfiIp6Routes.
 | 
						|
 | 
						|
  @param[in, out] RtTable           The route table to remove the route from.
 | 
						|
  @param[in]      Destination       The destination network.
 | 
						|
  @param[in]      PrefixLength      The PrefixLength of the Destination.
 | 
						|
  @param[in]      GatewayAddress    The next hop address.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The route entry was successfully removed.
 | 
						|
  @retval EFI_NOT_FOUND         There is no route entry in the table with that
 | 
						|
                                property.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Ip6DelRoute (
 | 
						|
  IN OUT IP6_ROUTE_TABLE    *RtTable,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Destination,
 | 
						|
  IN UINT8                  PrefixLength,
 | 
						|
  IN EFI_IPv6_ADDRESS       *GatewayAddress
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *ListHead;
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  LIST_ENTRY                *Next;
 | 
						|
  IP6_ROUTE_ENTRY           *Route;
 | 
						|
  UINT32                    TotalNum;
 | 
						|
 | 
						|
  ListHead = &RtTable->RouteArea[PrefixLength];
 | 
						|
  TotalNum = RtTable->TotalNum;
 | 
						|
 | 
						|
  NET_LIST_FOR_EACH_SAFE (Entry, Next, ListHead) {
 | 
						|
    Route = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_ENTRY, Link);
 | 
						|
 | 
						|
    if (Destination != NULL && !NetIp6IsNetEqual (Destination, &Route->Destination, PrefixLength)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    if (GatewayAddress != NULL && !EFI_IP6_EQUAL (GatewayAddress, &Route->NextHop)) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    Ip6PurgeRouteCache (&RtTable->Cache, (UINTN) Route);
 | 
						|
    RemoveEntryList (Entry);
 | 
						|
    Ip6FreeRouteEntry (Route);
 | 
						|
 | 
						|
    ASSERT (RtTable->TotalNum > 0);
 | 
						|
    RtTable->TotalNum--;
 | 
						|
  }
 | 
						|
 | 
						|
  return TotalNum == RtTable->TotalNum ? EFI_NOT_FOUND : EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Search the route table to route the packet. Return/create a route
 | 
						|
  cache if there is a route to the destination.
 | 
						|
 | 
						|
  @param[in]  IpSb          The IP6 service data.
 | 
						|
  @param[in]  Dest          The destination address to search for.
 | 
						|
  @param[in]  Src           The source address to search for.
 | 
						|
 | 
						|
  @return NULL if it failed to route the packet. Otherwise, a route cache
 | 
						|
          entry that can be used to route packets.
 | 
						|
 | 
						|
**/
 | 
						|
IP6_ROUTE_CACHE_ENTRY *
 | 
						|
Ip6Route (
 | 
						|
  IN IP6_SERVICE            *IpSb,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Dest,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Src
 | 
						|
  )
 | 
						|
{
 | 
						|
  IP6_ROUTE_TABLE           *RtTable;
 | 
						|
  LIST_ENTRY                *ListHead;
 | 
						|
  IP6_ROUTE_CACHE_ENTRY     *RtCacheEntry;
 | 
						|
  IP6_ROUTE_ENTRY           *RtEntry;
 | 
						|
  EFI_IPv6_ADDRESS          NextHop;
 | 
						|
  UINT32                    Index;
 | 
						|
 | 
						|
  RtTable = IpSb->RouteTable;
 | 
						|
 | 
						|
  ASSERT (RtTable != NULL);
 | 
						|
 | 
						|
  //
 | 
						|
  // Search the destination cache in IP6_ROUTE_TABLE.
 | 
						|
  //
 | 
						|
  Index    = IP6_ROUTE_CACHE_HASH (Dest, Src);
 | 
						|
  ListHead = &RtTable->Cache.CacheBucket[Index];
 | 
						|
 | 
						|
  RtCacheEntry = Ip6FindRouteCache (RtTable, Dest, Src);
 | 
						|
 | 
						|
  //
 | 
						|
  // If found, promote the cache entry to the head of the hash bucket.
 | 
						|
  //
 | 
						|
  if (RtCacheEntry != NULL) {
 | 
						|
    RemoveEntryList (&RtCacheEntry->Link);
 | 
						|
    InsertHeadList (ListHead, &RtCacheEntry->Link);
 | 
						|
    return RtCacheEntry;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Search the route table for the most specific route
 | 
						|
  //
 | 
						|
  RtEntry = Ip6FindRouteEntry (RtTable, Dest, NULL);
 | 
						|
  if (RtEntry == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Found a route to the Dest, if it is a direct route, the packet
 | 
						|
  // will be send directly to the destination, such as for connected
 | 
						|
  // network. Otherwise, it is an indirect route, the packet will be
 | 
						|
  // send the next hop router.
 | 
						|
  //
 | 
						|
  if ((RtEntry->Flag & IP6_DIRECT_ROUTE) == IP6_DIRECT_ROUTE) {
 | 
						|
    IP6_COPY_ADDRESS (&NextHop, Dest);
 | 
						|
  } else {
 | 
						|
    IP6_COPY_ADDRESS (&NextHop, &RtEntry->NextHop);
 | 
						|
  }
 | 
						|
 | 
						|
  Ip6FreeRouteEntry (RtEntry);
 | 
						|
 | 
						|
  //
 | 
						|
  // Create a route cache entry, and tag it as spawned from this route entry
 | 
						|
  //
 | 
						|
  RtCacheEntry = Ip6CreateRouteCacheEntry (Dest, Src, &NextHop, (UINTN) RtEntry);
 | 
						|
 | 
						|
  if (RtCacheEntry == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  InsertHeadList (ListHead, &RtCacheEntry->Link);
 | 
						|
  NET_GET_REF (RtCacheEntry);
 | 
						|
  RtTable->Cache.CacheNum[Index]++;
 | 
						|
 | 
						|
  return RtCacheEntry;
 | 
						|
}
 | 
						|
 |