REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3737 Apply uncrustify changes to .c/.h files in the NetworkPkg package Cc: Andrew Fish <afish@apple.com> Cc: Leif Lindholm <leif@nuviainc.com> Cc: Michael D Kinney <michael.d.kinney@intel.com> Signed-off-by: Michael Kubacki <michael.kubacki@microsoft.com> Reviewed-by: Maciej Rabeda <maciej.rabeda@linux.intel.com>
		
			
				
	
	
		
			1607 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1607 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   IP4 input process.
 | |
| 
 | |
| Copyright (c) 2005 - 2020, Intel Corporation. All rights reserved.<BR>
 | |
| (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
 | |
| 
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "Ip4Impl.h"
 | |
| 
 | |
| /**
 | |
|   Create an empty assemble entry for the packet identified by
 | |
|   (Dst, Src, Id, Protocol). The default life for the packet is
 | |
|   120 seconds.
 | |
| 
 | |
|   @param[in]  Dst                    The destination address
 | |
|   @param[in]  Src                    The source address
 | |
|   @param[in]  Id                     The ID field in IP header
 | |
|   @param[in]  Protocol               The protocol field in IP header
 | |
| 
 | |
|   @return NULL if failed to allocate memory for the entry, otherwise
 | |
|           the point to just created reassemble entry.
 | |
| 
 | |
| **/
 | |
| IP4_ASSEMBLE_ENTRY *
 | |
| Ip4CreateAssembleEntry (
 | |
|   IN IP4_ADDR  Dst,
 | |
|   IN IP4_ADDR  Src,
 | |
|   IN UINT16    Id,
 | |
|   IN UINT8     Protocol
 | |
|   )
 | |
| {
 | |
|   IP4_ASSEMBLE_ENTRY  *Assemble;
 | |
| 
 | |
|   Assemble = AllocatePool (sizeof (IP4_ASSEMBLE_ENTRY));
 | |
| 
 | |
|   if (Assemble == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   InitializeListHead (&Assemble->Link);
 | |
|   InitializeListHead (&Assemble->Fragments);
 | |
| 
 | |
|   Assemble->Dst      = Dst;
 | |
|   Assemble->Src      = Src;
 | |
|   Assemble->Id       = Id;
 | |
|   Assemble->Protocol = Protocol;
 | |
|   Assemble->TotalLen = 0;
 | |
|   Assemble->CurLen   = 0;
 | |
|   Assemble->Head     = NULL;
 | |
|   Assemble->Info     = NULL;
 | |
|   Assemble->Life     = IP4_FRAGMENT_LIFE;
 | |
| 
 | |
|   return Assemble;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release all the fragments of a packet, then free the assemble entry.
 | |
| 
 | |
|   @param[in]  Assemble               The assemble entry to free
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4FreeAssembleEntry (
 | |
|   IN IP4_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);
 | |
|   }
 | |
| 
 | |
|   FreePool (Assemble);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize an already allocated assemble table. This is generally
 | |
|   the assemble table embedded in the IP4 service instance.
 | |
| 
 | |
|   @param[in, out]  Table                  The assemble table to initialize.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4InitAssembleTable (
 | |
|   IN OUT IP4_ASSEMBLE_TABLE  *Table
 | |
|   )
 | |
| {
 | |
|   UINT32  Index;
 | |
| 
 | |
|   for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
 | |
|     InitializeListHead (&Table->Bucket[Index]);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Clean up the assemble table: remove all the fragments
 | |
|   and assemble entries.
 | |
| 
 | |
|   @param[in]  Table                  The assemble table to clean up
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4CleanAssembleTable (
 | |
|   IN IP4_ASSEMBLE_TABLE  *Table
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY          *Entry;
 | |
|   LIST_ENTRY          *Next;
 | |
|   IP4_ASSEMBLE_ENTRY  *Assemble;
 | |
|   UINT32              Index;
 | |
| 
 | |
|   for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
 | |
|     NET_LIST_FOR_EACH_SAFE (Entry, Next, &Table->Bucket[Index]) {
 | |
|       Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);
 | |
| 
 | |
|       RemoveEntryList (Entry);
 | |
|       Ip4FreeAssembleEntry (Assemble);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Trim the packet to fit in [Start, End), and update the per
 | |
|   packet information.
 | |
| 
 | |
|   @param  Packet                 Packet to trim
 | |
|   @param  Start                  The sequence of the first byte to fit in
 | |
|   @param  End                    One beyond the sequence of last byte to fit in.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4TrimPacket (
 | |
|   IN OUT NET_BUF  *Packet,
 | |
|   IN     INTN     Start,
 | |
|   IN     INTN     End
 | |
|   )
 | |
| {
 | |
|   IP4_CLIP_INFO  *Info;
 | |
|   INTN           Len;
 | |
| 
 | |
|   Info = IP4_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   = Start;
 | |
|     Info->Length -= Len;
 | |
|   }
 | |
| 
 | |
|   if (End < Info->End) {
 | |
|     Len = End - Info->End;
 | |
| 
 | |
|     NetbufTrim (Packet, (UINT32)Len, NET_BUF_TAIL);
 | |
|     Info->End     = End;
 | |
|     Info->Length -= Len;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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 will free all the fragments of the packet.
 | |
| 
 | |
|   @param[in]  Arg                    The assemble entry to free
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| Ip4OnFreeFragments (
 | |
|   IN VOID  *Arg
 | |
|   )
 | |
| {
 | |
|   Ip4FreeAssembleEntry ((IP4_ASSEMBLE_ENTRY *)Arg);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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
 | |
|   return.
 | |
| 
 | |
|   @param  Table     The assemble table used. New assemble entry will be created
 | |
|                     if the Packet is from a new chain of fragments.
 | |
|   @param  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 reassemble. The point to just assembled
 | |
|           packet if all the fragments of the packet have arrived.
 | |
| 
 | |
| **/
 | |
| NET_BUF *
 | |
| Ip4Reassemble (
 | |
|   IN OUT IP4_ASSEMBLE_TABLE  *Table,
 | |
|   IN OUT NET_BUF             *Packet
 | |
|   )
 | |
| {
 | |
|   IP4_HEAD            *IpHead;
 | |
|   IP4_CLIP_INFO       *This;
 | |
|   IP4_CLIP_INFO       *Node;
 | |
|   IP4_ASSEMBLE_ENTRY  *Assemble;
 | |
|   LIST_ENTRY          *Head;
 | |
|   LIST_ENTRY          *Prev;
 | |
|   LIST_ENTRY          *Cur;
 | |
|   NET_BUF             *Fragment;
 | |
|   NET_BUF             *NewPacket;
 | |
|   INTN                Index;
 | |
| 
 | |
|   IpHead = Packet->Ip.Ip4;
 | |
|   This   = IP4_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|   ASSERT (IpHead != NULL);
 | |
| 
 | |
|   //
 | |
|   // First: find the related assemble entry
 | |
|   //
 | |
|   Assemble = NULL;
 | |
|   Index    = IP4_ASSEMBLE_HASH (IpHead->Dst, IpHead->Src, IpHead->Id, IpHead->Protocol);
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Cur, &Table->Bucket[Index]) {
 | |
|     Assemble = NET_LIST_USER_STRUCT (Cur, IP4_ASSEMBLE_ENTRY, Link);
 | |
| 
 | |
|     if ((Assemble->Dst == IpHead->Dst) && (Assemble->Src == IpHead->Src) &&
 | |
|         (Assemble->Id == IpHead->Id)   && (Assemble->Protocol == IpHead->Protocol))
 | |
|     {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Create a new assemble entry if no assemble entry is related to this packet
 | |
|   //
 | |
|   if (Cur == &Table->Bucket[Index]) {
 | |
|     Assemble = Ip4CreateAssembleEntry (
 | |
|                  IpHead->Dst,
 | |
|                  IpHead->Src,
 | |
|                  IpHead->Id,
 | |
|                  IpHead->Protocol
 | |
|                  );
 | |
| 
 | |
|     if (Assemble == NULL) {
 | |
|       goto DROP;
 | |
|     }
 | |
| 
 | |
|     InsertHeadList (&Table->Bucket[Index], &Assemble->Link);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Assemble shouldn't be NULL here
 | |
|   //
 | |
|   ASSERT (Assemble != NULL);
 | |
| 
 | |
|   //
 | |
|   // 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.
 | |
|   //
 | |
|   Head = &Assemble->Fragments;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Cur, Head) {
 | |
|     Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
 | |
| 
 | |
|     if (This->Start < IP4_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) != Head) {
 | |
|     Fragment = NET_LIST_USER_STRUCT (Prev, NET_BUF, List);
 | |
|     Node     = IP4_GET_CLIP_INFO (Fragment);
 | |
| 
 | |
|     if (This->Start < Node->End) {
 | |
|       if (This->End <= Node->End) {
 | |
|         NetbufFree (Packet);
 | |
|         return NULL;
 | |
|       }
 | |
| 
 | |
|       Ip4TrimPacket (Packet, Node->End, This->End);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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 != Head) {
 | |
|     Fragment = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);
 | |
|     Node     = IP4_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 DROP;
 | |
|       }
 | |
| 
 | |
|       Ip4TrimPacket (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.
 | |
|     //
 | |
|     ASSERT (Assemble->Head == NULL);
 | |
| 
 | |
|     Assemble->Head = IpHead;
 | |
|     Assemble->Info = IP4_GET_CLIP_INFO (Packet);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Don't update the length more than once.
 | |
|   //
 | |
|   if (IP4_LAST_FRAGMENT (IpHead->Fragment) && (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 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 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 (Head->BackLink, NET_BUF, List);
 | |
| 
 | |
|     if (IP4_GET_CLIP_INFO (Fragment)->End != Assemble->TotalLen) {
 | |
|       Ip4FreeAssembleEntry (Assemble);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Wrap the packet in a net buffer then deliver it up
 | |
|     //
 | |
|     NewPacket = NetbufFromBufList (
 | |
|                   &Assemble->Fragments,
 | |
|                   0,
 | |
|                   0,
 | |
|                   Ip4OnFreeFragments,
 | |
|                   Assemble
 | |
|                   );
 | |
| 
 | |
|     if (NewPacket == NULL) {
 | |
|       Ip4FreeAssembleEntry (Assemble);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|     NewPacket->Ip.Ip4 = Assemble->Head;
 | |
| 
 | |
|     ASSERT (Assemble->Info != NULL);
 | |
| 
 | |
|     CopyMem (
 | |
|       IP4_GET_CLIP_INFO (NewPacket),
 | |
|       Assemble->Info,
 | |
|       sizeof (*IP4_GET_CLIP_INFO (NewPacket))
 | |
|       );
 | |
| 
 | |
|     return NewPacket;
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| 
 | |
| DROP:
 | |
|   NetbufFree (Packet);
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The callback function for the net buffer which 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
 | |
| Ip4IpSecFree (
 | |
|   IN VOID  *Arg
 | |
|   )
 | |
| {
 | |
|   IP4_IPSEC_WRAP  *Wrap;
 | |
| 
 | |
|   Wrap = (IP4_IPSEC_WRAP *)Arg;
 | |
| 
 | |
|   if (Wrap->IpSecRecycleSignal != NULL) {
 | |
|     gBS->SignalEvent (Wrap->IpSecRecycleSignal);
 | |
|   }
 | |
| 
 | |
|   NetbufFree (Wrap->Packet);
 | |
| 
 | |
|   FreePool (Wrap);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The work function to locate IPsec protocol to process the inbound or
 | |
|   outbound IP packets. The process routine handls the packet with following
 | |
|   actions: bypass the packet, discard the packet, or protect the packet.
 | |
| 
 | |
|   @param[in]       IpSb          The IP4 service instance.
 | |
|   @param[in, out]  Head          The caller supplied IP4 header.
 | |
|   @param[in, out]  Netbuf        The IP4 packet to be processed by IPsec.
 | |
|   @param[in, out]  Options       The caller supplied options.
 | |
|   @param[in, out]  OptionsLen    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 is no sufficient resource to complete the operation.
 | |
|   @retval EFI_BUFFER_TOO_SMALL   The number of non-empty block is bigger than the
 | |
|                                  number of input data blocks when build a fragment table.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip4IpSecProcessPacket (
 | |
|   IN     IP4_SERVICE            *IpSb,
 | |
|   IN OUT IP4_HEAD               **Head,
 | |
|   IN OUT NET_BUF                **Netbuf,
 | |
|   IN OUT UINT8                  **Options,
 | |
|   IN OUT UINT32                 *OptionsLen,
 | |
|   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;
 | |
|   IP4_TXTOKEN_WRAP  *TxWrap;
 | |
|   IP4_IPSEC_WRAP    *IpSecWrap;
 | |
|   EFI_STATUS        Status;
 | |
|   IP4_HEAD          ZeroHead;
 | |
| 
 | |
|   Status = EFI_SUCCESS;
 | |
| 
 | |
|   if (!mIpSec2Installed) {
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   ASSERT (mIpSec != NULL);
 | |
| 
 | |
|   Packet        = *Netbuf;
 | |
|   RecycleEvent  = NULL;
 | |
|   IpSecWrap     = NULL;
 | |
|   FragmentTable = NULL;
 | |
|   TxWrap        = (IP4_TXTOKEN_WRAP *)Context;
 | |
|   FragmentCount = Packet->BlockOpNum;
 | |
| 
 | |
|   ZeroMem (&ZeroHead, sizeof (IP4_HEAD));
 | |
| 
 | |
|   //
 | |
|   // 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 - IP4_MAX_IPSEC_HEADLEN;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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);
 | |
| 
 | |
|   //
 | |
|   // Record the original FragmentTable and count.
 | |
|   //
 | |
|   OriginalFragmentTable = FragmentTable;
 | |
|   OriginalFragmentCount = FragmentCount;
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (FragmentTable);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Convert host byte order to network byte order
 | |
|   //
 | |
|   Ip4NtohHead (*Head);
 | |
| 
 | |
|   Status = mIpSec->ProcessExt (
 | |
|                      mIpSec,
 | |
|                      IpSb->Controller,
 | |
|                      IP_VERSION_4,
 | |
|                      (VOID *)(*Head),
 | |
|                      &(*Head)->Protocol,
 | |
|                      (VOID **)Options,
 | |
|                      OptionsLen,
 | |
|                      (EFI_IPSEC_FRAGMENT_DATA **)(&FragmentTable),
 | |
|                      &FragmentCount,
 | |
|                      Direction,
 | |
|                      &RecycleEvent
 | |
|                      );
 | |
|   //
 | |
|   // Convert back to host byte order
 | |
|   //
 | |
|   Ip4NtohHead (*Head);
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (OriginalFragmentTable);
 | |
|     goto ON_EXIT;
 | |
|   }
 | |
| 
 | |
|   if ((OriginalFragmentTable == FragmentTable) && (OriginalFragmentCount == FragmentCount)) {
 | |
|     //
 | |
|     // 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,
 | |
|                                    IP4_MAX_HEADLEN,
 | |
|                                    0,
 | |
|                                    Ip4FreeTxToken,
 | |
|                                    TxWrap
 | |
|                                    );
 | |
|     if (TxWrap->Packet == NULL) {
 | |
|       //
 | |
|       // Recover the TxWrap->Packet, if meet a error, and the caller will free
 | |
|       // the TxWrap.
 | |
|       //
 | |
|       TxWrap->Packet = *Netbuf;
 | |
|       Status         = EFI_OUT_OF_RESOURCES;
 | |
|       goto ON_EXIT;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Free original Netbuf.
 | |
|     //
 | |
|     NetIpSecNetbufFree (*Netbuf);
 | |
|     *Netbuf = TxWrap->Packet;
 | |
|   } else {
 | |
|     IpSecWrap = AllocateZeroPool (sizeof (IP4_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,
 | |
|                                       IP4_MAX_HEADLEN,
 | |
|                                       0,
 | |
|                                       Ip4IpSecFree,
 | |
|                                       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 (*Head, &ZeroHead, sizeof (IP4_HEAD)))) {
 | |
|       Ip4PrependHead (Packet, *Head, *Options, *OptionsLen);
 | |
|       Ip4NtohHead (Packet->Ip.Ip4);
 | |
|       NetbufTrim (Packet, ((*Head)->HeadLen << 2), TRUE);
 | |
| 
 | |
|       CopyMem (
 | |
|         IP4_GET_CLIP_INFO (Packet),
 | |
|         IP4_GET_CLIP_INFO (IpSecWrap->Packet),
 | |
|         sizeof (IP4_CLIP_INFO)
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     *Netbuf = Packet;
 | |
|   }
 | |
| 
 | |
| ON_EXIT:
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Pre-process the IPv4 packet. First validates the IPv4 packet, and
 | |
|   then reassembles packet if it is necessary.
 | |
| 
 | |
|   @param[in]       IpSb            Pointer to IP4_SERVICE.
 | |
|   @param[in, out]  Packet          Pointer to the Packet to be processed.
 | |
|   @param[in]       Head            Pointer to the IP4_HEAD.
 | |
|   @param[in]       Option          Pointer to a buffer which contains the IPv4 option.
 | |
|   @param[in]       OptionLen       The length of Option in bytes.
 | |
|   @param[in]       Flag            The link layer flag for the packet received, such
 | |
|                                    as multicast.
 | |
| 
 | |
|   @retval     EFI_SUCCESS                The received packet is in well form.
 | |
|   @retval     EFI_INVALID_PARAMETER      The received packet is malformed.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip4PreProcessPacket (
 | |
|   IN     IP4_SERVICE  *IpSb,
 | |
|   IN OUT NET_BUF      **Packet,
 | |
|   IN     IP4_HEAD     *Head,
 | |
|   IN     UINT8        *Option,
 | |
|   IN     UINT32       OptionLen,
 | |
|   IN     UINT32       Flag
 | |
|   )
 | |
| {
 | |
|   IP4_CLIP_INFO  *Info;
 | |
|   UINT32         HeadLen;
 | |
|   UINT32         TotalLen;
 | |
|   UINT16         Checksum;
 | |
| 
 | |
|   //
 | |
|   // Check if the IP4 header is correctly formatted.
 | |
|   //
 | |
|   HeadLen  = (Head->HeadLen << 2);
 | |
|   TotalLen = NTOHS (Head->TotalLen);
 | |
| 
 | |
|   //
 | |
|   // Mnp may deliver frame trailer sequence up, trim it off.
 | |
|   //
 | |
|   if (TotalLen < (*Packet)->TotalSize) {
 | |
|     NetbufTrim (*Packet, (*Packet)->TotalSize - TotalLen, FALSE);
 | |
|   }
 | |
| 
 | |
|   if ((Head->Ver != 4) || (HeadLen < IP4_MIN_HEADLEN) ||
 | |
|       (TotalLen < HeadLen) || (TotalLen != (*Packet)->TotalSize))
 | |
|   {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Some OS may send IP packets without checksum.
 | |
|   //
 | |
|   Checksum = (UINT16)(~NetblockChecksum ((UINT8 *)Head, HeadLen));
 | |
| 
 | |
|   if ((Head->Checksum != 0) && (Checksum != 0)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Convert the IP header to host byte order, then get the per packet info.
 | |
|   //
 | |
|   (*Packet)->Ip.Ip4 = Ip4NtohHead (Head);
 | |
| 
 | |
|   Info           = IP4_GET_CLIP_INFO (*Packet);
 | |
|   Info->LinkFlag = Flag;
 | |
|   Info->CastType = Ip4GetHostCast (IpSb, Head->Dst, Head->Src);
 | |
|   Info->Start    = (Head->Fragment & IP4_HEAD_OFFSET_MASK) << 3;
 | |
|   Info->Length   = Head->TotalLen - HeadLen;
 | |
|   Info->End      = Info->Start + Info->Length;
 | |
|   Info->Status   = EFI_SUCCESS;
 | |
| 
 | |
|   //
 | |
|   // The packet is destinated to us if the CastType is non-zero.
 | |
|   //
 | |
|   if ((Info->CastType == 0) || (Info->End > IP4_MAX_PACKET_SIZE)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Validate the options. Don't call the Ip4OptionIsValid if
 | |
|   // there is no option to save some CPU process.
 | |
|   //
 | |
| 
 | |
|   if ((OptionLen > 0) && !Ip4OptionIsValid (Option, OptionLen, TRUE)) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Trim the head off, after this point, the packet is headless,
 | |
|   // and Packet->TotalLen == Info->Length.
 | |
|   //
 | |
|   NetbufTrim (*Packet, HeadLen, TRUE);
 | |
| 
 | |
|   //
 | |
|   // Reassemble the packet if this is a fragment. The packet is a
 | |
|   // fragment if its head has MF (more fragment) set, or it starts
 | |
|   // at non-zero byte.
 | |
|   //
 | |
|   if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) || (Info->Start != 0)) {
 | |
|     //
 | |
|     // Drop the fragment if DF is set but it is fragmented. Gateway
 | |
|     // need to send a type 4 destination unreache ICMP message here.
 | |
|     //
 | |
|     if ((Head->Fragment & IP4_HEAD_DF_MASK) != 0) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // The length of all but the last fragments is in the unit of 8 bytes.
 | |
|     //
 | |
|     if (((Head->Fragment & IP4_HEAD_MF_MASK) != 0) && (Info->Length % 8 != 0)) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     *Packet = Ip4Reassemble (&IpSb->Assemble, *Packet);
 | |
| 
 | |
|     //
 | |
|     // Packet assembly isn't complete, start receive more packet.
 | |
|     //
 | |
|     if (*Packet == NULL) {
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function checks the IPv4 packet length.
 | |
| 
 | |
|   @param[in]       Packet          Pointer to the IPv4 Packet to be checked.
 | |
| 
 | |
|   @retval TRUE                   The input IPv4 packet length is valid.
 | |
|   @retval FALSE                  The input IPv4 packet length is invalid.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip4IsValidPacketLength (
 | |
|   IN NET_BUF  *Packet
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Check the IP4 packet length.
 | |
|   //
 | |
|   if (Packet->TotalSize < IP4_MIN_HEADLEN) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The IP4 input routine. It is called by the IP4_INTERFACE when a
 | |
|   IP4 fragment is received from MNP.
 | |
| 
 | |
|   @param[in]  Ip4Instance        The IP4 child that request the receive, most like
 | |
|                                  it is NULL.
 | |
|   @param[in]  Packet             The IP4 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 IP4 service instance that own the MNP.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4AccpetFrame (
 | |
|   IN IP4_PROTOCOL  *Ip4Instance,
 | |
|   IN NET_BUF       *Packet,
 | |
|   IN EFI_STATUS    IoStatus,
 | |
|   IN UINT32        Flag,
 | |
|   IN VOID          *Context
 | |
|   )
 | |
| {
 | |
|   IP4_SERVICE  *IpSb;
 | |
|   IP4_HEAD     *Head;
 | |
|   EFI_STATUS   Status;
 | |
|   IP4_HEAD     ZeroHead;
 | |
|   UINT8        *Option;
 | |
|   UINT32       OptionLen;
 | |
| 
 | |
|   IpSb   = (IP4_SERVICE *)Context;
 | |
|   Option = NULL;
 | |
| 
 | |
|   if (EFI_ERROR (IoStatus) || (IpSb->State == IP4_SERVICE_DESTROY)) {
 | |
|     goto DROP;
 | |
|   }
 | |
| 
 | |
|   if (!Ip4IsValidPacketLength (Packet)) {
 | |
|     goto RESTART;
 | |
|   }
 | |
| 
 | |
|   Head = (IP4_HEAD *)NetbufGetByte (Packet, 0, NULL);
 | |
|   ASSERT (Head != NULL);
 | |
|   OptionLen = (Head->HeadLen << 2) - IP4_MIN_HEADLEN;
 | |
|   if (OptionLen > 0) {
 | |
|     Option = (UINT8 *)(Head + 1);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Validate packet format and reassemble packet if it is necessary.
 | |
|   //
 | |
|   Status = Ip4PreProcessPacket (
 | |
|              IpSb,
 | |
|              &Packet,
 | |
|              Head,
 | |
|              Option,
 | |
|              OptionLen,
 | |
|              Flag
 | |
|              );
 | |
| 
 | |
|   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 = Ip4IpSecProcessPacket (
 | |
|              IpSb,
 | |
|              &Head,
 | |
|              &Packet,
 | |
|              &Option,
 | |
|              &OptionLen,
 | |
|              EfiIPsecInBound,
 | |
|              NULL
 | |
|              );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     goto RESTART;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If the packet is protected by tunnel mode, parse the inner Ip Packet.
 | |
|   //
 | |
|   ZeroMem (&ZeroHead, sizeof (IP4_HEAD));
 | |
|   if (0 == CompareMem (Head, &ZeroHead, sizeof (IP4_HEAD))) {
 | |
|     // Packet may have been changed. Head, HeadLen, TotalLen, and
 | |
|     // info must be reloaded before use. The ownership of the packet
 | |
|     // is transferred to the packet process logic.
 | |
|     //
 | |
|     if (!Ip4IsValidPacketLength (Packet)) {
 | |
|       goto RESTART;
 | |
|     }
 | |
| 
 | |
|     Head = (IP4_HEAD *)NetbufGetByte (Packet, 0, NULL);
 | |
|     ASSERT (Head != NULL);
 | |
|     Status = Ip4PreProcessPacket (
 | |
|                IpSb,
 | |
|                &Packet,
 | |
|                Head,
 | |
|                Option,
 | |
|                OptionLen,
 | |
|                Flag
 | |
|                );
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       goto RESTART;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ASSERT (Packet != NULL);
 | |
|   Head                               = Packet->Ip.Ip4;
 | |
|   IP4_GET_CLIP_INFO (Packet)->Status = EFI_SUCCESS;
 | |
| 
 | |
|   switch (Head->Protocol) {
 | |
|     case EFI_IP_PROTO_ICMP:
 | |
|       Ip4IcmpHandle (IpSb, Head, Packet);
 | |
|       break;
 | |
| 
 | |
|     case IP4_PROTO_IGMP:
 | |
|       Ip4IgmpHandle (IpSb, Head, Packet);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       Ip4Demultiplex (IpSb, Head, Packet, Option, OptionLen);
 | |
|   }
 | |
| 
 | |
|   Packet = NULL;
 | |
| 
 | |
|   //
 | |
|   // Dispatch the DPCs queued by the NotifyFunction of the rx token's events
 | |
|   // which are signaled with received data.
 | |
|   //
 | |
|   DispatchDpc ();
 | |
| 
 | |
| RESTART:
 | |
|   Ip4ReceiveFrame (IpSb->DefaultInterface, NULL, Ip4AccpetFrame, IpSb);
 | |
| 
 | |
| DROP:
 | |
|   if (Packet != NULL) {
 | |
|     NetbufFree (Packet);
 | |
|   }
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   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   If the child wants to receive the packet.
 | |
|   @retval FALSE  Otherwise.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| Ip4InstanceFrameAcceptable (
 | |
|   IN IP4_PROTOCOL  *IpInstance,
 | |
|   IN IP4_HEAD      *Head,
 | |
|   IN NET_BUF       *Packet
 | |
|   )
 | |
| {
 | |
|   IP4_ICMP_ERROR_HEAD  Icmp;
 | |
|   EFI_IP4_CONFIG_DATA  *Config;
 | |
|   IP4_CLIP_INFO        *Info;
 | |
|   UINT16               Proto;
 | |
|   UINT32               Index;
 | |
| 
 | |
|   Config = &IpInstance->ConfigData;
 | |
| 
 | |
|   //
 | |
|   // 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 capability. 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;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Use protocol from the IP header embedded in the ICMP error
 | |
|   // message to filter, instead of ICMP itself. ICMP handle will
 | |
|   // call Ip4Demultiplex to deliver ICMP errors.
 | |
|   //
 | |
|   Proto = Head->Protocol;
 | |
| 
 | |
|   if ((Proto == EFI_IP_PROTO_ICMP) && (!Config->AcceptAnyProtocol) && (Proto != Config->DefaultProtocol)) {
 | |
|     NetbufCopy (Packet, 0, sizeof (Icmp.Head), (UINT8 *)&Icmp.Head);
 | |
| 
 | |
|     if (mIcmpClass[Icmp.Head.Type].IcmpClass == ICMP_ERROR_MESSAGE) {
 | |
|       if (!Config->AcceptIcmpErrors) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|       NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *)&Icmp);
 | |
|       Proto = Icmp.IpHead.Protocol;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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 = IP4_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|   if (IP4_IS_BROADCAST (Info->CastType)) {
 | |
|     return Config->AcceptBroadcast;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // If it is a multicast packet, check whether we are in the group.
 | |
|   //
 | |
|   if (Info->CastType == IP4_MULTICAST) {
 | |
|     //
 | |
|     // Receive the multicast if the instance wants to receive all packets.
 | |
|     //
 | |
|     if (!IpInstance->ConfigData.UseDefaultAddress && (IpInstance->Interface->Ip == 0)) {
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|     for (Index = 0; Index < IpInstance->GroupCount; Index++) {
 | |
|       if (IpInstance->Groups[Index] == HTONL (Head->Dst)) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return (BOOLEAN)(Index < IpInstance->GroupCount);
 | |
|   }
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enqueue a shared copy of the packet to the IP4 child if the
 | |
|   packet is acceptable to it. Here the data of the packet is
 | |
|   shared, but the net buffer isn't.
 | |
| 
 | |
|   @param[in]  IpInstance             The IP4 child to enqueue the packet to
 | |
|   @param[in]  Head                   The IP header of the received packet
 | |
|   @param[in]  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 resource
 | |
|   @retval EFI_SUCCESS            A shared copy the packet is enqueued to the child.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip4InstanceEnquePacket (
 | |
|   IN IP4_PROTOCOL  *IpInstance,
 | |
|   IN IP4_HEAD      *Head,
 | |
|   IN NET_BUF       *Packet
 | |
|   )
 | |
| {
 | |
|   IP4_CLIP_INFO  *Info;
 | |
|   NET_BUF        *Clone;
 | |
| 
 | |
|   //
 | |
|   // Check whether the packet is acceptable to this instance.
 | |
|   //
 | |
|   if (IpInstance->State != IP4_STATE_CONFIGED) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   if (!Ip4InstanceFrameAcceptable (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       = IP4_GET_CLIP_INFO (Clone);
 | |
|   Info->Life = IP4_US_TO_SEC (IpInstance->ConfigData.ReceiveTimeout);
 | |
| 
 | |
|   InsertTailList (&IpInstance->Received, &Clone->List);
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The signal handle of IP4's recycle event. It is called back
 | |
|   when the upper layer release the packet.
 | |
| 
 | |
|   @param  Event              The IP4's recycle event.
 | |
|   @param  Context            The context of the handle, which is a
 | |
|                              IP4_RXDATA_WRAP
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| Ip4OnRecyclePacket (
 | |
|   IN EFI_EVENT  Event,
 | |
|   IN VOID       *Context
 | |
|   )
 | |
| {
 | |
|   IP4_RXDATA_WRAP  *Wrap;
 | |
| 
 | |
|   Wrap = (IP4_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 IP4_RXDATA_WRAP, which will be
 | |
|   delivered to the upper layer. Each IP4 child that accepts the
 | |
|   packet will get a not-shared copy of the packet which is wrapped
 | |
|   in the IP4_RXDATA_WRAP. The IP4_RXDATA_WRAP->RxData is passed
 | |
|   to the upper layer. Upper layer will signal the recycle event in
 | |
|   it when it is done with the packet.
 | |
| 
 | |
|   @param[in]  IpInstance    The IP4 child to receive the packet.
 | |
|   @param[in]  Packet        The packet to deliver up.
 | |
| 
 | |
|   @retval Wrap              if warp the packet succeed.
 | |
|   @retval NULL              failed to wrap the packet .
 | |
| 
 | |
| **/
 | |
| IP4_RXDATA_WRAP *
 | |
| Ip4WrapRxData (
 | |
|   IN IP4_PROTOCOL  *IpInstance,
 | |
|   IN NET_BUF       *Packet
 | |
|   )
 | |
| {
 | |
|   IP4_RXDATA_WRAP       *Wrap;
 | |
|   EFI_IP4_RECEIVE_DATA  *RxData;
 | |
|   EFI_STATUS            Status;
 | |
|   BOOLEAN               RawData;
 | |
| 
 | |
|   Wrap = AllocatePool (IP4_RXDATA_WRAP_SIZE (Packet->BlockOpNum));
 | |
| 
 | |
|   if (Wrap == NULL) {
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   InitializeListHead (&Wrap->Link);
 | |
| 
 | |
|   Wrap->IpInstance = IpInstance;
 | |
|   Wrap->Packet     = Packet;
 | |
|   RxData           = &Wrap->RxData;
 | |
| 
 | |
|   ZeroMem (RxData, sizeof (EFI_IP4_RECEIVE_DATA));
 | |
| 
 | |
|   Status = gBS->CreateEvent (
 | |
|                   EVT_NOTIFY_SIGNAL,
 | |
|                   TPL_NOTIFY,
 | |
|                   Ip4OnRecyclePacket,
 | |
|                   Wrap,
 | |
|                   &RxData->RecycleSignal
 | |
|                   );
 | |
| 
 | |
|   if (EFI_ERROR (Status)) {
 | |
|     FreePool (Wrap);
 | |
|     return NULL;
 | |
|   }
 | |
| 
 | |
|   ASSERT (Packet->Ip.Ip4 != NULL);
 | |
| 
 | |
|   ASSERT (IpInstance != NULL);
 | |
|   RawData = IpInstance->ConfigData.RawData;
 | |
| 
 | |
|   //
 | |
|   // The application expects a network byte order header.
 | |
|   //
 | |
|   if (!RawData) {
 | |
|     RxData->HeaderLength  = (Packet->Ip.Ip4->HeadLen << 2);
 | |
|     RxData->Header        = (EFI_IP4_HEADER *)Ip4NtohHead (Packet->Ip.Ip4);
 | |
|     RxData->OptionsLength = RxData->HeaderLength - IP4_MIN_HEADLEN;
 | |
|     RxData->Options       = NULL;
 | |
| 
 | |
|     if (RxData->OptionsLength != 0) {
 | |
|       RxData->Options = (VOID *)(RxData->Header + 1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Deliver the received packets to 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
 | |
| Ip4InstanceDeliverPacket (
 | |
|   IN IP4_PROTOCOL  *IpInstance
 | |
|   )
 | |
| {
 | |
|   EFI_IP4_COMPLETION_TOKEN  *Token;
 | |
|   IP4_RXDATA_WRAP           *Wrap;
 | |
|   NET_BUF                   *Packet;
 | |
|   NET_BUF                   *Dup;
 | |
|   UINT8                     *Head;
 | |
|   UINT32                    HeadLen;
 | |
| 
 | |
|   //
 | |
|   // 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 = Ip4WrapRxData (IpInstance, Packet);
 | |
| 
 | |
|       if (Wrap == NULL) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
| 
 | |
|       RemoveEntryList (&Packet->List);
 | |
|     } else {
 | |
|       //
 | |
|       // Create a duplicated packet if this packet is shared
 | |
|       //
 | |
|       if (IpInstance->ConfigData.RawData) {
 | |
|         HeadLen = 0;
 | |
|       } else {
 | |
|         HeadLen = IP4_MAX_HEADLEN;
 | |
|       }
 | |
| 
 | |
|       Dup = NetbufDuplicate (Packet, NULL, HeadLen);
 | |
| 
 | |
|       if (Dup == NULL) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
| 
 | |
|       if (!IpInstance->ConfigData.RawData) {
 | |
|         //
 | |
|         // 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, IP4_MAX_HEADLEN, NET_BUF_HEAD);
 | |
|         ASSERT (Head != NULL);
 | |
| 
 | |
|         Dup->Ip.Ip4 = (IP4_HEAD *)Head;
 | |
| 
 | |
|         CopyMem (Head, Packet->Ip.Ip4, Packet->Ip.Ip4->HeadLen << 2);
 | |
|         NetbufTrim (Dup, IP4_MAX_HEADLEN, TRUE);
 | |
|       }
 | |
| 
 | |
|       Wrap = Ip4WrapRxData (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        = IP4_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 IP4 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]  Option             Point to the IP4 packet header options.
 | |
|   @param[in]  OptionLen          Length of the IP4 packet header options.
 | |
|   @param[in]  IpIf               The interface to enqueue the packet to.
 | |
| 
 | |
|   @return The number of the IP4 children that accepts the packet
 | |
| 
 | |
| **/
 | |
| INTN
 | |
| Ip4InterfaceEnquePacket (
 | |
|   IN IP4_SERVICE    *IpSb,
 | |
|   IN IP4_HEAD       *Head,
 | |
|   IN NET_BUF        *Packet,
 | |
|   IN UINT8          *Option,
 | |
|   IN UINT32         OptionLen,
 | |
|   IN IP4_INTERFACE  *IpIf
 | |
|   )
 | |
| {
 | |
|   IP4_PROTOCOL   *IpInstance;
 | |
|   IP4_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. A packet sent
 | |
|   // to say 192.168.1.1 should NOT be deliver to 10.0.0.1 unless
 | |
|   // promiscuous receiving.
 | |
|   //
 | |
|   LocalType = 0;
 | |
|   Info      = IP4_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|   if ((Info->CastType == IP4_MULTICAST) || (Info->CastType == IP4_LOCAL_BROADCAST)) {
 | |
|     //
 | |
|     // If the CastType is multicast, don't need to filter against
 | |
|     // the group address here, Ip4InstanceFrameAcceptable will do
 | |
|     // that later.
 | |
|     //
 | |
|     LocalType = Info->CastType;
 | |
|   } else {
 | |
|     //
 | |
|     // Check the destination against local IP. If the station
 | |
|     // address is 0.0.0.0, it means receiving all the IP destined
 | |
|     // to local non-zero IP. Otherwise, it is necessary to compare
 | |
|     // the destination to the interface's IP address.
 | |
|     //
 | |
|     if (IpIf->Ip == IP4_ALLZERO_ADDRESS) {
 | |
|       LocalType = IP4_LOCAL_HOST;
 | |
|     } else {
 | |
|       LocalType = Ip4GetNetCast (Head->Dst, IpIf);
 | |
| 
 | |
|       if ((LocalType == 0) && IpIf->PromiscRecv) {
 | |
|         LocalType = IP4_PROMISCUOUS;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (LocalType == 0) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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 = LocalType;
 | |
| 
 | |
|   Enqueued = 0;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
 | |
|     IpInstance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
 | |
|     NET_CHECK_SIGNATURE (IpInstance, IP4_PROTOCOL_SIGNATURE);
 | |
| 
 | |
|     //
 | |
|     // In RawData mode, add IPv4 headers and options back to packet.
 | |
|     //
 | |
|     if ((IpInstance->ConfigData.RawData) && (Option != NULL) && (OptionLen != 0)) {
 | |
|       Ip4PrependHead (Packet, Head, Option, OptionLen);
 | |
|     }
 | |
| 
 | |
|     if (Ip4InstanceEnquePacket (IpInstance, Head, Packet) == EFI_SUCCESS) {
 | |
|       Enqueued++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Info->CastType = SavedType;
 | |
|   return Enqueued;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Deliver the packet for each IP4 child on the interface.
 | |
| 
 | |
|   @param[in]  IpSb               The IP4 service instance that received the packet
 | |
|   @param[in]  IpIf               The IP4 interface to deliver the packet.
 | |
| 
 | |
|   @retval EFI_SUCCESS            It always returns EFI_SUCCESS now
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| Ip4InterfaceDeliverPacket (
 | |
|   IN IP4_SERVICE    *IpSb,
 | |
|   IN IP4_INTERFACE  *IpIf
 | |
|   )
 | |
| {
 | |
|   IP4_PROTOCOL  *Ip4Instance;
 | |
|   LIST_ENTRY    *Entry;
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpIf->IpInstances) {
 | |
|     Ip4Instance = NET_LIST_USER_STRUCT (Entry, IP4_PROTOCOL, AddrLink);
 | |
|     Ip4InstanceDeliverPacket (Ip4Instance);
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Demultiple the packet. the packet delivery is processed in two
 | |
|   passes. The first pass will enqueue a shared copy of the packet
 | |
|   to each IP4 child that accepts the packet. The second pass will
 | |
|   deliver a non-shared copy of the packet to each IP4 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 IP4 service instance that received the packet.
 | |
|   @param[in]  Head               The header of the received packet.
 | |
|   @param[in]  Packet             The data of the received packet.
 | |
|   @param[in]  Option             Point to the IP4 packet header options.
 | |
|   @param[in]  OptionLen          Length of the IP4 packet header options.
 | |
| 
 | |
|   @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
 | |
| Ip4Demultiplex (
 | |
|   IN IP4_SERVICE  *IpSb,
 | |
|   IN IP4_HEAD     *Head,
 | |
|   IN NET_BUF      *Packet,
 | |
|   IN UINT8        *Option,
 | |
|   IN UINT32       OptionLen
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY     *Entry;
 | |
|   IP4_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, IP4_INTERFACE, Link);
 | |
| 
 | |
|     if (IpIf->Configured) {
 | |
|       Enqueued += Ip4InterfaceEnquePacket (
 | |
|                     IpSb,
 | |
|                     Head,
 | |
|                     Packet,
 | |
|                     Option,
 | |
|                     OptionLen,
 | |
|                     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);
 | |
| 
 | |
|   if (Enqueued == 0) {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
 | |
|     IpIf = NET_LIST_USER_STRUCT (Entry, IP4_INTERFACE, Link);
 | |
| 
 | |
|     if (IpIf->Configured) {
 | |
|       Ip4InterfaceDeliverPacket (IpSb, IpIf);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Timeout the fragment and enqueued packets.
 | |
| 
 | |
|   @param[in]  IpSb                   The IP4 service instance to timeout
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| Ip4PacketTimerTicking (
 | |
|   IN IP4_SERVICE  *IpSb
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY          *InstanceEntry;
 | |
|   LIST_ENTRY          *Entry;
 | |
|   LIST_ENTRY          *Next;
 | |
|   IP4_PROTOCOL        *IpInstance;
 | |
|   IP4_ASSEMBLE_ENTRY  *Assemble;
 | |
|   NET_BUF             *Packet;
 | |
|   IP4_CLIP_INFO       *Info;
 | |
|   UINT32              Index;
 | |
| 
 | |
|   //
 | |
|   // First, time out the fragments. The packet's life is counting down
 | |
|   // once the first-arrived fragment was received.
 | |
|   //
 | |
|   for (Index = 0; Index < IP4_ASSEMLE_HASH_SIZE; Index++) {
 | |
|     NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->Assemble.Bucket[Index]) {
 | |
|       Assemble = NET_LIST_USER_STRUCT (Entry, IP4_ASSEMBLE_ENTRY, Link);
 | |
| 
 | |
|       if ((Assemble->Life > 0) && (--Assemble->Life == 0)) {
 | |
|         RemoveEntryList (Entry);
 | |
|         Ip4FreeAssembleEntry (Assemble);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   NET_LIST_FOR_EACH (InstanceEntry, &IpSb->Children) {
 | |
|     IpInstance = NET_LIST_USER_STRUCT (InstanceEntry, IP4_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   = IP4_GET_CLIP_INFO (Packet);
 | |
| 
 | |
|       if ((Info->Life > 0) && (--Info->Life == 0)) {
 | |
|         RemoveEntryList (Entry);
 | |
|         NetbufFree (Packet);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Third: time out the transmitted packets.
 | |
|     //
 | |
|     NetMapIterate (&IpInstance->TxTokens, Ip4SentPacketTicking, NULL);
 | |
|   }
 | |
| }
 |