Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Bruce Cran <bruce@cran.org.uk> Cc: Siyuan Fu <siyuan.fu@intel.com> Reviewed-by: Fu Siyuan <siyuan.fu@intel.com> [lersek@redhat.com: updated patch based on Siyuan's feedback] Signed-off-by: Laszlo Ersek <lersek@redhat.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@18045 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2178 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2178 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   The implementation of IPsec.
 | |
| 
 | |
|   (C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
 | |
|   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;
 | |
|   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;
 | |
|   CountD  = 0;
 | |
|   CountF  = 0;
 | |
|   CountA  = 0;
 | |
| 
 | |
|   while (Pointer <= ExtHdrsLen) {
 | |
| 
 | |
|     switch (*NextHeader) {
 | |
|     case IP6_HOP_BY_HOP:
 | |
|       if (Pointer != 0) {
 | |
|         return FALSE;
 | |
|       }
 | |
| 
 | |
|     //
 | |
|     // 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;
 | |
| }
 |