2.Delete all SAs in the IPsec->Stop(). Signed-off-by: qianouyang Reviewed-by: sfu5 git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12768 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2181 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2181 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  The implementation of IPsec.
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>
 | 
						|
 | 
						|
  This program and the accompanying materials
 | 
						|
  are licensed and made available under the terms and conditions of the BSD License
 | 
						|
  which accompanies this distribution.  The full text of the license may be found at
 | 
						|
  http://opensource.org/licenses/bsd-license.php.
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | 
						|
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "IpSecImpl.h"
 | 
						|
#include "IkeService.h"
 | 
						|
#include "IpSecDebug.h"
 | 
						|
#include "IpSecCryptIo.h"
 | 
						|
#include "IpSecConfigImpl.h"
 | 
						|
 | 
						|
/**
 | 
						|
  Check if the specified Address is the Valid Address Range.
 | 
						|
 | 
						|
  This function checks if the bytes after prefixed length are all Zero in this
 | 
						|
  Address. This Address is supposed to point to a range address. That means it
 | 
						|
  should gives the correct prefixed address and the bytes outside the prefixed are
 | 
						|
  zero.
 | 
						|
 | 
						|
  @param[in]  IpVersion         The IP version.
 | 
						|
  @param[in]  Address           Points to EFI_IP_ADDRESS to be checked.
 | 
						|
  @param[in]  PrefixLength      The PrefixeLength of this address.
 | 
						|
 | 
						|
  @retval     TRUE      The address is a vaild address range.
 | 
						|
  @retval     FALSE     The address is not a vaild address range.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IpSecValidAddressRange (
 | 
						|
  IN UINT8                     IpVersion,
 | 
						|
  IN EFI_IP_ADDRESS            *Address,
 | 
						|
  IN UINT8                     PrefixLength
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8           Div;
 | 
						|
  UINT8           Mod;
 | 
						|
  UINT8           Mask;
 | 
						|
  UINT8           AddrLen;
 | 
						|
  UINT8           *Addr;
 | 
						|
  EFI_IP_ADDRESS  ZeroAddr;
 | 
						|
 | 
						|
  if (PrefixLength == 0) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  AddrLen = (UINT8) ((IpVersion == IP_VERSION_4) ? 32 : 128);
 | 
						|
 | 
						|
  if (AddrLen <= PrefixLength) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  Div   = (UINT8) (PrefixLength / 8);
 | 
						|
  Mod   = (UINT8) (PrefixLength % 8);
 | 
						|
  Addr  = (UINT8 *) Address;
 | 
						|
  ZeroMem (&ZeroAddr, sizeof (EFI_IP_ADDRESS));
 | 
						|
 | 
						|
  //
 | 
						|
  // Check whether the mod part of host scope is zero or not.
 | 
						|
  //
 | 
						|
  if (Mod > 0) {
 | 
						|
    Mask = (UINT8) (0xFF << (8 - Mod));
 | 
						|
 | 
						|
    if ((Addr[Div] | Mask) != Mask) {
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
    Div++;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Check whether the div part of host scope is zero or not.
 | 
						|
  //
 | 
						|
  if (CompareMem (
 | 
						|
        &Addr[Div],
 | 
						|
        &ZeroAddr,
 | 
						|
        sizeof (EFI_IP_ADDRESS) - Div
 | 
						|
        ) != 0) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Extrct the Address Range from a Address.
 | 
						|
 | 
						|
  This function keep the prefix address and zero other part address.
 | 
						|
 | 
						|
  @param[in]  Address           Point to a specified address.
 | 
						|
  @param[in]  PrefixLength      The prefix length.
 | 
						|
  @param[out] Range             Contain the return Address Range.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
IpSecExtractAddressRange (
 | 
						|
  IN EFI_IP_ADDRESS            *Address,
 | 
						|
  IN UINT8                     PrefixLength,
 | 
						|
  OUT EFI_IP_ADDRESS           *Range
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8 Div;
 | 
						|
  UINT8 Mod;
 | 
						|
  UINT8 Mask;
 | 
						|
  UINT8 *Addr;
 | 
						|
 | 
						|
  if (PrefixLength == 0) {
 | 
						|
    return ;
 | 
						|
  }
 | 
						|
 | 
						|
  Div   = (UINT8) (PrefixLength / 8);
 | 
						|
  Mod   = (UINT8) (PrefixLength % 8);
 | 
						|
  Addr  = (UINT8 *) Range;
 | 
						|
 | 
						|
  CopyMem (Range, Address, sizeof (EFI_IP_ADDRESS));
 | 
						|
 | 
						|
  //
 | 
						|
  // Zero the mod part of host scope.
 | 
						|
  //
 | 
						|
  if (Mod > 0) {
 | 
						|
    Mask      = (UINT8) (0xFF << (8 - Mod));
 | 
						|
    Addr[Div] = (UINT8) (Addr[Div] & Mask);
 | 
						|
    Div++;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Zero the div part of host scope.
 | 
						|
  //
 | 
						|
  ZeroMem (&Addr[Div], sizeof (EFI_IP_ADDRESS) - Div);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Checks if the IP Address in the address range of AddressInfos specified.
 | 
						|
 | 
						|
  @param[in]  IpVersion         The IP version.
 | 
						|
  @param[in]  IpAddr            Point to EFI_IP_ADDRESS to be check.
 | 
						|
  @param[in]  AddressInfo       A list of EFI_IP_ADDRESS_INFO that is used to check
 | 
						|
                                the IP Address is matched.
 | 
						|
  @param[in]  AddressCount      The total numbers of the AddressInfo.
 | 
						|
 | 
						|
  @retval   TRUE    If the Specified IP Address is in the range of the AddressInfos specified.
 | 
						|
  @retval   FALSE   If the Specified IP Address is not in the range of the AddressInfos specified.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IpSecMatchIpAddress (
 | 
						|
  IN UINT8                     IpVersion,
 | 
						|
  IN EFI_IP_ADDRESS            *IpAddr,
 | 
						|
  IN EFI_IP_ADDRESS_INFO       *AddressInfo,
 | 
						|
  IN UINT32                    AddressCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_IP_ADDRESS  Range;
 | 
						|
  UINT32          Index;
 | 
						|
  BOOLEAN         IsMatch;
 | 
						|
 | 
						|
  IsMatch = FALSE;
 | 
						|
 | 
						|
  for (Index = 0; Index < AddressCount; Index++) {
 | 
						|
    //
 | 
						|
    // Check whether the target address is in the address range
 | 
						|
    // if it's a valid range of address.
 | 
						|
    //
 | 
						|
    if (IpSecValidAddressRange (
 | 
						|
          IpVersion,
 | 
						|
          &AddressInfo[Index].Address,
 | 
						|
          AddressInfo[Index].PrefixLength
 | 
						|
          )) {
 | 
						|
      //
 | 
						|
      // Get the range of the target address belongs to.
 | 
						|
      //
 | 
						|
      ZeroMem (&Range, sizeof (EFI_IP_ADDRESS));
 | 
						|
      IpSecExtractAddressRange (
 | 
						|
        IpAddr,
 | 
						|
        AddressInfo[Index].PrefixLength,
 | 
						|
        &Range
 | 
						|
        );
 | 
						|
 | 
						|
      if (CompareMem (
 | 
						|
            &Range,
 | 
						|
            &AddressInfo[Index].Address,
 | 
						|
            sizeof (EFI_IP_ADDRESS)
 | 
						|
            ) == 0) {
 | 
						|
        //
 | 
						|
        // The target address is in the address range.
 | 
						|
        //
 | 
						|
        IsMatch = TRUE;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (CompareMem (
 | 
						|
          IpAddr,
 | 
						|
          &AddressInfo[Index].Address,
 | 
						|
          sizeof (EFI_IP_ADDRESS)
 | 
						|
          ) == 0) {
 | 
						|
      //
 | 
						|
      // The target address is exact same as the address.
 | 
						|
      //
 | 
						|
      IsMatch = TRUE;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return IsMatch;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check if the specified Protocol and Prot is supported by the specified SPD Entry.
 | 
						|
 | 
						|
  This function is the subfunction of IPsecLookUpSpdEntry() that is used to
 | 
						|
  check if the sent/received IKE packet has the related SPD entry support.
 | 
						|
 | 
						|
  @param[in]  Protocol          The Protocol to be checked.
 | 
						|
  @param[in]  IpPayload         Point to IP Payload to be check.
 | 
						|
  @param[in]  SpdProtocol       The Protocol supported by SPD.
 | 
						|
  @param[in]  SpdLocalPort      The Local Port in SPD.
 | 
						|
  @param[in]  SpdRemotePort     The Remote Port in SPD.
 | 
						|
  @param[in]  IsOutbound        Flag to indicate the is for IKE Packet sending or recieving.
 | 
						|
 | 
						|
  @retval     TRUE      The Protocol and Port are supported by the SPD Entry.
 | 
						|
  @retval     FALSE     The Protocol and Port are not supported by the SPD Entry.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IpSecMatchNextLayerProtocol (
 | 
						|
  IN UINT8                     Protocol,
 | 
						|
  IN UINT8                     *IpPayload,
 | 
						|
  IN UINT16                    SpdProtocol,
 | 
						|
  IN UINT16                    SpdLocalPort,
 | 
						|
  IN UINT16                    SpdRemotePort,
 | 
						|
  IN BOOLEAN                   IsOutbound
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN IsMatch;
 | 
						|
 | 
						|
  if (SpdProtocol == EFI_IPSEC_ANY_PROTOCOL) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  IsMatch = FALSE;
 | 
						|
 | 
						|
  if (SpdProtocol == Protocol) {
 | 
						|
    switch (Protocol) {
 | 
						|
    case EFI_IP_PROTO_UDP:
 | 
						|
    case EFI_IP_PROTO_TCP:
 | 
						|
      //
 | 
						|
      // For udp and tcp, (0, 0) means no need to check local and remote
 | 
						|
      // port. The payload is passed from upper level, which means it should
 | 
						|
      // be in network order.
 | 
						|
      //
 | 
						|
      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
 | 
						|
      IsMatch = (BOOLEAN) (IsMatch ||
 | 
						|
                           (IsOutbound &&
 | 
						|
                           (BOOLEAN)(
 | 
						|
                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdLocalPort &&
 | 
						|
                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdRemotePort
 | 
						|
                              )
 | 
						|
                            ));
 | 
						|
 | 
						|
      IsMatch = (BOOLEAN) (IsMatch ||
 | 
						|
                           (!IsOutbound &&
 | 
						|
                           (BOOLEAN)(
 | 
						|
                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->DstPort) == SpdLocalPort &&
 | 
						|
                              NTOHS (((EFI_UDP_HEADER *) IpPayload)->SrcPort) == SpdRemotePort
 | 
						|
                              )
 | 
						|
                           ));
 | 
						|
      break;
 | 
						|
 | 
						|
    case EFI_IP_PROTO_ICMP:
 | 
						|
      //
 | 
						|
      // For icmpv4, type code is replaced with local port and remote port,
 | 
						|
      // and (0, 0) means no need to check.
 | 
						|
      //
 | 
						|
      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
 | 
						|
      IsMatch = (BOOLEAN) (IsMatch ||
 | 
						|
                           (BOOLEAN) (((IP4_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&
 | 
						|
                                      ((IP4_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort
 | 
						|
                                      )
 | 
						|
                           );
 | 
						|
      break;
 | 
						|
 | 
						|
    case IP6_ICMP:
 | 
						|
      //
 | 
						|
      // For icmpv6, type code is replaced with local port and remote port,
 | 
						|
      // and (0, 0) means no need to check.
 | 
						|
      //
 | 
						|
      IsMatch = (BOOLEAN) (SpdLocalPort == 0 && SpdRemotePort == 0);
 | 
						|
 | 
						|
      IsMatch = (BOOLEAN) (IsMatch ||
 | 
						|
                           (BOOLEAN) (((IP6_ICMP_HEAD *) IpPayload)->Type == SpdLocalPort &&
 | 
						|
                                      ((IP6_ICMP_HEAD *) IpPayload)->Code == SpdRemotePort
 | 
						|
                                      )
 | 
						|
                          );
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      IsMatch = TRUE;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return IsMatch;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Find the SAD through a specified SPD's SAD list.
 | 
						|
 | 
						|
  @param[in]  SadList           SAD list related to a specified SPD entry.
 | 
						|
  @param[in]  DestAddress       The destination address used to find the SAD entry.
 | 
						|
  @param[in]  IpVersion         The IP version. Ip4 or Ip6.
 | 
						|
 | 
						|
  @return  The pointer to a certain SAD entry.
 | 
						|
 | 
						|
**/
 | 
						|
IPSEC_SAD_ENTRY *
 | 
						|
IpSecLookupSadBySpd (
 | 
						|
  IN LIST_ENTRY                 *SadList,
 | 
						|
  IN EFI_IP_ADDRESS             *DestAddress,
 | 
						|
  IN UINT8                      IpVersion
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY      *Entry;
 | 
						|
  IPSEC_SAD_ENTRY *SadEntry;
 | 
						|
 | 
						|
  NET_LIST_FOR_EACH (Entry, SadList) {
 | 
						|
 | 
						|
    SadEntry = IPSEC_SAD_ENTRY_FROM_SPD (Entry);
 | 
						|
    //
 | 
						|
    // Find the right SAD entry which contains the appointed dest address.
 | 
						|
    //
 | 
						|
    if (IpSecMatchIpAddress (
 | 
						|
          IpVersion,
 | 
						|
          DestAddress,
 | 
						|
          SadEntry->Data->SpdSelector->RemoteAddress,
 | 
						|
          SadEntry->Data->SpdSelector->RemoteAddressCount
 | 
						|
          )){
 | 
						|
      return SadEntry;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Find the SAD through whole SAD list.
 | 
						|
 | 
						|
  @param[in]  Spi               The SPI used to search the SAD entry.
 | 
						|
  @param[in]  DestAddress       The destination used to search the SAD entry.
 | 
						|
  @param[in]  IpVersion         The IP version. Ip4 or Ip6.
 | 
						|
 | 
						|
  @return  the pointer to a certain SAD entry.
 | 
						|
 | 
						|
**/
 | 
						|
IPSEC_SAD_ENTRY *
 | 
						|
IpSecLookupSadBySpi (
 | 
						|
  IN UINT32                   Spi,
 | 
						|
  IN EFI_IP_ADDRESS           *DestAddress,
 | 
						|
  IN UINT8                    IpVersion
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY      *Entry;
 | 
						|
  LIST_ENTRY      *SadList;
 | 
						|
  IPSEC_SAD_ENTRY *SadEntry;
 | 
						|
 | 
						|
  SadList = &mConfigData[IPsecConfigDataTypeSad];
 | 
						|
 | 
						|
  NET_LIST_FOR_EACH (Entry, SadList) {
 | 
						|
 | 
						|
    SadEntry = IPSEC_SAD_ENTRY_FROM_LIST (Entry);
 | 
						|
 | 
						|
    //
 | 
						|
    // Find the right SAD entry which contain the appointed spi and dest addr.
 | 
						|
    //
 | 
						|
    if (SadEntry->Id->Spi == Spi) {
 | 
						|
      if (SadEntry->Data->Mode == EfiIPsecTunnel) {
 | 
						|
        if (CompareMem (
 | 
						|
              &DestAddress,
 | 
						|
              &SadEntry->Data->TunnelDestAddress,
 | 
						|
              sizeof (EFI_IP_ADDRESS)
 | 
						|
              )) {
 | 
						|
          return SadEntry;
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        if (SadEntry->Data->SpdSelector != NULL &&
 | 
						|
            IpSecMatchIpAddress (
 | 
						|
              IpVersion,
 | 
						|
              DestAddress,
 | 
						|
              SadEntry->Data->SpdSelector->RemoteAddress,
 | 
						|
              SadEntry->Data->SpdSelector->RemoteAddressCount
 | 
						|
              )
 | 
						|
            ) {
 | 
						|
          return SadEntry;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Look up if there is existing SAD entry for specified IP packet sending.
 | 
						|
 | 
						|
  This function is called by the IPsecProcess when there is some IP packet needed to
 | 
						|
  send out. This function checks if there is an existing SAD entry that can be serviced
 | 
						|
  to this IP packet sending. If no existing SAD entry could be used, this
 | 
						|
  function will invoke an IPsec Key Exchange Negotiation.
 | 
						|
 | 
						|
  @param[in]  Private           Points to private data.
 | 
						|
  @param[in]  NicHandle         Points to a NIC handle.
 | 
						|
  @param[in]  IpVersion         The version of IP.
 | 
						|
  @param[in]  IpHead            The IP Header of packet to be sent out.
 | 
						|
  @param[in]  IpPayload         The IP Payload to be sent out.
 | 
						|
  @param[in]  OldLastHead       The Last protocol of the IP packet.
 | 
						|
  @param[in]  SpdEntry          Points to a related SPD entry.
 | 
						|
  @param[out] SadEntry          Contains the Point of a related SAD entry.
 | 
						|
 | 
						|
  @retval EFI_DEVICE_ERROR  One of following conditions is TRUE:
 | 
						|
                            - If don't find related UDP service.
 | 
						|
                            - Sequence Number is used up.
 | 
						|
                            - Extension Sequence Number is used up.
 | 
						|
  @retval EFI_NOT_READY     No existing SAD entry could be used.
 | 
						|
  @retval EFI_SUCCESS       Find the related SAD entry.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IpSecLookupSadEntry (
 | 
						|
  IN IPSEC_PRIVATE_DATA      *Private,
 | 
						|
  IN EFI_HANDLE              NicHandle,
 | 
						|
  IN UINT8                   IpVersion,
 | 
						|
  IN VOID                    *IpHead,
 | 
						|
  IN UINT8                   *IpPayload,
 | 
						|
  IN UINT8                   OldLastHead,
 | 
						|
  IN IPSEC_SPD_ENTRY         *SpdEntry,
 | 
						|
  OUT IPSEC_SAD_ENTRY        **SadEntry
 | 
						|
  )
 | 
						|
{
 | 
						|
  IKE_UDP_SERVICE *UdpService;
 | 
						|
  IPSEC_SAD_ENTRY *Entry;
 | 
						|
  IPSEC_SAD_DATA  *Data;
 | 
						|
  EFI_IP_ADDRESS  DestIp;
 | 
						|
  UINT32          SeqNum32;
 | 
						|
 | 
						|
  *SadEntry   = NULL;
 | 
						|
  UdpService  = IkeLookupUdp (Private, NicHandle, IpVersion);
 | 
						|
 | 
						|
  if (UdpService == NULL) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Parse the destination address from ip header.
 | 
						|
  //
 | 
						|
  ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));
 | 
						|
  if (IpVersion == IP_VERSION_4) {
 | 
						|
    CopyMem (
 | 
						|
      &DestIp,
 | 
						|
      &((IP4_HEAD *) IpHead)->Dst,
 | 
						|
      sizeof (IP4_ADDR)
 | 
						|
      );
 | 
						|
  } else {
 | 
						|
    CopyMem (
 | 
						|
      &DestIp,
 | 
						|
      &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
 | 
						|
      sizeof (EFI_IP_ADDRESS)
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Find the SAD entry in the spd.sas list according to the dest address.
 | 
						|
  //
 | 
						|
  Entry = IpSecLookupSadBySpd (&SpdEntry->Data->Sas, &DestIp, IpVersion);
 | 
						|
 | 
						|
  if (Entry == NULL) {
 | 
						|
    if (OldLastHead != IP6_ICMP ||
 | 
						|
        (OldLastHead == IP6_ICMP && *IpPayload == ICMP_V6_ECHO_REQUEST)
 | 
						|
        ) {
 | 
						|
      //
 | 
						|
      // Start ike negotiation process except the request packet of ping.
 | 
						|
      //
 | 
						|
      if (SpdEntry->Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
 | 
						|
        IkeNegotiate (
 | 
						|
          UdpService,
 | 
						|
          SpdEntry,
 | 
						|
          &SpdEntry->Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress
 | 
						|
          );
 | 
						|
      } else {
 | 
						|
        IkeNegotiate (
 | 
						|
          UdpService,
 | 
						|
          SpdEntry,
 | 
						|
          &DestIp
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    return EFI_NOT_READY;
 | 
						|
  }
 | 
						|
 | 
						|
  Data = Entry->Data;
 | 
						|
 | 
						|
  if (!Data->ManualSet) {
 | 
						|
    if (Data->ESNEnabled) {
 | 
						|
      //
 | 
						|
      // Validate the 64bit sn number if 64bit sn enabled.
 | 
						|
      //
 | 
						|
      if ((UINT64) (Data->SequenceNumber + 1) == 0) {
 | 
						|
        //
 | 
						|
        // TODO: Re-negotiate SA
 | 
						|
        //
 | 
						|
        return EFI_DEVICE_ERROR;
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      //
 | 
						|
      // Validate the 32bit sn number if 64bit sn disabled.
 | 
						|
      //
 | 
						|
      SeqNum32 = (UINT32) Data->SequenceNumber;
 | 
						|
      if ((UINT32) (SeqNum32 + 1) == 0) {
 | 
						|
        //
 | 
						|
        // TODO: Re-negotiate SA
 | 
						|
        //
 | 
						|
        return EFI_DEVICE_ERROR;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  *SadEntry = Entry;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Find a PAD entry according to a remote IP address.
 | 
						|
 | 
						|
  @param[in]  IpVersion         The version of IP.
 | 
						|
  @param[in]  IpAddr            Points to remote IP address.
 | 
						|
 | 
						|
  @return the pointer of related PAD entry.
 | 
						|
 | 
						|
**/
 | 
						|
IPSEC_PAD_ENTRY *
 | 
						|
IpSecLookupPadEntry (
 | 
						|
  IN UINT8                   IpVersion,
 | 
						|
  IN EFI_IP_ADDRESS          *IpAddr
 | 
						|
  )
 | 
						|
{
 | 
						|
  LIST_ENTRY          *PadList;
 | 
						|
  LIST_ENTRY          *Entry;
 | 
						|
  EFI_IP_ADDRESS_INFO *IpAddrInfo;
 | 
						|
  IPSEC_PAD_ENTRY     *PadEntry;
 | 
						|
 | 
						|
  PadList = &mConfigData[IPsecConfigDataTypePad];
 | 
						|
 | 
						|
  for (Entry = PadList->ForwardLink; Entry != PadList; Entry = Entry->ForwardLink) {
 | 
						|
 | 
						|
    PadEntry    = IPSEC_PAD_ENTRY_FROM_LIST (Entry);
 | 
						|
    IpAddrInfo  = &PadEntry->Id->Id.IpAddress;
 | 
						|
    //
 | 
						|
    // Find the right pad entry which contain the appointed dest addr.
 | 
						|
    //
 | 
						|
    if (IpSecMatchIpAddress (IpVersion, IpAddr, IpAddrInfo, 1)) {
 | 
						|
      return PadEntry;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Check if the specified IP packet can be serviced by this SPD entry.
 | 
						|
 | 
						|
  @param[in]  SpdEntry          Point to SPD entry.
 | 
						|
  @param[in]  IpVersion         Version of IP.
 | 
						|
  @param[in]  IpHead            Point to IP header.
 | 
						|
  @param[in]  IpPayload         Point to IP payload.
 | 
						|
  @param[in]  Protocol          The Last protocol of IP packet.
 | 
						|
  @param[in]  IsOutbound        Traffic direction.
 | 
						|
  @param[out] Action            The support action of SPD entry.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       Find the related SPD.
 | 
						|
  @retval EFI_NOT_FOUND     Not find the related SPD entry;
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IpSecLookupSpdEntry (
 | 
						|
  IN     IPSEC_SPD_ENTRY         *SpdEntry,
 | 
						|
  IN     UINT8                   IpVersion,
 | 
						|
  IN     VOID                    *IpHead,
 | 
						|
  IN     UINT8                   *IpPayload,
 | 
						|
  IN     UINT8                   Protocol,
 | 
						|
  IN     BOOLEAN                 IsOutbound,
 | 
						|
     OUT EFI_IPSEC_ACTION        *Action
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_IPSEC_SPD_SELECTOR  *SpdSel;
 | 
						|
  IP4_HEAD                *Ip4;
 | 
						|
  EFI_IP6_HEADER          *Ip6;
 | 
						|
  EFI_IP_ADDRESS          SrcAddr;
 | 
						|
  EFI_IP_ADDRESS          DstAddr;
 | 
						|
  BOOLEAN                 SpdMatch;
 | 
						|
 | 
						|
  ASSERT (SpdEntry != NULL);
 | 
						|
  SpdSel  = SpdEntry->Selector;
 | 
						|
  Ip4     = (IP4_HEAD *) IpHead;
 | 
						|
  Ip6     = (EFI_IP6_HEADER *) IpHead;
 | 
						|
 | 
						|
  ZeroMem (&SrcAddr, sizeof (EFI_IP_ADDRESS));
 | 
						|
  ZeroMem (&DstAddr, sizeof (EFI_IP_ADDRESS));
 | 
						|
 | 
						|
  //
 | 
						|
  // Parse the source and destination address from ip header.
 | 
						|
  //
 | 
						|
  if (IpVersion == IP_VERSION_4) {
 | 
						|
    CopyMem (&SrcAddr, &Ip4->Src, sizeof (IP4_ADDR));
 | 
						|
    CopyMem (&DstAddr, &Ip4->Dst, sizeof (IP4_ADDR));
 | 
						|
  } else {
 | 
						|
    CopyMem (&SrcAddr, &Ip6->SourceAddress, sizeof (EFI_IPv6_ADDRESS));
 | 
						|
    CopyMem (&DstAddr, &Ip6->DestinationAddress, sizeof (EFI_IPv6_ADDRESS));
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Check the local and remote addresses for outbound traffic
 | 
						|
  //
 | 
						|
  SpdMatch = (BOOLEAN)(IsOutbound &&
 | 
						|
                       IpSecMatchIpAddress (
 | 
						|
                         IpVersion,
 | 
						|
                         &SrcAddr,
 | 
						|
                         SpdSel->LocalAddress,
 | 
						|
                         SpdSel->LocalAddressCount
 | 
						|
                         ) &&
 | 
						|
                       IpSecMatchIpAddress (
 | 
						|
                         IpVersion,
 | 
						|
                         &DstAddr,
 | 
						|
                         SpdSel->RemoteAddress,
 | 
						|
                         SpdSel->RemoteAddressCount
 | 
						|
                         )
 | 
						|
                       );
 | 
						|
 | 
						|
  //
 | 
						|
  // Check the local and remote addresses for inbound traffic
 | 
						|
  //
 | 
						|
  SpdMatch = (BOOLEAN) (SpdMatch ||
 | 
						|
                        (!IsOutbound &&
 | 
						|
                        IpSecMatchIpAddress (
 | 
						|
                          IpVersion,
 | 
						|
                          &DstAddr,
 | 
						|
                          SpdSel->LocalAddress,
 | 
						|
                          SpdSel->LocalAddressCount
 | 
						|
                          ) &&
 | 
						|
                        IpSecMatchIpAddress (
 | 
						|
                          IpVersion,
 | 
						|
                          &SrcAddr,
 | 
						|
                          SpdSel->RemoteAddress,
 | 
						|
                          SpdSel->RemoteAddressCount
 | 
						|
                          )
 | 
						|
                        ));
 | 
						|
 | 
						|
  //
 | 
						|
  // Check the next layer protocol and local and remote ports.
 | 
						|
  //
 | 
						|
  SpdMatch = (BOOLEAN) (SpdMatch &&
 | 
						|
                        IpSecMatchNextLayerProtocol (
 | 
						|
                          Protocol,
 | 
						|
                          IpPayload,
 | 
						|
                          SpdSel->NextLayerProtocol,
 | 
						|
                          SpdSel->LocalPort,
 | 
						|
                          SpdSel->RemotePort,
 | 
						|
                          IsOutbound
 | 
						|
                          )
 | 
						|
                        );
 | 
						|
 | 
						|
  if (SpdMatch) {
 | 
						|
    //
 | 
						|
    // Find the right SPD entry if match the 5 key elements.
 | 
						|
    //
 | 
						|
    *Action = SpdEntry->Data->Action;
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_NOT_FOUND;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The call back function of NetbufFromExt.
 | 
						|
 | 
						|
  @param[in]  Arg            The argument passed from the caller.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
IpSecOnRecyclePacket (
 | 
						|
  IN VOID                            *Arg
 | 
						|
  )
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This is a Notification function. It is called when the related IP6_TXTOKEN_WRAP
 | 
						|
  is released.
 | 
						|
 | 
						|
  @param[in]  Event              The related event.
 | 
						|
  @param[in]  Context            The data passed by the caller.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
IpSecRecycleCallback (
 | 
						|
  IN EFI_EVENT                       Event,
 | 
						|
  IN VOID                            *Context
 | 
						|
  )
 | 
						|
{
 | 
						|
  IPSEC_RECYCLE_CONTEXT *RecycleContext;
 | 
						|
 | 
						|
  RecycleContext = (IPSEC_RECYCLE_CONTEXT *) Context;
 | 
						|
 | 
						|
  if (RecycleContext->FragmentTable != NULL) {
 | 
						|
    FreePool (RecycleContext->FragmentTable);
 | 
						|
  }
 | 
						|
 | 
						|
  if (RecycleContext->PayloadBuffer != NULL) {
 | 
						|
    FreePool (RecycleContext->PayloadBuffer);
 | 
						|
  }
 | 
						|
 | 
						|
  FreePool (RecycleContext);
 | 
						|
  gBS->CloseEvent (Event);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Calculate the extension hader of IP. The return length only doesn't contain
 | 
						|
  the fixed IP header length.
 | 
						|
 | 
						|
  @param[in]  IpHead             Points to an IP head to be calculated.
 | 
						|
  @param[in]  LastHead           Points to the last header of the IP header.
 | 
						|
 | 
						|
  @return The length of the extension header.
 | 
						|
 | 
						|
**/
 | 
						|
UINT16
 | 
						|
IpSecGetPlainExtHeadSize (
 | 
						|
  IN VOID                             *IpHead,
 | 
						|
  IN UINT8                            *LastHead
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT16  Size;
 | 
						|
 | 
						|
  Size = (UINT16) (LastHead - (UINT8 *) IpHead);
 | 
						|
 | 
						|
  if (Size > sizeof (EFI_IP6_HEADER)) {
 | 
						|
    //
 | 
						|
    // * (LastHead+1) point the last header's length but not include the first
 | 
						|
    // 8 octers, so this formluation add 8 at the end.
 | 
						|
    //
 | 
						|
    Size = (UINT16) (Size - sizeof (EFI_IP6_HEADER) + *(LastHead + 1) + 8);
 | 
						|
  } else {
 | 
						|
    Size = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return Size;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Verify if the Authentication payload is correct.
 | 
						|
 | 
						|
  @param[in]  EspBuffer          Points to the ESP wrapped buffer.
 | 
						|
  @param[in]  EspSize            The size of the ESP wrapped buffer.
 | 
						|
  @param[in]  SadEntry           The related SAD entry to store the authentication
 | 
						|
                                 algorithm key.
 | 
						|
  @param[in]  IcvSize            The length of ICV.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS        The authentication data is correct.
 | 
						|
  @retval EFI_ACCESS_DENIED  The authentication data is not correct.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IpSecEspAuthVerifyPayload (
 | 
						|
  IN UINT8                           *EspBuffer,
 | 
						|
  IN UINTN                           EspSize,
 | 
						|
  IN IPSEC_SAD_ENTRY                 *SadEntry,
 | 
						|
  IN UINTN                           IcvSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS           Status;
 | 
						|
  UINTN                AuthSize;
 | 
						|
  UINT8                IcvBuffer[12];
 | 
						|
  HASH_DATA_FRAGMENT   HashFragment[1];
 | 
						|
 | 
						|
  //
 | 
						|
  // Calculate the size of authentication payload.
 | 
						|
  //
 | 
						|
  AuthSize  = EspSize - IcvSize;
 | 
						|
 | 
						|
  //
 | 
						|
  // Calculate the icv buffer and size of the payload.
 | 
						|
  //
 | 
						|
  HashFragment[0].Data     = EspBuffer;
 | 
						|
  HashFragment[0].DataSize = AuthSize;
 | 
						|
 | 
						|
  Status = IpSecCryptoIoHmac (
 | 
						|
             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,
 | 
						|
             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
 | 
						|
             SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,
 | 
						|
             HashFragment,
 | 
						|
             1,
 | 
						|
             IcvBuffer,
 | 
						|
             IcvSize
 | 
						|
             );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Compare the calculated icv and the appended original icv.
 | 
						|
  //
 | 
						|
  if (CompareMem (EspBuffer + AuthSize, IcvBuffer, IcvSize) == 0) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_ERROR, "Error auth verify payload\n"));
 | 
						|
  return EFI_ACCESS_DENIED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Search the related SAD entry by the input .
 | 
						|
 | 
						|
  @param[in]  IpHead       The pointer to IP header.
 | 
						|
  @param[in]  IpVersion    The version of IP (IP4 or IP6).
 | 
						|
  @param[in]  Spi          The SPI used to search the related SAD entry.
 | 
						|
 | 
						|
 | 
						|
  @retval     NULL             Not find the related SAD entry.
 | 
						|
  @retval     IPSEC_SAD_ENTRY  Return the related SAD entry.
 | 
						|
 | 
						|
**/
 | 
						|
IPSEC_SAD_ENTRY *
 | 
						|
IpSecFoundSadFromInboundPacket (
 | 
						|
   UINT8   *IpHead,
 | 
						|
   UINT8   IpVersion,
 | 
						|
   UINT32  Spi
 | 
						|
   )
 | 
						|
{
 | 
						|
  EFI_IP_ADDRESS   DestIp;
 | 
						|
 | 
						|
  //
 | 
						|
  // Parse destination address from ip header.
 | 
						|
  //
 | 
						|
  ZeroMem (&DestIp, sizeof (EFI_IP_ADDRESS));
 | 
						|
  if (IpVersion == IP_VERSION_4) {
 | 
						|
    CopyMem (
 | 
						|
      &DestIp,
 | 
						|
      &((IP4_HEAD *) IpHead)->Dst,
 | 
						|
      sizeof (IP4_ADDR)
 | 
						|
      );
 | 
						|
  } else {
 | 
						|
    CopyMem (
 | 
						|
      &DestIp,
 | 
						|
      &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
 | 
						|
      sizeof (EFI_IPv6_ADDRESS)
 | 
						|
      );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Lookup SAD entry according to the spi and dest address.
 | 
						|
  //
 | 
						|
  return IpSecLookupSadBySpi (Spi, &DestIp, IpVersion);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Validate the IP6 extension header format for both the packets we received
 | 
						|
  and that we will transmit.
 | 
						|
 | 
						|
  @param[in]  NextHeader    The next header field in IPv6 basic header.
 | 
						|
  @param[in]  ExtHdrs       The first bye of the option.
 | 
						|
  @param[in]  ExtHdrsLen    The length of the whole option.
 | 
						|
  @param[out] LastHeader    The pointer of NextHeader of the last extension
 | 
						|
                            header processed by IP6.
 | 
						|
  @param[out] RealExtsLen   The length of extension headers processed by IP6 layer.
 | 
						|
                            This is an optional parameter that may be NULL.
 | 
						|
 | 
						|
  @retval     TRUE          The option is properly formated.
 | 
						|
  @retval     FALSE         The option is malformated.
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
IpSecIsIp6ExtsValid (
 | 
						|
  IN UINT8                  *NextHeader,
 | 
						|
  IN UINT8                  *ExtHdrs,
 | 
						|
  IN UINT32                 ExtHdrsLen,
 | 
						|
  OUT UINT8                 **LastHeader,
 | 
						|
  OUT UINT32                *RealExtsLen    OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32                     Pointer;
 | 
						|
  UINT8                      *Option;
 | 
						|
  UINT8                      OptionLen;
 | 
						|
  BOOLEAN                    Flag;
 | 
						|
  UINT8                      CountD;
 | 
						|
  UINT8                      CountF;
 | 
						|
  UINT8                      CountA;
 | 
						|
 | 
						|
  if (RealExtsLen != NULL) {
 | 
						|
    *RealExtsLen = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  *LastHeader = NextHeader;
 | 
						|
 | 
						|
  if (ExtHdrs == NULL && ExtHdrsLen == 0) {
 | 
						|
    return TRUE;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((ExtHdrs == NULL && ExtHdrsLen != 0) || (ExtHdrs != NULL && ExtHdrsLen == 0)) {
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  Pointer = 0;
 | 
						|
  Flag    = FALSE;
 | 
						|
  CountD  = 0;
 | 
						|
  CountF  = 0;
 | 
						|
  CountA  = 0;
 | 
						|
 | 
						|
  while (Pointer <= ExtHdrsLen) {
 | 
						|
 | 
						|
    switch (*NextHeader) {
 | 
						|
    case IP6_HOP_BY_HOP:
 | 
						|
      if (Pointer != 0) {
 | 
						|
        return FALSE;
 | 
						|
      }
 | 
						|
 | 
						|
      Flag = TRUE;
 | 
						|
 | 
						|
    //
 | 
						|
    // Fall through
 | 
						|
    //
 | 
						|
    case IP6_DESTINATION:
 | 
						|
      if (*NextHeader == IP6_DESTINATION) {
 | 
						|
        CountD++;
 | 
						|
      }
 | 
						|
 | 
						|
      if (CountD > 2) {
 | 
						|
        return FALSE;
 | 
						|
      }
 | 
						|
 | 
						|
      NextHeader = ExtHdrs + Pointer;
 | 
						|
 | 
						|
      Pointer++;
 | 
						|
      Option     = ExtHdrs + Pointer;
 | 
						|
      OptionLen  = (UINT8) ((*Option + 1) * 8 - 2);
 | 
						|
      Option++;
 | 
						|
      Pointer++;
 | 
						|
 | 
						|
      Pointer = Pointer + OptionLen;
 | 
						|
      break;
 | 
						|
 | 
						|
    case IP6_FRAGMENT:
 | 
						|
      if (++CountF > 1) {
 | 
						|
        return FALSE;
 | 
						|
      }
 | 
						|
      //
 | 
						|
      // RFC2402, AH header should after fragment header.
 | 
						|
      //
 | 
						|
      if (CountA > 1) {
 | 
						|
        return FALSE;
 | 
						|
      }
 | 
						|
 | 
						|
      NextHeader = ExtHdrs + Pointer;
 | 
						|
      Pointer    = Pointer + 8;
 | 
						|
      break;
 | 
						|
 | 
						|
    case IP6_AH:
 | 
						|
      if (++CountA > 1) {
 | 
						|
        return FALSE;
 | 
						|
      }
 | 
						|
 | 
						|
      Option     = ExtHdrs + Pointer;
 | 
						|
      NextHeader = Option;
 | 
						|
      Option++;
 | 
						|
      //
 | 
						|
      // RFC2402, Payload length is specified in 32-bit words, minus "2".
 | 
						|
      //
 | 
						|
      OptionLen  = (UINT8) ((*Option + 2) * 4);
 | 
						|
      Pointer    = Pointer + OptionLen;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      *LastHeader = NextHeader;
 | 
						|
       if (RealExtsLen != NULL) {
 | 
						|
         *RealExtsLen = Pointer;
 | 
						|
       }
 | 
						|
 | 
						|
       return TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  *LastHeader = NextHeader;
 | 
						|
 | 
						|
  if (RealExtsLen != NULL) {
 | 
						|
    *RealExtsLen = Pointer;
 | 
						|
  }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The actual entry to process the tunnel header and inner header for tunnel mode
 | 
						|
  outbound traffic.
 | 
						|
 | 
						|
  This function is the subfunction of IpSecEspInboundPacket(). It change the destination
 | 
						|
  Ip address to the station address and recalculate the uplayyer's checksum.
 | 
						|
 | 
						|
 | 
						|
  @param[in, out] IpHead             Points to the IP header containing the ESP header
 | 
						|
                                     to be trimed on input, and without ESP header
 | 
						|
                                     on return.
 | 
						|
  @param[in]      IpPayload          The decrypted Ip payload. It start from the inner
 | 
						|
                                     header.
 | 
						|
  @param[in]      IpVersion          The version of IP.
 | 
						|
  @param[in]      SadData            Pointer of the relevant SAD.
 | 
						|
  @param[in, out] LastHead           The Last Header in IP header on return.
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
IpSecTunnelInboundPacket (
 | 
						|
  IN OUT UINT8           *IpHead,
 | 
						|
  IN     UINT8           *IpPayload,
 | 
						|
  IN     UINT8           IpVersion,
 | 
						|
  IN     IPSEC_SAD_DATA  *SadData,
 | 
						|
  IN OUT UINT8           *LastHead
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_UDP_HEADER   *UdpHeader;
 | 
						|
  TCP_HEAD         *TcpHeader;
 | 
						|
  UINT16            *Checksum;
 | 
						|
  UINT16           PseudoChecksum;
 | 
						|
  UINT16           PacketChecksum;
 | 
						|
  UINT32           OptionLen;
 | 
						|
  IP6_ICMP_HEAD    *Icmp6Head;
 | 
						|
 | 
						|
  Checksum = NULL;
 | 
						|
 | 
						|
  if (IpVersion == IP_VERSION_4) {
 | 
						|
    //
 | 
						|
    // Zero OutIP header use this to indicate the input packet is under
 | 
						|
    // IPsec Tunnel protected.
 | 
						|
    //
 | 
						|
    ZeroMem (
 | 
						|
      (IP4_HEAD *)IpHead,
 | 
						|
      sizeof (IP4_HEAD)
 | 
						|
      );
 | 
						|
    CopyMem (
 | 
						|
      &((IP4_HEAD *)IpPayload)->Dst,
 | 
						|
      &SadData->TunnelDestAddress.v4,
 | 
						|
      sizeof (EFI_IPv4_ADDRESS)
 | 
						|
      );
 | 
						|
 | 
						|
    //
 | 
						|
    // Recalculate IpHeader Checksum
 | 
						|
    //
 | 
						|
    if (((IP4_HEAD *)(IpPayload))->Checksum != 0 ) {
 | 
						|
      ((IP4_HEAD *)(IpPayload))->Checksum = 0;
 | 
						|
      ((IP4_HEAD *)(IpPayload))->Checksum = (UINT16) (~NetblockChecksum (
 | 
						|
                                                        (UINT8 *)IpPayload,
 | 
						|
                                                        ((IP4_HEAD *)IpPayload)->HeadLen << 2
 | 
						|
                                                        ));
 | 
						|
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Recalcualte PseudoChecksum
 | 
						|
    //
 | 
						|
    switch (((IP4_HEAD *)IpPayload)->Protocol) {
 | 
						|
    case EFI_IP_PROTO_UDP :
 | 
						|
      UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));
 | 
						|
      Checksum  = & UdpHeader->Checksum;
 | 
						|
      *Checksum = 0;
 | 
						|
      break;
 | 
						|
 | 
						|
    case EFI_IP_PROTO_TCP:
 | 
						|
      TcpHeader = (TCP_HEAD *) ((UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2));
 | 
						|
      Checksum  = &TcpHeader->Checksum;
 | 
						|
      *Checksum = 0;
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
      }
 | 
						|
    PacketChecksum = NetblockChecksum (
 | 
						|
                       (UINT8 *)IpPayload + (((IP4_HEAD *)IpPayload)->HeadLen << 2),
 | 
						|
                       NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)
 | 
						|
                       );
 | 
						|
    PseudoChecksum = NetPseudoHeadChecksum (
 | 
						|
                       ((IP4_HEAD *)IpPayload)->Src,
 | 
						|
                       ((IP4_HEAD *)IpPayload)->Dst,
 | 
						|
                       ((IP4_HEAD *)IpPayload)->Protocol,
 | 
						|
                       0
 | 
						|
                       );
 | 
						|
 | 
						|
      if (Checksum != NULL) {
 | 
						|
        *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);
 | 
						|
        *Checksum = (UINT16) ~(NetAddChecksum (*Checksum, HTONS((UINT16)(NTOHS (((IP4_HEAD *)IpPayload)->TotalLen) - (((IP4_HEAD *)IpPayload)->HeadLen << 2)))));
 | 
						|
      }
 | 
						|
    }else {
 | 
						|
      //
 | 
						|
      //  Zero OutIP header use this to indicate the input packet is under
 | 
						|
      //  IPsec Tunnel protected.
 | 
						|
      //
 | 
						|
      ZeroMem (
 | 
						|
        IpHead,
 | 
						|
        sizeof (EFI_IP6_HEADER)
 | 
						|
        );
 | 
						|
      CopyMem (
 | 
						|
        &((EFI_IP6_HEADER*)IpPayload)->DestinationAddress,
 | 
						|
        &SadData->TunnelDestAddress.v6,
 | 
						|
        sizeof (EFI_IPv6_ADDRESS)
 | 
						|
        );
 | 
						|
 | 
						|
      //
 | 
						|
      // Get the Extension Header and Header length.
 | 
						|
      //
 | 
						|
      IpSecIsIp6ExtsValid (
 | 
						|
        &((EFI_IP6_HEADER *)IpPayload)->NextHeader,
 | 
						|
        IpPayload + sizeof (EFI_IP6_HEADER),
 | 
						|
        ((EFI_IP6_HEADER *)IpPayload)->PayloadLength,
 | 
						|
        &LastHead,
 | 
						|
        &OptionLen
 | 
						|
        );
 | 
						|
 | 
						|
      //
 | 
						|
      // Recalcualte PseudoChecksum
 | 
						|
      //
 | 
						|
      switch (*LastHead) {
 | 
						|
      case EFI_IP_PROTO_UDP:
 | 
						|
        UdpHeader = (EFI_UDP_HEADER *)((UINT8 *)IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);
 | 
						|
        Checksum  = &UdpHeader->Checksum;
 | 
						|
        *Checksum = 0;
 | 
						|
        break;
 | 
						|
 | 
						|
      case EFI_IP_PROTO_TCP:
 | 
						|
        TcpHeader = (TCP_HEAD *)(IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);
 | 
						|
        Checksum  = &TcpHeader->Checksum;
 | 
						|
        *Checksum = 0;
 | 
						|
        break;
 | 
						|
 | 
						|
      case IP6_ICMP:
 | 
						|
        Icmp6Head  = (IP6_ICMP_HEAD *) (IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen);
 | 
						|
        Checksum   = &Icmp6Head->Checksum;
 | 
						|
        *Checksum  = 0;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      PacketChecksum = NetblockChecksum (
 | 
						|
                         IpPayload + sizeof (EFI_IP6_HEADER) + OptionLen,
 | 
						|
                         NTOHS(((EFI_IP6_HEADER *)IpPayload)->PayloadLength) - OptionLen
 | 
						|
                         );
 | 
						|
      PseudoChecksum = NetIp6PseudoHeadChecksum (
 | 
						|
                         &((EFI_IP6_HEADER *)IpPayload)->SourceAddress,
 | 
						|
                         &((EFI_IP6_HEADER *)IpPayload)->DestinationAddress,
 | 
						|
                         *LastHead,
 | 
						|
                         0
 | 
						|
                         );
 | 
						|
 | 
						|
    if (Checksum != NULL) {
 | 
						|
      *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);
 | 
						|
      *Checksum = (UINT16) ~(NetAddChecksum (
 | 
						|
                               *Checksum,
 | 
						|
                               HTONS ((UINT16)((NTOHS (((EFI_IP6_HEADER *)(IpPayload))->PayloadLength)) - OptionLen))
 | 
						|
                               ));
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The actual entry to create inner header for tunnel mode inbound traffic.
 | 
						|
 | 
						|
  This function is the subfunction of IpSecEspOutboundPacket(). It create
 | 
						|
  the sending packet by encrypting its payload and inserting ESP header in the orginal
 | 
						|
  IP header, then return the IpHeader and IPsec protected Fragmentable.
 | 
						|
 | 
						|
  @param[in, out] IpHead             Points to IP header containing the orginal IP header
 | 
						|
                                     to be processed on input, and inserted ESP header
 | 
						|
                                     on return.
 | 
						|
  @param[in]      IpVersion          The version of IP.
 | 
						|
  @param[in]      SadData            The related SAD data.
 | 
						|
  @param[in, out] LastHead           The Last Header in IP header.
 | 
						|
  @param[in]      OptionsBuffer      Pointer to the options buffer.
 | 
						|
  @param[in]      OptionsLength      Length of the options buffer.
 | 
						|
  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
 | 
						|
                                     IPsec on input, and with IPsec protected
 | 
						|
                                     on return.
 | 
						|
  @param[in]      FragmentCount      The number of fragments.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              The operation was successful.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.
 | 
						|
 | 
						|
**/
 | 
						|
UINT8 *
 | 
						|
IpSecTunnelOutboundPacket (
 | 
						|
  IN OUT UINT8                   *IpHead,
 | 
						|
  IN     UINT8                   IpVersion,
 | 
						|
  IN     IPSEC_SAD_DATA          *SadData,
 | 
						|
  IN OUT UINT8                   *LastHead,
 | 
						|
  IN     VOID                    **OptionsBuffer,
 | 
						|
  IN     UINT32                  *OptionsLength,
 | 
						|
  IN OUT EFI_IPSEC_FRAGMENT_DATA **FragmentTable,
 | 
						|
  IN     UINT32                  *FragmentCount
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8         *InnerHead;
 | 
						|
  NET_BUF       *Packet;
 | 
						|
  UINT16        PacketChecksum;
 | 
						|
  UINT16        *Checksum;
 | 
						|
  UINT16        PseudoChecksum;
 | 
						|
  IP6_ICMP_HEAD *IcmpHead;
 | 
						|
 | 
						|
  Checksum = NULL;
 | 
						|
  if (OptionsLength == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (IpVersion == IP_VERSION_4) {
 | 
						|
    InnerHead = AllocateZeroPool (sizeof (IP4_HEAD) + *OptionsLength);
 | 
						|
    ASSERT (InnerHead != NULL);
 | 
						|
    CopyMem (
 | 
						|
      InnerHead,
 | 
						|
      IpHead,
 | 
						|
      sizeof (IP4_HEAD)
 | 
						|
      );
 | 
						|
    CopyMem (
 | 
						|
      InnerHead + sizeof (IP4_HEAD),
 | 
						|
      *OptionsBuffer,
 | 
						|
      *OptionsLength
 | 
						|
      );
 | 
						|
  } else {
 | 
						|
    InnerHead = AllocateZeroPool (sizeof (EFI_IP6_HEADER) + *OptionsLength);
 | 
						|
    ASSERT (InnerHead != NULL);
 | 
						|
    CopyMem (
 | 
						|
      InnerHead,
 | 
						|
      IpHead,
 | 
						|
      sizeof (EFI_IP6_HEADER)
 | 
						|
      );
 | 
						|
    CopyMem (
 | 
						|
      InnerHead + sizeof (EFI_IP6_HEADER),
 | 
						|
      *OptionsBuffer,
 | 
						|
      *OptionsLength
 | 
						|
      );
 | 
						|
  }
 | 
						|
  if (OptionsBuffer != NULL) {
 | 
						|
    if (*OptionsLength != 0) {
 | 
						|
 | 
						|
      *OptionsBuffer = NULL;
 | 
						|
      *OptionsLength = 0;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // 2. Reassamlbe Fragment into Packet
 | 
						|
  //
 | 
						|
  Packet = NetbufFromExt (
 | 
						|
             (NET_FRAGMENT *)(*FragmentTable),
 | 
						|
             *FragmentCount,
 | 
						|
             0,
 | 
						|
             0,
 | 
						|
             IpSecOnRecyclePacket,
 | 
						|
             NULL
 | 
						|
             );
 | 
						|
  ASSERT (Packet != NULL);
 | 
						|
  //
 | 
						|
  // 3. Check the Last Header, if it is TCP, UDP or ICMP recalcualate its pesudo
 | 
						|
  //    CheckSum.
 | 
						|
  //
 | 
						|
  switch (*LastHead) {
 | 
						|
  case EFI_IP_PROTO_UDP:
 | 
						|
    Packet->Udp = (EFI_UDP_HEADER *) NetbufGetByte (Packet, 0, 0);
 | 
						|
    ASSERT (Packet->Udp != NULL);
 | 
						|
    Checksum = &Packet->Udp->Checksum;
 | 
						|
    *Checksum = 0;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EFI_IP_PROTO_TCP:
 | 
						|
    Packet->Tcp = (TCP_HEAD *) NetbufGetByte (Packet, 0, 0);
 | 
						|
    ASSERT (Packet->Tcp != NULL);
 | 
						|
    Checksum = &Packet->Tcp->Checksum;
 | 
						|
    *Checksum = 0;
 | 
						|
    break;
 | 
						|
 | 
						|
  case IP6_ICMP:
 | 
						|
    IcmpHead = (IP6_ICMP_HEAD *) NetbufGetByte (Packet, 0, NULL);
 | 
						|
    ASSERT (IcmpHead != NULL);
 | 
						|
    Checksum = &IcmpHead->Checksum;
 | 
						|
    *Checksum = 0;
 | 
						|
    break;
 | 
						|
 | 
						|
  default:
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  PacketChecksum = NetbufChecksum (Packet);
 | 
						|
 | 
						|
  if (IpVersion == IP_VERSION_4) {
 | 
						|
    //
 | 
						|
    // Replace the source address of Inner Header.
 | 
						|
    //
 | 
						|
    CopyMem (
 | 
						|
      &((IP4_HEAD *)InnerHead)->Src,
 | 
						|
      &SadData->SpdSelector->LocalAddress[0].Address.v4,
 | 
						|
      sizeof (EFI_IPv4_ADDRESS)
 | 
						|
      );
 | 
						|
 | 
						|
    PacketChecksum = NetbufChecksum (Packet);
 | 
						|
    PseudoChecksum = NetPseudoHeadChecksum (
 | 
						|
                       ((IP4_HEAD *)InnerHead)->Src,
 | 
						|
                       ((IP4_HEAD *)InnerHead)->Dst,
 | 
						|
                       *LastHead,
 | 
						|
                       0
 | 
						|
                       );
 | 
						|
 | 
						|
   } else {
 | 
						|
     //
 | 
						|
     // Replace the source address of Inner Header.
 | 
						|
     //
 | 
						|
     CopyMem (
 | 
						|
       &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,
 | 
						|
       &(SadData->SpdSelector->LocalAddress[0].Address.v6),
 | 
						|
       sizeof (EFI_IPv6_ADDRESS)
 | 
						|
       );
 | 
						|
     PacketChecksum = NetbufChecksum (Packet);
 | 
						|
     PseudoChecksum = NetIp6PseudoHeadChecksum (
 | 
						|
                      &((EFI_IP6_HEADER *)InnerHead)->SourceAddress,
 | 
						|
                      &((EFI_IP6_HEADER *)InnerHead)->DestinationAddress,
 | 
						|
                      *LastHead,
 | 
						|
                      0
 | 
						|
                      );
 | 
						|
 | 
						|
   }
 | 
						|
   if (Checksum != NULL) {
 | 
						|
     *Checksum = NetAddChecksum (PacketChecksum, PseudoChecksum);
 | 
						|
     *Checksum = (UINT16) ~(NetAddChecksum ((UINT16)*Checksum, HTONS ((UINT16) Packet->TotalSize)));
 | 
						|
   }
 | 
						|
 | 
						|
  if (Packet != NULL) {
 | 
						|
    NetbufFree (Packet);
 | 
						|
  }
 | 
						|
  return InnerHead;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The actual entry to relative function processes the inbound traffic of ESP header.
 | 
						|
 | 
						|
  This function is the subfunction of IpSecProtectInboundPacket(). It checks the
 | 
						|
  received packet security property and trim the ESP header and then returns without
 | 
						|
  an IPsec protected IP Header and FramgmentTable.
 | 
						|
 | 
						|
  @param[in]      IpVersion          The version of IP.
 | 
						|
  @param[in, out] IpHead             Points to the IP header containing the ESP header
 | 
						|
                                     to be trimed on input, and without ESP header
 | 
						|
                                     on return.
 | 
						|
  @param[out]     LastHead           The Last Header in IP header on return.
 | 
						|
  @param[in, out] OptionsBuffer      Pointer to the options buffer.
 | 
						|
  @param[in, out] OptionsLength      Length of the options buffer.
 | 
						|
  @param[in, out] FragmentTable      Pointer to a list of fragments in the form of IPsec
 | 
						|
                                     protected on input, and without IPsec protected
 | 
						|
                                     on return.
 | 
						|
  @param[in, out] FragmentCount      The number of fragments.
 | 
						|
  @param[out]     SpdSelector        Pointer to contain the address of SPD selector on return.
 | 
						|
  @param[out]     RecycleEvent       The event for recycling of resources.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              The operation was successful.
 | 
						|
  @retval EFI_ACCESS_DENIED        One or more following conditions is TRUE:
 | 
						|
                                   - ESP header was not found or mal-format.
 | 
						|
                                   - The related SAD entry was not found.
 | 
						|
                                   - The related SAD entry does not support the ESP protocol.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     The required system resource can't be allocated.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IpSecEspInboundPacket (
 | 
						|
  IN     UINT8                       IpVersion,
 | 
						|
  IN OUT VOID                        *IpHead,
 | 
						|
     OUT UINT8                       *LastHead,
 | 
						|
  IN OUT VOID                        **OptionsBuffer,
 | 
						|
  IN OUT UINT32                      *OptionsLength,
 | 
						|
  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
 | 
						|
  IN OUT UINT32                      *FragmentCount,
 | 
						|
     OUT EFI_IPSEC_SPD_SELECTOR      **SpdSelector,
 | 
						|
     OUT EFI_EVENT                   *RecycleEvent
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  NET_BUF               *Payload;
 | 
						|
  UINTN                 EspSize;
 | 
						|
  UINTN                 IvSize;
 | 
						|
  UINTN                 BlockSize;
 | 
						|
  UINTN                 MiscSize;
 | 
						|
  UINTN                 PlainPayloadSize;
 | 
						|
  UINTN                 PaddingSize;
 | 
						|
  UINTN                 IcvSize;
 | 
						|
  UINT8                 *ProcessBuffer;
 | 
						|
  EFI_ESP_HEADER        *EspHeader;
 | 
						|
  EFI_ESP_TAIL          *EspTail;
 | 
						|
  EFI_IPSEC_SA_ID       *SaId;
 | 
						|
  IPSEC_SAD_DATA        *SadData;
 | 
						|
  IPSEC_SAD_ENTRY       *SadEntry;
 | 
						|
  IPSEC_RECYCLE_CONTEXT *RecycleContext;
 | 
						|
  UINT8                 NextHeader;
 | 
						|
  UINT16                IpSecHeadSize;
 | 
						|
  UINT8                 *InnerHead;
 | 
						|
 | 
						|
  Status            = EFI_SUCCESS;
 | 
						|
  Payload           = NULL;
 | 
						|
  ProcessBuffer     = NULL;
 | 
						|
  RecycleContext    = NULL;
 | 
						|
  *RecycleEvent     = NULL;
 | 
						|
  PlainPayloadSize  = 0;
 | 
						|
  NextHeader        = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  // Build netbuf from fragment table first.
 | 
						|
  //
 | 
						|
  Payload = NetbufFromExt (
 | 
						|
              (NET_FRAGMENT *) *FragmentTable,
 | 
						|
              *FragmentCount,
 | 
						|
              0,
 | 
						|
              sizeof (EFI_ESP_HEADER),
 | 
						|
              IpSecOnRecyclePacket,
 | 
						|
              NULL
 | 
						|
              );
 | 
						|
  if (Payload == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the esp size and esp header from netbuf.
 | 
						|
  //
 | 
						|
  EspSize   = Payload->TotalSize;
 | 
						|
  EspHeader = (EFI_ESP_HEADER *) NetbufGetByte (Payload, 0, NULL);
 | 
						|
 | 
						|
  if (EspHeader == NULL) {
 | 
						|
    Status = EFI_ACCESS_DENIED;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Parse destination address from ip header and found the related SAD Entry.
 | 
						|
  //
 | 
						|
  SadEntry = IpSecFoundSadFromInboundPacket (
 | 
						|
               IpHead,
 | 
						|
               IpVersion,
 | 
						|
               NTOHL (EspHeader->Spi)
 | 
						|
               );
 | 
						|
 | 
						|
  if (SadEntry == NULL) {
 | 
						|
    Status = EFI_ACCESS_DENIED;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  SaId    = SadEntry->Id;
 | 
						|
  SadData = SadEntry->Data;
 | 
						|
 | 
						|
  //
 | 
						|
  // Only support esp protocol currently.
 | 
						|
  //
 | 
						|
  if (SaId->Proto != EfiIPsecESP) {
 | 
						|
    Status = EFI_ACCESS_DENIED;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!SadData->ManualSet) {
 | 
						|
    //
 | 
						|
    // TODO: Check SA lifetime and sequence number
 | 
						|
    //
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Allocate buffer for decryption and authentication.
 | 
						|
  //
 | 
						|
  ProcessBuffer = AllocateZeroPool (EspSize);
 | 
						|
  if (ProcessBuffer == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  NetbufCopy (Payload, 0, (UINT32) EspSize, ProcessBuffer);
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the IcvSize for authentication and BlockSize/IvSize for Decryption.
 | 
						|
  //
 | 
						|
  IcvSize   = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
 | 
						|
  IvSize    = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
 | 
						|
  BlockSize = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
 | 
						|
 | 
						|
  //
 | 
						|
  // Make sure the ESP packet is not mal-formt.
 | 
						|
  // 1. Check whether the Espsize is larger than ESP header + IvSize + EspTail + IcvSize.
 | 
						|
  // 2. Check whether the left payload size is multiple of IvSize.
 | 
						|
  //
 | 
						|
  MiscSize = sizeof (EFI_ESP_HEADER) + IvSize + IcvSize;
 | 
						|
  if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL))) {
 | 
						|
    Status = EFI_ACCESS_DENIED;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
  if ((EspSize - MiscSize) % BlockSize != 0) {
 | 
						|
    Status = EFI_ACCESS_DENIED;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Authenticate the ESP packet.
 | 
						|
  //
 | 
						|
  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
 | 
						|
    Status = IpSecEspAuthVerifyPayload (
 | 
						|
               ProcessBuffer,
 | 
						|
               EspSize,
 | 
						|
               SadEntry,
 | 
						|
               IcvSize
 | 
						|
               );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Decrypt the payload by the SAD entry if it has decrypt key.
 | 
						|
  //
 | 
						|
  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
 | 
						|
    Status = IpSecCryptoIoDecrypt (
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,
 | 
						|
               ProcessBuffer + sizeof (EFI_ESP_HEADER),
 | 
						|
               ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize,
 | 
						|
               EspSize - sizeof (EFI_ESP_HEADER) - IvSize - IcvSize,
 | 
						|
               ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize
 | 
						|
               );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Parse EspTail and compute the plain payload size.
 | 
						|
  //
 | 
						|
  EspTail           = (EFI_ESP_TAIL *) (ProcessBuffer + EspSize - IcvSize - sizeof (EFI_ESP_TAIL));
 | 
						|
  PaddingSize       = EspTail->PaddingLength;
 | 
						|
  NextHeader        = EspTail->NextHeader;
 | 
						|
 | 
						|
  if (EspSize <= (MiscSize + sizeof (EFI_ESP_TAIL) + PaddingSize)) {
 | 
						|
    Status = EFI_ACCESS_DENIED;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
  PlainPayloadSize  = EspSize - MiscSize - sizeof (EFI_ESP_TAIL) - PaddingSize;
 | 
						|
 | 
						|
  //
 | 
						|
  // TODO: handle anti-replay window
 | 
						|
  //
 | 
						|
  //
 | 
						|
  // Decryption and authentication with esp has been done, so it's time to
 | 
						|
  // reload the new packet, create recycle event and fixup ip header.
 | 
						|
  //
 | 
						|
  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
 | 
						|
  if (RecycleContext == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = gBS->CreateEvent (
 | 
						|
                  EVT_NOTIFY_SIGNAL,
 | 
						|
                  TPL_NOTIFY,
 | 
						|
                  IpSecRecycleCallback,
 | 
						|
                  RecycleContext,
 | 
						|
                  RecycleEvent
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // The caller will take responsible to handle the original fragment table
 | 
						|
  //
 | 
						|
  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
 | 
						|
  if (*FragmentTable == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  RecycleContext->PayloadBuffer       = ProcessBuffer;
 | 
						|
  RecycleContext->FragmentTable       = *FragmentTable;
 | 
						|
 | 
						|
  //
 | 
						|
  // If Tunnel, recalculate upper-layyer PesudoCheckSum and trim the out
 | 
						|
  //
 | 
						|
  if (SadData->Mode == EfiIPsecTunnel) {
 | 
						|
    InnerHead = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;
 | 
						|
    IpSecTunnelInboundPacket (
 | 
						|
      IpHead,
 | 
						|
      InnerHead,
 | 
						|
      IpVersion,
 | 
						|
      SadData,
 | 
						|
      LastHead
 | 
						|
      );
 | 
						|
 | 
						|
    if (IpVersion == IP_VERSION_4) {
 | 
						|
      (*FragmentTable)[0].FragmentBuffer  = InnerHead ;
 | 
						|
      (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
 | 
						|
 | 
						|
    }else {
 | 
						|
      (*FragmentTable)[0].FragmentBuffer  = InnerHead;
 | 
						|
      (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer + sizeof (EFI_ESP_HEADER) + IvSize;
 | 
						|
    (*FragmentTable)[0].FragmentLength  = (UINT32) PlainPayloadSize;
 | 
						|
  }
 | 
						|
 | 
						|
  *FragmentCount                      = 1;
 | 
						|
 | 
						|
  //
 | 
						|
  // Update the total length field in ip header since processed by esp.
 | 
						|
  //
 | 
						|
  if (!SadData->Mode == EfiIPsecTunnel) {
 | 
						|
    if (IpVersion == IP_VERSION_4) {
 | 
						|
      ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + PlainPayloadSize));
 | 
						|
    } else {
 | 
						|
      IpSecHeadSize                              = IpSecGetPlainExtHeadSize (IpHead, LastHead);
 | 
						|
      ((EFI_IP6_HEADER *) IpHead)->PayloadLength = HTONS ((UINT16)(IpSecHeadSize + PlainPayloadSize));
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // Update the next layer field in ip header since esp header inserted.
 | 
						|
    //
 | 
						|
    *LastHead = NextHeader;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  //
 | 
						|
  // Update the SPD association of the SAD entry.
 | 
						|
  //
 | 
						|
  *SpdSelector = SadData->SpdSelector;
 | 
						|
 | 
						|
ON_EXIT:
 | 
						|
  if (Payload != NULL) {
 | 
						|
    NetbufFree (Payload);
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    if (ProcessBuffer != NULL) {
 | 
						|
      FreePool (ProcessBuffer);
 | 
						|
    }
 | 
						|
 | 
						|
    if (RecycleContext != NULL) {
 | 
						|
      FreePool (RecycleContext);
 | 
						|
    }
 | 
						|
 | 
						|
    if (*RecycleEvent != NULL) {
 | 
						|
      gBS->CloseEvent (*RecycleEvent);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  The actual entry to the relative function processes the output traffic using the ESP protocol.
 | 
						|
 | 
						|
  This function is the subfunction of IpSecProtectOutboundPacket(). It protected
 | 
						|
  the sending packet by encrypting its payload and inserting ESP header in the orginal
 | 
						|
  IP header, then return the IpHeader and IPsec protected Fragmentable.
 | 
						|
 | 
						|
  @param[in]      IpVersion          The version of IP.
 | 
						|
  @param[in, out] IpHead             Points to IP header containing the orginal IP header
 | 
						|
                                     to be processed on input, and inserted ESP header
 | 
						|
                                     on return.
 | 
						|
  @param[in, out] LastHead           The Last Header in IP header.
 | 
						|
  @param[in, out] OptionsBuffer      Pointer to the options buffer.
 | 
						|
  @param[in, out] OptionsLength      Length of the options buffer.
 | 
						|
  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
 | 
						|
                                     IPsec on input, and with IPsec protected
 | 
						|
                                     on return.
 | 
						|
  @param[in, out] FragmentCount      The number of fragments.
 | 
						|
  @param[in]      SadEntry           The related SAD entry.
 | 
						|
  @param[out]     RecycleEvent       The event for recycling of resources.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              The operation was successful.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES     The required system resources can't be allocated.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IpSecEspOutboundPacket (
 | 
						|
  IN UINT8                           IpVersion,
 | 
						|
  IN OUT VOID                        *IpHead,
 | 
						|
  IN OUT UINT8                       *LastHead,
 | 
						|
  IN OUT VOID                        **OptionsBuffer,
 | 
						|
  IN OUT UINT32                      *OptionsLength,
 | 
						|
  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
 | 
						|
  IN OUT UINT32                      *FragmentCount,
 | 
						|
  IN     IPSEC_SAD_ENTRY             *SadEntry,
 | 
						|
     OUT EFI_EVENT                   *RecycleEvent
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  UINTN                 Index;
 | 
						|
  EFI_IPSEC_SA_ID       *SaId;
 | 
						|
  IPSEC_SAD_DATA        *SadData;
 | 
						|
  IPSEC_RECYCLE_CONTEXT *RecycleContext;
 | 
						|
  UINT8                 *ProcessBuffer;
 | 
						|
  UINTN                 BytesCopied;
 | 
						|
  INTN                  EncryptBlockSize;// Size of encryption block, 4 bytes aligned and >= 4
 | 
						|
  UINTN                 EspSize;         // Total size of esp wrapped ip payload
 | 
						|
  UINTN                 IvSize;          // Size of IV, optional, might be 0
 | 
						|
  UINTN                 PlainPayloadSize;// Original IP payload size
 | 
						|
  UINTN                 PaddingSize;     // Size of padding
 | 
						|
  UINTN                 EncryptSize;     // Size of data to be encrypted, start after IV and
 | 
						|
                                         // stop before ICV
 | 
						|
  UINTN                 IcvSize;         // Size of ICV, optional, might be 0
 | 
						|
  UINT8                 *RestOfPayload;  // Start of Payload after IV
 | 
						|
  UINT8                 *Padding;        // Start address of padding
 | 
						|
  EFI_ESP_HEADER        *EspHeader;      // Start address of ESP frame
 | 
						|
  EFI_ESP_TAIL          *EspTail;        // Address behind padding
 | 
						|
  UINT8                 *InnerHead;
 | 
						|
  HASH_DATA_FRAGMENT    HashFragment[1];
 | 
						|
 | 
						|
  Status          = EFI_ACCESS_DENIED;
 | 
						|
  SaId            = SadEntry->Id;
 | 
						|
  SadData         = SadEntry->Data;
 | 
						|
  ProcessBuffer   = NULL;
 | 
						|
  RecycleContext  = NULL;
 | 
						|
  *RecycleEvent   = NULL;
 | 
						|
  InnerHead       = NULL;
 | 
						|
 | 
						|
  if (!SadData->ManualSet &&
 | 
						|
      SadData->AlgoInfo.EspAlgoInfo.EncKey == NULL &&
 | 
						|
      SadData->AlgoInfo.EspAlgoInfo.AuthKey == NULL
 | 
						|
      ) {
 | 
						|
    //
 | 
						|
    // Invalid manual SAD entry configuration.
 | 
						|
    //
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Create OutHeader according to Inner Header
 | 
						|
  //
 | 
						|
  if (SadData->Mode == EfiIPsecTunnel) {
 | 
						|
    InnerHead = IpSecTunnelOutboundPacket (
 | 
						|
                  IpHead,
 | 
						|
                  IpVersion,
 | 
						|
                  SadData,
 | 
						|
                  LastHead,
 | 
						|
                  OptionsBuffer,
 | 
						|
                  OptionsLength,
 | 
						|
                  FragmentTable,
 | 
						|
                  FragmentCount
 | 
						|
                  );
 | 
						|
 | 
						|
    if (InnerHead == NULL) {
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
    }
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Calculate enctrypt block size, need iv by default and 4 bytes alignment.
 | 
						|
  //
 | 
						|
  EncryptBlockSize  = 4;
 | 
						|
 | 
						|
  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
 | 
						|
    EncryptBlockSize  = IpSecGetEncryptBlockSize (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
 | 
						|
 | 
						|
    if (EncryptBlockSize < 0 || (EncryptBlockSize != 1 && EncryptBlockSize % 4 != 0)) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Calculate the plain payload size accroding to the fragment table.
 | 
						|
  //
 | 
						|
  PlainPayloadSize = 0;
 | 
						|
  for (Index = 0; Index < *FragmentCount; Index++) {
 | 
						|
    PlainPayloadSize += (*FragmentTable)[Index].FragmentLength;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Add IPHeader size for Tunnel Mode
 | 
						|
  //
 | 
						|
  if (SadData->Mode == EfiIPsecTunnel) {
 | 
						|
    if (IpVersion == IP_VERSION_4) {
 | 
						|
      PlainPayloadSize += sizeof (IP4_HEAD);
 | 
						|
    } else {
 | 
						|
      PlainPayloadSize += sizeof (EFI_IP6_HEADER);
 | 
						|
    }
 | 
						|
    //
 | 
						|
    // OPtions should be encryption into it
 | 
						|
    //
 | 
						|
    PlainPayloadSize += *OptionsLength;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  //
 | 
						|
  // Calculate icv size, optional by default and 4 bytes alignment.
 | 
						|
  //
 | 
						|
  IcvSize = 0;
 | 
						|
  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
 | 
						|
    IcvSize = IpSecGetIcvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId);
 | 
						|
    if (IcvSize % 4 != 0) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Calcuate the total size of esp wrapped ip payload.
 | 
						|
  //
 | 
						|
  IvSize        = IpSecGetEncryptIvLength (SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId);
 | 
						|
  EncryptSize   = (PlainPayloadSize + sizeof (EFI_ESP_TAIL) + EncryptBlockSize - 1) / EncryptBlockSize * EncryptBlockSize;
 | 
						|
  PaddingSize   = EncryptSize - PlainPayloadSize - sizeof (EFI_ESP_TAIL);
 | 
						|
  EspSize       = sizeof (EFI_ESP_HEADER) + IvSize + EncryptSize + IcvSize;
 | 
						|
 | 
						|
  ProcessBuffer = AllocateZeroPool (EspSize);
 | 
						|
  if (ProcessBuffer == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Calculate esp header and esp tail including header, payload and padding.
 | 
						|
  //
 | 
						|
  EspHeader     = (EFI_ESP_HEADER *) ProcessBuffer;
 | 
						|
  RestOfPayload = (UINT8 *) (EspHeader + 1) + IvSize;
 | 
						|
  Padding       = RestOfPayload + PlainPayloadSize;
 | 
						|
  EspTail       = (EFI_ESP_TAIL *) (Padding + PaddingSize);
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the sn and spi fields in esp header.
 | 
						|
  //
 | 
						|
  EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber + 1);
 | 
						|
  //EspHeader->SequenceNumber = HTONL ((UINT32) SadData->SequenceNumber);
 | 
						|
  EspHeader->Spi            = HTONL (SaId->Spi);
 | 
						|
 | 
						|
  //
 | 
						|
  // Copy the rest of payload (after iv) from the original fragment buffer.
 | 
						|
  //
 | 
						|
  BytesCopied = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  // For Tunnel Mode
 | 
						|
  //
 | 
						|
  if (SadData->Mode == EfiIPsecTunnel) {
 | 
						|
    if (IpVersion == IP_VERSION_4) {
 | 
						|
      //
 | 
						|
      // HeadLen, Total Length
 | 
						|
      //
 | 
						|
      ((IP4_HEAD *)InnerHead)->HeadLen  = (UINT8) ((sizeof (IP4_HEAD) + *OptionsLength) >> 2);
 | 
						|
      ((IP4_HEAD *)InnerHead)->TotalLen = HTONS ((UINT16) PlainPayloadSize);
 | 
						|
      ((IP4_HEAD *)InnerHead)->Checksum = 0;
 | 
						|
      ((IP4_HEAD *)InnerHead)->Checksum = (UINT16) (~NetblockChecksum (
 | 
						|
                                                  (UINT8 *)InnerHead,
 | 
						|
                                                  sizeof(IP4_HEAD)
 | 
						|
                                                  ));
 | 
						|
      CopyMem (
 | 
						|
        RestOfPayload + BytesCopied,
 | 
						|
        InnerHead,
 | 
						|
        sizeof (IP4_HEAD) + *OptionsLength
 | 
						|
        );
 | 
						|
      BytesCopied += sizeof (IP4_HEAD) + *OptionsLength;
 | 
						|
 | 
						|
    } else {
 | 
						|
    ((EFI_IP6_HEADER *)InnerHead)->PayloadLength = HTONS ((UINT16) (PlainPayloadSize - sizeof (EFI_IP6_HEADER)));
 | 
						|
      CopyMem (
 | 
						|
        RestOfPayload + BytesCopied,
 | 
						|
        InnerHead,
 | 
						|
        sizeof (EFI_IP6_HEADER) + *OptionsLength
 | 
						|
        );
 | 
						|
      BytesCopied += sizeof (EFI_IP6_HEADER) + *OptionsLength;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  for (Index = 0; Index < *FragmentCount; Index++) {
 | 
						|
    CopyMem (
 | 
						|
      (RestOfPayload + BytesCopied),
 | 
						|
      (*FragmentTable)[Index].FragmentBuffer,
 | 
						|
      (*FragmentTable)[Index].FragmentLength
 | 
						|
      );
 | 
						|
    BytesCopied += (*FragmentTable)[Index].FragmentLength;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Fill the padding buffer by natural number sequence.
 | 
						|
  //
 | 
						|
  for (Index = 0; Index < PaddingSize; Index++) {
 | 
						|
    Padding[Index] = (UINT8) (Index + 1);
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Fill the padding length and next header fields in esp tail.
 | 
						|
  //
 | 
						|
  EspTail->PaddingLength  = (UINT8) PaddingSize;
 | 
						|
  EspTail->NextHeader     = *LastHead;
 | 
						|
 | 
						|
  //
 | 
						|
  // Fill the next header for Tunnel mode.
 | 
						|
  //
 | 
						|
  if (SadData->Mode == EfiIPsecTunnel) {
 | 
						|
    if (IpVersion == IP_VERSION_4) {
 | 
						|
      EspTail->NextHeader = 4;
 | 
						|
    } else {
 | 
						|
      EspTail->NextHeader = 41;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Generate iv at random by crypt library.
 | 
						|
  //
 | 
						|
  Status = IpSecGenerateIv (
 | 
						|
             (UINT8 *) (EspHeader + 1),
 | 
						|
             IvSize
 | 
						|
             );
 | 
						|
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Encryption the payload (after iv) by the SAD entry if has encrypt key.
 | 
						|
  //
 | 
						|
  if (SadData->AlgoInfo.EspAlgoInfo.EncKey != NULL) {
 | 
						|
    Status = IpSecCryptoIoEncrypt (
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncAlgoId,
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKey,
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.EncKeyLength << 3,
 | 
						|
               (UINT8 *)(EspHeader + 1),
 | 
						|
               RestOfPayload,
 | 
						|
               EncryptSize,
 | 
						|
               RestOfPayload
 | 
						|
               );
 | 
						|
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Authenticate the esp wrapped buffer by the SAD entry if it has auth key.
 | 
						|
  //
 | 
						|
  if (SadData->AlgoInfo.EspAlgoInfo.AuthKey != NULL) {
 | 
						|
 | 
						|
    HashFragment[0].Data     = ProcessBuffer;
 | 
						|
    HashFragment[0].DataSize = EspSize - IcvSize;
 | 
						|
    Status = IpSecCryptoIoHmac (
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthAlgoId,
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKey,
 | 
						|
               SadEntry->Data->AlgoInfo.EspAlgoInfo.AuthKeyLength,
 | 
						|
               HashFragment,
 | 
						|
               1,
 | 
						|
               ProcessBuffer + EspSize - IcvSize,
 | 
						|
               IcvSize
 | 
						|
               );
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto ON_EXIT;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Encryption and authentication with esp has been done, so it's time to
 | 
						|
  // reload the new packet, create recycle event and fixup ip header.
 | 
						|
  //
 | 
						|
  RecycleContext = AllocateZeroPool (sizeof (IPSEC_RECYCLE_CONTEXT));
 | 
						|
  if (RecycleContext == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = gBS->CreateEvent (
 | 
						|
                  EVT_NOTIFY_SIGNAL,
 | 
						|
                  TPL_NOTIFY,
 | 
						|
                  IpSecRecycleCallback,
 | 
						|
                  RecycleContext,
 | 
						|
                  RecycleEvent
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // Caller take responsible to handle the original fragment table.
 | 
						|
  //
 | 
						|
  *FragmentTable = AllocateZeroPool (sizeof (EFI_IPSEC_FRAGMENT_DATA));
 | 
						|
  if (*FragmentTable == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto ON_EXIT;
 | 
						|
  }
 | 
						|
 | 
						|
  RecycleContext->FragmentTable       = *FragmentTable;
 | 
						|
  RecycleContext->PayloadBuffer       = ProcessBuffer;
 | 
						|
  (*FragmentTable)[0].FragmentBuffer  = ProcessBuffer;
 | 
						|
  (*FragmentTable)[0].FragmentLength  = (UINT32) EspSize;
 | 
						|
  *FragmentCount                      = 1;
 | 
						|
 | 
						|
  //
 | 
						|
  // Update the total length field in ip header since processed by esp.
 | 
						|
  //
 | 
						|
  if (IpVersion == IP_VERSION_4) {
 | 
						|
    ((IP4_HEAD *) IpHead)->TotalLen = HTONS ((UINT16) ((((IP4_HEAD *) IpHead)->HeadLen << 2) + EspSize));
 | 
						|
  } else {
 | 
						|
    ((EFI_IP6_HEADER *) IpHead)->PayloadLength = (UINT16) (IpSecGetPlainExtHeadSize (IpHead, LastHead) + EspSize);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If tunnel mode, it should change the outer Ip header with tunnel source address
 | 
						|
  // and destination tunnel address.
 | 
						|
  //
 | 
						|
  if (SadData->Mode == EfiIPsecTunnel) {
 | 
						|
    if (IpVersion == IP_VERSION_4) {
 | 
						|
      CopyMem (
 | 
						|
        &((IP4_HEAD *) IpHead)->Src,
 | 
						|
        &SadData->TunnelSourceAddress.v4,
 | 
						|
        sizeof (EFI_IPv4_ADDRESS)
 | 
						|
        );
 | 
						|
      CopyMem (
 | 
						|
        &((IP4_HEAD *) IpHead)->Dst,
 | 
						|
        &SadData->TunnelDestAddress.v4,
 | 
						|
        sizeof (EFI_IPv4_ADDRESS)
 | 
						|
        );
 | 
						|
    } else {
 | 
						|
      CopyMem (
 | 
						|
        &((EFI_IP6_HEADER *) IpHead)->SourceAddress,
 | 
						|
        &SadData->TunnelSourceAddress.v6,
 | 
						|
        sizeof (EFI_IPv6_ADDRESS)
 | 
						|
        );
 | 
						|
      CopyMem (
 | 
						|
        &((EFI_IP6_HEADER *) IpHead)->DestinationAddress,
 | 
						|
        &SadData->TunnelDestAddress.v6,
 | 
						|
        sizeof (EFI_IPv6_ADDRESS)
 | 
						|
        );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Update the next layer field in ip header since esp header inserted.
 | 
						|
  //
 | 
						|
  *LastHead = IPSEC_ESP_PROTOCOL;
 | 
						|
 | 
						|
  //
 | 
						|
  // Increase the sn number in SAD entry according to rfc4303.
 | 
						|
  //
 | 
						|
  SadData->SequenceNumber++;
 | 
						|
 | 
						|
ON_EXIT:
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    if (ProcessBuffer != NULL) {
 | 
						|
      FreePool (ProcessBuffer);
 | 
						|
    }
 | 
						|
 | 
						|
    if (RecycleContext != NULL) {
 | 
						|
      FreePool (RecycleContext);
 | 
						|
    }
 | 
						|
 | 
						|
    if (*RecycleEvent != NULL) {
 | 
						|
      gBS->CloseEvent (*RecycleEvent);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This function processes the inbound traffic with IPsec.
 | 
						|
 | 
						|
  It checks the received packet security property, trims the ESP/AH header, and then
 | 
						|
  returns without an IPsec protected IP Header and FragmentTable.
 | 
						|
 | 
						|
  @param[in]      IpVersion          The version of IP.
 | 
						|
  @param[in, out] IpHead             Points to IP header containing the ESP/AH header
 | 
						|
                                     to be trimed on input, and without ESP/AH header
 | 
						|
                                     on return.
 | 
						|
  @param[in, out] LastHead           The Last Header in IP header on return.
 | 
						|
  @param[in, out] OptionsBuffer      Pointer to the options buffer.
 | 
						|
  @param[in, out] OptionsLength      Length of the options buffer.
 | 
						|
  @param[in, out] FragmentTable      Pointer to a list of fragments in form of IPsec
 | 
						|
                                     protected on input, and without IPsec protected
 | 
						|
                                     on return.
 | 
						|
  @param[in, out] FragmentCount      The number of fragments.
 | 
						|
  @param[out]     SpdEntry           Pointer to contain the address of SPD entry on return.
 | 
						|
  @param[out]     RecycleEvent       The event for recycling of resources.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              The operation was successful.
 | 
						|
  @retval EFI_UNSUPPORTED          The IPSEC protocol is not supported.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IpSecProtectInboundPacket (
 | 
						|
  IN     UINT8                       IpVersion,
 | 
						|
  IN OUT VOID                        *IpHead,
 | 
						|
  IN OUT UINT8                       *LastHead,
 | 
						|
  IN OUT VOID                        **OptionsBuffer,
 | 
						|
  IN OUT UINT32                      *OptionsLength,
 | 
						|
  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
 | 
						|
  IN OUT UINT32                      *FragmentCount,
 | 
						|
     OUT EFI_IPSEC_SPD_SELECTOR      **SpdEntry,
 | 
						|
     OUT EFI_EVENT                   *RecycleEvent
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (*LastHead == IPSEC_ESP_PROTOCOL) {
 | 
						|
    //
 | 
						|
    // Process the esp ipsec header of the inbound traffic.
 | 
						|
    //
 | 
						|
    return IpSecEspInboundPacket (
 | 
						|
             IpVersion,
 | 
						|
             IpHead,
 | 
						|
             LastHead,
 | 
						|
             OptionsBuffer,
 | 
						|
             OptionsLength,
 | 
						|
             FragmentTable,
 | 
						|
             FragmentCount,
 | 
						|
             SpdEntry,
 | 
						|
             RecycleEvent
 | 
						|
             );
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // The other protocols are not supported.
 | 
						|
  //
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  This fucntion processes the output traffic with IPsec.
 | 
						|
 | 
						|
  It protected the sending packet by encrypting it payload and inserting ESP/AH header
 | 
						|
  in the orginal IP header, then return the IpHeader and IPsec protected Fragmentable.
 | 
						|
 | 
						|
  @param[in]      IpVersion          The version of IP.
 | 
						|
  @param[in, out] IpHead             Point to IP header containing the orginal IP header
 | 
						|
                                     to be processed on input, and inserted ESP/AH header
 | 
						|
                                     on return.
 | 
						|
  @param[in, out] LastHead           The Last Header in IP header.
 | 
						|
  @param[in, out] OptionsBuffer      Pointer to the options buffer.
 | 
						|
  @param[in, out] OptionsLength      Length of the options buffer.
 | 
						|
  @param[in, out] FragmentTable      Pointer to a list of fragments to be protected by
 | 
						|
                                     IPsec on input, and with IPsec protected
 | 
						|
                                     on return.
 | 
						|
  @param[in, out] FragmentCount      Number of fragments.
 | 
						|
  @param[in]      SadEntry           Related SAD entry.
 | 
						|
  @param[out]     RecycleEvent       Event for recycling of resources.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              The operation is successful.
 | 
						|
  @retval EFI_UNSUPPORTED          If the IPSEC protocol is not supported.
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
IpSecProtectOutboundPacket (
 | 
						|
  IN     UINT8                       IpVersion,
 | 
						|
  IN OUT VOID                        *IpHead,
 | 
						|
  IN OUT UINT8                       *LastHead,
 | 
						|
  IN OUT VOID                        **OptionsBuffer,
 | 
						|
  IN OUT UINT32                      *OptionsLength,
 | 
						|
  IN OUT EFI_IPSEC_FRAGMENT_DATA     **FragmentTable,
 | 
						|
  IN OUT UINT32                      *FragmentCount,
 | 
						|
  IN     IPSEC_SAD_ENTRY             *SadEntry,
 | 
						|
     OUT EFI_EVENT                   *RecycleEvent
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (SadEntry->Id->Proto == EfiIPsecESP) {
 | 
						|
    //
 | 
						|
    // Process the esp ipsec header of the outbound traffic.
 | 
						|
    //
 | 
						|
    return IpSecEspOutboundPacket (
 | 
						|
             IpVersion,
 | 
						|
             IpHead,
 | 
						|
             LastHead,
 | 
						|
             OptionsBuffer,
 | 
						|
             OptionsLength,
 | 
						|
             FragmentTable,
 | 
						|
             FragmentCount,
 | 
						|
             SadEntry,
 | 
						|
             RecycleEvent
 | 
						|
             );
 | 
						|
  }
 | 
						|
  //
 | 
						|
  // The other protocols are not supported.
 | 
						|
  //
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 |