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);
 | |
| }
 | |
| 
 |