/** @file The mian interface of IPsec Protocol. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "IpSecConfigImpl.h" #include "IpSecImpl.h" EFI_IPSEC2_PROTOCOL mIpSecInstance = { IpSecProcess, NULL, TRUE }; /** Handles IPsec packet processing for inbound and outbound IP packets. The EFI_IPSEC_PROCESS process routine handles each inbound or outbound packet. The behavior is that it can perform one of the following actions: bypass the packet, discard the packet, or protect the packet. @param[in] This Pointer to the EFI_IPSEC2_PROTOCOL instance. @param[in] NicHandle Instance of the network interface. @param[in] IpVersion IPV4 or IPV6. @param[in, out] IpHead Pointer to the IP Header. @param[in, out] LastHead The protocol of the next layer to be processed by IPsec. @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. @param[in, out] FragmentCount Number of fragments. @param[in] TrafficDirection Traffic direction. @param[out] RecycleSignal Event for recycling of resources. @retval EFI_SUCCESS The packet was bypassed and all buffers remain the same. @retval EFI_SUCCESS The packet was protected. @retval EFI_ACCESS_DENIED The packet was discarded. **/ EFI_STATUS EFIAPI IpSecProcess ( IN EFI_IPSEC2_PROTOCOL *This, IN EFI_HANDLE NicHandle, 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 EFI_IPSEC_TRAFFIC_DIR TrafficDirection, OUT EFI_EVENT *RecycleSignal ) { IPSEC_PRIVATE_DATA *Private; IPSEC_SPD_ENTRY *SpdEntry; EFI_IPSEC_SPD_SELECTOR *SpdSelector; IPSEC_SAD_ENTRY *SadEntry; LIST_ENTRY *SpdList; LIST_ENTRY *Entry; EFI_IPSEC_ACTION Action; EFI_STATUS Status; UINT8 *IpPayload; UINT8 OldLastHead; BOOLEAN IsOutbound; if (OptionsBuffer == NULL || OptionsLength == NULL || FragmentTable == NULL || FragmentCount == NULL ) { return EFI_INVALID_PARAMETER; } Private = IPSEC_PRIVATE_DATA_FROM_IPSEC (This); IpPayload = (*FragmentTable)[0].FragmentBuffer; IsOutbound = (BOOLEAN) ((TrafficDirection == EfiIPsecOutBound) ? TRUE : FALSE); OldLastHead = *LastHead; *RecycleSignal = NULL; SpdList = &mConfigData[IPsecConfigDataTypeSpd]; if (!IsOutbound) { // // For inbound traffic, process the ipsec header of the packet. // Status = IpSecProtectInboundPacket ( IpVersion, IpHead, LastHead, OptionsBuffer, OptionsLength, FragmentTable, FragmentCount, &SpdSelector, RecycleSignal ); if (Status == EFI_ACCESS_DENIED || Status == EFI_OUT_OF_RESOURCES) { // // The packet is denied to access. // goto ON_EXIT; } if (Status == EFI_SUCCESS) { // // Check the spd entry if the packet is accessible. // if (SpdSelector == NULL) { Status = EFI_ACCESS_DENIED; goto ON_EXIT; } Status = EFI_ACCESS_DENIED; NET_LIST_FOR_EACH (Entry, SpdList) { SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); if (IsSubSpdSelector ( (EFI_IPSEC_CONFIG_SELECTOR *) SpdSelector, (EFI_IPSEC_CONFIG_SELECTOR *) SpdEntry->Selector )) { Status = EFI_SUCCESS; } } goto ON_EXIT; } } Status = EFI_ACCESS_DENIED; NET_LIST_FOR_EACH (Entry, SpdList) { // // For outbound and non-ipsec Inbound traffic: check the spd entry. // SpdEntry = IPSEC_SPD_ENTRY_FROM_LIST (Entry); if (EFI_ERROR (IpSecLookupSpdEntry ( SpdEntry, IpVersion, IpHead, IpPayload, OldLastHead, IsOutbound, &Action ))) { // // If the related SPD not find // continue; } switch (Action) { case EfiIPsecActionProtect: if (IsOutbound) { // // For outbound traffic, lookup the sad entry. // Status = IpSecLookupSadEntry ( Private, NicHandle, IpVersion, IpHead, IpPayload, OldLastHead, SpdEntry, &SadEntry ); if (SadEntry != NULL) { // // Process the packet by the found sad entry. // Status = IpSecProtectOutboundPacket ( IpVersion, IpHead, LastHead, OptionsBuffer, OptionsLength, FragmentTable, FragmentCount, SadEntry, RecycleSignal ); } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) { // // TODO: if no need return not ready to upper layer, change here. // Status = EFI_SUCCESS; } } else if (OldLastHead == IP6_ICMP && *IpPayload != ICMP_V6_ECHO_REQUEST) { // // For inbound icmpv6 traffic except ping request, accept the packet // although no sad entry associated with protect spd entry. // Status = IpSecLookupSadEntry ( Private, NicHandle, IpVersion, IpHead, IpPayload, OldLastHead, SpdEntry, &SadEntry ); if (SadEntry == NULL) { Status = EFI_SUCCESS; } } goto ON_EXIT; case EfiIPsecActionBypass: Status = EFI_SUCCESS; goto ON_EXIT; case EfiIPsecActionDiscard: goto ON_EXIT; } } // // If don't find the related SPD entry, return the EFI_ACCESS_DENIED and discard it. // But it the packet is NS/NA, it should be by passed even not find the related SPD entry. // if (OldLastHead == IP6_ICMP && (*IpPayload == ICMP_V6_NEIGHBOR_SOLICIT || *IpPayload == ICMP_V6_NEIGHBOR_ADVERTISE) ){ Status = EFI_SUCCESS; } ON_EXIT: return Status; }