Fix various typos in comments and documentation. Cc: Jiaxin Wu <jiaxin.wu@intel.com> Cc: Siyuan Fu <siyuan.fu@intel.com> Cc: Maciej Rabeda <maciej.rabeda@intel.com> Signed-off-by: Antoine Coeur <coeur@gmx.fr> Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com> Reviewed-by: Maciej Rabeda <maciej.rabeda@intel.com> Signed-off-by: Philippe Mathieu-Daude <philmd@redhat.com> Message-Id: <20200207010831.9046-41-philmd@redhat.com>
		
			
				
	
	
		
			1816 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1816 lines
		
	
	
		
			51 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  IP6 internal functions to process the incoming packets.
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
  (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
 | 
						|
 | 
						|
  SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#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 reassembly 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 total length is known
 | 
						|
  //  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 formatted, 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 excluded.
 | 
						|
    //
 | 
						|
    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 sufficient 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;
 | 
						|
 | 
						|
  if (!mIpSec2Installed) {
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
  ASSERT (mIpSec != NULL);
 | 
						|
 | 
						|
  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));
 | 
						|
 | 
						|
  //
 | 
						|
  // 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 received 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 transferred 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 implementation. 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;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Enqueue 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, enqueue 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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 |