audk/NetworkPkg/IpSecDxe/IpSecMain.c

237 lines
7.2 KiB
C

/** @file
The mian interface of IPsec Protocol.
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
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;
}