Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Shumin Qiu <shumin.qiu@intel.com> Reviewed-by: Ting Ye <ting.ye@intel.com> Reviewed-by: Siyuan Fu <siyuan.fu@intel.com> Reviewed-by: Mudusuru Giri P <giri.p.mudusuru@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17049 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1092 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1092 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  The internal functions and routines to transmit the IP6 packet.
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2015, 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"
 | 
						|
 | 
						|
UINT32 mIp6Id;
 | 
						|
 | 
						|
/**
 | 
						|
  Output all the available source addresses to a list entry head SourceList. The
 | 
						|
  number of source addresses are also returned.
 | 
						|
 | 
						|
  @param[in]       IpSb             Points to an IP6 service binding instance.
 | 
						|
  @param[out]      SourceList       The list entry head of all source addresses.
 | 
						|
                                    It is the caller's responsiblity to free the
 | 
						|
                                    resources.
 | 
						|
  @param[out]      SourceCount      The number of source addresses.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The source addresses were copied to a list entry head
 | 
						|
                                SourceList.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources to complete the operation.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Ip6CandidateSource (
 | 
						|
  IN IP6_SERVICE            *IpSb,
 | 
						|
  OUT LIST_ENTRY            *SourceList,
 | 
						|
  OUT UINT32                *SourceCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  IP6_INTERFACE             *IpIf;
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  LIST_ENTRY                *Entry2;
 | 
						|
  IP6_ADDRESS_INFO          *AddrInfo;
 | 
						|
  IP6_ADDRESS_INFO          *Copy;
 | 
						|
 | 
						|
  *SourceCount = 0;
 | 
						|
 | 
						|
  if (IpSb->LinkLocalOk) {
 | 
						|
    Copy = AllocatePool (sizeof (IP6_ADDRESS_INFO));
 | 
						|
    if (Copy == NULL) {
 | 
						|
      return EFI_OUT_OF_RESOURCES;
 | 
						|
    }
 | 
						|
 | 
						|
    Copy->Signature         = IP6_ADDR_INFO_SIGNATURE;
 | 
						|
    IP6_COPY_ADDRESS (&Copy->Address, &IpSb->LinkLocalAddr);
 | 
						|
    Copy->IsAnycast         = FALSE;
 | 
						|
    Copy->PrefixLength      = IP6_LINK_LOCAL_PREFIX_LENGTH;
 | 
						|
    Copy->ValidLifetime     = (UINT32) IP6_INFINIT_LIFETIME;
 | 
						|
    Copy->PreferredLifetime = (UINT32) IP6_INFINIT_LIFETIME;
 | 
						|
 | 
						|
    InsertTailList (SourceList, &Copy->Link);
 | 
						|
    (*SourceCount)++;
 | 
						|
  }
 | 
						|
 | 
						|
  NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
 | 
						|
    IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
 | 
						|
 | 
						|
    NET_LIST_FOR_EACH (Entry2, &IpIf->AddressList) {
 | 
						|
      AddrInfo = NET_LIST_USER_STRUCT_S (Entry2, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
 | 
						|
 | 
						|
      if (AddrInfo->IsAnycast) {
 | 
						|
        //
 | 
						|
        // Never use an anycast address.
 | 
						|
        //
 | 
						|
        continue;
 | 
						|
      }
 | 
						|
 | 
						|
      Copy = AllocateCopyPool (sizeof (IP6_ADDRESS_INFO), AddrInfo);
 | 
						|
      if (Copy == NULL) {
 | 
						|
        return EFI_OUT_OF_RESOURCES;
 | 
						|
      }
 | 
						|
 | 
						|
      InsertTailList (SourceList, &Copy->Link);
 | 
						|
      (*SourceCount)++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate how many bits are the same between two IPv6 addresses.
 | 
						|
 | 
						|
  @param[in]       AddressA         Points to an IPv6 address.
 | 
						|
  @param[in]       AddressB         Points to another IPv6 address.
 | 
						|
 | 
						|
  @return The common bits of the AddressA and AddressB.
 | 
						|
 | 
						|
**/
 | 
						|
UINT8
 | 
						|
Ip6CommonPrefixLen (
 | 
						|
  IN EFI_IPv6_ADDRESS       *AddressA,
 | 
						|
  IN EFI_IPv6_ADDRESS       *AddressB
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8                     Count;
 | 
						|
  UINT8                     Index;
 | 
						|
  UINT8                     ByteA;
 | 
						|
  UINT8                     ByteB;
 | 
						|
  UINT8                     NumBits;
 | 
						|
 | 
						|
  Count = 0;
 | 
						|
  Index = 0;
 | 
						|
 | 
						|
  while (Index < 16) {
 | 
						|
    ByteA = AddressA->Addr[Index];
 | 
						|
    ByteB = AddressB->Addr[Index];
 | 
						|
 | 
						|
    if (ByteA == ByteB) {
 | 
						|
      Count += 8;
 | 
						|
      Index++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Check how many bits are common between the two bytes.
 | 
						|
    //
 | 
						|
    NumBits = 8;
 | 
						|
    ByteA   = (UINT8) (ByteA ^ ByteB);
 | 
						|
 | 
						|
    while (ByteA != 0) {
 | 
						|
      NumBits--;
 | 
						|
      ByteA = (UINT8) (ByteA >> 1);
 | 
						|
    }
 | 
						|
 | 
						|
    return (UINT8) (Count + NumBits);
 | 
						|
  }
 | 
						|
 | 
						|
  return Count;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Output all the available source addresses to a list entry head SourceList. The
 | 
						|
  number of source addresses are also returned.
 | 
						|
 | 
						|
  @param[in]       IpSb             Points to a IP6 service binding instance.
 | 
						|
  @param[in]       Destination      The IPv6 destination address.
 | 
						|
  @param[out]      Source           The selected IPv6 source address according to
 | 
						|
                                    the Destination.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The source addresses were copied to a list entry
 | 
						|
                                head SourceList.
 | 
						|
  @retval EFI_NO_MAPPING        The IPv6 stack is not auto configured.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Ip6SelectSourceAddress (
 | 
						|
  IN IP6_SERVICE            *IpSb,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Destination,
 | 
						|
  OUT EFI_IPv6_ADDRESS      *Source
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  LIST_ENTRY                SourceList;
 | 
						|
  UINT32                    SourceCount;
 | 
						|
  UINT8                     ScopeD;
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  IP6_ADDRESS_INFO          *AddrInfo;
 | 
						|
  IP6_PREFIX_LIST_ENTRY     *Prefix;
 | 
						|
  UINT8                     LastCommonLength;
 | 
						|
  UINT8                     CurrentCommonLength;
 | 
						|
  EFI_IPv6_ADDRESS          *TmpAddress;
 | 
						|
 | 
						|
  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
  InitializeListHead (&SourceList);
 | 
						|
 | 
						|
  if (!IpSb->LinkLocalOk) {
 | 
						|
    return EFI_NO_MAPPING;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Rule 1: Prefer same address.
 | 
						|
  //
 | 
						|
  if (Ip6IsOneOfSetAddress (IpSb, Destination, NULL, NULL)) {
 | 
						|
    IP6_COPY_ADDRESS (Source, Destination);
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Rule 2: Prefer appropriate scope.
 | 
						|
  //
 | 
						|
  if (IP6_IS_MULTICAST (Destination)) {
 | 
						|
    ScopeD = (UINT8) (Destination->Addr[1] >> 4);
 | 
						|
  } else if (NetIp6IsLinkLocalAddr (Destination)) {
 | 
						|
    ScopeD = 0x2;
 | 
						|
  } else {
 | 
						|
    ScopeD = 0xE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (ScopeD <= 0x2) {
 | 
						|
    //
 | 
						|
    // Return the link-local address if it exists
 | 
						|
    // One IP6_SERVICE only has one link-local address.
 | 
						|
    //
 | 
						|
    IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // All candidate source addresses are global unicast address.
 | 
						|
  //
 | 
						|
  Ip6CandidateSource (IpSb, &SourceList, &SourceCount);
 | 
						|
 | 
						|
  if (SourceCount == 0) {
 | 
						|
    Status = EFI_NO_MAPPING;
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  IP6_COPY_ADDRESS (Source, &IpSb->LinkLocalAddr);
 | 
						|
 | 
						|
  if (SourceCount == 1) {
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Rule 3: Avoid deprecated addresses.
 | 
						|
  // TODO: check the "deprecated" state of the stateful configured address
 | 
						|
  //
 | 
						|
  NET_LIST_FOR_EACH (Entry, &IpSb->AutonomousPrefix) {
 | 
						|
    Prefix = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
 | 
						|
    if (Prefix->PreferredLifetime == 0) {
 | 
						|
      Ip6RemoveAddr (NULL, &SourceList, &SourceCount, &Prefix->Prefix, Prefix->PrefixLength);
 | 
						|
 | 
						|
      if (SourceCount == 1) {
 | 
						|
        goto Exit;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // TODO: Rule 4: Prefer home addresses.
 | 
						|
  // TODO: Rule 5: Prefer outgoing interface.
 | 
						|
  // TODO: Rule 6: Prefer matching label.
 | 
						|
  // TODO: Rule 7: Prefer public addresses.
 | 
						|
  //
 | 
						|
 | 
						|
  //
 | 
						|
  // Rule 8: Use longest matching prefix.
 | 
						|
  //
 | 
						|
  LastCommonLength = Ip6CommonPrefixLen (Source, Destination);
 | 
						|
  TmpAddress       = NULL;
 | 
						|
 | 
						|
  for (Entry = SourceList.ForwardLink; Entry != &SourceList; Entry = Entry->ForwardLink) {
 | 
						|
    AddrInfo = NET_LIST_USER_STRUCT_S (Entry, IP6_ADDRESS_INFO, Link, IP6_ADDR_INFO_SIGNATURE);
 | 
						|
 | 
						|
    CurrentCommonLength = Ip6CommonPrefixLen (&AddrInfo->Address, Destination);
 | 
						|
    if (CurrentCommonLength > LastCommonLength) {
 | 
						|
      LastCommonLength = CurrentCommonLength;
 | 
						|
      TmpAddress       = &AddrInfo->Address;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (TmpAddress != NULL) {
 | 
						|
    IP6_COPY_ADDRESS (Source, TmpAddress);
 | 
						|
  }
 | 
						|
 | 
						|
Exit:
 | 
						|
 | 
						|
  Ip6RemoveAddr (NULL, &SourceList, &SourceCount, NULL, 0);
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Select an interface to send the packet generated in the IP6 driver
 | 
						|
  itself: that is, not by the requests of the IP6 child's consumer. Such
 | 
						|
  packets include the ICMPv6 echo replies and other ICMPv6 error packets.
 | 
						|
 | 
						|
  @param[in]  IpSb                 The IP4 service that wants to send the packets.
 | 
						|
  @param[in]  Destination          The destination of the packet.
 | 
						|
  @param[in, out]  Source          The source of the packet.
 | 
						|
 | 
						|
  @return NULL if no proper interface is found, otherwise, the interface that
 | 
						|
          can be used to send the system packet from.
 | 
						|
 | 
						|
**/
 | 
						|
IP6_INTERFACE *
 | 
						|
Ip6SelectInterface (
 | 
						|
  IN IP6_SERVICE            *IpSb,
 | 
						|
  IN EFI_IPv6_ADDRESS       *Destination,
 | 
						|
  IN OUT EFI_IPv6_ADDRESS   *Source
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  EFI_IPv6_ADDRESS          SelectedSource;
 | 
						|
  IP6_INTERFACE             *IpIf;
 | 
						|
  BOOLEAN                   Exist;
 | 
						|
 | 
						|
  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
 | 
						|
  ASSERT (Destination != NULL && Source != NULL);
 | 
						|
 | 
						|
  if (NetIp6IsUnspecifiedAddr (Destination)) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!NetIp6IsUnspecifiedAddr (Source)) {
 | 
						|
    Exist = Ip6IsOneOfSetAddress (IpSb, Source, &IpIf, NULL);
 | 
						|
    ASSERT (Exist);
 | 
						|
 | 
						|
    return IpIf;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If source is unspecified, select a source according to the destination.
 | 
						|
  //
 | 
						|
  Status = Ip6SelectSourceAddress (IpSb, Destination, &SelectedSource);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return IpSb->DefaultInterface;
 | 
						|
  }
 | 
						|
 | 
						|
  Ip6IsOneOfSetAddress (IpSb, &SelectedSource, &IpIf, NULL);
 | 
						|
  IP6_COPY_ADDRESS (Source, &SelectedSource);
 | 
						|
 | 
						|
  return IpIf;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The default callback function for the system generated packet.
 | 
						|
  It will free the packet.
 | 
						|
 | 
						|
  @param[in]  Packet        The packet that transmitted.
 | 
						|
  @param[in]  IoStatus      The result of the transmission, succeeded or failed.
 | 
						|
  @param[in]  LinkFlag      Not used when transmitted. Check IP6_FRAME_CALLBACK
 | 
						|
                            for reference.
 | 
						|
  @param[in]  Context       The context provided by us.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
Ip6SysPacketSent (
 | 
						|
  NET_BUF                   *Packet,
 | 
						|
  EFI_STATUS                IoStatus,
 | 
						|
  UINT32                    LinkFlag,
 | 
						|
  VOID                      *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  NetbufFree (Packet);
 | 
						|
  Packet = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Prefix an IP6 basic head and unfragmentable extension headers and a fragment header
 | 
						|
  to the Packet. Used for IP6 fragmentation.
 | 
						|
 | 
						|
  @param[in]  IpSb             The IP6 service instance to transmit the packet.
 | 
						|
  @param[in]  Packet           The packet to prefix the IP6 header to.
 | 
						|
  @param[in]  Head             The caller supplied header.
 | 
						|
  @param[in]  FragmentOffset   The fragment offset of the data following the header.
 | 
						|
  @param[in]  ExtHdrs          The length of the original extension header.
 | 
						|
  @param[in]  ExtHdrsLen       The length of the extension headers.
 | 
						|
  @param[in]  LastHeader       The pointer of next header of last extension header.
 | 
						|
  @param[in]  HeadLen          The length of the unfragmented part of the IP6 header.
 | 
						|
 | 
						|
  @retval EFI_BAD_BUFFER_SIZE  There is no enought room in the head space of
 | 
						|
                               Packet.
 | 
						|
  @retval EFI_SUCCESS          The operation performed successfully.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Ip6PrependHead (
 | 
						|
  IN IP6_SERVICE            *IpSb,
 | 
						|
  IN NET_BUF                *Packet,
 | 
						|
  IN EFI_IP6_HEADER         *Head,
 | 
						|
  IN UINT16                 FragmentOffset,
 | 
						|
  IN UINT8                  *ExtHdrs,
 | 
						|
  IN UINT32                 ExtHdrsLen,
 | 
						|
  IN UINT8                  LastHeader,
 | 
						|
  IN UINT32                 HeadLen
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32                    Len;
 | 
						|
  UINT32                    UnFragExtHdrsLen;
 | 
						|
  EFI_IP6_HEADER            *PacketHead;
 | 
						|
  UINT8                     *UpdatedExtHdrs;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  UINT8                     NextHeader;
 | 
						|
 | 
						|
  UpdatedExtHdrs = NULL;
 | 
						|
 | 
						|
  //
 | 
						|
  // HeadLen is the length of the fixed part of the sequences of fragments, i.e.
 | 
						|
  // the unfragment part.
 | 
						|
  //
 | 
						|
  PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
 | 
						|
  if (PacketHead == NULL) {
 | 
						|
    return EFI_BAD_BUFFER_SIZE;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Set the head up, convert the host byte order to network byte order
 | 
						|
  //
 | 
						|
  CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
 | 
						|
  PacketHead->PayloadLength = HTONS ((UINT16) (Packet->TotalSize - sizeof (EFI_IP6_HEADER)));
 | 
						|
  Packet->Ip.Ip6            = PacketHead;
 | 
						|
 | 
						|
  Len              = HeadLen - sizeof (EFI_IP6_HEADER);
 | 
						|
  UnFragExtHdrsLen = Len - sizeof (IP6_FRAGMENT_HEADER);
 | 
						|
 | 
						|
  if (UnFragExtHdrsLen == 0) {
 | 
						|
    PacketHead->NextHeader = IP6_FRAGMENT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Append the extension headers: firstly copy the unfragmentable headers, then append
 | 
						|
  // fragmentation header.
 | 
						|
  //
 | 
						|
  if ((FragmentOffset & IP6_FRAGMENT_OFFSET_MASK) == 0) {
 | 
						|
    NextHeader = Head->NextHeader;
 | 
						|
  } else {
 | 
						|
    NextHeader = PacketHead->NextHeader;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = Ip6FillFragmentHeader (
 | 
						|
             IpSb,
 | 
						|
             NextHeader,
 | 
						|
             LastHeader,
 | 
						|
             ExtHdrs,
 | 
						|
             ExtHdrsLen,
 | 
						|
             FragmentOffset,
 | 
						|
             &UpdatedExtHdrs
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  CopyMem (
 | 
						|
    (UINT8 *) (PacketHead + 1),
 | 
						|
    UpdatedExtHdrs,
 | 
						|
    UnFragExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER)
 | 
						|
    );
 | 
						|
 | 
						|
  FreePool (UpdatedExtHdrs);
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Transmit an IP6 packet. The packet comes either from the IP6
 | 
						|
  child's consumer (IpInstance != NULL) or the IP6 driver itself
 | 
						|
  (IpInstance == NULL). It will route the packet, fragment it,
 | 
						|
  then transmit all the fragments through an interface.
 | 
						|
 | 
						|
  @param[in]  IpSb             The IP6 service instance to transmit the packet.
 | 
						|
  @param[in]  Interface        The IP6 interface to transmit the packet. Ignored
 | 
						|
                               if NULL.
 | 
						|
  @param[in]  IpInstance       The IP6 child that issues the transmission.  It is
 | 
						|
                               NULL if the packet is from the system.
 | 
						|
  @param[in]  Packet           The user data to send, excluding the IP header.
 | 
						|
  @param[in]  Head             The caller supplied header. The caller should set
 | 
						|
                               the  following header fields: NextHeader, HopLimit,
 | 
						|
                               Src, Dest, FlowLabel, PayloadLength. This function
 | 
						|
                               will fill in the Ver, TrafficClass.
 | 
						|
  @param[in]  ExtHdrs          The extension headers to append to the IPv6 basic
 | 
						|
                               header.
 | 
						|
  @param[in]  ExtHdrsLen       The length of the extension headers.
 | 
						|
  @param[in]  Callback         The callback function to issue when transmission
 | 
						|
                               completed.
 | 
						|
  @param[in]  Context          The opaque context for the callback.
 | 
						|
 | 
						|
  @retval EFI_INVALID_PARAMETER Any input parameter or the packet is invalid.
 | 
						|
  @retval EFI_NO_MAPPING        There is no interface to the destination.
 | 
						|
  @retval EFI_NOT_FOUND         There is no route to the destination.
 | 
						|
  @retval EFI_SUCCESS           The packet successfully transmitted.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to finish the operation due to lack of
 | 
						|
                                resources.
 | 
						|
  @retval Others                Failed to transmit the packet.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
Ip6Output (
 | 
						|
  IN IP6_SERVICE            *IpSb,
 | 
						|
  IN IP6_INTERFACE          *Interface   OPTIONAL,
 | 
						|
  IN IP6_PROTOCOL           *IpInstance  OPTIONAL,
 | 
						|
  IN NET_BUF                *Packet,
 | 
						|
  IN EFI_IP6_HEADER         *Head,
 | 
						|
  IN UINT8                  *ExtHdrs,
 | 
						|
  IN UINT32                 ExtHdrsLen,
 | 
						|
  IN IP6_FRAME_CALLBACK     Callback,
 | 
						|
  IN VOID                   *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  IP6_INTERFACE             *IpIf;
 | 
						|
  EFI_IPv6_ADDRESS          NextHop;
 | 
						|
  IP6_NEIGHBOR_ENTRY        *NeighborCache;
 | 
						|
  IP6_ROUTE_CACHE_ENTRY     *RouteCache;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  UINT32                    Mtu;
 | 
						|
  UINT32                    HeadLen;
 | 
						|
  UINT16                    FragmentOffset;
 | 
						|
  UINT8                     *LastHeader;
 | 
						|
  UINT32                    UnFragmentLen;
 | 
						|
  UINT32                    UnFragmentHdrsLen;
 | 
						|
  UINT32                    FragmentHdrsLen;
 | 
						|
  UINT16                    *Checksum;
 | 
						|
  UINT16                    PacketChecksum;
 | 
						|
  UINT16                    PseudoChecksum;
 | 
						|
  UINT32                    Index;
 | 
						|
  UINT32                    PacketLen;
 | 
						|
  UINT32                    RealExtLen;
 | 
						|
  UINT32                    Offset;
 | 
						|
  NET_BUF                   *TmpPacket;
 | 
						|
  NET_BUF                   *Fragment;
 | 
						|
  UINT32                    Num;
 | 
						|
  UINT8                     *Buf;
 | 
						|
  EFI_IP6_HEADER            *PacketHead;
 | 
						|
  IP6_ICMP_HEAD             *IcmpHead;
 | 
						|
  IP6_TXTOKEN_WRAP          *Wrap;
 | 
						|
  IP6_ROUTE_ENTRY           *RouteEntry;
 | 
						|
  UINT8                     *UpdatedExtHdrs;
 | 
						|
  UINT8                     NextHeader;
 | 
						|
  UINT8                     LastHeaderBackup;
 | 
						|
  BOOLEAN                   FragmentHeadInserted;
 | 
						|
  UINT8                     *ExtHdrsBackup;
 | 
						|
  UINT8                     NextHeaderBackup;
 | 
						|
  EFI_IPv6_ADDRESS          Source;
 | 
						|
  EFI_IPv6_ADDRESS          Destination;
 | 
						|
 | 
						|
  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
 | 
						|
 | 
						|
  //
 | 
						|
  // RFC2460: Each extension header is an integer multiple of 8 octets long,
 | 
						|
  // in order to retain 8-octet alignment for subsequent headers.
 | 
						|
  //
 | 
						|
  if ((ExtHdrsLen & 0x7) != 0) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  LastHeader = NULL;
 | 
						|
 | 
						|
  Ip6IsExtsValid (
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    &Head->NextHeader,
 | 
						|
    ExtHdrs,
 | 
						|
    ExtHdrsLen,
 | 
						|
    FALSE,
 | 
						|
    NULL,
 | 
						|
    &LastHeader,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL
 | 
						|
    );
 | 
						|
 | 
						|
  //
 | 
						|
  // Select an interface/source for system packet, application
 | 
						|
  // should select them itself.
 | 
						|
  //
 | 
						|
  IpIf = Interface;
 | 
						|
  if (IpIf == NULL) {
 | 
						|
    //
 | 
						|
    // IpInstance->Interface is NULL when IpInstance is configured with both stationaddress
 | 
						|
    // and destinationaddress is unspecified.
 | 
						|
    //
 | 
						|
    if (IpInstance == NULL || IpInstance->Interface == NULL) {
 | 
						|
      IpIf = Ip6SelectInterface (IpSb, &Head->DestinationAddress, &Head->SourceAddress);
 | 
						|
      if (IpInstance != NULL) {
 | 
						|
        IpInstance->Interface = IpIf;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      IpIf = IpInstance->Interface;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (IpIf == NULL) {
 | 
						|
    return EFI_NO_MAPPING;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Update the common field in Head here.
 | 
						|
  //
 | 
						|
  Head->Version       = 6;
 | 
						|
  Head->TrafficClassL = 0;
 | 
						|
  Head->TrafficClassH = 0;
 | 
						|
 | 
						|
  Checksum            = NULL;
 | 
						|
  NextHeader          = *LastHeader;
 | 
						|
 | 
						|
  switch (NextHeader) {
 | 
						|
  case EFI_IP_PROTO_UDP:
 | 
						|
    Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, NULL);
 | 
						|
    ASSERT (Packet->Udp != NULL);
 | 
						|
    if (Packet->Udp->Checksum == 0) {
 | 
						|
      Checksum = &Packet->Udp->Checksum;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case EFI_IP_PROTO_TCP:
 | 
						|
    Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, NULL);
 | 
						|
    ASSERT (Packet->Tcp != NULL);
 | 
						|
    if (Packet->Tcp->Checksum == 0) {
 | 
						|
      Checksum = &Packet->Tcp->Checksum;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case IP6_ICMP:
 | 
						|
    //
 | 
						|
    // Don't send ICMP packet to an IPv6 anycast address.
 | 
						|
    //
 | 
						|
    if (Ip6IsAnycast (IpSb, &Head->DestinationAddress)) {
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
 | 
						|
    IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);
 | 
						|
    ASSERT (IcmpHead != NULL);
 | 
						|
    if (IcmpHead->Checksum == 0) {
 | 
						|
      Checksum = &IcmpHead->Checksum;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (Checksum != NULL) {
 | 
						|
    //
 | 
						|
    // Calculate the checksum for upper layer protocol if it is not calculated due to lack of
 | 
						|
    // IPv6 source address.
 | 
						|
    //
 | 
						|
    PacketChecksum = NetbufChecksum (Packet);
 | 
						|
    PseudoChecksum = NetIp6PseudoHeadChecksum (
 | 
						|
                      &Head->SourceAddress,
 | 
						|
                      &Head->DestinationAddress,
 | 
						|
                      NextHeader,
 | 
						|
                      Packet->TotalSize
 | 
						|
                      );
 | 
						|
    *Checksum = (UINT16) ~NetAddChecksum (PacketChecksum, PseudoChecksum);
 | 
						|
  }
 | 
						|
 | 
						|
  Status = Ip6IpSecProcessPacket (
 | 
						|
             IpSb,
 | 
						|
             &Head,
 | 
						|
             LastHeader, // no need get the lasthead value for output
 | 
						|
             &Packet,
 | 
						|
             &ExtHdrs,
 | 
						|
             &ExtHdrsLen,
 | 
						|
             EfiIPsecOutBound,
 | 
						|
             Context
 | 
						|
             );
 | 
						|
 | 
						|
  if (EFI_ERROR(Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  LastHeader = NULL;
 | 
						|
  //
 | 
						|
  // Check incoming parameters.
 | 
						|
  //
 | 
						|
  if (!Ip6IsExtsValid (
 | 
						|
         IpSb,
 | 
						|
         Packet,
 | 
						|
         &Head->NextHeader,
 | 
						|
         ExtHdrs,
 | 
						|
         ExtHdrsLen,
 | 
						|
         FALSE,
 | 
						|
         NULL,
 | 
						|
         &LastHeader,
 | 
						|
         &RealExtLen,
 | 
						|
         &UnFragmentHdrsLen,
 | 
						|
         NULL
 | 
						|
         )) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((RealExtLen & 0x7) != 0) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  LastHeaderBackup = *LastHeader;
 | 
						|
 | 
						|
  //
 | 
						|
  // Perform next hop determination:
 | 
						|
  // For multicast packets, the next-hop is always the destination address and
 | 
						|
  // is considered to be on-link.
 | 
						|
  //
 | 
						|
  if (IP6_IS_MULTICAST (&Head->DestinationAddress)) {
 | 
						|
    IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // For unicast packets, use a combination of the Destination Cache, the Prefix List
 | 
						|
    // and the Default Router List to determine the IP address of the appropriate next hop.
 | 
						|
    //
 | 
						|
 | 
						|
    NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->DestinationAddress);
 | 
						|
    if (NeighborCache != NULL) {
 | 
						|
      //
 | 
						|
      // Hit Neighbor Cache.
 | 
						|
      //
 | 
						|
      IP6_COPY_ADDRESS (&NextHop, &Head->DestinationAddress);
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // Not in Neighbor Cache, check Router cache
 | 
						|
      //
 | 
						|
      RouteCache = Ip6Route (IpSb, &Head->DestinationAddress, &Head->SourceAddress);
 | 
						|
      if (RouteCache == NULL) {
 | 
						|
        return EFI_NOT_FOUND;
 | 
						|
      }
 | 
						|
 | 
						|
      IP6_COPY_ADDRESS (&NextHop, &RouteCache->NextHop);
 | 
						|
      Ip6FreeRouteCacheEntry (RouteCache);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Examines the Neighbor Cache for link-layer information about that neighbor.
 | 
						|
  // DO NOT create neighbor cache if neighbor is itself - when reporting ICMP error.
 | 
						|
  //
 | 
						|
  if (!IP6_IS_MULTICAST (&NextHop) && !EFI_IP6_EQUAL (&Head->DestinationAddress, &Head->SourceAddress)) {
 | 
						|
    NeighborCache = Ip6FindNeighborEntry (IpSb, &NextHop);
 | 
						|
    if (NeighborCache == NULL) {
 | 
						|
      NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &NextHop, NULL);
 | 
						|
 | 
						|
      if (NeighborCache == NULL) {
 | 
						|
        return EFI_OUT_OF_RESOURCES;
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // Send out multicast neighbor solicitation for address resolution immediatly.
 | 
						|
      //
 | 
						|
      Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);
 | 
						|
      Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
 | 
						|
      Status = Ip6SendNeighborSolicit (
 | 
						|
                 IpSb,
 | 
						|
                 &Source,
 | 
						|
                 &Destination,
 | 
						|
                 &NeighborCache->Neighbor,
 | 
						|
                 &IpSb->SnpMode.CurrentAddress
 | 
						|
                 );
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
 | 
						|
      --NeighborCache->Transmit;
 | 
						|
      NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer) + 1;
 | 
						|
    }
 | 
						|
 | 
						|
    NeighborCache->Interface = IpIf;
 | 
						|
  }
 | 
						|
 | 
						|
  UpdatedExtHdrs       = NULL;
 | 
						|
  ExtHdrsBackup        = NULL;
 | 
						|
  NextHeaderBackup     = 0;
 | 
						|
  FragmentHeadInserted = FALSE;
 | 
						|
 | 
						|
  //
 | 
						|
  // Check whether we received Packet Too Big message for the packet sent to the
 | 
						|
  // Destination. If yes include a Fragment Header in the subsequent packets.
 | 
						|
  //
 | 
						|
  RouteEntry = Ip6FindRouteEntry (
 | 
						|
                 IpSb->RouteTable,
 | 
						|
                 &Head->DestinationAddress,
 | 
						|
                 NULL
 | 
						|
                 );
 | 
						|
  if (RouteEntry != NULL) {
 | 
						|
    if ((RouteEntry->Flag & IP6_PACKET_TOO_BIG) == IP6_PACKET_TOO_BIG) {
 | 
						|
 | 
						|
      //
 | 
						|
      // FragmentHead is inserted after Hop-by-Hop Options header, Destination
 | 
						|
      // Options header (first occur), Routing header, and before Fragment header,
 | 
						|
      // Authentication header, Encapsulating Security Payload header, and
 | 
						|
      // Destination Options header (last occur), and upper-layer header.
 | 
						|
      //
 | 
						|
      Status = Ip6FillFragmentHeader (
 | 
						|
                 IpSb,
 | 
						|
                 Head->NextHeader,
 | 
						|
                 LastHeaderBackup,
 | 
						|
                 ExtHdrs,
 | 
						|
                 ExtHdrsLen,
 | 
						|
                 0,
 | 
						|
                 &UpdatedExtHdrs
 | 
						|
                 );
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
 | 
						|
      if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {
 | 
						|
        NextHeaderBackup = Head->NextHeader;
 | 
						|
        Head->NextHeader = IP6_FRAGMENT;
 | 
						|
      }
 | 
						|
 | 
						|
      ExtHdrsBackup    = ExtHdrs;
 | 
						|
      ExtHdrs          = UpdatedExtHdrs;
 | 
						|
      ExtHdrsLen       = ExtHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
 | 
						|
      RealExtLen       = RealExtLen + sizeof (IP6_FRAGMENT_HEADER);
 | 
						|
 | 
						|
      mIp6Id++;
 | 
						|
 | 
						|
      FragmentHeadInserted = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    Ip6FreeRouteEntry (RouteEntry);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // OK, selected the source and route, fragment the packet then send
 | 
						|
  // them. Tag each fragment other than the first one as spawn from it.
 | 
						|
  // Each extension header is an integar multiple of 8 octets long, in
 | 
						|
  // order to retain 8-octet alignment for subsequent headers.
 | 
						|
  //
 | 
						|
  Mtu     = IpSb->MaxPacketSize + sizeof (EFI_IP6_HEADER);
 | 
						|
  HeadLen = sizeof (EFI_IP6_HEADER) + RealExtLen;
 | 
						|
 | 
						|
  if (Packet->TotalSize + HeadLen > Mtu) {
 | 
						|
    //
 | 
						|
    // Remove the inserted Fragment Header since we need fragment the packet.
 | 
						|
    //
 | 
						|
    if (FragmentHeadInserted) {
 | 
						|
      ExtHdrs    = ExtHdrsBackup;
 | 
						|
      ExtHdrsLen = ExtHdrsLen - sizeof (IP6_FRAGMENT_HEADER);
 | 
						|
 | 
						|
      if ((ExtHdrs == NULL) && (ExtHdrsLen == 0)) {
 | 
						|
        Head->NextHeader = NextHeaderBackup;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    FragmentHdrsLen = ExtHdrsLen - UnFragmentHdrsLen;
 | 
						|
 | 
						|
    //
 | 
						|
    // The packet is beyond the maximum which can be described through the
 | 
						|
    // fragment offset field in Fragment header.
 | 
						|
    //
 | 
						|
    if ((((Packet->TotalSize + FragmentHdrsLen) >> 3) & (~0x1fff)) != 0) {
 | 
						|
      Status = EFI_BAD_BUFFER_SIZE;
 | 
						|
      goto Error;
 | 
						|
    }
 | 
						|
 | 
						|
    if (FragmentHdrsLen != 0) {
 | 
						|
      //
 | 
						|
      // Append the fragmentable extension hdrs before the upper layer payload
 | 
						|
      // to form a new NET_BUF. This NET_BUF contains all the buffer which will
 | 
						|
      // be fragmented below.
 | 
						|
      //
 | 
						|
      TmpPacket = NetbufGetFragment (Packet, 0, Packet->TotalSize, FragmentHdrsLen);
 | 
						|
      ASSERT (TmpPacket != NULL);
 | 
						|
 | 
						|
      //
 | 
						|
      // Allocate the space to contain the fragmentable hdrs and copy the data.
 | 
						|
      //
 | 
						|
      Buf = NetbufAllocSpace (TmpPacket, FragmentHdrsLen, TRUE);
 | 
						|
      ASSERT (Buf != NULL);
 | 
						|
      CopyMem (Buf, ExtHdrs + UnFragmentHdrsLen, FragmentHdrsLen);
 | 
						|
 | 
						|
      //
 | 
						|
      // Free the old Packet.
 | 
						|
      //
 | 
						|
      NetbufFree (Packet);
 | 
						|
      Packet = TmpPacket;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // The unfragment part which appears in every fragmented IPv6 packet includes
 | 
						|
    // the IPv6 header, the unfragmentable extension hdrs and the fragment header.
 | 
						|
    //
 | 
						|
    UnFragmentLen = sizeof (EFI_IP6_HEADER) + UnFragmentHdrsLen + sizeof (IP6_FRAGMENT_HEADER);
 | 
						|
 | 
						|
    //
 | 
						|
    // Mtu now is the length of the fragment part in a full-length fragment.
 | 
						|
    //
 | 
						|
    Mtu = (Mtu - UnFragmentLen) & (~0x07);
 | 
						|
    Num = (Packet->TotalSize + Mtu - 1) / Mtu;
 | 
						|
 | 
						|
    for (Index = 0, Offset = 0, PacketLen = Mtu; Index < Num; Index++) {
 | 
						|
      //
 | 
						|
      // Get fragment from the Packet, append UnFragnmentLen spare buffer
 | 
						|
      // before the fragmented data, the corresponding data is filled in later.
 | 
						|
      //
 | 
						|
      Fragment = NetbufGetFragment (Packet, Offset, PacketLen, UnFragmentLen);
 | 
						|
      if (Fragment == NULL) {
 | 
						|
        Status = EFI_OUT_OF_RESOURCES;
 | 
						|
        goto Error;
 | 
						|
      }
 | 
						|
 | 
						|
      FragmentOffset = (UINT16) ((UINT16) Offset | 0x1);
 | 
						|
      if (Index == Num - 1){
 | 
						|
        //
 | 
						|
        // The last fragment, clear the M flag.
 | 
						|
        //
 | 
						|
        FragmentOffset &= (~0x1);
 | 
						|
      }
 | 
						|
 | 
						|
      Status = Ip6PrependHead (
 | 
						|
                 IpSb,
 | 
						|
                 Fragment,
 | 
						|
                 Head,
 | 
						|
                 FragmentOffset,
 | 
						|
                 ExtHdrs,
 | 
						|
                 ExtHdrsLen,
 | 
						|
                 LastHeaderBackup,
 | 
						|
                 UnFragmentLen
 | 
						|
                 );
 | 
						|
      ASSERT (Status == EFI_SUCCESS);
 | 
						|
 | 
						|
      Status = Ip6SendFrame (
 | 
						|
                 IpIf,
 | 
						|
                 IpInstance,
 | 
						|
                 Fragment,
 | 
						|
                 &NextHop,
 | 
						|
                 Ip6SysPacketSent,
 | 
						|
                 Packet
 | 
						|
                 );
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        goto Error;
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      // The last fragment of upper layer packet, update the IP6 token status.
 | 
						|
      //
 | 
						|
      if ((Index == Num -1) && (Context != NULL)) {
 | 
						|
        Wrap                = (IP6_TXTOKEN_WRAP *) Context;
 | 
						|
        Wrap->Token->Status = Status;
 | 
						|
      }
 | 
						|
 | 
						|
      Offset    += PacketLen;
 | 
						|
      PacketLen = Packet->TotalSize - Offset;
 | 
						|
      if (PacketLen > Mtu) {
 | 
						|
        PacketLen = Mtu;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    NetbufFree (Packet);
 | 
						|
    mIp6Id++;
 | 
						|
 | 
						|
    if (UpdatedExtHdrs != NULL) {
 | 
						|
      FreePool (UpdatedExtHdrs);
 | 
						|
    }
 | 
						|
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Need not fragment the packet, send it in one frame.
 | 
						|
  //
 | 
						|
  PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (Packet, HeadLen, NET_BUF_HEAD);
 | 
						|
  if (PacketHead == NULL) {
 | 
						|
    Status = EFI_BAD_BUFFER_SIZE;
 | 
						|
    goto Error;
 | 
						|
  }
 | 
						|
 | 
						|
  CopyMem (PacketHead, Head, sizeof (EFI_IP6_HEADER));
 | 
						|
  Packet->Ip.Ip6 = PacketHead;
 | 
						|
 | 
						|
  if (ExtHdrs != NULL) {
 | 
						|
    Buf = (UINT8 *) (PacketHead + 1);
 | 
						|
    CopyMem (Buf, ExtHdrs, ExtHdrsLen);
 | 
						|
  }
 | 
						|
 | 
						|
  if (UpdatedExtHdrs != NULL) {
 | 
						|
    //
 | 
						|
    // A Fragment Header is inserted to the packet, update the payload length.
 | 
						|
    //
 | 
						|
    PacketHead->PayloadLength = (UINT16) (NTOHS (PacketHead->PayloadLength) +
 | 
						|
                                sizeof (IP6_FRAGMENT_HEADER));
 | 
						|
    PacketHead->PayloadLength = HTONS (PacketHead->PayloadLength);
 | 
						|
    FreePool (UpdatedExtHdrs);
 | 
						|
  }
 | 
						|
 | 
						|
  return Ip6SendFrame (
 | 
						|
           IpIf,
 | 
						|
           IpInstance,
 | 
						|
           Packet,
 | 
						|
           &NextHop,
 | 
						|
           Callback,
 | 
						|
           Context
 | 
						|
           );
 | 
						|
 | 
						|
Error:
 | 
						|
  if (UpdatedExtHdrs != NULL) {
 | 
						|
    FreePool (UpdatedExtHdrs);
 | 
						|
  }
 | 
						|
  Ip6CancelPacket (IpIf, Packet, Status);
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The filter function to find a packet and all its fragments.
 | 
						|
  The packet's fragments have their Context set to the packet.
 | 
						|
 | 
						|
  @param[in]  Frame            The frames hold by the low level interface.
 | 
						|
  @param[in]  Context          Context to the function, which is the packet.
 | 
						|
 | 
						|
  @retval TRUE                 This is the packet to cancel or its fragments.
 | 
						|
  @retval FALSE                This is an unrelated packet.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
Ip6CancelPacketFragments (
 | 
						|
  IN IP6_LINK_TX_TOKEN   *Frame,
 | 
						|
  IN VOID                *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  if ((Frame->Packet == (NET_BUF *) Context) || (Frame->Context == Context)) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Remove all the frames on the interface that pass the FrameToCancel,
 | 
						|
  either queued on ARP queues or that have already been delivered to
 | 
						|
  MNP and not yet recycled.
 | 
						|
 | 
						|
  @param[in]  Interface     Interface to remove the frames from.
 | 
						|
  @param[in]  IoStatus      The transmit status returned to the frames' callback.
 | 
						|
  @param[in]  FrameToCancel Function to select the frame to cancel; NULL to select all.
 | 
						|
  @param[in]  Context       Opaque parameters passed to FrameToCancel. Ignored if
 | 
						|
                            FrameToCancel is NULL.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
Ip6CancelFrames (
 | 
						|
  IN IP6_INTERFACE          *Interface,
 | 
						|
  IN EFI_STATUS             IoStatus,
 | 
						|
  IN IP6_FRAME_TO_CANCEL    FrameToCancel   OPTIONAL,
 | 
						|
  IN VOID                   *Context        OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY                *Entry;
 | 
						|
  LIST_ENTRY                *Next;
 | 
						|
  IP6_LINK_TX_TOKEN         *Token;
 | 
						|
  IP6_SERVICE               *IpSb;
 | 
						|
  IP6_NEIGHBOR_ENTRY        *ArpQue;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
 | 
						|
  IpSb = Interface->Service;
 | 
						|
  NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
 | 
						|
 | 
						|
  //
 | 
						|
  // Cancel all the pending frames on ARP requests
 | 
						|
  //
 | 
						|
  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {
 | 
						|
    ArpQue = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, ArpList);
 | 
						|
 | 
						|
    Status = Ip6FreeNeighborEntry (
 | 
						|
               IpSb,
 | 
						|
               ArpQue,
 | 
						|
               FALSE,
 | 
						|
               FALSE,
 | 
						|
               IoStatus,
 | 
						|
               FrameToCancel,
 | 
						|
               Context
 | 
						|
               );
 | 
						|
    ASSERT_EFI_ERROR (Status);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Cancel all the frames that have been delivered to MNP
 | 
						|
  // but not yet recycled.
 | 
						|
  //
 | 
						|
  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {
 | 
						|
    Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
 | 
						|
 | 
						|
    if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {
 | 
						|
      IpSb->Mnp->Cancel (IpSb->Mnp, &Token->MnpToken);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Cancel the Packet and all its fragments.
 | 
						|
 | 
						|
  @param[in]  IpIf                 The interface from which the Packet is sent.
 | 
						|
  @param[in]  Packet               The Packet to cancel.
 | 
						|
  @param[in]  IoStatus             The status returns to the sender.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
Ip6CancelPacket (
 | 
						|
  IN IP6_INTERFACE    *IpIf,
 | 
						|
  IN NET_BUF          *Packet,
 | 
						|
  IN EFI_STATUS       IoStatus
 | 
						|
  )
 | 
						|
{
 | 
						|
  Ip6CancelFrames (IpIf, IoStatus, Ip6CancelPacketFragments, Packet);
 | 
						|
}
 | 
						|
 |