You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1541 lines
48 KiB
1541 lines
48 KiB
/*++
|
|
|
|
Copyright (c) 1997-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
offload.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code that handles offload.
|
|
|
|
Author:
|
|
|
|
ChunYe
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
#ifdef RUN_WPP
|
|
#include "offload.tmh"
|
|
#endif
|
|
|
|
|
|
VOID
|
|
IPSecFillHwAddSA(
|
|
IN PSA_TABLE_ENTRY pSA,
|
|
IN PUCHAR Buf,
|
|
IN ULONG Len
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fills in the ADD_SA hw request from pSA
|
|
|
|
Arguments:
|
|
|
|
pSA - the SA
|
|
Buf - buffer to set info
|
|
Len - length
|
|
|
|
Return Value:
|
|
|
|
status of the operation
|
|
|
|
--*/
|
|
{
|
|
POFFLOAD_IPSEC_ADD_SA pAddSA = (POFFLOAD_IPSEC_ADD_SA)Buf;
|
|
POFFLOAD_SECURITY_ASSOCIATION pSAInfo;
|
|
LONG Index;
|
|
ULONG Offset = 0;
|
|
|
|
pAddSA->NumSAs = (SHORT)pSA->sa_NumOps;
|
|
pAddSA->SrcAddr = pSA->SA_SRC_ADDR;
|
|
pAddSA->SrcMask = pSA->SA_SRC_MASK;
|
|
pAddSA->DestAddr = pSA->SA_DEST_ADDR;
|
|
pAddSA->DestMask = pSA->SA_DEST_MASK;
|
|
pAddSA->Protocol = pSA->SA_PROTO;
|
|
pAddSA->SrcPort = SA_SRC_PORT(pSA);
|
|
pAddSA->DestPort = SA_DEST_PORT(pSA);
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_OUTBOUND) {
|
|
pAddSA->Flags |= OFFLOAD_OUTBOUND_SA;
|
|
} else {
|
|
pAddSA->Flags |= OFFLOAD_INBOUND_SA;
|
|
}
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_TUNNEL) {
|
|
pAddSA->DestTunnelAddr = pSA->sa_TunnelAddr;
|
|
pAddSA->SrcTunnelAddr = pSA->sa_SrcTunnelAddr;
|
|
}
|
|
|
|
for (Index = 0; Index < pSA->sa_NumOps; Index++) {
|
|
pSAInfo = &pAddSA->SecAssoc[Index];
|
|
|
|
pSAInfo->Operation = pSA->sa_Operation[Index];
|
|
pSAInfo->SPI = pSA->sa_OtherSPIs[Index];
|
|
pSAInfo->EXT_INT_ALGO = pSA->INT_ALGO(Index);
|
|
pSAInfo->EXT_INT_KEYLEN = pSA->INT_KEYLEN(Index);
|
|
pSAInfo->EXT_INT_ROUNDS = pSA->INT_ROUNDS(Index);
|
|
|
|
pSAInfo->EXT_CONF_ALGO = pSA->CONF_ALGO(Index);
|
|
pSAInfo->EXT_CONF_KEYLEN = pSA->CONF_KEYLEN(Index);
|
|
pSAInfo->EXT_CONF_ROUNDS = pSA->CONF_ROUNDS(Index);
|
|
|
|
//
|
|
// now get the keys in
|
|
//
|
|
ASSERT(Len >= sizeof(OFFLOAD_IPSEC_ADD_SA) + pSA->INT_KEYLEN(Index) + pSA->CONF_KEYLEN(Index));
|
|
|
|
RtlCopyMemory( pAddSA->KeyMat + Offset,
|
|
pSA->CONF_KEY(Index),
|
|
pSA->CONF_KEYLEN(Index));
|
|
|
|
RtlCopyMemory( pAddSA->KeyMat + Offset + pSA->CONF_KEYLEN(Index),
|
|
pSA->INT_KEY(Index),
|
|
pSA->INT_KEYLEN(Index));
|
|
|
|
Offset += pSA->INT_KEYLEN(Index) + pSA->CONF_KEYLEN(Index);
|
|
pAddSA->KeyLen += Offset;
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("pAddSA: %p, keylen: %lx, KeyMat: %p", pAddSA, pAddSA->KeyLen, pAddSA->KeyMat));
|
|
}
|
|
}
|
|
|
|
|
|
NDIS_STATUS
|
|
IPSecPlumbHw(
|
|
IN PVOID DestIF,
|
|
IN PVOID Buf,
|
|
IN ULONG Len,
|
|
IN NDIS_OID Oid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Plumbs the input outbound and its corresponding inbound SA
|
|
into the hw accelerator.
|
|
|
|
Arguments:
|
|
|
|
DestIF - the IP Interface
|
|
Buf - buffer to set info
|
|
Len - length
|
|
|
|
Return Value:
|
|
|
|
status of the operation
|
|
|
|
--*/
|
|
{
|
|
#if DBG
|
|
NTSTATUS status;
|
|
|
|
if (Oid == OID_TCP_TASK_IPSEC_ADD_SA) {
|
|
IPSEC_INCREMENT(NumAddSA);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_DELETE_SA) {
|
|
IPSEC_INCREMENT(NumDelSA);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_ADD_UDPESP_SA) {
|
|
IPSEC_INCREMENT(NumAddEncapSA);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA) {
|
|
IPSEC_INCREMENT(NumDelEncapSA);
|
|
}
|
|
|
|
status = TCPIP_NDIS_REQUEST(DestIF,
|
|
NdisRequestSetInformation,
|
|
Oid,
|
|
Buf,
|
|
Len,
|
|
NULL);
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
if (Oid == OID_TCP_TASK_IPSEC_ADD_SA) {
|
|
IPSEC_INCREMENT(NumAddSU);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_DELETE_SA) {
|
|
IPSEC_INCREMENT(NumDelSU);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_ADD_UDPESP_SA) {
|
|
IPSEC_INCREMENT(NumAddEncapSU);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA) {
|
|
IPSEC_INCREMENT(NumDelEncapSU);
|
|
}
|
|
} else {
|
|
if (Oid == OID_TCP_TASK_IPSEC_ADD_SA) {
|
|
IPSEC_INCREMENT(NumAddFA);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_DELETE_SA) {
|
|
IPSEC_INCREMENT(NumDelFA);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_ADD_UDPESP_SA) {
|
|
IPSEC_INCREMENT(NumAddEncapFA);
|
|
}
|
|
if (Oid == OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA) {
|
|
IPSEC_INCREMENT(NumDelEncapFA);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
#else
|
|
return TCPIP_NDIS_REQUEST( DestIF,
|
|
NdisRequestSetInformation,
|
|
Oid,
|
|
Buf,
|
|
Len,
|
|
NULL);
|
|
#endif
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecSendOffload(
|
|
IN IPHeader UNALIGNED *pIPHeader,
|
|
IN PNDIS_PACKET Packet,
|
|
IN Interface *DestIF,
|
|
IN PSA_TABLE_ENTRY pSA,
|
|
IN PSA_TABLE_ENTRY pNextSA,
|
|
IN PVOID *ppSCContext,
|
|
IN BOOLEAN *pfCryptoOnly
|
|
)
|
|
{
|
|
KIRQL kIrql;
|
|
BOOLEAN fRefBumped = FALSE;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PSA_TABLE_ENTRY pSaveSA = NULL;
|
|
PIPSEC_SEND_COMPLETE_CONTEXT pContext = NULL;
|
|
PNDIS_IPSEC_PACKET_INFO IPSecPktInfo = NULL;
|
|
PNDIS_PACKET_EXTENSION PktExt = NDIS_PACKET_EXTENSION_FROM_PACKET(Packet);
|
|
BOOL bTptOverTunCheckDone = FALSE;
|
|
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("IPSecSendOffload: DestIF: %p, DestIF->Flags: %lx", DestIF, DestIF->if_IPSecOffloadFlags));
|
|
|
|
*pfCryptoOnly = FALSE;
|
|
|
|
//
|
|
// See if options are supported.
|
|
//
|
|
if (((pIPHeader->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2) > sizeof(IPHeader) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_V4_OPTIONS)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Options present - not offloading the packet. HdrLen %d",
|
|
((pIPHeader->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2)));
|
|
return (status);
|
|
}
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
do {
|
|
if ((pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) &&
|
|
pSA->sa_IPIF == DestIF) {
|
|
|
|
if (*ppSCContext == NULL) {
|
|
pContext = IPSecAllocateSendCompleteCtx(IPSEC_TAG_HW);
|
|
|
|
if (!pContext) {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Failed to alloc. SendCtx"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
*ppSCContext = NULL;
|
|
break;
|
|
}
|
|
|
|
IPSEC_INCREMENT(g_ipsec.NumSends);
|
|
|
|
IPSecZeroMemory(pContext, sizeof(IPSEC_SEND_COMPLETE_CONTEXT));
|
|
|
|
#if DBG
|
|
RtlCopyMemory(pContext->Signature, "ISC5", 4);
|
|
#endif
|
|
*ppSCContext = pContext;
|
|
} else {
|
|
pContext = *ppSCContext;
|
|
}
|
|
|
|
if (IPSecPktInfo == NULL) {
|
|
if (IPSecPktInfo = IPSecAllocatePktInfo(IPSEC_TAG_HW_PKTINFO)) {
|
|
IPSecZeroMemory(IPSecPktInfo, sizeof(NDIS_IPSEC_PACKET_INFO));
|
|
|
|
pContext->Flags |= SCF_PKTINFO;
|
|
pContext->PktInfo = IPSecPktInfo;
|
|
} else {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Failed to alloc. PktInfo"));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PktExt->NdisPacketInfo[IpSecPacketInfo] = IPSecPktInfo;
|
|
|
|
//
|
|
// if this is the nextOperationSA
|
|
//
|
|
if (fRefBumped) {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Offloading... pSA: %p, NextOffloadHandle %p", pSA, pSA->sa_OffloadHandle));
|
|
IPSecPktInfo->Transmit.NextOffloadHandle = pSA->sa_OffloadHandle;
|
|
} else {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Offloading... pSA: %p, OffloadHandle %p", pSA, pSA->sa_OffloadHandle));
|
|
IPSecPktInfo->Transmit.OffloadHandle = pSA->sa_OffloadHandle;
|
|
}
|
|
|
|
*pfCryptoOnly = TRUE;
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Using Hw for SA->handle: %p on IF: %p IPSecPktInfo: %p *pfCryptoOnly %d", pSA->sa_OffloadHandle, DestIF, IPSecPktInfo, *pfCryptoOnly));
|
|
|
|
status = STATUS_SUCCESS;
|
|
} else if (!(pSA->sa_Flags & FLAGS_SA_HW_PLUMB_FAILED) && !pSA->sa_IPIF) {
|
|
PUCHAR outBuf;
|
|
ULONG outLen;
|
|
LONG Index;
|
|
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMB_FAILED;
|
|
|
|
/* DELETE
|
|
if ((SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType ) ||
|
|
(pNextSA && (SA_UDP_ENCAP_TYPE_OTHER == pNextSA->sa_EncapType ))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}*/
|
|
|
|
//
|
|
// See if CryptoOnly mode is supported.
|
|
//
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_CRYPTO_ONLY)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// No need to offload soft SAs.
|
|
//
|
|
if (pSA->sa_Operation[0] == None) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// See if transport over tunnel mode is supported.
|
|
//
|
|
if (pNextSA && !bTptOverTunCheckDone) {
|
|
bTptOverTunCheckDone = TRUE;
|
|
ASSERT((pNextSA->sa_Flags & FLAGS_SA_TUNNEL));
|
|
if (SA_UDP_ENCAP_TYPE_IKE == pNextSA->sa_EncapType ) {
|
|
ASSERT((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType));
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_IKE)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
else if (SA_UDP_ENCAP_TYPE_OTHER == pNextSA->sa_EncapType){
|
|
ASSERT((SA_UDP_ENCAP_TYPE_OTHER==pSA->sa_EncapType));
|
|
if ( !(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_OVER_TUNNEL_UDPESP_OTHER)){
|
|
status=STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (SA_UDP_ENCAP_TYPE_IKE==pSA->sa_EncapType ) {
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_IKE)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
if (SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType ) {
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OVER_PURE_TUNNEL_OTHER)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_TUNNEL)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tunnel required, but not supported, don't plumb.
|
|
//
|
|
if (pSA->sa_Flags & FLAGS_SA_TUNNEL) {
|
|
if ((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) ||(SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType)) {
|
|
ASSERT(IS_AH_SA(pSA) == 0);
|
|
ASSERT(IS_ESP_SA(pSA));
|
|
ASSERT(pSA->sa_NumOps == 1);
|
|
if ((!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE) && (SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType ))
|
|
||(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER) && (SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType)))
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if ((IS_AH_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TUNNEL)) ||
|
|
(IS_ESP_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TUNNEL))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if ((SA_UDP_ENCAP_TYPE_IKE==pSA->sa_EncapType) ||(SA_UDP_ENCAP_TYPE_OTHER==pSA->sa_EncapType)) {
|
|
ASSERT(IS_AH_SA(pSA) == 0);
|
|
ASSERT(IS_ESP_SA(pSA));
|
|
ASSERT(pSA->sa_NumOps == 1);
|
|
if (((!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_IKE)) && (SA_UDP_ENCAP_TYPE_IKE==pSA->sa_EncapType))
|
|
||((!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OTHER)) && (SA_UDP_ENCAP_TYPE_OTHER==pSA->sa_EncapType))){
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if ((IS_AH_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TPT)) ||
|
|
(IS_ESP_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TPT))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// AH + ESP required, but not supported, don't plumb.
|
|
//
|
|
if (pSA->sa_NumOps > 1 &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_ESP)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check XMT capabilities.
|
|
//
|
|
if ((IS_AH_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_XMT)) ||
|
|
(IS_ESP_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_XMT))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
if ((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) ||(SA_UDP_ENCAP_TYPE_OTHER==pSA->sa_EncapType )) {
|
|
outLen = sizeof(OFFLOAD_IPSEC_ADD_UDPESP_SA);
|
|
}
|
|
else {
|
|
outLen = sizeof(OFFLOAD_IPSEC_ADD_SA);
|
|
}
|
|
|
|
for (Index = 0; Index < pSA->sa_NumOps; Index++) {
|
|
//
|
|
// Check offload capability bits with those in the SA.
|
|
//
|
|
if ((pSA->INT_ALGO(Index) == IPSEC_AH_MD5) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_MD5)) ||
|
|
((pSA->INT_ALGO(Index) == IPSEC_AH_SHA) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_SHA_1))) ||
|
|
((pSA->CONF_ALGO(Index) == IPSEC_ESP_NONE) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_NONE))) ||
|
|
((pSA->CONF_ALGO(Index) == IPSEC_ESP_DES) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_DES))) ||
|
|
((pSA->CONF_ALGO(Index) == IPSEC_ESP_3_DES) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_3_DES)))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
|
|
outLen += pSA->INT_KEYLEN(Index) + pSA->CONF_KEYLEN(Index);
|
|
}
|
|
|
|
//
|
|
// This SA can be offloaded.
|
|
//
|
|
pSA->sa_Flags |= FLAGS_SA_OFFLOADABLE;
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("outLen: %lx", outLen));
|
|
|
|
outBuf = IPSecAllocateMemory(outLen, IPSEC_TAG_HW_ADDSA);
|
|
|
|
if (outBuf) {
|
|
|
|
IPSecZeroMemory(outBuf, outLen);
|
|
|
|
if ((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) || (SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType )){
|
|
IPSecFillHwAddEncapSa(pSA, NULL, outBuf, outLen);
|
|
}
|
|
else {
|
|
IPSecFillHwAddSA(pSA, outBuf, outLen);
|
|
}
|
|
|
|
//
|
|
// Bump the SA reference count to make sure they won't
|
|
// go away during the processing of the work item.
|
|
//
|
|
IPSecRefSA(pSA);
|
|
|
|
//
|
|
// Plumb the SA by scheduling a work item; the SA will
|
|
// not be used for offload until plumbing succeeds.
|
|
//
|
|
IPSecBufferPlumbSA( DestIF,
|
|
pSA,
|
|
NULL,
|
|
outBuf,
|
|
outLen);
|
|
|
|
//
|
|
// Return failure here so the caller does it in software.
|
|
//
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
} else {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Memory: Failed to plumb outboundSA: %p on IF: %p", pSA, DestIF));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
} else {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
if (pNextSA && !fRefBumped) {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("RefBumped on SA: %p", pSA));
|
|
pSaveSA = pSA;
|
|
pSA = pNextSA;
|
|
fRefBumped = TRUE;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (TRUE);
|
|
|
|
out:
|
|
if (status == STATUS_SUCCESS && (*pfCryptoOnly)) {
|
|
ASSERT(pContext);
|
|
ASSERT(pContext->Flags & SCF_PKTINFO);
|
|
|
|
if (fRefBumped) {
|
|
IPSecRefSA(pSaveSA);
|
|
IPSecRefSA(pNextSA);
|
|
IPSEC_INCREMENT(pSaveSA->sa_NumSends);
|
|
IPSEC_INCREMENT(pNextSA->sa_NumSends);
|
|
pContext->pSA = pSaveSA;
|
|
pContext->pNextSA = pNextSA;
|
|
} else {
|
|
IPSecRefSA(pSA);
|
|
IPSEC_INCREMENT(pSA->sa_NumSends);
|
|
pContext->pSA = pSA;
|
|
}
|
|
} else {
|
|
if (IPSecPktInfo) {
|
|
ASSERT(pContext);
|
|
ASSERT(pContext->Flags & SCF_PKTINFO);
|
|
|
|
IPSecFreePktInfo(pContext->PktInfo);
|
|
|
|
pContext->Flags &= ~SCF_PKTINFO;
|
|
pContext->PktInfo = NULL;
|
|
|
|
PktExt->NdisPacketInfo[IpSecPacketInfo] = NULL;
|
|
}
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
*pfCryptoOnly = FALSE;
|
|
}
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecRecvOffload(
|
|
IN IPHeader UNALIGNED *pIPHeader,
|
|
IN Interface *DestIF,
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
{
|
|
KIRQL kIrql;
|
|
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
|
PPARSER_IFENTRY pParserIfEntry = NULL;
|
|
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("IPSecRecvOffload: DestIF: %p, DestIF->Flags: %lx", DestIF, DestIF->if_IPSecOffloadFlags));
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
// Make sure SA is fully associated
|
|
if (!pSA->sa_AssociatedSA) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
|
|
if (!(pSA->sa_Flags & FLAGS_SA_HW_PLUMB_FAILED) &&
|
|
!(pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) &&
|
|
!pSA->sa_IPIF) {
|
|
PUCHAR inBuf;
|
|
ULONG inLen;
|
|
LONG Index;
|
|
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMB_FAILED;
|
|
|
|
/* DELETE
|
|
if (SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType){
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
*/
|
|
|
|
//
|
|
// See if CryptoOnly mode is supported.
|
|
//
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_CRYPTO_ONLY)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// No need to offload soft SAs.
|
|
//
|
|
if (pSA->sa_Operation[0] == None) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Tunnel required, but not supported, don't plumb.
|
|
//
|
|
if (pSA->sa_Flags & FLAGS_SA_TUNNEL) {
|
|
if (SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) {
|
|
ASSERT(IS_AH_SA(pSA) == 0);
|
|
ASSERT(IS_ESP_SA(pSA));
|
|
ASSERT(pSA->sa_NumOps == 1);
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_IKE)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
if (SA_UDP_ENCAP_TYPE_OTHER== pSA->sa_EncapType) {
|
|
ASSERT(IS_AH_SA(pSA) == 0);
|
|
ASSERT(IS_ESP_SA(pSA));
|
|
ASSERT(pSA->sa_NumOps == 1);
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TUNNEL_UDPESP_OTHER )) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
}
|
|
else {
|
|
if ((IS_AH_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TUNNEL)) ||
|
|
(IS_ESP_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TUNNEL))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) {
|
|
ASSERT(IS_AH_SA(pSA) == 0);
|
|
ASSERT(IS_ESP_SA(pSA));
|
|
ASSERT(pSA->sa_NumOps == 1);
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_IKE)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
}
|
|
else
|
|
if (SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType) {
|
|
ASSERT(IS_AH_SA(pSA) == 0);
|
|
ASSERT(IS_ESP_SA(pSA));
|
|
ASSERT(pSA->sa_NumOps == 1);
|
|
if (!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_TPT_UDPESP_OTHER)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
}
|
|
else {
|
|
if ((IS_AH_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_TPT)) ||
|
|
(IS_ESP_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_TPT))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// AH + ESP required, but not supported, don't plumb.
|
|
//
|
|
if (pSA->sa_NumOps > 1 &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_ESP)) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
|
|
//
|
|
// Check RCV capabilities.
|
|
//
|
|
if ((IS_AH_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_RCV)) ||
|
|
(IS_ESP_SA(pSA) &&
|
|
!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_RCV))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
|
|
if ((SA_UDP_ENCAP_TYPE_IKE==pSA->sa_EncapType) || (SA_UDP_ENCAP_TYPE_OTHER==pSA->sa_EncapType)){
|
|
inLen = sizeof(OFFLOAD_IPSEC_ADD_UDPESP_SA);
|
|
}
|
|
else {
|
|
inLen = sizeof(OFFLOAD_IPSEC_ADD_SA);
|
|
}
|
|
|
|
for (Index = 0; Index < pSA->sa_NumOps; Index++) {
|
|
//
|
|
// Check offload capability bits with those in the SA
|
|
//
|
|
if ((pSA->INT_ALGO(Index) == IPSEC_AH_MD5) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_MD5)) ||
|
|
((pSA->INT_ALGO(Index) == IPSEC_AH_SHA) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_AH_SHA_1))) ||
|
|
((pSA->CONF_ALGO(Index) == IPSEC_ESP_NONE) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_NONE))) ||
|
|
((pSA->CONF_ALGO(Index) == IPSEC_ESP_DES) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_DES))) ||
|
|
((pSA->CONF_ALGO(Index) == IPSEC_ESP_3_DES) &&
|
|
(!(DestIF->if_IPSecOffloadFlags & IPSEC_OFFLOAD_ESP_3_DES)))) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
|
|
inLen += pSA->INT_KEYLEN(Index) + pSA->CONF_KEYLEN(Index);
|
|
}
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("inLen: %lx", inLen));
|
|
|
|
inBuf = IPSecAllocateMemory(inLen, IPSEC_TAG_HW_ADDSA);
|
|
|
|
if (inBuf) {
|
|
|
|
IPSecZeroMemory(inBuf, inLen);
|
|
|
|
if ((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) || (SA_UDP_ENCAP_TYPE_OTHER==pSA->sa_EncapType)){
|
|
status = GetParserEntry(pSA, DestIF, &pParserIfEntry);
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSecFreeMemory(inBuf);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
IPSecFillHwAddEncapSa(pSA, pParserIfEntry, inBuf, inLen);
|
|
}
|
|
else {
|
|
IPSecFillHwAddSA(pSA, inBuf, inLen);
|
|
}
|
|
|
|
//
|
|
// Bump the SA reference count to make sure they won't
|
|
// go away during the processing of the work item.
|
|
//
|
|
IPSecRefSA(pSA);
|
|
|
|
//
|
|
// Plumb the SA by scheduling a work item; the SA will
|
|
// not be used for offload until plumbing succeeds.
|
|
//
|
|
status = IPSecBufferPlumbSA(DestIF,
|
|
pSA,
|
|
pParserIfEntry,
|
|
inBuf,
|
|
inLen);
|
|
|
|
//
|
|
// Return failure here so the caller does it in software.
|
|
//
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
} else {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Memory: Failed to plumb inboundSA: %p on IF: %p", pSA, DestIF));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecDelHWSA(
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
OFFLOAD_IPSEC_DELETE_UDPESP_SA OffldDelUdpEspSa;
|
|
KIRQL kIrql = 0;
|
|
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_HW_PLUMBED);
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_PLUMBED;
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMB_FAILED;
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_OUTBOUND) &&
|
|
IPSEC_GET_VALUE(pSA->sa_NumSends) > 0) {
|
|
pSA->sa_Flags |= FLAGS_SA_HW_DELETE_SA;
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_DELETE_SA;
|
|
pSA->sa_Flags |= FLAGS_SA_HW_DELETE_QUEUED;
|
|
|
|
ASSERT(pSA->sa_IPIF);
|
|
|
|
if (pSA->sa_IPIF) {
|
|
/* DELETE
|
|
ASSERT((SA_UDP_ENCAP_TYPE_OTHER!=pSA->sa_EncapType));
|
|
*/
|
|
if ((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType ) || (SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType)){
|
|
|
|
OffldDelUdpEspSa.OffloadHandle = pSA->sa_OffloadHandle;
|
|
|
|
if (!(pSA->sa_Flags & FLAGS_SA_OUTBOUND)) {
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
OffldDelUdpEspSa.EncapTypeEntryOffldHandle = UploadParserEntryAndGetHandle(pSA, pSA->sa_IPIF);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
} else {
|
|
OffldDelUdpEspSa.EncapTypeEntryOffldHandle = NULL;
|
|
}
|
|
|
|
status = IPSecPlumbHw( pSA->sa_IPIF,
|
|
&OffldDelUdpEspSa,
|
|
sizeof(OFFLOAD_IPSEC_DELETE_UDPESP_SA),
|
|
OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA);
|
|
|
|
} else {
|
|
status = IPSecPlumbHw( pSA->sa_IPIF,
|
|
&pSA->sa_OffloadHandle,
|
|
sizeof(OFFLOAD_IPSEC_DELETE_SA),
|
|
OID_TCP_TASK_IPSEC_DELETE_SA);
|
|
}
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("DelHWSA %s: %p, handle: %p, status: %lx",
|
|
(pSA->sa_Flags & FLAGS_SA_OUTBOUND)? "outbound": "inbound",
|
|
pSA,
|
|
pSA->sa_OffloadHandle,
|
|
status));
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumOffloadedSAs);
|
|
|
|
IPSecDerefSA(pSA);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecDelHWSAAtDpc(
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
{
|
|
ASSERT(pSA->sa_Flags & (FLAGS_SA_HW_PLUMBED | FLAGS_SA_HW_DELETE_SA));
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_PLUMBED;
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMB_FAILED;
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_OUTBOUND) &&
|
|
IPSEC_GET_VALUE(pSA->sa_NumSends) > 0) {
|
|
pSA->sa_Flags |= FLAGS_SA_HW_DELETE_SA;
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_DELETE_SA;
|
|
pSA->sa_Flags |= FLAGS_SA_HW_DELETE_QUEUED;
|
|
|
|
ASSERT(pSA->sa_IPIF);
|
|
|
|
ExInitializeWorkItem( &pSA->sa_QueueItem,
|
|
IPSecProcessDeleteSA,
|
|
(PVOID)pSA);
|
|
|
|
ExQueueWorkItem(&pSA->sa_QueueItem, DelayedWorkQueue);
|
|
|
|
IPSEC_INCREMENT(g_ipsec.NumWorkers);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecBufferPlumbSA(
|
|
IN Interface *DestIF,
|
|
IN PSA_TABLE_ENTRY pSA,
|
|
PPARSER_IFENTRY pParserIfEntry,
|
|
IN PUCHAR Buf,
|
|
IN ULONG Len
|
|
)
|
|
{
|
|
PIPSEC_PLUMB_SA pPlumbSA;
|
|
|
|
pPlumbSA = IPSecAllocateMemory(sizeof(IPSEC_PLUMB_SA), IPSEC_TAG_HW_PLUMB);
|
|
if (pPlumbSA == NULL) {
|
|
IPSecFreeMemory(Buf);
|
|
|
|
if (pSA) {
|
|
IPSecDerefSA(pSA);
|
|
}
|
|
|
|
if (NULL != pParserIfEntry) {
|
|
DerefParserEntry (pParserIfEntry);
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
pPlumbSA->pSA = pSA;
|
|
pPlumbSA->pParserIfEntry = pParserIfEntry;
|
|
pPlumbSA->DestIF = DestIF;
|
|
pPlumbSA->Buf = Buf;
|
|
pPlumbSA->Len = Len;
|
|
|
|
ExInitializeWorkItem( &pPlumbSA->PlumbQueueItem,
|
|
IPSecProcessPlumbSA,
|
|
(PVOID)pPlumbSA);
|
|
|
|
ExQueueWorkItem(&pPlumbSA->PlumbQueueItem, DelayedWorkQueue);
|
|
|
|
IPSEC_INCREMENT(g_ipsec.NumWorkers);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecProcessPlumbSA(
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PIPSEC_PLUMB_SA pPlumbSA = (PIPSEC_PLUMB_SA)Context;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KIRQL kIrql;
|
|
Interface *DestIF = pPlumbSA->DestIF;
|
|
PSA_TABLE_ENTRY pSA = pPlumbSA->pSA;
|
|
PUCHAR Buf = pPlumbSA->Buf;
|
|
ULONG Len = pPlumbSA->Len;
|
|
PPARSER_IFENTRY pParserIfEntry = pPlumbSA->pParserIfEntry;
|
|
NDIS_OID Oid = OID_TCP_TASK_IPSEC_ADD_SA;
|
|
|
|
//
|
|
// Plumb this SA into the hw if acceleration is enabled
|
|
// on this card and it has not been plumbed already.
|
|
//
|
|
if (pSA) {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("About to plumb outbound"));
|
|
/* Delete
|
|
ASSERT(SA_UDP_ENCAP_TYPE_OTHER != pSA->sa_EncapType);
|
|
*/
|
|
if ((SA_UDP_ENCAP_TYPE_IKE== pSA->sa_EncapType) || (SA_UDP_ENCAP_TYPE_OTHER== pSA->sa_EncapType)){
|
|
Oid = OID_TCP_TASK_IPSEC_ADD_UDPESP_SA;
|
|
}
|
|
|
|
status = IPSecPlumbHw( DestIF,
|
|
Buf,
|
|
Len,
|
|
Oid);
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("AddHWSA %s: %p, handle: %p, status: %lx",
|
|
(pSA->sa_Flags & FLAGS_SA_OUTBOUND)? "outbound": "inbound",
|
|
pSA,
|
|
((POFFLOAD_IPSEC_ADD_SA)Buf)->OffloadHandle,
|
|
status));
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
pSA->sa_IPIF = DestIF;
|
|
|
|
if (status != STATUS_SUCCESS) {
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Failed to plumb SA: %p on IF: %p, status: %lx", pSA, DestIF, status));
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMB_FAILED;
|
|
IPSecDerefSA(pSA);
|
|
if (NULL != pParserIfEntry) {
|
|
DerefParserEntry(pParserIfEntry);
|
|
}
|
|
} else {
|
|
/* DELETE
|
|
ASSERT(SA_UDP_ENCAP_TYPE_OTHER != pSA->sa_EncapType);
|
|
*/
|
|
if ((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) || (SA_UDP_ENCAP_TYPE_OTHER == pSA->sa_EncapType)){
|
|
|
|
pSA->sa_OffloadHandle = ((POFFLOAD_IPSEC_ADD_UDPESP_SA)Buf)->OffloadHandle;
|
|
if (NULL != pParserIfEntry) {
|
|
pParserIfEntry->hParserIfOffload = ((POFFLOAD_IPSEC_ADD_UDPESP_SA)Buf)->EncapTypeEntryOffldHandle;
|
|
ASSERT (pParserIfEntry->hParserIfOffload != NULL);
|
|
}
|
|
|
|
} else {
|
|
pSA->sa_OffloadHandle = ((POFFLOAD_IPSEC_ADD_SA)Buf)->OffloadHandle;
|
|
}
|
|
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HW, ("Success plumb SA: %p, pSA->sa_OffloadHandle %p", pSA, pSA->sa_OffloadHandle));
|
|
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMBED;
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_PLUMB_FAILED;
|
|
|
|
IPSEC_INC_STATISTIC(dwNumOffloadedSAs);
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS &&
|
|
!(pSA->sa_State == STATE_SA_ACTIVE &&
|
|
(pSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) &&
|
|
pSA->sa_AssociatedSA)) {
|
|
//
|
|
// SA got deleted before we plumb, call DelHWSA now since
|
|
// HW_PLUMBED wasn't set when the SA was deleted.
|
|
//
|
|
IPSecDelHWSAAtDpc(pSA);
|
|
}
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
}
|
|
|
|
IPSecFreeMemory(Buf);
|
|
IPSecFreeMemory(pPlumbSA);
|
|
|
|
IPSEC_DECREMENT(g_ipsec.NumWorkers);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecProcessDeleteSA(
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
PSA_TABLE_ENTRY pSA = (PSA_TABLE_ENTRY)Context;
|
|
NTSTATUS status;
|
|
OFFLOAD_IPSEC_DELETE_UDPESP_SA OffldDelUdpEspSa;
|
|
KIRQL kIrql = 0;
|
|
|
|
ASSERT(IPSEC_GET_VALUE(pSA->sa_NumSends) == 0);
|
|
/* DELETE
|
|
ASSERT(SA_UDP_ENCAP_TYPE_OTHER != pSA->sa_EncapType);
|
|
*/
|
|
|
|
if ((SA_UDP_ENCAP_TYPE_IKE == pSA->sa_EncapType) || (SA_UDP_ENCAP_TYPE_OTHER ==pSA->sa_EncapType)) {
|
|
|
|
OffldDelUdpEspSa.OffloadHandle = pSA->sa_OffloadHandle;
|
|
|
|
if (!(pSA->sa_Flags & FLAGS_SA_OUTBOUND)) {
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
OffldDelUdpEspSa.EncapTypeEntryOffldHandle = UploadParserEntryAndGetHandle(pSA, pSA->sa_IPIF);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
} else {
|
|
OffldDelUdpEspSa.EncapTypeEntryOffldHandle = NULL;
|
|
}
|
|
|
|
status = IPSecPlumbHw( pSA->sa_IPIF,
|
|
&OffldDelUdpEspSa,
|
|
sizeof(OFFLOAD_IPSEC_DELETE_UDPESP_SA),
|
|
OID_TCP_TASK_IPSEC_DELETE_UDPESP_SA);
|
|
|
|
} else {
|
|
|
|
status = IPSecPlumbHw( pSA->sa_IPIF,
|
|
&pSA->sa_OffloadHandle,
|
|
sizeof(OFFLOAD_IPSEC_DELETE_SA),
|
|
OID_TCP_TASK_IPSEC_DELETE_SA);
|
|
|
|
}
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("ProcessDeleteSA %s: %p, handle: %p, status: %lx",
|
|
(pSA->sa_Flags & FLAGS_SA_OUTBOUND)? "outbound": "inbound",
|
|
pSA,
|
|
pSA->sa_OffloadHandle,
|
|
status));
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumOffloadedSAs);
|
|
|
|
IPSecDerefSA(pSA);
|
|
|
|
IPSEC_DECREMENT(g_ipsec.NumWorkers);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecNdisStatus(
|
|
IN PVOID IPContext,
|
|
IN UINT Status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify Interface has a NDIS status change.
|
|
|
|
Arguments:
|
|
|
|
IPContext - This is the Interface notified of status change.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("IPSecNdisStatus %lx called for interface %p", Status, IPContext));
|
|
|
|
switch (Status) {
|
|
case NDIS_STATUS_NETWORK_UNREACHABLE:
|
|
IPSecDeleteIF(IPContext);
|
|
break;
|
|
|
|
case NDIS_STATUS_RESET_START:
|
|
IPSecResetStart(IPContext);
|
|
break;
|
|
|
|
case NDIS_STATUS_RESET_END:
|
|
IPSecResetEnd(IPContext);
|
|
break;
|
|
|
|
case NDIS_STATUS_INTERFACE_UP:
|
|
IPSecWakeUp(IPContext);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecDeleteIF(
|
|
IN PVOID IPContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify Interface is deleted so need to clean up SA's that are offloaded
|
|
on the deleted interface.
|
|
|
|
Arguments:
|
|
|
|
IPContext - This is the Interface being deleted.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
Interface *DestIF = (Interface *)IPContext;
|
|
PLIST_ENTRY pFilterEntry;
|
|
PLIST_ENTRY pSAEntry;
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pSA;
|
|
KIRQL kIrql;
|
|
LONG Index;
|
|
LONG SAIndex;
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("IPSecDeleteIF called for interface %p", DestIF));
|
|
|
|
//
|
|
// Go through all SA's and unmark the offload bits.
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
for ( Index = MIN_FILTER;
|
|
Index <= MAX_FILTER;
|
|
Index++) {
|
|
|
|
for ( pFilterEntry = g_ipsec.FilterList[Index].Flink;
|
|
pFilterEntry != &g_ipsec.FilterList[Index];
|
|
pFilterEntry = pFilterEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pFilterEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex++) {
|
|
|
|
for ( pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
pSAEntry != &pFilter->SAChain[SAIndex];
|
|
pSAEntry = pSAEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) &&
|
|
(DestIF == pSA->sa_IPIF)) {
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_PLUMBED;
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMB_FAILED;
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_DELETE_SA;
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumOffloadedSAs);
|
|
|
|
#if DBG
|
|
NumReset++;
|
|
#endif
|
|
|
|
IPSecDerefSA(pSA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FlushParserEntriesForInterface(DestIF);
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecResetStart(
|
|
IN PVOID IPContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify Interface is being reset.
|
|
|
|
Arguments:
|
|
|
|
IPContext - This is the Interface being reset.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
Interface *DestIF = (Interface *)IPContext;
|
|
PLIST_ENTRY pFilterEntry;
|
|
PLIST_ENTRY pSAEntry;
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pSA;
|
|
KIRQL kIrql;
|
|
LONG Index;
|
|
LONG SAIndex;
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("IPSecResetStart called for interface %p", DestIF));
|
|
|
|
//
|
|
// Go through all SA's and unmark the offload bits.
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
for ( Index = MIN_FILTER;
|
|
Index <= MAX_FILTER;
|
|
Index++) {
|
|
|
|
for ( pFilterEntry = g_ipsec.FilterList[Index].Flink;
|
|
pFilterEntry != &g_ipsec.FilterList[Index];
|
|
pFilterEntry = pFilterEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pFilterEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex++) {
|
|
|
|
for ( pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
pSAEntry != &pFilter->SAChain[SAIndex];
|
|
pSAEntry = pSAEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) &&
|
|
(DestIF == pSA->sa_IPIF)) {
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_PLUMBED;
|
|
pSA->sa_Flags |= FLAGS_SA_HW_PLUMB_FAILED;
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_DELETE_SA;
|
|
pSA->sa_Flags |= FLAGS_SA_HW_RESET;
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumOffloadedSAs);
|
|
|
|
#if DBG
|
|
NumReset++;
|
|
#endif
|
|
|
|
IPSecDerefSA(pSA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IPSecNumResets++;
|
|
|
|
FlushParserEntriesForInterface(DestIF);
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecResetEnd(
|
|
IN PVOID IPContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify Interface reset is completed.
|
|
|
|
Arguments:
|
|
|
|
IPContext - This is the Interface being reset.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
Interface *DestIF = (Interface *)IPContext;
|
|
PLIST_ENTRY pFilterEntry;
|
|
PLIST_ENTRY pSAEntry;
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pSA;
|
|
KIRQL kIrql;
|
|
LONG Index;
|
|
LONG SAIndex;
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("IPSecResetEnd called for interface %p", DestIF));
|
|
|
|
//
|
|
// Go through all SA's and unmark the offload bits.
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
for ( Index = MIN_FILTER;
|
|
Index <= MAX_FILTER;
|
|
Index++) {
|
|
|
|
for ( pFilterEntry = g_ipsec.FilterList[Index].Flink;
|
|
pFilterEntry != &g_ipsec.FilterList[Index];
|
|
pFilterEntry = pFilterEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pFilterEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex++) {
|
|
|
|
for ( pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
pSAEntry != &pFilter->SAChain[SAIndex];
|
|
pSAEntry = pSAEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_HW_PLUMB_FAILED) &&
|
|
!(pSA->sa_Flags & FLAGS_SA_HW_DELETE_QUEUED) &&
|
|
(DestIF == pSA->sa_IPIF)) {
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_PLUMB_FAILED;
|
|
pSA->sa_IPIF = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecWakeUp(
|
|
IN PVOID IPContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify Interface has waken up from hibernate.
|
|
|
|
Arguments:
|
|
|
|
IPContext - This is the Interface that wakes up.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
Interface *DestIF = (Interface *)IPContext;
|
|
PLIST_ENTRY pFilterEntry;
|
|
PLIST_ENTRY pSAEntry;
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pSA;
|
|
KIRQL kIrql;
|
|
LONG Index;
|
|
LONG SAIndex;
|
|
|
|
IPSEC_DEBUG(LL_A, DBF_HWAPI, ("IPSecWakeUp called for interface %p", DestIF));
|
|
|
|
//
|
|
// Go through all SA's and unmark the offload bits.
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
for ( Index = MIN_FILTER;
|
|
Index <= MAX_FILTER;
|
|
Index++) {
|
|
|
|
for ( pFilterEntry = g_ipsec.FilterList[Index].Flink;
|
|
pFilterEntry != &g_ipsec.FilterList[Index];
|
|
pFilterEntry = pFilterEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pFilterEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex++) {
|
|
|
|
for ( pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
pSAEntry != &pFilter->SAChain[SAIndex];
|
|
pSAEntry = pSAEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) &&
|
|
(DestIF == pSA->sa_IPIF)) {
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_HW_PLUMBED;
|
|
pSA->sa_IPIF = NULL;
|
|
pSA->sa_Flags |= FLAGS_SA_HIBERNATED;
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumOffloadedSAs);
|
|
|
|
IPSecDerefSA(pSA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FlushParserEntriesForInterface(DestIF);
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecBufferOffloadEvent(
|
|
IN IPHeader UNALIGNED *pIPH,
|
|
IN PNDIS_IPSEC_PACKET_INFO IPSecPktInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Log an event for offload failures.
|
|
|
|
Arguments:
|
|
|
|
pIPH - The IP header of the problem packet.
|
|
IPSecPktInfo - The per-packet IPSec offload info.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
switch (IPSecPktInfo->Receive.CryptoStatus) {
|
|
case CRYPTO_TRANSPORT_AH_AUTH_FAILED:
|
|
IPSEC_INC_STATISTIC(dwNumPacketsNotAuthenticated);
|
|
IPSecBufferEvent( pIPH->iph_src,
|
|
EVENT_IPSEC_AUTH_FAILURE,
|
|
3,
|
|
TRUE);
|
|
break;
|
|
|
|
case CRYPTO_TRANSPORT_ESP_AUTH_FAILED:
|
|
IPSEC_INC_STATISTIC(dwNumPacketsNotAuthenticated);
|
|
IPSecBufferEvent( pIPH->iph_src,
|
|
EVENT_IPSEC_AUTH_FAILURE,
|
|
4,
|
|
TRUE);
|
|
break;
|
|
|
|
case CRYPTO_TUNNEL_AH_AUTH_FAILED:
|
|
IPSEC_INC_STATISTIC(dwNumPacketsNotAuthenticated);
|
|
IPSecBufferEvent( pIPH->iph_src,
|
|
EVENT_IPSEC_AUTH_FAILURE,
|
|
5,
|
|
TRUE);
|
|
break;
|
|
|
|
case CRYPTO_TUNNEL_ESP_AUTH_FAILED:
|
|
IPSEC_INC_STATISTIC(dwNumPacketsNotAuthenticated);
|
|
IPSecBufferEvent( pIPH->iph_src,
|
|
EVENT_IPSEC_AUTH_FAILURE,
|
|
6,
|
|
TRUE);
|
|
break;
|
|
|
|
case CRYPTO_INVALID_PACKET_SYNTAX:
|
|
IPSecBufferEvent( pIPH->iph_src,
|
|
EVENT_IPSEC_BAD_PACKET_SYNTAX,
|
|
1,
|
|
TRUE);
|
|
break;
|
|
|
|
case CRYPTO_INVALID_PROTOCOL:
|
|
IPSecBufferEvent( pIPH->iph_src,
|
|
EVENT_IPSEC_BAD_PROTOCOL_RECEIVED,
|
|
3,
|
|
TRUE);
|
|
break;
|
|
|
|
case CRYPTO_GENERIC_ERROR:
|
|
default:
|
|
IPSecBufferEvent( pIPH->iph_src,
|
|
EVENT_IPSEC_GENERIC_FAILURE,
|
|
1,
|
|
TRUE);
|
|
break;
|
|
}
|
|
}
|
|
|