585 lines
13 KiB
C
585 lines
13 KiB
C
|
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
|
||
|
#pragma hdrstop
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecGetSPI(
|
||
|
PIPSEC_GET_SPI pIpsecGetSPI
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This routine returns the SPI.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pIpsecGetSPI - Pointer to the ipsec get spi structure.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
NTSTATUS - The status code from this routine.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
PIPSEC_ACQUIRE_CONTEXT pIpsecAcquireCtx = NULL;
|
||
|
|
||
|
|
||
|
pIpsecAcquireCtx = (PIPSEC_ACQUIRE_CONTEXT) pIpsecGetSPI->Context;
|
||
|
|
||
|
//
|
||
|
// If context was passed in, then there's a larval SA already setup.
|
||
|
// This is the case for the initiator.
|
||
|
//
|
||
|
|
||
|
if (pIpsecAcquireCtx) {
|
||
|
ntStatus = IPSecInitiatorGetSPI(
|
||
|
pIpsecGetSPI
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
ntStatus = IPSecResponderGetSPI(
|
||
|
pIpsecGetSPI
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecInitiatorGetSPI(
|
||
|
PIPSEC_GET_SPI pIpsecGetSPI
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
PIPSEC_ACQUIRE_CONTEXT pIpsecAcquireCtx = NULL;
|
||
|
KIRQL kIrql;
|
||
|
|
||
|
|
||
|
pIpsecAcquireCtx = (PIPSEC_ACQUIRE_CONTEXT) pIpsecGetSPI->Context;
|
||
|
|
||
|
//
|
||
|
// Sanity check the incoming context to see if it is actually
|
||
|
// an SA block.
|
||
|
//
|
||
|
|
||
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
||
|
|
||
|
if (!NT_SUCCESS(IPSecValidateHandle(pIpsecAcquireCtx, STATE_SA_LARVAL))) {
|
||
|
ntStatus = STATUS_INVALID_PARAMETER;
|
||
|
BAIL_ON_LOCK_NTSTATUS_ERROR(ntStatus);
|
||
|
}
|
||
|
|
||
|
pIpsecGetSPI->SPI = pIpsecAcquireCtx->pSA->sa_SPI;
|
||
|
|
||
|
pIpsecAcquireCtx->pSA->sa_Flags |= FLAGS_SA_INITIATOR;
|
||
|
|
||
|
ntStatus = STATUS_SUCCESS;
|
||
|
|
||
|
lock:
|
||
|
|
||
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
||
|
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecResponderGetSPI(
|
||
|
PIPSEC_GET_SPI pIpsecGetSPI
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
KIRQL kIrql;
|
||
|
PSA_TABLE_ENTRY pInboundSA = NULL;
|
||
|
ULARGE_INTEGER uliSrcDstAddr = {0};
|
||
|
PSA_TABLE_ENTRY pSA = NULL;
|
||
|
PIPSEC_ACQUIRE_CONTEXT pIpsecAcquireCtx = NULL;
|
||
|
|
||
|
|
||
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
||
|
|
||
|
if (pIpsecGetSPI->SPI) {
|
||
|
|
||
|
if (!(pIpsecGetSPI->InstantiatedFilter.TunnelFilter)) {
|
||
|
pInboundSA = IPSecLookupSABySPI(
|
||
|
pIpsecGetSPI->SPI,
|
||
|
pIpsecGetSPI->InstantiatedFilter.DestAddr
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
pInboundSA = IPSecLookupSABySPI(
|
||
|
pIpsecGetSPI->SPI,
|
||
|
pIpsecGetSPI->InstantiatedFilter.TunnelAddr
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (pInboundSA) {
|
||
|
ntStatus = STATUS_UNSUCCESSFUL;
|
||
|
BAIL_ON_LOCK_NTSTATUS_ERROR(ntStatus);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
ntStatus = IPSecResponderCreateLarvalSA(
|
||
|
pIpsecGetSPI,
|
||
|
uliSrcDstAddr,
|
||
|
&pSA
|
||
|
);
|
||
|
BAIL_ON_LOCK_NTSTATUS_ERROR(ntStatus);
|
||
|
|
||
|
//
|
||
|
// Get the acquire Context and associate it with the larval SA.
|
||
|
//
|
||
|
|
||
|
pIpsecAcquireCtx = IPSecGetAcquireContext();
|
||
|
|
||
|
if (!pIpsecAcquireCtx) {
|
||
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
BAIL_ON_LOCK_NTSTATUS_ERROR(ntStatus);
|
||
|
}
|
||
|
|
||
|
pIpsecAcquireCtx->AcquireId = (ULONG)(ULONG_PTR) pSA;
|
||
|
|
||
|
IPSecGenerateRandom(
|
||
|
(PUCHAR)&pIpsecAcquireCtx->AcquireId,
|
||
|
sizeof(ULONG)
|
||
|
);
|
||
|
|
||
|
pIpsecAcquireCtx->pSA = pSA;
|
||
|
pSA->sa_AcquireId = pIpsecAcquireCtx->AcquireId;
|
||
|
|
||
|
pIpsecGetSPI->Context = pIpsecAcquireCtx;
|
||
|
pSA->sa_AcquireCtx = pIpsecAcquireCtx;
|
||
|
|
||
|
pIpsecGetSPI->SPI = pSA->sa_SPI;
|
||
|
|
||
|
ntStatus = STATUS_SUCCESS;
|
||
|
|
||
|
lock:
|
||
|
|
||
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
||
|
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecResponderCreateLarvalSA(
|
||
|
PIPSEC_GET_SPI pIpsecGetSPI,
|
||
|
ULARGE_INTEGER uliAddr,
|
||
|
PSA_TABLE_ENTRY * ppSA
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
PSA_TABLE_ENTRY pSA = NULL;
|
||
|
KIRQL kIrql;
|
||
|
|
||
|
|
||
|
ntStatus = IPSecCreateSA(&pSA);
|
||
|
BAIL_ON_NTSTATUS_ERROR(ntStatus);
|
||
|
|
||
|
pSA->sa_Filter = NULL;
|
||
|
pSA->sa_State = STATE_SA_LARVAL;
|
||
|
|
||
|
IPSEC_BUILD_SRC_DEST_ADDR(
|
||
|
pSA->sa_uliSrcDstAddr,
|
||
|
pIpsecGetSPI->InstantiatedFilter.SrcAddr,
|
||
|
pIpsecGetSPI->InstantiatedFilter.DestAddr
|
||
|
);
|
||
|
|
||
|
IPSEC_BUILD_SRC_DEST_MASK(
|
||
|
pSA->sa_uliSrcDstMask,
|
||
|
pIpsecGetSPI->InstantiatedFilter.SrcMask,
|
||
|
pIpsecGetSPI->InstantiatedFilter.DestMask
|
||
|
);
|
||
|
|
||
|
IPSEC_BUILD_PROTO_PORT_LI(
|
||
|
pSA->sa_uliProtoSrcDstPort,
|
||
|
pIpsecGetSPI->InstantiatedFilter.Protocol,
|
||
|
pIpsecGetSPI->InstantiatedFilter.SrcPort,
|
||
|
pIpsecGetSPI->InstantiatedFilter.DestPort
|
||
|
);
|
||
|
|
||
|
ntStatus = IPSecResponderInsertInboundSA(
|
||
|
pSA,
|
||
|
pIpsecGetSPI,
|
||
|
pIpsecGetSPI->InstantiatedFilter.TunnelFilter
|
||
|
);
|
||
|
BAIL_ON_NTSTATUS_ERROR(ntStatus);
|
||
|
|
||
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
||
|
|
||
|
InsertTailList(&g_ipsec.LarvalSAList, &pSA->sa_LarvalLinkage);
|
||
|
|
||
|
IPSEC_INC_STATISTIC(dwNumPendingKeyOps);
|
||
|
|
||
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
||
|
|
||
|
ACQUIRE_LOCK(&pSA->sa_Lock, &kIrql);
|
||
|
|
||
|
IPSecStartSATimer(
|
||
|
pSA,
|
||
|
IPSecSAExpired,
|
||
|
pSA->sa_ExpiryTime
|
||
|
);
|
||
|
|
||
|
RELEASE_LOCK(&pSA->sa_Lock, kIrql);
|
||
|
|
||
|
*ppSA = pSA;
|
||
|
|
||
|
return (ntStatus);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pSA) {
|
||
|
IPSecFreeSA(pSA);
|
||
|
}
|
||
|
|
||
|
*ppSA = NULL;
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecInitiatorCreateLarvalSA(
|
||
|
PFILTER pFilter,
|
||
|
ULARGE_INTEGER uliAddr,
|
||
|
PSA_TABLE_ENTRY * ppSA,
|
||
|
UCHAR DestType
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
PSA_TABLE_ENTRY pSA = NULL;
|
||
|
ULARGE_INTEGER uliSrcDstAddr = {0};
|
||
|
ULARGE_INTEGER uliSrcDstMask = {0};
|
||
|
ULARGE_INTEGER uliProtoSrcDstPort = {0};
|
||
|
KIRQL kIrql;
|
||
|
|
||
|
|
||
|
ntStatus = IPSecCreateSA(&pSA);
|
||
|
BAIL_ON_NTSTATUS_ERROR(ntStatus);
|
||
|
|
||
|
pSA->sa_Filter = pFilter;
|
||
|
pSA->sa_State = STATE_SA_LARVAL;
|
||
|
|
||
|
uliSrcDstAddr = uliAddr;
|
||
|
uliSrcDstMask = pFilter->uliSrcDstMask;
|
||
|
uliProtoSrcDstPort = pFilter->uliProtoSrcDstPort;
|
||
|
|
||
|
IPSEC_BUILD_SRC_DEST_ADDR(
|
||
|
pSA->sa_uliSrcDstAddr,
|
||
|
DEST_ADDR,
|
||
|
SRC_ADDR
|
||
|
);
|
||
|
|
||
|
IPSEC_BUILD_SRC_DEST_MASK(
|
||
|
pSA->sa_uliSrcDstMask,
|
||
|
DEST_MASK,
|
||
|
SRC_MASK
|
||
|
);
|
||
|
|
||
|
IPSEC_BUILD_PROTO_PORT_LI(
|
||
|
pSA->sa_uliProtoSrcDstPort,
|
||
|
PROTO,
|
||
|
DEST_PORT,
|
||
|
SRC_PORT
|
||
|
);
|
||
|
|
||
|
pSA->sa_DestType=DestType;
|
||
|
|
||
|
ntStatus = IPSecInitiatorInsertInboundSA(
|
||
|
pSA,
|
||
|
pFilter->TunnelFilter
|
||
|
);
|
||
|
BAIL_ON_NTSTATUS_ERROR(ntStatus);
|
||
|
|
||
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
||
|
|
||
|
InsertTailList(&g_ipsec.LarvalSAList, &pSA->sa_LarvalLinkage);
|
||
|
|
||
|
IPSEC_INC_STATISTIC(dwNumPendingKeyOps);
|
||
|
|
||
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
||
|
|
||
|
ACQUIRE_LOCK(&pSA->sa_Lock, &kIrql);
|
||
|
|
||
|
IPSecStartSATimer(
|
||
|
pSA,
|
||
|
IPSecSAExpired,
|
||
|
pSA->sa_ExpiryTime
|
||
|
);
|
||
|
|
||
|
RELEASE_LOCK(&pSA->sa_Lock, kIrql);
|
||
|
|
||
|
*ppSA = pSA;
|
||
|
|
||
|
return (ntStatus);
|
||
|
|
||
|
error:
|
||
|
|
||
|
if (pSA) {
|
||
|
IPSecFreeSA(pSA);
|
||
|
}
|
||
|
|
||
|
*ppSA = NULL;
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecFindSA(
|
||
|
BOOLEAN bTunnelFilter,
|
||
|
ULARGE_INTEGER uliSrcDstAddr,
|
||
|
ULARGE_INTEGER uliProtoSrcDstPort,
|
||
|
PFILTER * ppFilter,
|
||
|
PSA_TABLE_ENTRY * ppSA
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
|
||
|
|
||
|
if (bTunnelFilter) {
|
||
|
ntStatus = IPSecLookupTunnelSA(
|
||
|
uliSrcDstAddr,
|
||
|
uliProtoSrcDstPort,
|
||
|
ppFilter,
|
||
|
ppSA,
|
||
|
FALSE
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
|
||
|
#if GPC
|
||
|
|
||
|
if (IS_GPC_ACTIVE()) {
|
||
|
ntStatus = IPSecLookupGpcMaskedSA(
|
||
|
uliSrcDstAddr,
|
||
|
uliProtoSrcDstPort,
|
||
|
ppFilter,
|
||
|
ppSA,
|
||
|
FALSE
|
||
|
);
|
||
|
}
|
||
|
else {
|
||
|
ntStatus = IPSecLookupMaskedSA(
|
||
|
uliSrcDstAddr,
|
||
|
uliProtoSrcDstPort,
|
||
|
ppFilter,
|
||
|
ppSA,
|
||
|
FALSE
|
||
|
);
|
||
|
}
|
||
|
#else
|
||
|
|
||
|
ntStatus = IPSecLookupMaskedSA(
|
||
|
uliSrcDstAddr,
|
||
|
uliProtoSrcDstPort,
|
||
|
ppFilter,
|
||
|
ppSA,
|
||
|
FALSE
|
||
|
);
|
||
|
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecResponderInsertInboundSA(
|
||
|
PSA_TABLE_ENTRY pSA,
|
||
|
PIPSEC_GET_SPI pIpsecGetSPI,
|
||
|
BOOLEAN bTunnelFilter
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
PFILTER pFilter = NULL;
|
||
|
PSA_TABLE_ENTRY pInboundSA = NULL;
|
||
|
PLIST_ENTRY pSAChain = NULL;
|
||
|
KIRQL kIrql;
|
||
|
tSPI tSpi = 0;
|
||
|
PSA_HASH pHash = NULL;
|
||
|
|
||
|
|
||
|
ntStatus = IPSecFindSA(
|
||
|
bTunnelFilter,
|
||
|
pSA->sa_uliSrcDstAddr,
|
||
|
pSA->sa_uliProtoSrcDstPort,
|
||
|
&pFilter,
|
||
|
&pInboundSA
|
||
|
);
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
IPSecBufferEvent(
|
||
|
pSA->SA_SRC_ADDR,
|
||
|
EVENT_IPSEC_NEG_FAILURE,
|
||
|
1,
|
||
|
FALSE
|
||
|
);
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
ASSERT(pFilter);
|
||
|
|
||
|
if (pIpsecGetSPI->InstantiatedFilter.Protocol != pFilter->PROTO ||
|
||
|
pIpsecGetSPI->InstantiatedFilter.SrcPort != FI_SRC_PORT(pFilter) ||
|
||
|
pIpsecGetSPI->InstantiatedFilter.DestPort != FI_DEST_PORT(pFilter)) {
|
||
|
ntStatus = STATUS_OBJECT_TYPE_MISMATCH;
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
pSAChain = IPSecResolveSAChain(pFilter, pSA->SA_SRC_ADDR);
|
||
|
|
||
|
InsertHeadList(pSAChain, &pSA->sa_FilterLinkage);
|
||
|
|
||
|
pSA->sa_Flags |= FLAGS_SA_ON_FILTER_LIST;
|
||
|
|
||
|
if (pFilter->Flags & FILTER_FLAGS_PASS_THRU) {
|
||
|
pSA->sa_Flags |= FLAGS_SA_PASSTHRU_FILTER;
|
||
|
}
|
||
|
|
||
|
if (pFilter->TunnelFilter) {
|
||
|
pSA->sa_Flags |= FLAGS_SA_TUNNEL;
|
||
|
pSA->sa_TunnelAddr = pFilter->TunnelAddr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Flush this filter from the cache table so that the SA instead of the
|
||
|
// filter is matched on the next lookup.
|
||
|
//
|
||
|
|
||
|
if (IS_EXEMPT_FILTER(pFilter)) {
|
||
|
IPSecInvalidateFilterCacheEntry(pFilter);
|
||
|
}
|
||
|
|
||
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql);
|
||
|
|
||
|
tSpi = pIpsecGetSPI->SPI;
|
||
|
|
||
|
ntStatus = IPSecAllocateSPI(&tSpi, pSA);
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql);
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
pSA->sa_SPI = tSpi;
|
||
|
|
||
|
IPSEC_HASH_SPI(
|
||
|
(pSA->sa_TunnelAddr) ? pSA->sa_TunnelAddr : pSA->SA_DEST_ADDR,
|
||
|
tSpi,
|
||
|
pHash
|
||
|
);
|
||
|
|
||
|
InsertHeadList(&pHash->SAList, &pSA->sa_SPILinkage);
|
||
|
|
||
|
pSA->sa_Flags |= FLAGS_SA_ON_SPI_HASH;
|
||
|
|
||
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql);
|
||
|
|
||
|
return (STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
IPSecInitiatorInsertInboundSA(
|
||
|
PSA_TABLE_ENTRY pSA,
|
||
|
BOOLEAN bTunnelFilter
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
PFILTER pFilter = NULL;
|
||
|
PSA_TABLE_ENTRY pInboundSA = NULL;
|
||
|
PLIST_ENTRY pSAChain = NULL;
|
||
|
KIRQL kIrql;
|
||
|
tSPI tSpi = 0;
|
||
|
PSA_HASH pHash = NULL;
|
||
|
|
||
|
|
||
|
ntStatus = IPSecFindSA(
|
||
|
bTunnelFilter,
|
||
|
pSA->sa_uliSrcDstAddr,
|
||
|
pSA->sa_uliProtoSrcDstPort,
|
||
|
&pFilter,
|
||
|
&pInboundSA
|
||
|
);
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
IPSecBufferEvent(
|
||
|
pSA->SA_SRC_ADDR,
|
||
|
EVENT_IPSEC_NEG_FAILURE,
|
||
|
1,
|
||
|
FALSE
|
||
|
);
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
if (ntStatus == STATUS_SUCCESS) {
|
||
|
if (pInboundSA->sa_State == STATE_SA_LARVAL) {
|
||
|
ntStatus = STATUS_DUPLICATE_OBJECTID;
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT(pFilter);
|
||
|
|
||
|
pSAChain = IPSecResolveSAChain(pFilter, pSA->SA_SRC_ADDR);
|
||
|
|
||
|
InsertHeadList(pSAChain, &pSA->sa_FilterLinkage);
|
||
|
|
||
|
pSA->sa_Flags |= FLAGS_SA_ON_FILTER_LIST;
|
||
|
|
||
|
if (pFilter->Flags & FILTER_FLAGS_PASS_THRU) {
|
||
|
pSA->sa_Flags |= FLAGS_SA_PASSTHRU_FILTER;
|
||
|
}
|
||
|
|
||
|
if (pFilter->TunnelFilter) {
|
||
|
pSA->sa_Flags |= FLAGS_SA_TUNNEL;
|
||
|
pSA->sa_TunnelAddr = pFilter->TunnelAddr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Flush this filter from the cache table so that the SA instead of the
|
||
|
// filter is matched on the next lookup.
|
||
|
//
|
||
|
|
||
|
if (IS_EXEMPT_FILTER(pFilter)) {
|
||
|
IPSecInvalidateFilterCacheEntry(pFilter);
|
||
|
}
|
||
|
|
||
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql);
|
||
|
|
||
|
tSpi = 0;
|
||
|
|
||
|
ntStatus = IPSecAllocateSPI(&tSpi, pSA);
|
||
|
|
||
|
if (!NT_SUCCESS(ntStatus)) {
|
||
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql);
|
||
|
return (ntStatus);
|
||
|
}
|
||
|
|
||
|
pSA->sa_SPI = tSpi;
|
||
|
|
||
|
IPSEC_HASH_SPI(
|
||
|
(pSA->sa_TunnelAddr) ? pSA->sa_TunnelAddr : pSA->SA_DEST_ADDR,
|
||
|
tSpi,
|
||
|
pHash
|
||
|
);
|
||
|
|
||
|
InsertHeadList(&pHash->SAList, &pSA->sa_SPILinkage);
|
||
|
|
||
|
pSA->sa_Flags |= FLAGS_SA_ON_SPI_HASH;
|
||
|
|
||
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql);
|
||
|
|
||
|
return (STATUS_SUCCESS);
|
||
|
}
|
||
|
|