Signed-off-by: Fu Siyuan <siyuan.fu@intel.com> Reviewed-by: Ye, Ting <ting.ye@intel.com> Reviewed-by: Jin, Eric <eric.jin@intel.com> Contributed-under: TianoCore Contribution Agreement 1.0 git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15443 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1826 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1826 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   IP6 internal functions to process the incoming packets.
 | |
| 
 | |
|   Copyright (c) 2009 - 2014, 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"
 | |
| 
 | |
| /**
 | |
|   Create an empty assemble entry for the packet identified by
 | |
|   (Dst, Src, Id). The default life for the packet is 60 seconds.
 | |
| 
 | |
|   @param[in]  Dst                    The destination address.
 | |
|   @param[in]  Src                    The source address.
 | |
|   @param[in]  Id                     The ID field in the IP header.
 | |
| 
 | |
|   @return NULL if failed to allocate memory for the entry. Otherwise,
 | |
|           the pointer to the just created reassemble entry.
 | |
| 
 | |
| **/
 | |
| IP6_ASSEMBLE_ENTRY *
 | |
| Ip6CreateAssembleEntry (
 | |
|   IN EFI_IPv6_ADDRESS       *Dst,
 | |
|   IN EFI_IPv6_ADDRESS       *Src,
 | |
|   IN UINT32                 Id
 | |
|   )
 | |
| {
 | |
|   IP6_ASSEMBLE_ENTRY        *Assemble;
 | |
| 
 | |
|   Assemble = AllocatePool (sizeof (IP6_ASSEMBLE_ENTRY));
 | |
|   if (Assemble == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   IP6_COPY_ADDRESS (&Assemble->Dst, Dst);
 | |
|   IP6_COPY_ADDRESS (&Assemble->Src, Src);
 | |
|   InitializeListHead (&Assemble->Fragments);
 | |
| 
 | |
|   Assemble->Id       = Id;
 | |
|   Assemble->Life     = IP6_FRAGMENT_LIFE + 1;
 | |
| 
 | |
|   Assemble->TotalLen = 0;
 | |
|   Assemble->CurLen   = 0;
 | |
|   Assemble->Head     = NULL;
 | |
|   Assemble->Info     = NULL;
 | |
|   Assemble->Packet   = NULL;
 | |
| 
 | |
|   return Assemble;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release all the fragments of a packet, then free the assemble entry.
 | |
| 
 | |
|   @param[in]  Assemble               The assemble entry to free.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip6FreeAssembleEntry (
 | |
|   IN IP6_ASSEMBLE_ENTRY     *Assemble
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY                *Entry;
 | |
|   LIST_ENTRY                *Next;
 | |
|   NET_BUF                   *Fragment;
 | |
| 
 | |
|   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Assemble->Fragments) {
 | |
|     Fragment = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
 | |
| 
 | |
|     RemoveEntryList (Entry);
 | |
|     NetbufFree (Fragment);
 | |
|   }
 | |
| 
 | |
|   if (Assemble->Packet != NULL) {
 | |
|     NetbufFree (Assemble->Packet);
 | |
|   }
 | |
| 
 | |
|   FreePool (Assemble);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release all the fragments of the packet. This is the callback for
 | |
|   the assembled packet's OnFree. It will free the assemble entry,
 | |
|   which in turn frees all the fragments of the packet.
 | |
| 
 | |
|   @param[in]  Arg                    The assemble entry to free.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| Ip6OnFreeFragments (
 | |
|   IN VOID                   *Arg
 | |
|   )
 | |
| {
 | |
|   Ip6FreeAssembleEntry ((IP6_ASSEMBLE_ENTRY *) Arg);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Trim the packet to fit in [Start, End), and update per the
 | |
|   packet information.
 | |
| 
 | |
|   @param[in, out]  Packet   Packet to trim.
 | |
|   @param[in]       Start    The sequence of the first byte to fit in.
 | |
|   @param[in]       End      One beyond the sequence of last byte to fit in.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip6TrimPacket (
 | |
|   IN OUT NET_BUF            *Packet,
 | |
|   IN INTN                   Start,
 | |
|   IN INTN                   End
 | |
|   )
 | |
| {
 | |
|   IP6_CLIP_INFO             *Info;
 | |
|   INTN                      Len;
 | |
| 
 | |
|   Info = IP6_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|   ASSERT (Info->Start + Info->Length == Info->End);
 | |
|   ASSERT ((Info->Start < End) && (Start < Info->End));
 | |
| 
 | |
|    if (Info->Start < Start) {
 | |
|     Len = Start - Info->Start;
 | |
| 
 | |
|     NetbufTrim (Packet, (UINT32) Len, NET_BUF_HEAD);
 | |
|     Info->Start   = (UINT32) Start;
 | |
|     Info->Length -= (UINT32) Len;
 | |
|   }
 | |
| 
 | |
|   if (End < Info->End) {
 | |
|     Len = End - Info->End;
 | |
| 
 | |
|     NetbufTrim (Packet, (UINT32) Len, NET_BUF_TAIL);
 | |
|     Info->End     = (UINT32) End;
 | |
|     Info->Length -= (UINT32) Len;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Reassemble the IP fragments. If all the fragments of the packet
 | |
|   have been received, it will wrap the packet in a net buffer then
 | |
|   return it to caller. If the packet can't be assembled, NULL is
 | |
|   returned.
 | |
| 
 | |
|   @param[in, out] Table  The assemble table used. A new assemble entry will be created
 | |
|                          if the Packet is from a new chain of fragments.
 | |
|   @param[in]      Packet The fragment to assemble. It might be freed if the fragment
 | |
|                          can't be re-assembled.
 | |
| 
 | |
|   @return NULL if the packet can't be reassembled. The pointer to the just assembled
 | |
|           packet if all the fragments of the packet have arrived.
 | |
| 
 | |
| **/
 | |
| NET_BUF *
 | |
| Ip6Reassemble (
 | |
|   IN OUT IP6_ASSEMBLE_TABLE *Table,
 | |
|   IN NET_BUF                *Packet
 | |
|   )
 | |
| {
 | |
|   EFI_IP6_HEADER            *Head;
 | |
|   IP6_CLIP_INFO             *This;
 | |
|   IP6_CLIP_INFO             *Node;
 | |
|   IP6_ASSEMBLE_ENTRY        *Assemble;
 | |
|   IP6_ASSEMBLE_ENTRY        *Entry;
 | |
|   LIST_ENTRY                *ListHead;
 | |
|   LIST_ENTRY                *Prev;
 | |
|   LIST_ENTRY                *Cur;
 | |
|   NET_BUF                   *Fragment;
 | |
|   NET_BUF                   *TmpPacket;
 | |
|   NET_BUF                   *NewPacket;
 | |
|   NET_BUF                   *Duplicate;
 | |
|   UINT8                     *DupHead;
 | |
|   INTN                      Index;
 | |
|   UINT16                    UnFragmentLen;
 | |
|   UINT8                     *NextHeader;
 | |
| 
 | |
|   Head = Packet->Ip.Ip6;
 | |
|   This = IP6_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|   ASSERT (Head != NULL);
 | |
| 
 | |
|   //
 | |
|   // Find the corresponding assemble entry by (Dst, Src, Id)
 | |
|   //
 | |
|   Assemble  = NULL;
 | |
|   Index     = IP6_ASSEMBLE_HASH (&Head->DestinationAddress, &Head->SourceAddress, This->Id);
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {
 | |
|     Entry = NET_LIST_USER_STRUCT (Cur, IP6_ASSEMBLE_ENTRY, Link);
 | |
| 
 | |
|     if (Entry->Id == This->Id &&
 | |
|         EFI_IP6_EQUAL (&Entry->Src, &Head->SourceAddress) &&
 | |
|         EFI_IP6_EQUAL (&Entry->Dst, &Head->DestinationAddress)
 | |
|           ) {
 | |
|       Assemble = Entry;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create a new entry if can not find an existing one, insert it to assemble table
 | |
|   //
 | |
|   if (Assemble == NULL) {
 | |
|     Assemble = Ip6CreateAssembleEntry (
 | |
|                  &Head->DestinationAddress,
 | |
|                  &Head->SourceAddress,
 | |
|                  This->Id
 | |
|                  );
 | |
| 
 | |
|     if (Assemble == NULL) {
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     InsertHeadList (&Table->Bucket[Index], &Assemble->Link);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Find the point to insert the packet: before the first
 | |
|   // fragment with THIS.Start < CUR.Start. the previous one
 | |
|   // has PREV.Start <= THIS.Start < CUR.Start.
 | |
|   //
 | |
|   ListHead = &Assemble->Fragments;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Cur, ListHead) {
 | |
|     Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
 | |
| 
 | |
|     if (This->Start < IP6_GET_CLIP_INFO (Fragment)->Start) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check whether the current fragment overlaps with the previous one.
 | |
|   // It holds that: PREV.Start <= THIS.Start < THIS.End. Only need to
 | |
|   // check whether THIS.Start < PREV.End for overlap. If two fragments
 | |
|   // overlaps, trim the overlapped part off THIS fragment.
 | |
|   //
 | |
|   if ((Prev = Cur->BackLink) != ListHead) {
 | |
|     Fragment  = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
 | |
|     Node      = IP6_GET_CLIP_INFO (Fragment);
 | |
| 
 | |
|     if (This->Start < Node->End) {
 | |
|       if (This->End <= Node->End) {
 | |
|         goto Error;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Trim the previous fragment from tail.
 | |
|       //
 | |
|       Ip6TrimPacket (Fragment, Node->Start, This->Start);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Insert the fragment into the packet. The fragment may be removed
 | |
|   // from the list by the following checks.
 | |
|   //
 | |
|   NetListInsertBefore (Cur, &Packet->List);
 | |
| 
 | |
|   //
 | |
|   // Check the packets after the insert point. It holds that:
 | |
|   // THIS.Start <= NODE.Start < NODE.End. The equality holds
 | |
|   // if PREV and NEXT are continuous. THIS fragment may fill
 | |
|   // several holes. Remove the completely overlapped fragments
 | |
|   //
 | |
|   while (Cur != ListHead) {
 | |
|     Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
 | |
|     Node     = IP6_GET_CLIP_INFO (Fragment);
 | |
| 
 | |
|     //
 | |
|     // Remove fragments completely overlapped by this fragment
 | |
|     //
 | |
|     if (Node->End <= This->End) {
 | |
|       Cur = Cur->ForwardLink;
 | |
| 
 | |
|       RemoveEntryList (&Fragment->List);
 | |
|       Assemble->CurLen -= Node->Length;
 | |
| 
 | |
|       NetbufFree (Fragment);
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // The conditions are: THIS.Start <= NODE.Start, and THIS.End <
 | |
|     // NODE.End. Two fragments overlaps if NODE.Start < THIS.End.
 | |
|     // If two fragments start at the same offset, remove THIS fragment
 | |
|     // because ((THIS.Start == NODE.Start) && (THIS.End < NODE.End)).
 | |
|     //
 | |
|     if (Node->Start < This->End) {
 | |
|       if (This->Start == Node->Start) {
 | |
|         RemoveEntryList (&Packet->List);
 | |
|         goto Error;
 | |
|       }
 | |
| 
 | |
|       Ip6TrimPacket (Packet, This->Start, Node->Start);
 | |
|     }
 | |
| 
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Update the assemble info: increase the current length. If it is
 | |
|   // the frist fragment, update the packet's IP head and per packet
 | |
|   // info. If it is the last fragment, update the total length.
 | |
|   //
 | |
|   Assemble->CurLen += This->Length;
 | |
| 
 | |
|   if (This->Start == 0) {
 | |
|     //
 | |
|     // Once the first fragment is enqueued, it can't be removed
 | |
|     // from the fragment list. So, Assemble->Head always point
 | |
|     // to valid memory area.
 | |
|     //
 | |
|     if ((Assemble->Head != NULL) || (Assemble->Packet != NULL)) {
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Backup the first fragment in case the reasembly of that packet fail.
 | |
|     //
 | |
|     Duplicate = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));
 | |
|     if (Duplicate == NULL) {
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Revert IP head to network order.
 | |
|     //
 | |
|     DupHead = NetbufGetByte (Duplicate, 0, NULL);
 | |
|     ASSERT (DupHead != NULL);
 | |
|     Duplicate->Ip.Ip6 = Ip6NtohHead ((EFI_IP6_HEADER *) DupHead);
 | |
|     Assemble->Packet  = Duplicate;
 | |
| 
 | |
|     //
 | |
|     // Adjust the unfragmentable part in first fragment
 | |
|     //
 | |
|     UnFragmentLen = (UINT16) (This->HeadLen - sizeof (EFI_IP6_HEADER));
 | |
|     if (UnFragmentLen == 0) {
 | |
|       //
 | |
|       // There is not any unfragmentable extension header.
 | |
|       //
 | |
|       ASSERT (Head->NextHeader == IP6_FRAGMENT);
 | |
|       Head->NextHeader = This->NextHeader;
 | |
|     } else {
 | |
|       NextHeader = NetbufGetByte (
 | |
|                      Packet,
 | |
|                      This->FormerNextHeader + sizeof (EFI_IP6_HEADER),
 | |
|                      0
 | |
|                      );
 | |
|       if (NextHeader == NULL) {
 | |
|         goto Error;
 | |
|       }
 | |
| 
 | |
|       *NextHeader = This->NextHeader;
 | |
|     }
 | |
| 
 | |
|     Assemble->Head = Head;
 | |
|     Assemble->Info = IP6_GET_CLIP_INFO (Packet);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Don't update the length more than once.
 | |
|   //
 | |
|   if ((This->LastFrag != 0) && (Assemble->TotalLen == 0)) {
 | |
|     Assemble->TotalLen = This->End;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Deliver the whole packet if all the fragments received.
 | |
|   // All fragments received if:
 | |
|   //  1. received the last one, so, the totoal length is know
 | |
|   //  2. received all the data. If the last fragment on the
 | |
|   //     queue ends at the total length, all data is received.
 | |
|   //
 | |
|   if ((Assemble->TotalLen != 0) && (Assemble->CurLen >= Assemble->TotalLen)) {
 | |
| 
 | |
|     RemoveEntryList (&Assemble->Link);
 | |
| 
 | |
|     //
 | |
|     // If the packet is properly formated, the last fragment's End
 | |
|     // equals to the packet's total length. Otherwise, the packet
 | |
|     // is a fake, drop it now.
 | |
|     //
 | |
|     Fragment = NET_LIST_USER_STRUCT (ListHead->BackLink, NET_BUF, List);
 | |
|     if (IP6_GET_CLIP_INFO (Fragment)->End != (INTN) Assemble->TotalLen) {
 | |
|       Ip6FreeAssembleEntry (Assemble);
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     Fragment = NET_LIST_HEAD (ListHead, NET_BUF, List);
 | |
|     This     = Assemble->Info;
 | |
| 
 | |
|     //
 | |
|     // This TmpPacket is used to hold the unfragmentable part, i.e.,
 | |
|     // the IPv6 header and the unfragmentable extension headers. Be noted that
 | |
|     // the Fragment Header is exluded.
 | |
|     //
 | |
|     TmpPacket = NetbufGetFragment (Fragment, 0, This->HeadLen, 0);
 | |
|     ASSERT (TmpPacket != NULL);
 | |
| 
 | |
|     NET_LIST_FOR_EACH (Cur, ListHead) {
 | |
|       //
 | |
|       // Trim off the unfragment part plus the fragment header from all fragments.
 | |
|       //
 | |
|       Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
 | |
|       NetbufTrim (Fragment, This->HeadLen + sizeof (IP6_FRAGMENT_HEADER), TRUE);
 | |
|     }
 | |
| 
 | |
|     InsertHeadList (ListHead, &TmpPacket->List);
 | |
| 
 | |
|     //
 | |
|     // Wrap the packet in a net buffer then deliver it up
 | |
|     //
 | |
|     NewPacket = NetbufFromBufList (
 | |
|                   &Assemble->Fragments,
 | |
|                   0,
 | |
|                   0,
 | |
|                   Ip6OnFreeFragments,
 | |
|                   Assemble
 | |
|                   );
 | |
| 
 | |
|     if (NewPacket == NULL) {
 | |
|       Ip6FreeAssembleEntry (Assemble);
 | |
|       goto Error;
 | |
|     }
 | |
| 
 | |
|     NewPacket->Ip.Ip6 = Assemble->Head;
 | |
| 
 | |
|     CopyMem (IP6_GET_CLIP_INFO (NewPacket), Assemble->Info, sizeof (IP6_CLIP_INFO));
 | |
| 
 | |
|     return NewPacket;
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| 
 | |
| Error:
 | |
|   NetbufFree (Packet);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The callback function for the net buffer that wraps the packet processed by
 | |
|   IPsec. It releases the wrap packet and also signals IPsec to free the resources.
 | |
| 
 | |
|   @param[in]  Arg       The wrap context.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| Ip6IpSecFree (
 | |
|   IN VOID                   *Arg
 | |
|   )
 | |
| {
 | |
|   IP6_IPSEC_WRAP            *Wrap;
 | |
| 
 | |
|   Wrap = (IP6_IPSEC_WRAP *) Arg;
 | |
| 
 | |
|   if (Wrap->IpSecRecycleSignal != NULL) {
 | |
|     gBS->SignalEvent (Wrap->IpSecRecycleSignal);
 | |
|   }
 | |
| 
 | |
|   NetbufFree (Wrap->Packet);
 | |
| 
 | |
|   FreePool (Wrap);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The work function to locate the IPsec protocol to process the inbound or
 | |
|   outbound IP packets. The process routine handles the packet with the following
 | |
|   actions: bypass the packet, discard the packet, or protect the packet.
 | |
| 
 | |
|   @param[in]       IpSb          The IP6 service instance.
 | |
|   @param[in, out]  Head          The caller-supplied IP6 header.
 | |
|   @param[in, out]  LastHead      The next header field of last IP header.
 | |
|   @param[in, out]  Netbuf        The IP6 packet to be processed by IPsec.
 | |
|   @param[in, out]  ExtHdrs       The caller-supplied options.
 | |
|   @param[in, out]  ExtHdrsLen    The length of the option.
 | |
|   @param[in]       Direction     The directionality in an SPD entry,
 | |
|                                  EfiIPsecInBound, or EfiIPsecOutBound.
 | |
|   @param[in]       Context       The token's wrap.
 | |
| 
 | |
|   @retval EFI_SUCCESS            The IPsec protocol is not available or disabled.
 | |
|   @retval EFI_SUCCESS            The packet was bypassed, and all buffers remain the same.
 | |
|   @retval EFI_SUCCESS            The packet was protected.
 | |
|   @retval EFI_ACCESS_DENIED      The packet was discarded.
 | |
|   @retval EFI_OUT_OF_RESOURCES   There are not suffcient resources to complete the operation.
 | |
|   @retval EFI_BUFFER_TOO_SMALL   The number of non-empty blocks is bigger than the
 | |
|                                  number of input data blocks when building a fragment table.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip6IpSecProcessPacket (
 | |
|   IN     IP6_SERVICE            *IpSb,
 | |
|   IN OUT EFI_IP6_HEADER         **Head,
 | |
|   IN OUT UINT8                  *LastHead,
 | |
|   IN OUT NET_BUF                **Netbuf,
 | |
|   IN OUT UINT8                  **ExtHdrs,
 | |
|   IN OUT UINT32                 *ExtHdrsLen,
 | |
|   IN     EFI_IPSEC_TRAFFIC_DIR  Direction,
 | |
|   IN     VOID                   *Context
 | |
|   )
 | |
| {
 | |
|   NET_FRAGMENT              *FragmentTable;
 | |
|   NET_FRAGMENT              *OriginalFragmentTable;
 | |
|   UINT32                    FragmentCount;
 | |
|   UINT32                    OriginalFragmentCount;
 | |
|   EFI_EVENT                 RecycleEvent;
 | |
|   NET_BUF                   *Packet;
 | |
|   IP6_TXTOKEN_WRAP          *TxWrap;
 | |
|   IP6_IPSEC_WRAP            *IpSecWrap;
 | |
|   EFI_STATUS                Status;
 | |
|   EFI_IP6_HEADER            *PacketHead;
 | |
|   UINT8                     *Buf;
 | |
|   EFI_IP6_HEADER            ZeroHead;
 | |
| 
 | |
|   Status        = EFI_SUCCESS;
 | |
|   Packet        = *Netbuf;
 | |
|   RecycleEvent  = NULL;
 | |
|   IpSecWrap     = NULL;
 | |
|   FragmentTable = NULL;
 | |
|   PacketHead    = NULL;
 | |
|   Buf           = NULL;
 | |
|   TxWrap        = (IP6_TXTOKEN_WRAP *) Context;
 | |
|   FragmentCount = Packet->BlockOpNum;
 | |
|   ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER));
 | |
| 
 | |
|   if (mIpSec == NULL) {
 | |
|     gBS->LocateProtocol (&gEfiIpSec2ProtocolGuid, NULL, (VOID **) &mIpSec);
 | |
| 
 | |
|     //
 | |
|     // Check whether the ipsec protocol is available.
 | |
|     //
 | |
|     if (mIpSec == NULL) {
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check whether the ipsec enable variable is set.
 | |
|   //
 | |
|   if (mIpSec->DisabledFlag) {
 | |
|     //
 | |
|     // If IPsec is disabled, restore the original MTU
 | |
|     //
 | |
|     IpSb->MaxPacketSize = IpSb->OldMaxPacketSize;
 | |
|     goto ON_EXIT;
 | |
|   } else {
 | |
|     //
 | |
|     // If IPsec is enabled, use the MTU which reduce the IPsec header length.
 | |
|     //
 | |
|     IpSb->MaxPacketSize = IpSb->OldMaxPacketSize - IP6_MAX_IPSEC_HEADLEN;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   //
 | |
|   // Bypass all multicast inbound or outbound traffic.
 | |
|   //
 | |
|   if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress) || IP6_IS_MULTICAST (&(*Head)->SourceAddress)) {
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Rebuild fragment table from netbuf to ease ipsec process.
 | |
|   //
 | |
|   FragmentTable = AllocateZeroPool (FragmentCount * sizeof (NET_FRAGMENT));
 | |
| 
 | |
|   if (FragmentTable == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   Status = NetbufBuildExt (Packet, FragmentTable, &FragmentCount);
 | |
|   OriginalFragmentTable = FragmentTable;
 | |
|   OriginalFragmentCount = FragmentCount;
 | |
| 
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     FreePool (FragmentTable);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Convert host byte order to network byte order
 | |
|   //
 | |
|   Ip6NtohHead (*Head);
 | |
| 
 | |
|   Status = mIpSec->ProcessExt (
 | |
|                      mIpSec,
 | |
|                      IpSb->Controller,
 | |
|                      IP_VERSION_6,
 | |
|                      (VOID *) (*Head),
 | |
|                      LastHead,
 | |
|                      (VOID **) ExtHdrs,
 | |
|                      ExtHdrsLen,
 | |
|                      (EFI_IPSEC_FRAGMENT_DATA  **) (&FragmentTable),
 | |
|                      &FragmentCount,
 | |
|                      Direction,
 | |
|                      &RecycleEvent
 | |
|                      );
 | |
|   //
 | |
|   // Convert back to host byte order
 | |
|   //
 | |
|   Ip6NtohHead (*Head);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (OriginalFragmentTable);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   if (OriginalFragmentCount == FragmentCount && OriginalFragmentTable == FragmentTable) {
 | |
|     //
 | |
|     // For ByPass Packet
 | |
|     //
 | |
|     FreePool (FragmentTable);
 | |
|     goto ON_EXIT;
 | |
|   } else {
 | |
|     //
 | |
|     // Free the FragmentTable which allocated before calling the IPsec.
 | |
|     //
 | |
|     FreePool (OriginalFragmentTable);
 | |
|   }
 | |
| 
 | |
|   if (Direction == EfiIPsecOutBound && TxWrap != NULL) {
 | |
|     TxWrap->IpSecRecycleSignal = RecycleEvent;
 | |
|     TxWrap->Packet             = NetbufFromExt (
 | |
|                                    FragmentTable,
 | |
|                                    FragmentCount,
 | |
|                                    IP6_MAX_HEADLEN,
 | |
|                                    0,
 | |
|                                    Ip6FreeTxToken,
 | |
|                                    TxWrap
 | |
|                                    );
 | |
|     if (TxWrap->Packet == NULL) {
 | |
|       TxWrap->Packet = *Netbuf;
 | |
|       Status = EFI_OUT_OF_RESOURCES;
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
| 
 | |
|     CopyMem (
 | |
|       IP6_GET_CLIP_INFO (TxWrap->Packet),
 | |
|       IP6_GET_CLIP_INFO (Packet),
 | |
|       sizeof (IP6_CLIP_INFO)
 | |
|       );
 | |
|     
 | |
|     NetIpSecNetbufFree(Packet);
 | |
|     *Netbuf = TxWrap->Packet;
 | |
| 
 | |
|   } else {
 | |
| 
 | |
|     IpSecWrap = AllocateZeroPool (sizeof (IP6_IPSEC_WRAP));
 | |
| 
 | |
|     if (IpSecWrap == NULL) {
 | |
|       Status = EFI_OUT_OF_RESOURCES;
 | |
|       gBS->SignalEvent (RecycleEvent);
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
| 
 | |
|     IpSecWrap->IpSecRecycleSignal = RecycleEvent;
 | |
|     IpSecWrap->Packet             = Packet;
 | |
|     Packet                        = NetbufFromExt (
 | |
|                                       FragmentTable,
 | |
|                                       FragmentCount,
 | |
|                                       IP6_MAX_HEADLEN,
 | |
|                                       0,
 | |
|                                       Ip6IpSecFree,
 | |
|                                       IpSecWrap
 | |
|                                       );
 | |
| 
 | |
|     if (Packet == NULL) {
 | |
|       Packet = IpSecWrap->Packet;
 | |
|       gBS->SignalEvent (RecycleEvent);
 | |
|       FreePool (IpSecWrap);
 | |
|       Status = EFI_OUT_OF_RESOURCES;
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
| 
 | |
|     if (Direction == EfiIPsecInBound && 0 != CompareMem (&ZeroHead, *Head, sizeof (EFI_IP6_HEADER))) {
 | |
| 
 | |
|       PacketHead = (EFI_IP6_HEADER *) NetbufAllocSpace (
 | |
|                                         Packet,
 | |
|                                         sizeof (EFI_IP6_HEADER) + *ExtHdrsLen,
 | |
|                                         NET_BUF_HEAD
 | |
|                                         );
 | |
|       if (PacketHead == NULL) {
 | |
|         *Netbuf = Packet;
 | |
|         Status  = EFI_OUT_OF_RESOURCES;
 | |
|         goto ON_EXIT;
 | |
|       }
 | |
| 
 | |
|       CopyMem (PacketHead, *Head, sizeof (EFI_IP6_HEADER));
 | |
|       *Head = PacketHead;
 | |
|       Packet->Ip.Ip6 = PacketHead;
 | |
| 
 | |
|       if (*ExtHdrs != NULL) {
 | |
|         Buf = (UINT8 *) (PacketHead + 1);
 | |
|         CopyMem (Buf, *ExtHdrs, *ExtHdrsLen);
 | |
|       }
 | |
| 
 | |
|       NetbufTrim (Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE);
 | |
|       CopyMem (
 | |
|         IP6_GET_CLIP_INFO (Packet),
 | |
|         IP6_GET_CLIP_INFO (IpSecWrap->Packet),
 | |
|         sizeof (IP6_CLIP_INFO)
 | |
|         );
 | |
|     }
 | |
|     *Netbuf = Packet;
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Pre-process the IPv6 packet. First validates the IPv6 packet, and
 | |
|   then reassembles packet if it is necessary.
 | |
| 
 | |
|   @param[in]      IpSb          The IP6 service instance.
 | |
|   @param[in, out] Packet        The received IP6 packet to be processed.
 | |
|   @param[in]      Flag          The link layer flag for the packet received, such
 | |
|                                 as multicast.
 | |
|   @param[out]     Payload       The pointer to the payload of the recieved packet. 
 | |
|                                 it starts from the first byte of the extension header.                                 
 | |
|   @param[out]     LastHead      The pointer of NextHeader of the last extension
 | |
|                                 header processed by IP6.
 | |
|   @param[out]     ExtHdrsLen    The length of the whole option.
 | |
|   @param[out]     UnFragmentLen The length of unfragmented length of extension headers.
 | |
|   @param[out]     Fragmented    Indicate whether the packet is fragmented. 
 | |
|   @param[out]     Head          The pointer to the EFI_IP6_Header.
 | |
| 
 | |
|   @retval     EFI_SUCCESS              The received packet is well format.
 | |
|   @retval     EFI_INVALID_PARAMETER    The received packet is malformed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip6PreProcessPacket (
 | |
|   IN     IP6_SERVICE     *IpSb,
 | |
|   IN OUT NET_BUF         **Packet,
 | |
|   IN     UINT32          Flag,
 | |
|      OUT UINT8           **Payload,
 | |
|      OUT UINT8           **LastHead,
 | |
|      OUT UINT32          *ExtHdrsLen,
 | |
|      OUT UINT32          *UnFragmentLen,
 | |
|      OUT BOOLEAN         *Fragmented, 
 | |
|      OUT EFI_IP6_HEADER  **Head
 | |
|   )
 | |
| {
 | |
|   UINT16                    PayloadLen;
 | |
|   UINT16                    TotalLen;
 | |
|   UINT32                    FormerHeadOffset;
 | |
|   UINT32                    HeadLen;
 | |
|   IP6_FRAGMENT_HEADER       *FragmentHead;
 | |
|   UINT16                    FragmentOffset;
 | |
|   IP6_CLIP_INFO             *Info;
 | |
|   EFI_IPv6_ADDRESS          Loopback;
 | |
| 
 | |
|   HeadLen    = 0;
 | |
|   PayloadLen = 0;
 | |
|   //
 | |
|   // Check whether the input packet is a valid packet
 | |
|   //
 | |
|   if ((*Packet)->TotalSize < IP6_MIN_HEADLEN) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get header information of the packet.
 | |
|   //
 | |
|   *Head = (EFI_IP6_HEADER *) NetbufGetByte (*Packet, 0, NULL);
 | |
|   if (*Head == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Multicast addresses must not be used as source addresses in IPv6 packets.
 | |
|   //
 | |
|   if (((*Head)->Version != 6) || (IP6_IS_MULTICAST (&(*Head)->SourceAddress))) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // A packet with a destination address of loopback ::1/128 or unspecified must be dropped.
 | |
|   //
 | |
|   ZeroMem (&Loopback, sizeof (EFI_IPv6_ADDRESS));
 | |
|   Loopback.Addr[15] = 0x1;
 | |
|   if ((CompareMem (&Loopback, &(*Head)->DestinationAddress, sizeof (EFI_IPv6_ADDRESS)) == 0) ||
 | |
|       (NetIp6IsUnspecifiedAddr (&(*Head)->DestinationAddress))) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Convert the IP header to host byte order.
 | |
|   //
 | |
|   (*Packet)->Ip.Ip6 = Ip6NtohHead (*Head);
 | |
| 
 | |
|   //
 | |
|   // Get the per packet info.
 | |
|   //
 | |
|   Info           = IP6_GET_CLIP_INFO (*Packet);
 | |
|   Info->LinkFlag = Flag;
 | |
|   Info->CastType = 0;
 | |
| 
 | |
|   if (IpSb->MnpConfigData.EnablePromiscuousReceive) {
 | |
|     Info->CastType = Ip6Promiscuous;
 | |
|   }
 | |
| 
 | |
|   if (Ip6IsOneOfSetAddress (IpSb, &(*Head)->DestinationAddress, NULL, NULL)) {
 | |
|     Info->CastType = Ip6Unicast;
 | |
|   } else if (IP6_IS_MULTICAST (&(*Head)->DestinationAddress)) {
 | |
|     if (Ip6FindMldEntry (IpSb, &(*Head)->DestinationAddress) != NULL) {
 | |
|       Info->CastType = Ip6Multicast;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Drop the packet that is not delivered to us.
 | |
|   //
 | |
|   if (Info->CastType == 0) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   PayloadLen = (*Head)->PayloadLength;
 | |
| 
 | |
|   Info->Start    = 0;
 | |
|   Info->Length   = PayloadLen;
 | |
|   Info->End      = Info->Start + Info->Length;
 | |
|   Info->HeadLen  = (UINT16) sizeof (EFI_IP6_HEADER);
 | |
|   Info->Status   = EFI_SUCCESS;
 | |
|   Info->LastFrag = FALSE;
 | |
| 
 | |
|   TotalLen   = (UINT16) (PayloadLen + sizeof (EFI_IP6_HEADER));
 | |
| 
 | |
|   //
 | |
|   // Mnp may deliver frame trailer sequence up, trim it off.
 | |
|   //
 | |
|   if (TotalLen < (*Packet)->TotalSize) {
 | |
|     NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE);
 | |
|   }
 | |
| 
 | |
|   if (TotalLen != (*Packet)->TotalSize) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check the extension headers, if exist validate them
 | |
|   //
 | |
|   if (PayloadLen != 0) {
 | |
|     *Payload = AllocatePool ((UINTN) PayloadLen);
 | |
|     if (*Payload == NULL) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload);
 | |
|   }
 | |
| 
 | |
|   if (!Ip6IsExtsValid (
 | |
|          IpSb,
 | |
|          *Packet,
 | |
|          &(*Head)->NextHeader,
 | |
|          *Payload,
 | |
|          (UINT32) PayloadLen,
 | |
|          TRUE,
 | |
|          &FormerHeadOffset,
 | |
|          LastHead,
 | |
|          ExtHdrsLen,
 | |
|          UnFragmentLen,
 | |
|          Fragmented
 | |
|          )) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   HeadLen        = sizeof (EFI_IP6_HEADER) + *UnFragmentLen;
 | |
| 
 | |
|   if (*Fragmented) {
 | |
|     //
 | |
|     // Get the fragment offset from the Fragment header
 | |
|     //
 | |
|     FragmentHead = (IP6_FRAGMENT_HEADER *) NetbufGetByte (*Packet, HeadLen, NULL);
 | |
|     if (FragmentHead == NULL) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     FragmentOffset = NTOHS (FragmentHead->FragmentOffset);
 | |
| 
 | |
|     if ((FragmentOffset & 0x1) == 0) {
 | |
|       Info->LastFrag = TRUE;
 | |
|     }
 | |
| 
 | |
|     FragmentOffset &= (~0x1);
 | |
| 
 | |
|     //
 | |
|     // This is the first fragment of the packet
 | |
|     //
 | |
|     if (FragmentOffset == 0) {
 | |
|       Info->NextHeader = FragmentHead->NextHeader;
 | |
|     }
 | |
| 
 | |
|     Info->HeadLen          = (UINT16) HeadLen;
 | |
|     HeadLen                += sizeof (IP6_FRAGMENT_HEADER);
 | |
|     Info->Start            = FragmentOffset;
 | |
|     Info->Length           = TotalLen - (UINT16) HeadLen;
 | |
|     Info->End              = Info->Start + Info->Length;
 | |
|     Info->Id               = FragmentHead->Identification;
 | |
|     Info->FormerNextHeader = FormerHeadOffset;
 | |
| 
 | |
|     //
 | |
|     // Fragments should in the unit of 8 octets long except the last one.
 | |
|     //
 | |
|     if ((Info->LastFrag == 0) && (Info->Length % 8 != 0)) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Reassemble the packet.
 | |
|     //
 | |
|     *Packet = Ip6Reassemble (&IpSb->Assemble, *Packet);
 | |
|     if (*Packet == NULL) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Re-check the assembled packet to get the right values.
 | |
|     //
 | |
|     *Head       = (*Packet)->Ip.Ip6;
 | |
|     PayloadLen  = (*Head)->PayloadLength;
 | |
|     if (PayloadLen != 0) {
 | |
|       if (*Payload != NULL) {
 | |
|         FreePool (*Payload);
 | |
|       }
 | |
| 
 | |
|       *Payload = AllocatePool ((UINTN) PayloadLen);
 | |
|       if (*Payload == NULL) {
 | |
|         return EFI_INVALID_PARAMETER;
 | |
|       }
 | |
| 
 | |
|       NetbufCopy (*Packet, sizeof (EFI_IP6_HEADER), PayloadLen, *Payload);
 | |
|     }
 | |
| 
 | |
|     if (!Ip6IsExtsValid (
 | |
|            IpSb,
 | |
|            *Packet,
 | |
|            &(*Head)->NextHeader,
 | |
|            *Payload,
 | |
|            (UINT32) PayloadLen,
 | |
|            TRUE,
 | |
|            NULL,
 | |
|            LastHead,
 | |
|            ExtHdrsLen,
 | |
|            UnFragmentLen,
 | |
|            Fragmented
 | |
|            )) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Trim the head off, after this point, the packet is headless.
 | |
|   // and Packet->TotalLen == Info->Length.
 | |
|   //
 | |
|   NetbufTrim (*Packet, sizeof (EFI_IP6_HEADER) + *ExtHdrsLen, TRUE);
 | |
|   
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The IP6 input routine. It is called by the IP6_INTERFACE when an
 | |
|   IP6 fragment is received from MNP.
 | |
| 
 | |
|   @param[in]  Packet             The IP6 packet received.
 | |
|   @param[in]  IoStatus           The return status of receive request.
 | |
|   @param[in]  Flag               The link layer flag for the packet received, such
 | |
|                                  as multicast.
 | |
|   @param[in]  Context            The IP6 service instance that owns the MNP.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip6AcceptFrame (
 | |
|   IN NET_BUF                *Packet,
 | |
|   IN EFI_STATUS             IoStatus,
 | |
|   IN UINT32                 Flag,
 | |
|   IN VOID                   *Context
 | |
|   )
 | |
| {
 | |
|   IP6_SERVICE               *IpSb;
 | |
|   EFI_IP6_HEADER            *Head;
 | |
|   UINT8                     *Payload;
 | |
|   UINT8                     *LastHead;
 | |
|   UINT32                    UnFragmentLen;
 | |
|   UINT32                    ExtHdrsLen;
 | |
|   BOOLEAN                   Fragmented;
 | |
|   EFI_STATUS                Status;
 | |
|   EFI_IP6_HEADER            ZeroHead;
 | |
| 
 | |
|   IpSb = (IP6_SERVICE *) Context;
 | |
|   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
 | |
| 
 | |
|   Payload  = NULL;
 | |
|   LastHead = NULL;
 | |
| 
 | |
|   //
 | |
|   // Check input parameters
 | |
|   //
 | |
|   if (EFI_ERROR (IoStatus) || (IpSb->State == IP6_SERVICE_DESTROY)) {
 | |
|     goto Drop;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Pre-Process the Ipv6 Packet and then reassemble if it is necessary.
 | |
|   //
 | |
|   Status = Ip6PreProcessPacket (
 | |
|              IpSb, 
 | |
|              &Packet, 
 | |
|              Flag, 
 | |
|              &Payload, 
 | |
|              &LastHead, 
 | |
|              &ExtHdrsLen, 
 | |
|              &UnFragmentLen, 
 | |
|              &Fragmented,
 | |
|              &Head
 | |
|              );
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Restart;
 | |
|   }
 | |
|   //
 | |
|   // After trim off, the packet is a esp/ah/udp/tcp/icmp6 net buffer,
 | |
|   // and no need consider any other ahead ext headers.
 | |
|   //
 | |
|   Status = Ip6IpSecProcessPacket (
 | |
|              IpSb,
 | |
|              &Head,
 | |
|              LastHead, // need get the lasthead value for input
 | |
|              &Packet,
 | |
|              &Payload,
 | |
|              &ExtHdrsLen,
 | |
|              EfiIPsecInBound,
 | |
|              NULL
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto Restart;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If the packet is protected by IPsec Tunnel Mode, Check the Inner Ip Packet.
 | |
|   //
 | |
|   ZeroMem (&ZeroHead, sizeof (EFI_IP6_HEADER));
 | |
|   if (0 == CompareMem (Head, &ZeroHead, sizeof (EFI_IP6_HEADER))) {
 | |
|     Status = Ip6PreProcessPacket (
 | |
|                IpSb, 
 | |
|                &Packet, 
 | |
|                Flag, 
 | |
|                &Payload, 
 | |
|                &LastHead, 
 | |
|                &ExtHdrsLen, 
 | |
|                &UnFragmentLen, 
 | |
|                &Fragmented, 
 | |
|                &Head
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto Restart;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check the Packet again.
 | |
|   //
 | |
|   if (Packet == NULL) {
 | |
|     goto Restart;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Packet may have been changed. The ownership of the packet
 | |
|   // is transfered to the packet process logic.
 | |
|   //
 | |
|   Head  = Packet->Ip.Ip6;
 | |
|   IP6_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;
 | |
| 
 | |
|   switch (*LastHead) {
 | |
|   case IP6_ICMP:
 | |
|     Ip6IcmpHandle (IpSb, Head, Packet);
 | |
|     break;
 | |
|   default:
 | |
|     Ip6Demultiplex (IpSb, Head, Packet);
 | |
|   }
 | |
| 
 | |
|   Packet = NULL;
 | |
| 
 | |
|   //
 | |
|   // Dispatch the DPCs queued by the NotifyFunction of the rx token's events
 | |
|   // which are signaled with received data.
 | |
|   //
 | |
|   DispatchDpc ();
 | |
| 
 | |
| Restart:
 | |
|   if (Payload != NULL) {
 | |
|     FreePool (Payload);
 | |
|   }
 | |
| 
 | |
|   Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);
 | |
| 
 | |
| Drop:
 | |
|   if (Packet != NULL) {
 | |
|     NetbufFree (Packet);
 | |
|   }
 | |
| 
 | |
|   return ;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize an already allocated assemble table. This is generally
 | |
|   the assemble table embedded in the IP6 service instance.
 | |
| 
 | |
|   @param[in, out]  Table    The assemble table to initialize.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip6CreateAssembleTable (
 | |
|   IN OUT IP6_ASSEMBLE_TABLE *Table
 | |
|   )
 | |
| {
 | |
|   UINT32                    Index;
 | |
| 
 | |
|   for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
 | |
|     InitializeListHead (&Table->Bucket[Index]);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clean up the assemble table by removing all of the fragments
 | |
|   and assemble entries.
 | |
| 
 | |
|   @param[in, out]  Table    The assemble table to clean up.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip6CleanAssembleTable (
 | |
|   IN OUT IP6_ASSEMBLE_TABLE *Table
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY                *Entry;
 | |
|   LIST_ENTRY                *Next;
 | |
|   IP6_ASSEMBLE_ENTRY        *Assemble;
 | |
|   UINT32                    Index;
 | |
| 
 | |
|   for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
 | |
|     NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {
 | |
|       Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);
 | |
| 
 | |
|       RemoveEntryList (Entry);
 | |
|       Ip6FreeAssembleEntry (Assemble);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   The signal handle of IP6's recycle event. It is called back
 | |
|   when the upper layer releases the packet.
 | |
| 
 | |
|   @param[in]  Event         The IP6's recycle event.
 | |
|   @param[in]  Context       The context of the handle, which is a IP6_RXDATA_WRAP.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| Ip6OnRecyclePacket (
 | |
|   IN EFI_EVENT              Event,
 | |
|   IN VOID                   *Context
 | |
|   )
 | |
| {
 | |
|   IP6_RXDATA_WRAP           *Wrap;
 | |
| 
 | |
|   Wrap = (IP6_RXDATA_WRAP *) Context;
 | |
| 
 | |
|   EfiAcquireLockOrFail (&Wrap->IpInstance->RecycleLock);
 | |
|   RemoveEntryList (&Wrap->Link);
 | |
|   EfiReleaseLock (&Wrap->IpInstance->RecycleLock);
 | |
| 
 | |
|   ASSERT (!NET_BUF_SHARED (Wrap->Packet));
 | |
|   NetbufFree (Wrap->Packet);
 | |
| 
 | |
|   gBS->CloseEvent (Wrap->RxData.RecycleSignal);
 | |
|   FreePool (Wrap);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Wrap the received packet to a IP6_RXDATA_WRAP, which will be
 | |
|   delivered to the upper layer. Each IP6 child that accepts the
 | |
|   packet will get a not-shared copy of the packet which is wrapped
 | |
|   in the IP6_RXDATA_WRAP. The IP6_RXDATA_WRAP->RxData is passed
 | |
|   to the upper layer. The upper layer will signal the recycle event in
 | |
|   it when it is done with the packet.
 | |
| 
 | |
|   @param[in]  IpInstance    The IP6 child to receive the packet.
 | |
|   @param[in]  Packet        The packet to deliver up.
 | |
| 
 | |
|   @return NULL if it failed to wrap the packet; otherwise, the wrapper.
 | |
| 
 | |
| **/
 | |
| IP6_RXDATA_WRAP *
 | |
| Ip6WrapRxData (
 | |
|   IN IP6_PROTOCOL           *IpInstance,
 | |
|   IN NET_BUF                *Packet
 | |
|   )
 | |
| {
 | |
|   IP6_RXDATA_WRAP           *Wrap;
 | |
|   EFI_IP6_RECEIVE_DATA      *RxData;
 | |
|   EFI_STATUS                Status;
 | |
| 
 | |
|   Wrap = AllocatePool (IP6_RXDATA_WRAP_SIZE (Packet->BlockOpNum));
 | |
| 
 | |
|   if (Wrap == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   InitializeListHead (&Wrap->Link);
 | |
| 
 | |
|   Wrap->IpInstance  = IpInstance;
 | |
|   Wrap->Packet      = Packet;
 | |
|   RxData            = &Wrap->RxData;
 | |
| 
 | |
|   ZeroMem (&RxData->TimeStamp, sizeof (EFI_TIME));
 | |
| 
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_NOTIFY,
 | |
|                   Ip6OnRecyclePacket,
 | |
|                   Wrap,
 | |
|                   &RxData->RecycleSignal
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (Wrap);
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   ASSERT (Packet->Ip.Ip6 != NULL);
 | |
| 
 | |
|   //
 | |
|   // The application expects a network byte order header.
 | |
|   //
 | |
|   RxData->HeaderLength  = sizeof (EFI_IP6_HEADER);
 | |
|   RxData->Header        = (EFI_IP6_HEADER *) Ip6NtohHead (Packet->Ip.Ip6);
 | |
|   RxData->DataLength    = Packet->TotalSize;
 | |
| 
 | |
|   //
 | |
|   // Build the fragment table to be delivered up.
 | |
|   //
 | |
|   RxData->FragmentCount = Packet->BlockOpNum;
 | |
|   NetbufBuildExt (Packet, (NET_FRAGMENT *) RxData->FragmentTable, &RxData->FragmentCount);
 | |
| 
 | |
|   return Wrap;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check whether this IP child accepts the packet.
 | |
| 
 | |
|   @param[in]  IpInstance    The IP child to check.
 | |
|   @param[in]  Head          The IP header of the packet.
 | |
|   @param[in]  Packet        The data of the packet.
 | |
| 
 | |
|   @retval     TRUE          The child wants to receive the packet.
 | |
|   @retval     FALSE         The child does not want to receive the packet.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip6InstanceFrameAcceptable (
 | |
|   IN IP6_PROTOCOL           *IpInstance,
 | |
|   IN EFI_IP6_HEADER         *Head,
 | |
|   IN NET_BUF                *Packet
 | |
|   )
 | |
| {
 | |
|   IP6_ICMP_ERROR_HEAD       Icmp;
 | |
|   EFI_IP6_CONFIG_DATA       *Config;
 | |
|   IP6_CLIP_INFO             *Info;
 | |
|   UINT8                     *Proto;
 | |
|   UINT32                    Index;
 | |
|   UINT8                     *ExtHdrs;
 | |
|   UINT16                    ErrMsgPayloadLen;
 | |
|   UINT8                     *ErrMsgPayload;
 | |
| 
 | |
|   Config = &IpInstance->ConfigData;
 | |
|   Proto  = NULL;
 | |
| 
 | |
|   //
 | |
|   // Dirty trick for the Tiano UEFI network stack implmentation. If
 | |
|   // ReceiveTimeout == -1, the receive of the packet for this instance
 | |
|   // is disabled. The UEFI spec don't have such captibility. We add
 | |
|   // this to improve the performance because IP will make a copy of
 | |
|   // the received packet for each accepting instance. Some IP instances
 | |
|   // used by UDP/TCP only send packets, they don't wants to receive.
 | |
|   //
 | |
|   if (Config->ReceiveTimeout == (UINT32)(-1)) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   if (Config->AcceptPromiscuous) {
 | |
|     return TRUE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check whether the protocol is acceptable.
 | |
|   //
 | |
|   ExtHdrs = NetbufGetByte (Packet, 0, NULL);
 | |
| 
 | |
|   if (!Ip6IsExtsValid (
 | |
|          IpInstance->Service,
 | |
|          Packet,
 | |
|          &Head->NextHeader,
 | |
|          ExtHdrs,
 | |
|          (UINT32) Head->PayloadLength,
 | |
|          TRUE,
 | |
|          NULL,
 | |
|          &Proto,
 | |
|          NULL,
 | |
|          NULL,
 | |
|          NULL
 | |
|          )) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // The upper layer driver may want to receive the ICMPv6 error packet
 | |
|   // invoked by its packet, like UDP.
 | |
|   //
 | |
|   if ((*Proto == IP6_ICMP) && (!Config->AcceptAnyProtocol) && (*Proto != Config->DefaultProtocol)) {
 | |
|     NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
 | |
| 
 | |
|     if (Icmp.Head.Type <= ICMP_V6_ERROR_MAX) {
 | |
|       if (!Config->AcceptIcmpErrors) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Get the protocol of the invoking packet of ICMPv6 error packet.
 | |
|       //
 | |
|       ErrMsgPayloadLen = NTOHS (Icmp.IpHead.PayloadLength);
 | |
|       ErrMsgPayload    = NetbufGetByte (Packet, sizeof (Icmp), NULL);
 | |
| 
 | |
|       if (!Ip6IsExtsValid (
 | |
|              NULL,
 | |
|              NULL,
 | |
|              &Icmp.IpHead.NextHeader,
 | |
|              ErrMsgPayload,
 | |
|              ErrMsgPayloadLen,
 | |
|              TRUE,
 | |
|              NULL,
 | |
|              &Proto,
 | |
|              NULL,
 | |
|              NULL,
 | |
|              NULL
 | |
|              )) {
 | |
|         return FALSE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Match the protocol
 | |
|   //
 | |
|   if (!Config->AcceptAnyProtocol && (*Proto != Config->DefaultProtocol)) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Check for broadcast, the caller has computed the packet's
 | |
|   // cast type for this child's interface.
 | |
|   //
 | |
|   Info = IP6_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|   //
 | |
|   // If it is a multicast packet, check whether we are in the group.
 | |
|   //
 | |
|   if (Info->CastType == Ip6Multicast) {
 | |
|     //
 | |
|     // Receive the multicast if the instance wants to receive all packets.
 | |
|     //
 | |
|     if (NetIp6IsUnspecifiedAddr (&IpInstance->ConfigData.StationAddress)) {
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|     for (Index = 0; Index < IpInstance->GroupCount; Index++) {
 | |
|       if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, &Head->DestinationAddress)) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return (BOOLEAN)(Index < IpInstance->GroupCount);
 | |
|   }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enqueue a shared copy of the packet to the IP6 child if the
 | |
|   packet is acceptable to it. Here the data of the packet is
 | |
|   shared, but the net buffer isn't.
 | |
| 
 | |
|   @param  IpInstance             The IP6 child to enqueue the packet to.
 | |
|   @param  Head                   The IP header of the received packet.
 | |
|   @param  Packet                 The data of the received packet.
 | |
| 
 | |
|   @retval EFI_NOT_STARTED        The IP child hasn't been configured.
 | |
|   @retval EFI_INVALID_PARAMETER  The child doesn't want to receive the packet.
 | |
|   @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources
 | |
|   @retval EFI_SUCCESS            A shared copy the packet is enqueued to the child.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip6InstanceEnquePacket (
 | |
|   IN IP6_PROTOCOL           *IpInstance,
 | |
|   IN EFI_IP6_HEADER         *Head,
 | |
|   IN NET_BUF                *Packet
 | |
|   )
 | |
| {
 | |
|   IP6_CLIP_INFO             *Info;
 | |
|   NET_BUF                   *Clone;
 | |
| 
 | |
|   //
 | |
|   // Check whether the packet is acceptable to this instance.
 | |
|   //
 | |
|   if (IpInstance->State != IP6_STATE_CONFIGED) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   if (!Ip6InstanceFrameAcceptable (IpInstance, Head, Packet)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enque a shared copy of the packet.
 | |
|   //
 | |
|   Clone = NetbufClone (Packet);
 | |
| 
 | |
|   if (Clone == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Set the receive time out for the assembled packet. If it expires,
 | |
|   // packet will be removed from the queue.
 | |
|   //
 | |
|   Info        = IP6_GET_CLIP_INFO (Clone);
 | |
|   Info->Life  = IP6_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);
 | |
| 
 | |
|   InsertTailList (&IpInstance->Received, &Clone->List);
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Deliver the received packets to the upper layer if there are both received
 | |
|   requests and enqueued packets. If the enqueued packet is shared, it will
 | |
|   duplicate it to a non-shared packet, release the shared packet, then
 | |
|   deliver the non-shared packet up.
 | |
| 
 | |
|   @param[in]  IpInstance         The IP child to deliver the packet up.
 | |
| 
 | |
|   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources to deliver the
 | |
|                                  packets.
 | |
|   @retval EFI_SUCCESS            All the enqueued packets that can be delivered
 | |
|                                  are delivered up.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip6InstanceDeliverPacket (
 | |
|   IN IP6_PROTOCOL           *IpInstance
 | |
|   )
 | |
| {
 | |
|   EFI_IP6_COMPLETION_TOKEN  *Token;
 | |
|   IP6_RXDATA_WRAP           *Wrap;
 | |
|   NET_BUF                   *Packet;
 | |
|   NET_BUF                   *Dup;
 | |
|   UINT8                     *Head;
 | |
| 
 | |
|   //
 | |
|   // Deliver a packet if there are both a packet and a receive token.
 | |
|   //
 | |
|   while (!IsListEmpty (&IpInstance->Received) && !NetMapIsEmpty (&IpInstance->RxTokens)) {
 | |
| 
 | |
|     Packet = NET_LIST_HEAD (&IpInstance->Received, NET_BUF, List);
 | |
| 
 | |
|     if (!NET_BUF_SHARED (Packet)) {
 | |
|       //
 | |
|       // If this is the only instance that wants the packet, wrap it up.
 | |
|       //
 | |
|       Wrap = Ip6WrapRxData (IpInstance, Packet);
 | |
| 
 | |
|       if (Wrap == NULL) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
| 
 | |
|       RemoveEntryList (&Packet->List);
 | |
| 
 | |
|     } else {
 | |
|       //
 | |
|       // Create a duplicated packet if this packet is shared
 | |
|       //
 | |
|       Dup = NetbufDuplicate (Packet, NULL, sizeof (EFI_IP6_HEADER));
 | |
| 
 | |
|       if (Dup == NULL) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Copy the IP head over. The packet to deliver up is
 | |
|       // headless. Trim the head off after copy. The IP head
 | |
|       // may be not continuous before the data.
 | |
|       //
 | |
|       Head        = NetbufAllocSpace (Dup, sizeof (EFI_IP6_HEADER), NET_BUF_HEAD);
 | |
|       ASSERT (Head != NULL);
 | |
|       Dup->Ip.Ip6 = (EFI_IP6_HEADER *) Head;
 | |
| 
 | |
|       CopyMem (Head, Packet->Ip.Ip6, sizeof (EFI_IP6_HEADER));
 | |
|       NetbufTrim (Dup, sizeof (EFI_IP6_HEADER), TRUE);
 | |
| 
 | |
|       Wrap = Ip6WrapRxData (IpInstance, Dup);
 | |
| 
 | |
|       if (Wrap == NULL) {
 | |
|         NetbufFree (Dup);
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
| 
 | |
|       RemoveEntryList (&Packet->List);
 | |
|       NetbufFree (Packet);
 | |
| 
 | |
|       Packet = Dup;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Insert it into the delivered packet, then get a user's
 | |
|     // receive token, pass the wrapped packet up.
 | |
|     //
 | |
|     EfiAcquireLockOrFail (&IpInstance->RecycleLock);
 | |
|     InsertHeadList (&IpInstance->Delivered, &Wrap->Link);
 | |
|     EfiReleaseLock (&IpInstance->RecycleLock);
 | |
| 
 | |
|     Token                = NetMapRemoveHead (&IpInstance->RxTokens, NULL);
 | |
|     Token->Status        = IP6_GET_CLIP_INFO (Packet)->Status;
 | |
|     Token->Packet.RxData = &Wrap->RxData;
 | |
| 
 | |
|     gBS->SignalEvent (Token->Event);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enqueue a received packet to all the IP children that share
 | |
|   the same interface.
 | |
| 
 | |
|   @param[in]  IpSb          The IP6 service instance that receive the packet.
 | |
|   @param[in]  Head          The header of the received packet.
 | |
|   @param[in]  Packet        The data of the received packet.
 | |
|   @param[in]  IpIf          The interface to enqueue the packet to.
 | |
| 
 | |
|   @return The number of the IP6 children that accepts the packet.
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| Ip6InterfaceEnquePacket (
 | |
|   IN IP6_SERVICE            *IpSb,
 | |
|   IN EFI_IP6_HEADER         *Head,
 | |
|   IN NET_BUF                *Packet,
 | |
|   IN IP6_INTERFACE          *IpIf
 | |
|   )
 | |
| {
 | |
|   IP6_PROTOCOL              *IpInstance;
 | |
|   IP6_CLIP_INFO             *Info;
 | |
|   LIST_ENTRY                *Entry;
 | |
|   INTN                      Enqueued;
 | |
|   INTN                      LocalType;
 | |
|   INTN                      SavedType;
 | |
| 
 | |
|   //
 | |
|   // First, check that the packet is acceptable to this interface
 | |
|   // and find the local cast type for the interface.
 | |
|   //
 | |
|   LocalType = 0;
 | |
|   Info      = IP6_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|   if (IpIf->PromiscRecv) {
 | |
|     LocalType = Ip6Promiscuous;
 | |
|   } else {
 | |
|     LocalType = Info->CastType;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Iterate through the ip instances on the interface, enqueue
 | |
|   // the packet if filter passed. Save the original cast type,
 | |
|   // and pass the local cast type to the IP children on the
 | |
|   // interface. The global cast type will be restored later.
 | |
|   //
 | |
|   SavedType       = Info->CastType;
 | |
|   Info->CastType  = (UINT32) LocalType;
 | |
| 
 | |
|   Enqueued        = 0;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
 | |
|     IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);
 | |
|     NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
 | |
| 
 | |
|     if (Ip6InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {
 | |
|       Enqueued++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Info->CastType = (UINT32) SavedType;
 | |
|   return Enqueued;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Deliver the packet for each IP6 child on the interface.
 | |
| 
 | |
|   @param[in]  IpSb          The IP6 service instance that received the packet.
 | |
|   @param[in]  IpIf          The IP6 interface to deliver the packet.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip6InterfaceDeliverPacket (
 | |
|   IN IP6_SERVICE            *IpSb,
 | |
|   IN IP6_INTERFACE          *IpIf
 | |
|   )
 | |
| {
 | |
|   IP6_PROTOCOL              *IpInstance;
 | |
|   LIST_ENTRY                *Entry;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
 | |
|     IpInstance = NET_LIST_USER_STRUCT (Entry, IP6_PROTOCOL, AddrLink);
 | |
|     Ip6InstanceDeliverPacket (IpInstance);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   De-multiplex the packet. the packet delivery is processed in two
 | |
|   passes. The first pass will enqueue a shared copy of the packet
 | |
|   to each IP6 child that accepts the packet. The second pass will
 | |
|   deliver a non-shared copy of the packet to each IP6 child that
 | |
|   has pending receive requests. Data is copied if more than one
 | |
|   child wants to consume the packet, because each IP child needs
 | |
|   its own copy of the packet to make changes.
 | |
| 
 | |
|   @param[in]  IpSb          The IP6 service instance that received the packet.
 | |
|   @param[in]  Head          The header of the received packet.
 | |
|   @param[in]  Packet        The data of the received packet.
 | |
| 
 | |
|   @retval EFI_NOT_FOUND     No IP child accepts the packet.
 | |
|   @retval EFI_SUCCESS       The packet is enqueued or delivered to some IP
 | |
|                             children.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip6Demultiplex (
 | |
|   IN IP6_SERVICE            *IpSb,
 | |
|   IN EFI_IP6_HEADER         *Head,
 | |
|   IN NET_BUF                *Packet
 | |
|   )
 | |
| {
 | |
| 
 | |
|   LIST_ENTRY                *Entry;
 | |
|   IP6_INTERFACE             *IpIf;
 | |
|   INTN                      Enqueued;
 | |
| 
 | |
|   //
 | |
|   // Two pass delivery: first, enque a shared copy of the packet
 | |
|   // to each instance that accept the packet.
 | |
|   //
 | |
|   Enqueued = 0;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
 | |
|     IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
 | |
| 
 | |
|     if (IpIf->Configured) {
 | |
|       Enqueued += Ip6InterfaceEnquePacket (IpSb, Head, Packet, IpIf);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Second: deliver a duplicate of the packet to each instance.
 | |
|   // Release the local reference first, so that the last instance
 | |
|   // getting the packet will not copy the data.
 | |
|   //
 | |
|   NetbufFree (Packet);
 | |
|   Packet = NULL;
 | |
| 
 | |
|   if (Enqueued == 0) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
 | |
|     IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
 | |
| 
 | |
|     if (IpIf->Configured) {
 | |
|       Ip6InterfaceDeliverPacket (IpSb, IpIf);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Decrease the life of the transmitted packets. If it is
 | |
|   decreased to zero, cancel the packet. This function is
 | |
|   called by Ip6packetTimerTicking that provides timeout for both the
 | |
|   received-but-not-delivered and transmitted-but-not-recycle
 | |
|   packets.
 | |
| 
 | |
|   @param[in]  Map           The IP6 child's transmit map.
 | |
|   @param[in]  Item          Current transmitted packet.
 | |
|   @param[in]  Context       Not used.
 | |
| 
 | |
|   @retval EFI_SUCCESS       Always returns EFI_SUCCESS.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| Ip6SentPacketTicking (
 | |
|   IN NET_MAP                *Map,
 | |
|   IN NET_MAP_ITEM           *Item,
 | |
|   IN VOID                   *Context
 | |
|   )
 | |
| {
 | |
|   IP6_TXTOKEN_WRAP          *Wrap;
 | |
| 
 | |
|   Wrap = (IP6_TXTOKEN_WRAP *) Item->Value;
 | |
|   ASSERT (Wrap != NULL);
 | |
| 
 | |
|   if ((Wrap->Life > 0) && (--Wrap->Life == 0)) {
 | |
|     Ip6CancelPacket (Wrap->IpInstance->Interface, Wrap->Packet, EFI_ABORTED);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Timeout the fragments, and the enqueued, and transmitted packets.
 | |
| 
 | |
|   @param[in]  IpSb          The IP6 service instance to timeout.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip6PacketTimerTicking (
 | |
|   IN IP6_SERVICE            *IpSb
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY                *InstanceEntry;
 | |
|   LIST_ENTRY                *Entry;
 | |
|   LIST_ENTRY                *Next;
 | |
|   IP6_PROTOCOL              *IpInstance;
 | |
|   IP6_ASSEMBLE_ENTRY        *Assemble;
 | |
|   NET_BUF                   *Packet;
 | |
|   IP6_CLIP_INFO             *Info;
 | |
|   UINT32                    Index;
 | |
| 
 | |
|   //
 | |
|   // First, time out the fragments. The packet's life is counting down
 | |
|   // once the first-arriving fragment of that packet was received.
 | |
|   //
 | |
|   for (Index = 0; Index < IP6_ASSEMLE_HASH_SIZE; Index++) {
 | |
|     NET_LIST_FOR_EACH_SAFE (Entry, Next, &(IpSb->Assemble.Bucket[Index])) {
 | |
|       Assemble = NET_LIST_USER_STRUCT (Entry, IP6_ASSEMBLE_ENTRY, Link);
 | |
| 
 | |
|       if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {
 | |
|         //
 | |
|         // If the first fragment (the one with a Fragment Offset of zero)
 | |
|         // has been received, an ICMP Time Exceeded - Fragment Reassembly
 | |
|         // Time Exceeded message should be sent to the source of that fragment.
 | |
|         //
 | |
|         if ((Assemble->Packet != NULL) &&
 | |
|             !IP6_IS_MULTICAST (&Assemble->Head->DestinationAddress)) {
 | |
|           Ip6SendIcmpError (
 | |
|             IpSb,
 | |
|             Assemble->Packet,
 | |
|             NULL,
 | |
|             &Assemble->Head->SourceAddress,
 | |
|             ICMP_V6_TIME_EXCEEDED,
 | |
|             ICMP_V6_TIMEOUT_REASSEMBLE,
 | |
|             NULL
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // If reassembly of a packet is not completed within 60 seconds of
 | |
|         // the reception of the first-arriving fragment of that packet, the
 | |
|         // reassembly must be abandoned and all the fragments that have been
 | |
|         // received for that packet must be discarded.
 | |
|         //
 | |
|         RemoveEntryList (Entry);
 | |
|         Ip6FreeAssembleEntry (Assemble);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {
 | |
|     IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP6_PROTOCOL, Link);
 | |
| 
 | |
|     //
 | |
|     // Second, time out the assembled packets enqueued on each IP child.
 | |
|     //
 | |
|     NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpInstance->Received) {
 | |
|       Packet = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
 | |
|       Info   = IP6_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|       if ((Info->Life > 0) && (--Info->Life == 0)) {
 | |
|         RemoveEntryList (Entry);
 | |
|         NetbufFree (Packet);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Third: time out the transmitted packets.
 | |
|     //
 | |
|     NetMapIterate (&IpInstance->TxTokens, Ip6SentPacketTicking, NULL);
 | |
|   }
 | |
| }
 | |
| 
 |