/*++ Copyright (c) 1999-2001 Microsoft Corporation Module Name: gpc.c Abstract: This module contains the GPC implementation Author: ChunYe Environment: Kernel mode Revision History: --*/ #include "precomp.h" #ifdef RUN_WPP #include "gpc.tmh" #endif #if GPC NTSTATUS IPSecGpcInitialize() { NTSTATUS status; INT cf, i; PAGED_CODE(); // // Initialize FilterList for patterns that are not installed in GPC. // for (i = MIN_FILTER; i <= MAX_FILTER; i++) { InitializeListHead(&g_ipsec.GpcFilterList[i]); } // // Start with inactive state for the error path. // IPSEC_DRIVER_INIT_GPC() = FALSE; IPSEC_UNSET_GPC_ACTIVE(); // // GPC registration. // status = GpcInitialize(&g_ipsec.GpcEntries); if (status == STATUS_SUCCESS) { for (cf = GPC_CF_IPSEC_MIN; cf <= GPC_CF_IPSEC_MAX; cf++) { status = GPC_REGISTER_CLIENT( cf, 0, GPC_PRIORITY_IPSEC, NULL, NULL, &g_ipsec.GpcClients[cf]); if (status != STATUS_SUCCESS) { IPSEC_DEBUG(LL_A, DBF_LOAD, ("GPC failed to register cf %d", cf)); g_ipsec.GpcClients[cf] = NULL; IPSecGpcDeinitialize(); return status; } } } else { IPSEC_DEBUG(LL_A, DBF_LOAD, ("Failed to init GPC structures")); return status; } IPSEC_SET_GPC_ACTIVE(); IPSEC_DRIVER_INIT_GPC() = TRUE; return STATUS_SUCCESS; } NTSTATUS IPSecGpcDeinitialize() { INT cf; PAGED_CODE(); IPSEC_UNSET_GPC_ACTIVE(); // // GPC deregistration. // for (cf = GPC_CF_IPSEC_MIN; cf <= GPC_CF_IPSEC_MAX; cf++) { if (g_ipsec.GpcClients[cf]) { GPC_DEREGISTER_CLIENT(g_ipsec.GpcClients[cf]); } } GpcDeinitialize(&g_ipsec.GpcEntries); return STATUS_SUCCESS; } NTSTATUS IPSecEnableGpc() { KIRQL kIrql; PAGED_CODE(); if (IPSEC_DRIVER_INIT_GPC()) { AcquireWriteLock(&g_ipsec.SADBLock, &kIrql); IPSEC_SET_GPC_ACTIVE(); ReleaseWriteLock(&g_ipsec.SADBLock, kIrql); } return STATUS_SUCCESS; } NTSTATUS IPSecDisableGpc() { KIRQL kIrql; PAGED_CODE(); if (IPSEC_DRIVER_INIT_GPC()) { AcquireWriteLock(&g_ipsec.SADBLock, &kIrql); IPSEC_UNSET_GPC_ACTIVE(); ReleaseWriteLock(&g_ipsec.SADBLock, kIrql); } return STATUS_SUCCESS; } NTSTATUS IPSecInitGpcFilter( IN PFILTER pFilter, IN PGPC_IP_PATTERN pPattern, IN PGPC_IP_PATTERN pMask ) { PAGED_CODE(); RtlZeroMemory(pPattern, sizeof(GPC_IP_PATTERN)); RtlZeroMemory(pMask, sizeof(GPC_IP_PATTERN)); pPattern->SrcAddr = pFilter->SRC_ADDR; pPattern->DstAddr = pFilter->DEST_ADDR; pPattern->ProtocolId = (UCHAR)pFilter->PROTO; pPattern->gpcSrcPort = FI_SRC_PORT(pFilter); pPattern->gpcDstPort = FI_DEST_PORT(pFilter); pMask->SrcAddr = pFilter->SRC_MASK; pMask->DstAddr = pFilter->DEST_MASK; pMask->ProtocolId = (UCHAR)IPSEC_GPC_MASK_ALL; pMask->gpcSrcPort = IPSEC_GPC_MASK_NONE; pMask->gpcDstPort = IPSEC_GPC_MASK_NONE; switch (pFilter->PROTO) { case FILTER_PROTO_ANY: if (FI_SRC_PORT(pFilter) != FILTER_TCPUDP_PORT_ANY) { RtlFillMemory( &pMask->gpcSrcPort, sizeof(pMask->gpcSrcPort), IPSEC_GPC_MASK_ALL); } if (FI_DEST_PORT(pFilter) != FILTER_TCPUDP_PORT_ANY) { RtlFillMemory( &pMask->gpcDstPort, sizeof(pMask->gpcDstPort), IPSEC_GPC_MASK_ALL); } pMask->ProtocolId = (UCHAR)IPSEC_GPC_MASK_NONE; break; case FILTER_PROTO_TCP: case FILTER_PROTO_UDP: if (FI_SRC_PORT(pFilter) != FILTER_TCPUDP_PORT_ANY) { RtlFillMemory( &pMask->gpcSrcPort, sizeof(pMask->gpcSrcPort), IPSEC_GPC_MASK_ALL); } if (FI_DEST_PORT(pFilter) != FILTER_TCPUDP_PORT_ANY) { RtlFillMemory( &pMask->gpcDstPort, sizeof(pMask->gpcDstPort), IPSEC_GPC_MASK_ALL); } break; default: break; } return STATUS_SUCCESS; } NTSTATUS IPSecInsertGpcPattern( IN PFILTER pFilter ) { CLASSIFICATION_HANDLE GpcHandle; GPC_IP_PATTERN GpcPattern; GPC_IP_PATTERN GpcMask; ULONG GpcPriority; INT GpcCf; NTSTATUS status; PAGED_CODE(); GpcCf = IPSecResolveGpcCf(IS_OUTBOUND_FILTER(pFilter)); // // Add the filter as a CfInfo // status = GPC_ADD_CFINFO(g_ipsec.GpcClients[GpcCf], sizeof(PFILTER), (PVOID)&pFilter, (GPC_CLIENT_HANDLE)pFilter, &pFilter->GpcFilter.GpcCfInfoHandle); if (status == STATUS_SUCCESS) { // // Now add the filter as a pattern // IPSecInitGpcFilter(pFilter, &GpcPattern, &GpcMask); if (FI_DEST_PORT(pFilter) == FILTER_TCPUDP_PORT_ANY) { GpcPriority = 1; } else { GpcPriority = 0; } ASSERT(GpcPriority < GPC_PRIORITY_IPSEC); status = GPC_ADD_PATTERN( g_ipsec.GpcClients[GpcCf], GPC_PROTOCOL_TEMPLATE_IP, &GpcPattern, &GpcMask, GpcPriority, pFilter->GpcFilter.GpcCfInfoHandle, &pFilter->GpcFilter.GpcPatternHandle, &GpcHandle); if (status != STATUS_SUCCESS) { IPSEC_DEBUG(LL_A, DBF_GPC, ("GpcAddPattern: failed with status %lx", status)); GPC_REMOVE_CFINFO( g_ipsec.GpcClients[GpcCf], pFilter->GpcFilter.GpcCfInfoHandle); pFilter->GpcFilter.GpcCfInfoHandle = NULL; pFilter->GpcFilter.GpcPatternHandle = NULL; } else { g_ipsec.GpcNumFilters[GpcPriority]++; } } return status; } NTSTATUS IPSecDeleteGpcPattern( IN PFILTER pFilter ) { ULONG GpcPriority; INT GpcCf = IPSecResolveGpcCf(IS_OUTBOUND_FILTER(pFilter)); PAGED_CODE(); if (pFilter->GpcFilter.GpcPatternHandle) { GPC_REMOVE_PATTERN( g_ipsec.GpcClients[GpcCf], pFilter->GpcFilter.GpcPatternHandle); pFilter->GpcFilter.GpcPatternHandle = NULL; ASSERT(pFilter->GpcFilter.GpcCfInfoHandle); if (pFilter->GpcFilter.GpcCfInfoHandle) { GPC_REMOVE_CFINFO( g_ipsec.GpcClients[GpcCf], pFilter->GpcFilter.GpcCfInfoHandle); pFilter->GpcFilter.GpcCfInfoHandle = NULL; } if (FI_DEST_PORT(pFilter) == FILTER_TCPUDP_PORT_ANY) { GpcPriority = 1; } else { GpcPriority = 0; } ASSERT(GpcPriority < GPC_PRIORITY_IPSEC); g_ipsec.GpcNumFilters[GpcPriority]--; } return STATUS_SUCCESS; } NTSTATUS IPSecInsertGpcFilter( IN PFILTER pFilter ) { NTSTATUS status; PFILTER pTempFilter; BOOL InsertedFilter = FALSE; PLIST_ENTRY pEntry, pPrev; PLIST_ENTRY pFilterList; KIRQL kIrql; PAGED_CODE(); AcquireWriteLock(&g_ipsec.SADBLock, &kIrql); pFilterList = IPSecResolveGpcFilterList(IS_TUNNEL_FILTER(pFilter), IS_OUTBOUND_FILTER(pFilter)); pEntry = pFilterList->Flink; pPrev = pFilterList; while (pEntry != pFilterList) { pTempFilter = CONTAINING_RECORD(pEntry, FILTER, GpcLinkage); if (pFilter->Index > pTempFilter->Index) { // // found the spot, insert it before pTempFilter // InsertHeadList(pPrev, &pFilter->GpcLinkage); InsertedFilter = TRUE; break; } pPrev = pEntry; pEntry = pEntry->Flink; } if (!InsertedFilter) { // // didn't find spot, stick it in the end // InsertTailList(pFilterList, &pFilter->GpcLinkage); } ReleaseWriteLock(&g_ipsec.SADBLock, kIrql); return STATUS_SUCCESS; } NTSTATUS IPSecDeleteGpcFilter( IN PFILTER pFilter ) { KIRQL kIrql; PAGED_CODE(); if (!pFilter->GpcLinkage.Flink || !pFilter->GpcLinkage.Blink) { return STATUS_SUCCESS; } AcquireWriteLock(&g_ipsec.SADBLock, &kIrql); IPSecRemoveEntryList(&pFilter->GpcLinkage); ReleaseWriteLock(&g_ipsec.SADBLock, kIrql); return STATUS_SUCCESS; } NTSTATUS IPSecInstallGpcFilter( IN PFILTER pFilter ) { PAGED_CODE(); if (IS_TUNNEL_FILTER(pFilter)) { return STATUS_SUCCESS; } if (IS_GPC_FILTER(pFilter)) { return IPSecInsertGpcPattern(pFilter); } else { return IPSecInsertGpcFilter(pFilter); } } NTSTATUS IPSecUninstallGpcFilter( IN PFILTER pFilter ) { PAGED_CODE(); if (IS_TUNNEL_FILTER(pFilter)) { return STATUS_SUCCESS; } if (IS_GPC_FILTER(pFilter)) { return IPSecDeleteGpcPattern(pFilter); } else { return IPSecDeleteGpcFilter(pFilter); } } NTSTATUS IPSecLookupGpcSA( IN ULARGE_INTEGER uliSrcDstAddr, IN ULARGE_INTEGER uliProtoSrcDstPort, IN CLASSIFICATION_HANDLE GpcHandle, OUT PFILTER *ppFilter, OUT PSA_TABLE_ENTRY *ppSA, OUT PSA_TABLE_ENTRY *ppNextSA, OUT PSA_TABLE_ENTRY *ppTunnelSA, IN BOOLEAN fOutbound, IN BOOLEAN fVerify, IN PIPSEC_UDP_ENCAP_CONTEXT pNatContext ) { PFILTER pFilter; PFILTER pTempFilter; PLIST_ENTRY pEntry; PLIST_ENTRY pFilterList; PLIST_ENTRY pSAChain; PSA_TABLE_ENTRY pSA; REGISTER ULARGE_INTEGER uliPort; REGISTER ULARGE_INTEGER uliAddr; BOOLEAN fFound = FALSE; INT GpcCf; CLASSIFICATION_HANDLE TempGpcHandle; *ppSA = NULL; *ppFilter = NULL; *ppTunnelSA = NULL; // // Search in Tunnel filters list first. // pFilterList = IPSecResolveFilterList(TRUE, fOutbound); for ( pEntry = pFilterList->Flink; pEntry != pFilterList; pEntry = pEntry->Flink) { pFilter = CONTAINING_RECORD(pEntry, FILTER, MaskedLinkage); uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pFilter->uliSrcDstMask.QuadPart; uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pFilter->uliProtoSrcDstMask.QuadPart; if ((uliAddr.QuadPart == pFilter->uliSrcDstAddr.QuadPart) && (uliPort.QuadPart == pFilter->uliProtoSrcDstPort.QuadPart)) { // // Found filter // fFound = TRUE; break; } } if (fFound) { // // Search for the particular SA now. // fFound = FALSE; pSAChain = IPSecResolveSAChain(pFilter, fOutbound? DEST_ADDR: SRC_ADDR); for ( pEntry = pSAChain->Flink; pEntry != pSAChain; pEntry = pEntry->Flink) { pSA = CONTAINING_RECORD(pEntry, SA_TABLE_ENTRY, sa_FilterLinkage); ASSERT(pSA->sa_Flags & FLAGS_SA_TUNNEL); if (pFilter->TunnelAddr != 0 && EQUAL_NATENCAP(pNatContext,pSA)) { // // match the outbound flag also // ASSERT(fOutbound == (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_OUTBOUND) != 0)); fFound = TRUE; *ppTunnelSA = pSA; break; } } if (fFound) { fFound = FALSE; *ppFilter = pFilter; } else { // // Found a filter entry, but need to negotiate keys. // *ppFilter = pFilter; return STATUS_PENDING; } } #if DBG if (fOutbound) { ADD_TO_LARGE_INTEGER(&g_ipsec.GpcTotalPassedIn, 1); } #endif GpcCf = IPSecResolveGpcCf(fOutbound); TempGpcHandle = 0; if (GpcHandle == 0) { #if DBG if (fOutbound) { ADD_TO_LARGE_INTEGER(&g_ipsec.GpcClassifyNeeded, 1); } #endif // // Classify directly if no GpcHandle passed in. // IPSEC_CLASSIFY_PACKET( GpcCf, uliSrcDstAddr, uliProtoSrcDstPort, &pFilter, &TempGpcHandle); } else { NTSTATUS status; // // Or we use GpcHandle directly to get the filter installed. // pFilter = NULL; status = GPC_GET_CLIENT_CONTEXT(g_ipsec.GpcClients[GpcCf], GpcHandle, &pFilter); if (status == STATUS_INVALID_HANDLE) { // // Re-classify if handle is invalid. // IPSEC_CLASSIFY_PACKET( GpcCf, uliSrcDstAddr, uliProtoSrcDstPort, &pFilter, &TempGpcHandle); } } #if DBG if (IPSecDebug & DBF_EXTRADIAGNOSTIC) { PFILTER pDbgFilter = NULL; pFilterList = IPSecResolveFilterList(FALSE, fOutbound); for ( pEntry = pFilterList->Flink; pEntry != pFilterList; pEntry = pEntry->Flink) { pTempFilter = CONTAINING_RECORD(pEntry, FILTER, MaskedLinkage); uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pTempFilter->uliSrcDstMask.QuadPart; uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pTempFilter->uliProtoSrcDstMask.QuadPart; if ((uliAddr.QuadPart == pTempFilter->uliSrcDstAddr.QuadPart) && (uliPort.QuadPart == pTempFilter->uliProtoSrcDstPort.QuadPart)) { pDbgFilter = pTempFilter; break; } } if (pFilter != pDbgFilter && (!pDbgFilter || IS_GPC_FILTER(pDbgFilter))) { IPSEC_DEBUG(LL_A, DBF_GPC, ("LookupGpcSA: pFilter %p, pDbgFilter %p, GpcHandle %lx, TempGpcHandle %lx", pFilter, pDbgFilter, GpcHandle, TempGpcHandle)); IPSEC_DEBUG(LL_A, DBF_GPC, ("LookupGpcSA: Src %lx, Dest %lx, Protocol %d, SPort %lx, DPort %lx", SRC_ADDR, DEST_ADDR, PROTO, SRC_PORT, DEST_PORT)); if (DebugGPC) { DbgBreakPoint(); } } } #endif // // Continue searching the local GPC filter list if not found. // if (!pFilter) { pFilterList = IPSecResolveGpcFilterList(FALSE, fOutbound); for ( pEntry = pFilterList->Flink; pEntry != pFilterList; pEntry = pEntry->Flink) { pTempFilter = CONTAINING_RECORD(pEntry, FILTER, GpcLinkage); uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pTempFilter->uliSrcDstMask.QuadPart; uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pTempFilter->uliProtoSrcDstMask.QuadPart; if ((uliAddr.QuadPart == pTempFilter->uliSrcDstAddr.QuadPart) && (uliPort.QuadPart == pTempFilter->uliProtoSrcDstPort.QuadPart)) { pFilter = pTempFilter; break; } } } if (pFilter) { // // Search for the particular SA now. // fFound=FALSE; pSAChain = IPSecResolveSAChain(pFilter, fOutbound? DEST_ADDR: SRC_ADDR); for ( pEntry = pSAChain->Flink; pEntry != pSAChain; pEntry = pEntry->Flink) { pSA = CONTAINING_RECORD(pEntry, SA_TABLE_ENTRY, sa_FilterLinkage); if (IS_CLASSD(NET_LONG(pSA->SA_SRC_ADDR)) || IS_CLASSD(NET_LONG(pSA->SA_DEST_ADDR))) { uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pSA->sa_uliSrcDstMask.QuadPart; IPSEC_DEBUG(LL_A, DBF_HASH, ("MCAST: %d %d %d %d", uliAddr.LowPart, uliAddr.HighPart, pSA->sa_uliSrcDstAddr.LowPart,pSA->sa_uliSrcDstAddr.HighPart)); if (uliAddr.QuadPart == pSA->sa_uliSrcDstAddr.QuadPart) { fFound=TRUE; } } else if (uliSrcDstAddr.QuadPart == pSA->sa_uliSrcDstAddr.QuadPart && EQUAL_NATENCAP(pNatContext,pSA)) { fFound=TRUE; } if (fFound) { IPSEC_DEBUG(LL_A, DBF_HASH, ("Matched entry: %p", pSA)); ASSERT(fOutbound == (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_OUTBOUND) != 0)); // // if there is also a tunnel SA, associate it here. // if (*ppTunnelSA && (fOutbound || fVerify)) { *ppNextSA = *ppTunnelSA; *ppTunnelSA = NULL; } *ppFilter = pFilter; *ppSA = pSA; return STATUS_SUCCESS; } } // // Found a filter entry, but need to negotiate keys // Also, ppTunnelSA is set to the proper tunnel SA we need // to hook to this end-2-end SA once it is negotiated. // *ppFilter = pFilter; return STATUS_PENDING; } else { // // if only tunnel SA found, return that as the SA found. // if (*ppTunnelSA) { *ppSA = *ppTunnelSA; *ppTunnelSA = NULL; return STATUS_SUCCESS; } } // // no entry found // return STATUS_NOT_FOUND; } NTSTATUS IPSecLookupGpcMaskedSA( IN ULARGE_INTEGER uliSrcDstAddr, IN ULARGE_INTEGER uliProtoSrcDstPort, OUT PFILTER *ppFilter, OUT PSA_TABLE_ENTRY *ppSA, IN BOOLEAN fOutbound, IN PIPSEC_UDP_ENCAP_CONTEXT pNatContext ) /*++ Routine Description: Looks up the SA given the relevant addresses. Arguments: uliSrcDstAddr - src/dest IP addr uliProtoSrcDstPort - protocol, src/dest port ppFilter - filter found ppSA - SA found fOutbound - direction flag Return Value: STATUS_SUCCESS - both filter and SA found STATUS_UNSUCCESSFUL - none found STATUS_PENDING - filter found, but no SA Notes: Called with SADBLock held. --*/ { REGISTER ULARGE_INTEGER uliPort; REGISTER ULARGE_INTEGER uliAddr; PFILTER pFilter; PFILTER pTempFilter; PLIST_ENTRY pFilterList; PLIST_ENTRY pSAChain; PLIST_ENTRY pEntry; PSA_TABLE_ENTRY pSA; CLASSIFICATION_HANDLE GpcHandle; INT GpcCf; *ppSA = NULL; *ppFilter = NULL; GpcCf = IPSecResolveGpcCf(fOutbound); GpcHandle = 0; IPSEC_CLASSIFY_PACKET( GpcCf, uliSrcDstAddr, uliProtoSrcDstPort, &pFilter, &GpcHandle); #if DBG if (IPSecDebug & DBF_EXTRADIAGNOSTIC) { PFILTER pDbgFilter = NULL; pFilterList = IPSecResolveFilterList(FALSE, fOutbound); for ( pEntry = pFilterList->Flink; pEntry != pFilterList; pEntry = pEntry->Flink) { pTempFilter = CONTAINING_RECORD(pEntry, FILTER, MaskedLinkage); uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pTempFilter->uliSrcDstMask.QuadPart; uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pTempFilter->uliProtoSrcDstMask.QuadPart; if ((uliAddr.QuadPart == pTempFilter->uliSrcDstAddr.QuadPart) && (uliPort.QuadPart == pTempFilter->uliProtoSrcDstPort.QuadPart)) { pDbgFilter = pTempFilter; break; } } if (pFilter != pDbgFilter && (!pDbgFilter || IS_GPC_FILTER(pDbgFilter))) { IPSEC_DEBUG(LL_A, DBF_GPC, ("LookupMaskedSA: pFilter %p, pDbgFilter %p, GpcHandle %lx", pFilter, pDbgFilter, GpcHandle)); IPSEC_DEBUG(LL_A, DBF_GPC, ("LookupMaskedSA: Src %lx, Dest %lx, Protocol %d, SPort %lx, DPort %lx", SRC_ADDR, DEST_ADDR, PROTO, SRC_PORT, DEST_PORT)); if (DebugGPC) { DbgBreakPoint(); } } } #endif // // Continue searching the local GPC filter list if not found. // if (!pFilter) { pFilterList = IPSecResolveGpcFilterList(FALSE, fOutbound); for ( pEntry = pFilterList->Flink; pEntry != pFilterList; pEntry = pEntry->Flink) { pTempFilter = CONTAINING_RECORD(pEntry, FILTER, GpcLinkage); uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pTempFilter->uliSrcDstMask.QuadPart; uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pTempFilter->uliProtoSrcDstMask.QuadPart; if ((uliAddr.QuadPart == pTempFilter->uliSrcDstAddr.QuadPart) && (uliPort.QuadPart == pTempFilter->uliProtoSrcDstPort.QuadPart)) { pFilter = pTempFilter; break; } } } if (pFilter) { // // Search for the particular SA now. // pSAChain = IPSecResolveSAChain(pFilter, fOutbound? DEST_ADDR: SRC_ADDR); for ( pEntry = pSAChain->Flink; pEntry != pSAChain; pEntry = pEntry->Flink) { pSA = CONTAINING_RECORD(pEntry, SA_TABLE_ENTRY, sa_FilterLinkage); if (uliSrcDstAddr.QuadPart == pSA->sa_uliSrcDstAddr.QuadPart && EQUAL_NATENCAP(pNatContext,pSA) ) { IPSEC_DEBUG(LL_A, DBF_HASH, ("Matched entry: %p", pSA)); ASSERT(fOutbound == (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_OUTBOUND) != 0)); *ppFilter = pFilter; *ppSA = pSA; return STATUS_SUCCESS; } } // // Found a filter entry, but need to negotiate keys // *ppFilter = pFilter; return STATUS_PENDING; } // // no entry found // return STATUS_NOT_FOUND; } #endif