diff options
Diffstat (limited to 'network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp')
| -rw-r--r-- | network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp | 6604 |
1 files changed, 6604 insertions, 0 deletions
diff --git a/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp new file mode 100644 index 00000000..563672c6 --- /dev/null +++ b/network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp @@ -0,0 +1,6604 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2014 Microsoft Corporation. All Rights Reserved. +// +// Module Name: +// ClassifyFunctions_BasicPacketModificationCallouts.cpp +// +// Abstract: +// This module contains WFP Classify functions for modifying and injecting packets back into +// the data path using the clone / block / inject method. +// +// Naming Convention: +// +// <Module><Scenario> +// +// i.e. +// ClassifyBasicPacketModification +// +// <Module> +// Classify - Function is an FWPS_CALLOUT_CLASSIFY_FN +// <Scenario> +// BasicPacketModification - Function demonstrates the clone / block / modify / inject +// model. +// +// <Action><Scenario><Modifier> +// +// i.e. +// TriggerBasicPacketModificationOutOfBand +// +// <Action> +// { +// - +// Trigger - Initiates the desired scenario. +// Perform - Executes the desired scenario. +// } +// <Scenario> +// BasicPacketModification - Function demonstrates the clone / block / modify / inject +// model. +// <Modifier> +// DeferredProcedureCall - DPC routine for Out of Band injection which dispatches the +// proper Perform Function. +// WorkItemRoutine - WorkItem Routine for Out of Band Injection which dispatches +// the proper Perform Function. +// AtInboundMACFrame - Function operates on: +// FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_INBOUND_MAC_NATIVE. +// AtOutboundMACFrame - Function operates on: +// FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET, and +// FWPM_LAYER_OUTBOUND_MAC_NATIVE. +// AtEgressVSwitchEthernet - Function operates on: +// FWPM_LAYER_EGRESS_VSWITCH_ETHERNET. +// AtIngressVSwitchEthernet - Function operates on: +// FWPM_LAYER_INGRESS_VSWITCH_ETHERNET. +// AtInboundNetwork - Function operates on: +// FWPM_LAYER_INBOUND_IPPACKET_V{4/6} +// AtOutboundNetwork - Function operates on: +// FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6} +// AtForward - Function operates on: +// FWPM_LAYER_IPFORWARD_V{4/6} +// AtInboundTransport - Function operates on: +// FWPM_LAYER_INBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// AtOutboundTransport - Function operates on: +// FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6}, +// FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6}, +// FWPM_LAYER_DATAGRAM_DATA_V{4/6}, +// FWPM_LAYER_STREAM_PACKET_V{4/6}, and +// FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} +// FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} +// +// Private Functions: +// BasicPacketModificationDeferredProcedureCall(), +// BasicPacketModificationWorkItemRoutine(), +// PerformBasicPacketModificationAtEgressVSwitchEthernet(), +// PerformBasicPacketModificationAtForward(), +// PerformBasicPacketModificationAtInboundMACFrame(), +// PerformBasicPacketModificationAtInboundNetwork(), +// PerformBasicPacketModificationAtInboundTransport(), +// PerformBasicPacketModificationAtIngressVSwitchEthernet(), +// PerformBasicPacketModificationAtOutboundMACFrame(), +// PerformBasicPacketModificationAtOutboundNetwork(), +// PerformBasicPacketModificationAtOutboundTransport(), +// TriggerBasicPacketModificationInline(), +// TriggerBasicPacketModificationOutOfBand(), +// +// Public Functions: +// ClassifyBasicPacketModification(), +// +// Author: +// Dusty Harper (DHarper) +// +// Revision History: +// +// [ Month ][Day] [Year] - [Revision]-[ Comments ] +// May 01, 2010 - 1.0 - Creation +// December 13, 2013 - 1.1 - Enhance function declaration for IntelliSense, enhance +// traces, fix weakhost injection, fix expected +// offsets,fix copy / paste issues with modifying dst +// port, and add support for multiple injectors and +// controlData. +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "Framework_WFPSamplerCalloutDriver.h" /// . +#include "ClassifyFunctions_BasicPacketModificationCallouts.tmh" /// $(OBJ_PATH)\$(O)\ + +#if(NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketModificationAtInboundMACFrame" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context and + injects the clone back to the stack's inbound path from the incoming MAC Layers + using FwpsInjectMacReceiveAsync(). <br> + <br> + Notes: Applies to the following inbound layers: <br> + FWPM_LAYER_INBOUND_MAC_FRAME_ETHERNET <br> + FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439588.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtInboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtInboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 bytesRetreated = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// If NATIVE, initial offset is at the MAC Header ... + if(pClassifyValues->layerId != FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE && + FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_ETHERNET_MAC_HEADER_SIZE)) + bytesRetreated = pMetadata->ethernetMacHeaderSize; + + if(bytesRetreated) + { + /// ... otherwise the offset is at the IP Header, so retreat the size of the MAC Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + } + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + status = FwpsInjectMacReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundMACFrame: FwpsInjectMacReceiveAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtInboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketInjectionAtOutboundMACFrame" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + outgoing MAC Layers using FwpsInjectMacSendAsync(). <br> + <br> + Notes: Applies to the following inbound layers: <br> + FWPM_LAYER_OUTBOUND_MAC_FRAME_ETHERNET <br> + FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439593.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtOutboundMACFrame(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtOutboundMACFrame()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + IF_INDEX interfaceIndex = 0; + NDIS_PORT_NUMBER ndisPort = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pNDISPort = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pNDISPort = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_NDIS_PORT); + if(pNDISPort && + pNDISPort->type == FWP_UINT32) + ndisPort = (NDIS_PORT_NUMBER)pNDISPort->uint32; + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMacFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + status = FwpsInjectMacSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pClassifyValues->layerId, + interfaceIndex, + ndisPort, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundMACFrame: FwpsInjectMacSendAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtOutboundMACFrame() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtIngressVSwitchEthernet" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's + ingress path from the ingress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0(). <br> + <br> + Notes: Applies to the following ingress layers: <br> + FWPM_LAYER_INGRESS_VSWITCH_ETHERNET <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtIngressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtIngressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtIngressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtIngressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtEgressVSwitchEthernet" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the virtual switch's + ingress path from the egress VSwitch Layers using + FwpsInjectvSwitchEthernetIngressAsync0(). <br> + <br> + Notes: Applies to the following egress layers: <br> + FWPM_LAYER_EGRESS_VSWITCH_ETHERNET <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/HH439669.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtEgressVSwitchEthernet(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtEgressVSwitchEthernet()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + FWP_VALUE* pVSwitchIDValue = 0; + FWP_BYTE_BLOB* pVSwitchID = 0; + NDIS_SWITCH_PORT_ID sourcePortID = 0; + NDIS_SWITCH_NIC_INDEX sourceNICIndex = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_PORT_ID)) + sourcePortID = pMetadata->vSwitchSourcePortId; + + if(FWPS_IS_L2_METADATA_FIELD_PRESENT(pMetadata, + FWPS_L2_METADATA_FIELD_VSWITCH_SOURCE_NIC_INDEX)) + sourceNICIndex = (NDIS_SWITCH_NIC_INDEX)pMetadata->vSwitchSourceNicIndex; + + pVSwitchIDValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_VSWITCH_ID); + if(pVSwitchIDValue) + pVSwitchID = pVSwitchIDValue->byteBlob; + + if(pVSwitchID == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet() [status: %#x][pVSwitchID: %#p]\n", + status, + pVSwitchID); + + HLPR_BAIL; + } + + /// Initial offset is at the MAC Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList, + TRUE); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the IP and Transport headers at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// No Transport Modification if IPsec encrypted + if(protocol != IPPROTO_ESP && + protocol != IPPROTO_AH) + { + /// ... advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + /// The clone is at the Ethernet Header, so advance by the size of the Ethernet Header... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ethernetHeaderSize, + FALSE, + 0); + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE_WITH_LABEL(value.byteArray16, + status, + HLPR_BAIL_LABEL_3); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE_WITH_LABEL(status, + HLPR_BAIL_LABEL_3); + } + + HLPR_BAIL_LABEL_3: + + /// return the data offset to the beginning of the Ethernet Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_MAC_HEADER) + { + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pSourceMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifySourceAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->macData.flags & PCPMDF_MODIFY_MAC_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray6 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray6, + FWP_BYTE_ARRAY6, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray6, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY6_TYPE; + + RtlCopyMemory(value.byteArray6->byteArray6, + pModificationData->macData.pDestinationMACAddress, + ETHERNET_ADDRESS_SIZE); + + status = KrnlHlprMACHeaderModifyDestinationAddress(&value, + pNetBufferList); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + status = FwpsInjectvSwitchEthernetIngressAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pVSwitchID, + sourcePortID, + sourceNICIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtEgressVSwitchEthernet: FwpsInjectvSwitchEthernetIngressAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtEgressVSwitchEthernet() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) + +/** + @private_function="PerformBasicPacketModificationAtInboundNetwork" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + incoming Network Layers using FwpsInjectNetworkReceiveAsync(). <br> + <br> + Notes: Applies to the following inbound layers: <br> + FWPM_LAYER_INBOUND_IPPACKET_V{4/6} <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551183.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtInboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtInboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 bytesRetreated = 0; + UINT64 endpointHandle = 0; + IPPROTO protocol = IPPROTO_MAX; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + BYTE* pSourceAddress = 0; + BYTE* pDestinationAddress = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = ipHeaderSize = pMetadata->ipHeaderSize; + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Initial offset is at the Transport Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + if(pModificationData->flags) + { + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + NTSTATUS tmpStatus = STATUS_SUCCESS; + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX) + interfaceIndex = pModificationData->ipData.interfaceIndex; + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + /// Handle if this packet is destined for the software loopback + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + + pSourceAddress = KrnlHlprIPHeaderGetSourceAddressField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + pDestinationAddress = KrnlHlprIPHeaderGetDestinationAddressField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + ipHeaderSize, + pCompletionData->pInjectionData->addressFamily, + (UCHAR*)pSourceAddress, + (UCHAR*)pDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundNetwork: FwpsInjectNetworkReceiveAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtInboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtOutboundNetwork" + + Purpose: Clones the NET_BUFFER_LIST and injects the clone back to the stack from the + outgoing Network Layers using FwpsInjectNetworkSendAsync(). <br> + <br> + Notes: Applies to the following inbound layers: <br> + FWPM_LAYER_OUTBOUND_IPPACKET_V{4/6} <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551185.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtOutboundNetwork(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtOutboundNetwork()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundNetwork: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the Transport header at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundNetwork: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundNetwork: FwpsInjectNetworkSendAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtOutboundNetwork() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtForward" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context and + injects the clone back to the stack's forward path using FwpsInjectForwardAsync(). <br> + <br> + Notes: Applies to the following forwarding layers: <br> + FWPM_LAYER_IPFORWARD_V{4/6} <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtForward(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtForward()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + BOOLEAN isWeakHostReceive = FALSE; + BOOLEAN isWeakHostSend = FALSE; + PSTR pInjectionFn = "FwpsInjectForwardAsync"; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + ipHeaderSize = pMetadata->ipHeaderSize; + + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + /// Determine if this is a weakhost forward + if(flags & FWP_CONDITION_FLAG_IS_INBOUND_PASS_THRU) + isWeakHostReceive = TRUE; + + if(flags & FWP_CONDITION_FLAG_IS_OUTBOUND_PASS_THRU) + isWeakHostSend = TRUE; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + /// Initial offset is at the IP Header, so just clone the entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if this packet had the IP checksum offloaded or if it's loopback + if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + /// Various checks and balances must be performed to modify the Transport header at this modification point. + /// Parsing of the headers will need to occur, as well as spot checking to verify everything is as it should be. + /// Additionally, checksum routines will need to be written to recalculate checksums for some of the headers. + /// The following block of code is to get you started with modifying the headers with info not readily available. + /// (i.e. header parsing has not occurred so there is no relevant classifiable data nor metadata present). +/* + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + IPPROTO protocol = IPPROTO_MAX; + + protocol = KrnlHlprIPHeaderGetProtocolField(pNetBufferList, + pCompletionData->pInjectionData->addressFamily); + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + /// ToDo: Recalculate the Transport Checksum Here + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } +*/ + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX) + interfaceIndex = pModificationData->ipData.interfaceIndex; + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + TRUE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + } + } + + /// If the Forwarded NBL is destined locally, inject using FwpsInjectNetworkReceiveAsync rather + /// than the traditional FwpsInjectForwardAsync otherwise STATUS_INVALID_PARAMETER will be + /// returned in the NBL.status and the injection fails. + if(isWeakHostReceive) + { + UINT32 index = WFPSAMPLER_INDEX; + IF_INDEX subInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4InboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6InboundNetworkInjectionHandles[index]; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_DESTINATION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + status = FwpsInjectNetworkReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + } + /// If the Forwarded NBL is sourced locally, but another interface, inject using + /// FwpsInjectNetworkSendAsync rather than the traditional FwpsInjectForwardAsync otherwise + /// STATUS_INVALID_PARAMETER will be returned in the NBL.status and the injection fails + else if(isWeakHostSend) + { + UINT32 index = WFPSAMPLER_INDEX; + + if(pCompletionData->pClassifyData->pFilter->subLayerWeight == FWPM_SUBLAYER_UNIVERSAL_WEIGHT) + index = UNIVERSAL_INDEX; + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + pCompletionData->pInjectionData->injectionHandle = g_pIPv4OutboundNetworkInjectionHandles[index]; + else + pCompletionData->pInjectionData->injectionHandle = g_pIPv6OutboundNetworkInjectionHandles[index]; + + status = FwpsInjectNetworkSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + compartmentID, + pNetBufferList, + CompleteBasicPacketInjection, + pCompletionData); + } + else + status = FwpsInjectForwardAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtForward: %s() [status: %#x]\n", + pInjectionFn, + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtForward() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtInboundTransport" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context, and + injects the clone back to the stack's inbound path from the incoming Transport + Layers using FwpsInjectTransportRecveiveAsync(). <br> + <br> + Notes: Applies to the following inbound layers: <br> + FWPM_LAYER_INBOUND_TRANSPORT_V{4/6} <br> + FWPM_LAYER_INBOUND_ICMP_ERROR_V{4/6} <br> + FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Inbound only) <br> + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only) <br> + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Inbound only) <br> + FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Inbound, reauthorization only) <br> + FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Inbound, non-TCP only) <br> + FWPM_LAYER_STREAM_PACKET_V{4/6} (Inbound only) <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551186.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtInboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtInboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + IF_INDEX interfaceIndex = 0; + IF_INDEX subInterfaceIndex = 0; + UINT32 flags = 0; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + UINT32 ipHeaderSize = 0; + UINT32 transportHeaderSize = 0; + UINT32 bytesRetreated = 0; + IPPROTO protocol = IPPROTO_MAX; + FWP_VALUE* pProtocol = 0; + FWP_VALUE* pInterfaceIndex = 0; + FWP_VALUE* pSubInterfaceIndex = 0; + FWP_VALUE* pFlags = 0; + FWPS_PACKET_LIST_INFORMATION* pPacketInformation = 0; + BOOLEAN bypassInjection = FALSE; + UINT64 endpointHandle = 0; + BYTE* pSourceAddress = 0; + BYTE* pDestinationAddress = 0; + NDIS_TCP_IP_CHECKSUM_PACKET_INFO checksumInfo = {0}; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + HLPR_NEW(pPacketInformation, + FWPS_PACKET_LIST_INFORMATION, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pPacketInformation, + status); + pInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_INTERFACE_INDEX); + if(pInterfaceIndex && + pInterfaceIndex->type == FWP_UINT32) + interfaceIndex = (IF_INDEX)pInterfaceIndex->uint32; + + pSubInterfaceIndex = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_SUB_INTERFACE_INDEX); + if(pSubInterfaceIndex && + pSubInterfaceIndex->type == FWP_UINT32) + subInterfaceIndex = (IF_INDEX)pSubInterfaceIndex->uint32; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + flags = pFlags->uint32; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = IPPROTO_TCP; + +#endif /// (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + pProtocol = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + HLPR_BAIL_ON_NULL_POINTER(pProtocol); + + protocol = (IPPROTO)pProtocol->uint8; + } + + if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) + { + ipHeaderSize = IPV4_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMP) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + else if(pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6) + { + ipHeaderSize = IPV6_HEADER_MIN_SIZE; + + if(protocol == IPPROTO_ICMPV6) + transportHeaderSize = ICMP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_TCP) + transportHeaderSize = TCP_HEADER_MIN_SIZE; + else if(protocol == IPPROTO_UDP) + transportHeaderSize = UDP_HEADER_MIN_SIZE; + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE) && + pMetadata->ipHeaderSize) + ipHeaderSize = pMetadata->ipHeaderSize; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE) && + pMetadata->transportHeaderSize) + transportHeaderSize = pMetadata->transportHeaderSize; + + bytesRetreated = ipHeaderSize; + + if(protocol != IPPROTO_ICMP && + protocol != IPPROTO_ICMPV6) + { + if(!isInline && + protocol != IPPROTO_TCP && + !(protocol == IPPROTO_UDP && + flags & FWP_CONDITION_FLAG_IS_RAW_ENDPOINT) && + (pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6)) + { + /// For asynchronous execution, the drop will cause the stack to continue processing on the + /// NBL for auditing purposes. This processing retreats the NBL Offset to the Transport header. + /// We need to take this into account because we only took a reference on the NBL. + } + else + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + bytesRetreated += transportHeaderSize; + } + } + else + { + if(pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + bytesRetreated += transportHeaderSize; + } + } + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + /// Query to see if IPsec has applied tunnel mode SA's to this NET_BUFFER_LIST ... + status = FwpsGetPacketListSecurityInformation((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + FWPS_PACKET_LIST_INFORMATION_QUERY_ALL_INBOUND, + pPacketInformation); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: FwpsGetPacketListSecurityInformation() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... if it has, then bypass the injection until the NET_BUFFER_LIST has come out of the tunnel + if((pPacketInformation->ipsecInformation.inbound.isTunnelMode && + !(pPacketInformation->ipsecInformation.inbound.isDeTunneled)) || + pPacketInformation->ipsecInformation.inbound.isSecure) + { + bypassInjection = TRUE; + + HLPR_BAIL; + } + + /// Initial offset is at the data, so retreat the size of the IP Header and Transport Header ... + /// except for ICMP, offset is at the ICMP Header, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + /// ... clone the entire NET_BUFFER_LIST ... + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket), + bytesRetreated, + FALSE, + 0); + if(status != STATUS_SUCCESS || + !pNetBufferList) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + checksumInfo.Value = (ULONG)(ULONG_PTR)NET_BUFFER_LIST_INFO((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + TcpIpChecksumNetBufferListInfo); + + /// Handle if the packet was IPsec secured + if(pCompletionData->pInjectionData->isIPsecSecured) + { + /// For performance reasons, IPsec leaves the original ESP / AH information in the IP Header ... + UINT32 headerIncludeSize = 0; + UINT32 ipv4Address = 0; + UINT32 addressSize = 0; + FWP_VALUE* pRemoteAddressValue = 0; + FWP_VALUE* pLocalAddressValue = 0; + FWP_VALUE* pProtocolValue = 0; + + pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddressValue) + { + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pSourceAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSourceAddress, + status); + + if(pRemoteAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pSourceAddress, + pRemoteAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pRemoteAddressValue->uint32); + + RtlCopyMemory(pSourceAddress, + &ipv4Address, + addressSize); + } + } + + pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + if(pLocalAddressValue) + { + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + addressSize = IPV6_ADDRESS_SIZE; + else + addressSize = IPV4_ADDRESS_SIZE; + + HLPR_NEW_ARRAY(pDestinationAddress, + BYTE, + addressSize, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pDestinationAddress, + status); + + if(pLocalAddressValue->type == FWP_BYTE_ARRAY16_TYPE) + RtlCopyMemory(pDestinationAddress, + pLocalAddressValue->byteArray16->byteArray16, + addressSize); + else + { + ipv4Address = htonl(pLocalAddressValue->uint32); + + RtlCopyMemory(pDestinationAddress, + &ipv4Address, + addressSize); + } + } + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + else + protocol = IPPROTO_MAX; + + NT_ASSERT(protocol != IPPROTO_MAX); + +#if (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_INCLUDE_HEADER)) + headerIncludeSize = pMetadata->headerIncludeHeaderLength; + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + if(pSourceAddress == 0 || + pDestinationAddress == 0) + { + status = STATUS_INVALID_MEMBER; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport() [status: %#x][pSourceAddress: %#p][pDestinationAddress: %#p]\n", + status, + pSourceAddress, + pDestinationAddress); + + HLPR_BAIL; + } + + /// ... so we must re-construct the IPHeader with the appropriate information + /// for checksum offload, this will recalculate the checksums + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + headerIncludeSize, + pCompletionData->pInjectionData->addressFamily, + pSourceAddress, + pDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsConstructIpHeaderForTransportPacket() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + /// Handle if this packet had the IP or Transport checksums offloaded or if it's loopback + else if(checksumInfo.Receive.NdisPacketIpChecksumSucceeded || + checksumInfo.Receive.NdisPacketTcpChecksumSucceeded || + checksumInfo.Receive.NdisPacketUdpChecksumSucceeded || + flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + /// Prevent TCP/IP Zone crossing and recalculate the checksums + if(flags & FWP_CONDITION_FLAG_IS_LOOPBACK) + { + FWP_VALUE* pLocalAddress = 0; + FWP_VALUE* pRemoteAddress = 0; + FWP_VALUE* pLoopbackAddress = 0; + + pLocalAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pLocalAddress && + ((pLocalAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pLocalAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pLocalAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pLocalAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pLocalAddress; + + if(!pLoopbackAddress) + { + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(pRemoteAddress->byteArray16->byteArray16, + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + pLoopbackAddress = pRemoteAddress; + } + + if(pLoopbackAddress) + { + status = KrnlHlprIPHeaderModifyLoopbackToLocal(pMetadata, + pLoopbackAddress, + ipHeaderSize, + pNetBufferList, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: KrnlHlprIPHeaderModifyLoopbackToLocal() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + else + { + /// Recalculate the checksum + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + KrnlHlprIPHeaderCalculateV4Checksum(pNetBufferList, + ipHeaderSize); + } + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + FWP_VALUE* pLocalAddressValue = 0; + FWP_VALUE* pRemoteAddressValue = 0; + BYTE pIPDestinationAddress[16] = {0}; + BYTE pIPSourceAddress[16] = {0}; + + pRemoteAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pRemoteAddressValue, + status); + + pLocalAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_LOCAL_ADDRESS); + HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pLocalAddressValue, + status); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET6) + { + RtlCopyMemory(pIPSourceAddress, + pRemoteAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + pLocalAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + } + else + { + UINT32 sourceAddress = htonl(pRemoteAddressValue->uint32); + UINT32 destinationAddress = htonl(pLocalAddressValue->uint32); + + RtlCopyMemory(pIPSourceAddress, + &sourceAddress, + IPV4_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + &destinationAddress, + IPV4_ADDRESS_SIZE); + } + + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + UINT32 tmpStatus = STATUS_SUCCESS; + FWP_VALUE* pProtocolValue = 0; + + /// The clone is at the IP Header, so advance by the size of the IP Header. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + FALSE, + 0); + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + else + protocol = IPPROTO_MAX; + + NT_ASSERT(protocol != IPPROTO_MAX); + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = transportHeaderSize ? transportHeaderSize : ICMP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = transportHeaderSize ? transportHeaderSize : TCP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = transportHeaderSize ? transportHeaderSize : UDP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = transportHeaderSize ? transportHeaderSize : ICMP_HEADER_MIN_SIZE; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE_2(status); + } + + break; + } + } + + HLPR_BAIL_LABEL_2: + + /// return the data offset to the beginning of the IP Header + tmpStatus = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(pNetBufferList), + ipHeaderSize, + 0, + 0); + if(tmpStatus != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtInboundTransport: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_INTERFACE_INDEX) + interfaceIndex = pModificationData->ipData.interfaceIndex; + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_SOURCE_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + + RtlCopyMemory(pIPSourceAddress, + pModificationData->ipData.sourceAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + + RtlCopyMemory(pIPSourceAddress, + pModificationData->ipData.sourceAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifySourceAddress(&value, + pNetBufferList, + FALSE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + FWP_VALUE value; + + RtlZeroMemory(&value, + sizeof(FWP_VALUE)); + + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + value.type = FWP_UINT32; + + RtlCopyMemory(&(value.uint32), + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// value.byteArray16 will be freed in with call to KrnlHlprFwpValuePurgeLocalCopy + + HLPR_NEW(value.byteArray16, + FWP_BYTE_ARRAY16, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(value.byteArray16, + status); + +#pragma warning(pop) + + value.type = FWP_BYTE_ARRAY16_TYPE; + + RtlCopyMemory(value.byteArray16->byteArray16, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + + RtlCopyMemory(pIPDestinationAddress, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + + status = KrnlHlprIPHeaderModifyDestinationAddress(&value, + pNetBufferList, + FALSE); + + KrnlHlprFwpValuePurgeLocalCopy(&value); + + HLPR_BAIL_ON_FAILURE(status); + + } + } + + status = FwpsConstructIpHeaderForTransportPacket(pNetBufferList, + ipHeaderSize, + pCompletionData->pInjectionData->addressFamily, + (UCHAR*)pIPSourceAddress, + (UCHAR*)pIPDestinationAddress, + protocol, + endpointHandle, + (const WSACMSGHDR*)pCompletionData->pInjectionData->pControlData, + pCompletionData->pInjectionData->controlDataLength, + 0, + 0, + interfaceIndex, + subInterfaceIndex); + HLPR_BAIL_ON_FAILURE(status); + } + + status = FwpsInjectTransportReceiveAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + 0, + 0, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + interfaceIndex, + subInterfaceIndex, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketInjectionAtInboundTransport: FwpsInjectTransportReceiveAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS || + bypassInjection) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + + HLPR_DELETE_ARRAY(pSourceAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE_ARRAY(pDestinationAddress, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + + HLPR_DELETE(pPacketInformation, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtInboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="PerformBasicPacketModificationAtOutboundTransport" + + Purpose: Clones the NET_BUFFER_LIST, modifies it with data from the associated context, and + injects the clone back to the stack's outbound path from the outgoing Transport + Layers using FwpsInjectTransportSendAsync(). <br> + <br> + Notes: Applies to the following outbound layers: <br> + FWPM_LAYER_OUTBOUND_TRANSPORT_V{4/6} <br> + FWPM_LAYER_OUTBOUND_ICMP_ERROR_V{4/6} <br> + FWPM_LAYER_DATAGRAM_DATA_V{4/6} (Outbound only) <br> + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only) <br> + FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V{4/6} (Outbound only) <br> + FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} (Outbound, reauthorization only) <br> + FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} (Outbound, non-TCP only) <br> + FWPM_LAYER_STREAM_PACKET_V{4/6} (Outbound only) <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF551188.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF546324.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS PerformBasicPacketModificationAtOutboundTransport(_In_ CLASSIFY_DATA** ppClassifyData, + _In_ INJECTION_DATA** ppInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData, + _In_ BOOLEAN isInline = FALSE) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> PerformBasicPacketModificationAtOutboundTransport()\n"); + +#endif /// DBG + + NT_ASSERT(ppClassifyData); + NT_ASSERT(ppInjectionData); + NT_ASSERT(pModificationData); + NT_ASSERT(*ppClassifyData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)(*ppClassifyData)->pClassifyValues; + FWPS_INCOMING_METADATA_VALUES* pMetadata = (FWPS_INCOMING_METADATA_VALUES*)(*ppClassifyData)->pMetadataValues; + UINT64 endpointHandle = 0; + FWPS_TRANSPORT_SEND_PARAMS* pSendParams = 0; + COMPARTMENT_ID compartmentID = DEFAULT_COMPARTMENT_ID; + NET_BUFFER_LIST* pNetBufferList = 0; + BASIC_PACKET_MODIFICATION_COMPLETION_DATA* pCompletionData = 0; + BYTE* pRemoteAddress = 0; + FWP_VALUE* pAddressValue = 0; + +#if DBG + + KIRQL irql = KeGetCurrentIrql(); + +#endif /// DBG + +#pragma warning(push) +#pragma warning(disable: 6014) /// pCompletionData & pSendParams will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pCompletionData, + BASIC_PACKET_MODIFICATION_COMPLETION_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pCompletionData, + status); + + HLPR_NEW(pSendParams, + FWPS_TRANSPORT_SEND_PARAMS, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pSendParams, + status); + +#pragma warning(pop) + + KeInitializeSpinLock(&(pCompletionData->spinLock)); + + pCompletionData->performedInline = isInline; + pCompletionData->pClassifyData = *ppClassifyData; + pCompletionData->pInjectionData = *ppInjectionData; + pCompletionData->pSendParams = pSendParams; + + /// Responsibility for freeing this memory has been transferred to the pCompletionData + *ppClassifyData = 0; + + *ppInjectionData = 0; + + pSendParams = 0; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE)) + endpointHandle = pMetadata->transportEndpointHandle; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_COMPARTMENT_ID)) + compartmentID = (COMPARTMENT_ID)pMetadata->compartmentId; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA)) + { + pSendParams->controlData = pMetadata->controlData; + pSendParams->controlDataLength = pMetadata->controlDataLength; + } + + pAddressValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pAddressValue) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + { + UINT32 tempAddress = htonl(pAddressValue->uint32); + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV4_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + + RtlCopyMemory(pRemoteAddress, + &tempAddress, + IPV4_ADDRESS_SIZE); + } + else + { +#pragma warning(push) +#pragma warning(disable: 6014) /// pRemoteAddress will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW_ARRAY(pRemoteAddress, + BYTE, + IPV6_ADDRESS_SIZE, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pRemoteAddress, + status); + +#pragma warning(pop) + + RtlCopyMemory(pRemoteAddress, + pAddressValue->byteArray16->byteArray16, + IPV6_ADDRESS_SIZE); + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_REMOTE_SCOPE_ID)) + pCompletionData->pSendParams->remoteScopeId = pMetadata->remoteScopeId; + } + + pCompletionData->pSendParams->remoteAddress = pRemoteAddress; + } + + pCompletionData->pSendParams->controlData = (WSACMSGHDR*)pCompletionData->pInjectionData->pControlData; + pCompletionData->pSendParams->controlDataLength = pCompletionData->pInjectionData->controlDataLength; + + /// Initial offset is at Transport Header (no IP Header yet), so just clone entire NET_BUFFER_LIST. + status = FwpsAllocateCloneNetBufferList((NET_BUFFER_LIST*)pCompletionData->pClassifyData->pPacket, + g_pNDISPoolData->nblPoolHandle, + g_pNDISPoolData->nbPoolHandle, + 0, + &pNetBufferList); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundTransport: FwpsAllocateCloneNetBufferList() [status: %#x]\n", + status); + + HLPR_BAIL; + } + + pCompletionData->refCount = KrnlHlprNBLGetRequiredRefCount(pNetBufferList); + + if(pModificationData->flags) + { + if(pModificationData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER) + { + FWP_VALUE* pProtocolValue = 0; + IPPROTO protocol = IPPROTO_MAX; + + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->type == FWP_UINT8) + protocol = (IPPROTO)pProtocolValue->uint8; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4) + protocol = IPPROTO_ICMP; + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6) + protocol = IPPROTO_ICMPV6; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + protocol = IPPROTO_TCP; + +#endif + + switch(protocol) + { + case IPPROTO_ICMP: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv4HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv4HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + case IPPROTO_TCP: + { + UINT32 tcpHeaderSize = TCP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + tcpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprTCPHeaderModifySourcePort(&srcPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprTCPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + tcpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + case IPPROTO_UDP: + { + UINT32 udpHeaderSize = UDP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + udpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_SOURCE_PORT) + { + FWP_VALUE0 srcPort; + + srcPort.type = FWP_UINT16; + srcPort.uint16 = pModificationData->transportData.sourcePort; + + status = KrnlHlprUDPHeaderModifySourcePort(&srcPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_DESTINATION_PORT) + { + FWP_VALUE0 dstPort; + + dstPort.type = FWP_UINT16; + dstPort.uint16 = pModificationData->transportData.destinationPort; + + status = KrnlHlprUDPHeaderModifyDestinationPort(&dstPort, + pNetBufferList, + udpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + case IPPROTO_ICMPV6: + { + UINT32 icmpHeaderSize = ICMP_HEADER_MIN_SIZE; + + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE)) + icmpHeaderSize = pMetadata->transportHeaderSize; + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_TYPE) + { + FWP_VALUE0 icmpType; + + icmpType.type = FWP_UINT8; + icmpType.uint8 = (UINT8)ntohs(pModificationData->transportData.sourcePort); + + status = KrnlHlprICMPv6HeaderModifyType(&icmpType, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + if(pModificationData->transportData.flags & PCPMDF_MODIFY_TRANSPORT_HEADER_ICMP_CODE) + { + FWP_VALUE0 icmpCode; + + icmpCode.type = FWP_UINT8; + icmpCode.uint8 = (UINT8)ntohs(pModificationData->transportData.destinationPort); + + status = KrnlHlprICMPv6HeaderModifyCode(&icmpCode, + pNetBufferList, + icmpHeaderSize); + HLPR_BAIL_ON_FAILURE(status); + } + + break; + } + } + } + + /// As there is no IP Header yet, we can only modify the destination IP address via the FWPS_TRANSPORT_SEND_PARAMS + if(pModificationData->flags & PCPMDF_MODIFY_IP_HEADER) + { + if(pModificationData->ipData.flags & PCPMDF_MODIFY_IP_HEADER_DESTINATION_ADDRESS) + { + if(pCompletionData->pInjectionData->addressFamily == AF_INET) + RtlCopyMemory(pCompletionData->pSendParams->remoteAddress, + pModificationData->ipData.destinationAddress.pIPv4, + IPV4_ADDRESS_SIZE); + else + RtlCopyMemory(pCompletionData->pSendParams->remoteAddress, + pModificationData->ipData.destinationAddress.pIPv6, + IPV6_ADDRESS_SIZE); + } + } + } + + status = FwpsInjectTransportSendAsync(pCompletionData->pInjectionData->injectionHandle, + pCompletionData->pInjectionData->injectionContext, + endpointHandle, + 0, + pCompletionData->pSendParams, + pCompletionData->pInjectionData->addressFamily, + compartmentID, + pNetBufferList, + CompleteBasicPacketModification, + pCompletionData); + + NT_ASSERT(irql == KeGetCurrentIrql()); + + if(status != STATUS_SUCCESS) + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! PerformBasicPacketModificationAtOutboundTransport: FwpsInjectTransportSendAsync() [status: %#x]\n", + status); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pNetBufferList) + { + FwpsFreeCloneNetBufferList(pNetBufferList, + 0); + + pNetBufferList = 0; + } + + if(pCompletionData) + BasicPacketModificationCompletionDataDestroy(&pCompletionData, + TRUE); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- PerformBasicPacketModificationAtOutboundTransport() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="BasicPacketModificationDeferredProcedureCall" + + Purpose: Invokes the appropriate private injection routine to perform the injection at + DISPATCH_LEVEL. <br> + <br> + Notes: <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF542972.aspx <br> +*/ +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Function_class_(KDEFERRED_ROUTINE) +VOID BasicPacketModificationDeferredProcedureCall(_In_ KDPC* pDPC, + _In_opt_ PVOID pContext, + _In_opt_ PVOID pArg1, + _In_opt_ PVOID pArg2) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketModificationDeferredProcedureCall()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDPC); + UNREFERENCED_PARAMETER(pContext); + UNREFERENCED_PARAMETER(pArg2); + + NT_ASSERT(pDPC); + NT_ASSERT(pArg1); + NT_ASSERT(((DPC_DATA*)pArg1)->pClassifyData); + NT_ASSERT(((DPC_DATA*)pArg1)->pInjectionData); + + DPC_DATA* pDPCData = (DPC_DATA*)pArg1; + + if(pDPCData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pDPCData->pClassifyData->pClassifyValues; + FWPS_FILTER* pFilter = (FWPS_FILTER*)pDPCData->pClassifyData->pFilter; + PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtInboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtOutboundNetwork(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketModificationAtForward(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtInboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pDPCData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtOutboundTransport(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtInboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtOutboundMACFrame(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&(pDPCData->pClassifyData), + &(pDPCData->pInjectionData), + pModificationData, + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationDeferredProcedureCall() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pDPCData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pDPCData->pClassifyData)); + + if(pDPCData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pDPCData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationDeferredProcedureCall: PerformBasicPacketModification() [status: %#x]\n", + status); + } + + KrnlHlprDPCDataDestroy(&pDPCData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketModificationDeferredProcedureCall()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="BasicPacketModificationWorkItemRoutine" + + Purpose: Invokes the appropriate private routine to perform the modification and injection + at PASSIVE_LEVEL. <br> + <br> + Notes: <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx <br> +*/ +_IRQL_requires_(PASSIVE_LEVEL) +_IRQL_requires_same_ +_Function_class_(IO_WORKITEM_ROUTINE) +VOID BasicPacketModificationWorkItemRoutine(_In_ PDEVICE_OBJECT pDeviceObject, + _Inout_opt_ PVOID pContext) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> BasicPacketModificationWorkItemRoutine()\n"); + +#endif /// DBG + + UNREFERENCED_PARAMETER(pDeviceObject); + + NT_ASSERT(pContext); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pClassifyData); + NT_ASSERT(((WORKITEM_DATA*)pContext)->pInjectionData); + + WORKITEM_DATA* pWorkItemData = (WORKITEM_DATA*)pContext; + + if(pWorkItemData) + { + NTSTATUS status = STATUS_SUCCESS; + FWPS_INCOMING_VALUES* pClassifyValues = (FWPS_INCOMING_VALUES*)pWorkItemData->pClassifyData->pClassifyValues; + FWPS_FILTER* pFilter = (FWPS_FILTER*)pWorkItemData->pClassifyData->pFilter; + PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtInboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtOutboundNetwork(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketModificationAtForward(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + (pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtInboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pWorkItemData->pInjectionData->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtOutboundTransport(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtInboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtOutboundMACFrame(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&(pWorkItemData->pClassifyData), + &(pWorkItemData->pInjectionData), + pModificationData, + FALSE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationWorkItemRoutine() [status: %#x]\n", + (UINT32)STATUS_NOT_SUPPORTED); + + if(status != STATUS_SUCCESS) + { + if(pWorkItemData->pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&(pWorkItemData->pClassifyData)); + + if(pWorkItemData->pInjectionData) + KrnlHlprInjectionDataDestroy(&(pWorkItemData->pInjectionData)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! BasicPacketModificationWorkItemRoutine: PerformBasicPacketModification() [status: %#x]\n", + status); + } + + KrnlHlprWorkItemDataDestroy(&pWorkItemData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- BasicPacketModificationWorkItemRoutine()\n"); + +#endif /// DBG + + return; +} + +/** + @private_function="TriggerBasicPacketModificationInline" + + Purpose: Makes a reference to all the classification data structures and invokes the + appropriate private routine to perform the modification and injection. <br> + <br> + Notes: <br> + <br> + MSDN_Ref: <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicPacketModificationInline(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA** ppInjectionData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicPacketModificationInline()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(ppInjectionData); + NT_ASSERT(*ppInjectionData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + PC_BASIC_PACKET_MODIFICATION_DATA* pModificationData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + HLPR_NEW(pClassifyData, + CLASSIFY_DATA, + WFPSAMPLER_CALLOUT_DRIVER_TAG); + HLPR_BAIL_ON_ALLOC_FAILURE(pClassifyData, + status); + +#pragma warning(pop) + + pClassifyData->pClassifyValues = pClassifyValues; + pClassifyData->pMetadataValues = pMetadata; + pClassifyData->pPacket = pNetBufferList; + pClassifyData->pClassifyContext = pClassifyContext; + pClassifyData->pFilter = pFilter; + pClassifyData->flowContext = flowContext; + pClassifyData->pClassifyOut = pClassifyOut; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtInboundNetwork(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) + status = PerformBasicPacketModificationAtOutboundNetwork(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + status = PerformBasicPacketModificationAtForward(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtInboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + ((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || /// Policy Change Reauthorization + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6))) + status = PerformBasicPacketModificationAtOutboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN7) + + else if((*ppInjectionData)->direction == FWP_DIRECTION_INBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtInboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if((*ppInjectionData)->direction == FWP_DIRECTION_OUTBOUND && + (pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)) + status = PerformBasicPacketModificationAtOutboundTransport(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + else if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtInboundMACFrame(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE) + status = PerformBasicPacketModificationAtOutboundMACFrame(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtIngressVSwitchEthernet(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + else if(pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET) + status = PerformBasicPacketModificationAtEgressVSwitchEthernet(&pClassifyData, + ppInjectionData, + pModificationData, + TRUE); + +#endif // (NTDDI_VERSION >= NTDDI_WIN8) +#endif // (NTDDI_VERSION >= NTDDI_WIN7) + + else + { + status = STATUS_NOT_SUPPORTED; + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! TriggerBasicPacketModificationInline() [status: %#x]\n", + status); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerBasicPacketModificationInline() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +/** + @private_function="TriggerBasicPacketModificationOutOfBand" + + Purpose: Creates a local copy of the classification data structures and queues a WorkItem + to perform the modification and injection at PASSIVE_LEVEL. <br> + <br> + Notes: <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF550679.aspx <br> + HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF566380.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +_Check_return_ +NTSTATUS TriggerBasicPacketModificationOutOfBand(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _In_ FWPS_CLASSIFY_OUT* pClassifyOut, + _In_ INJECTION_DATA* pInjectionData, + _In_ PC_BASIC_PACKET_MODIFICATION_DATA* pPCData) +{ +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> TriggerBasicPacketModificationOutOfBand()\n"); + +#endif /// DBG + + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pNetBufferList); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pInjectionData); + NT_ASSERT(pPCData); + + NTSTATUS status = STATUS_SUCCESS; + CLASSIFY_DATA* pClassifyData = 0; + +#pragma warning(push) +#pragma warning(disable: 6014) /// pClassifyData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + status = KrnlHlprClassifyDataCreateLocalCopy(&pClassifyData, + pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pPCData->useWorkItems) + status = KrnlHlprWorkItemQueue(g_pWDMDevice, + BasicPacketModificationWorkItemRoutine, + pClassifyData, + pInjectionData, + 0); + else if(pPCData->useThreadedDPC) + status = KrnlHlprThreadedDPCQueue(BasicPacketModificationDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + else + status = KrnlHlprDPCQueue(BasicPacketModificationDeferredProcedureCall, + pClassifyData, + pInjectionData, + 0); + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + if(pClassifyData) + KrnlHlprClassifyDataDestroyLocalCopy(&pClassifyData); + } + +#if DBG + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- TriggerBasicPacketModificationOutOfBand() [status: %#x]\n", + status); + +#endif /// DBG + + return status; +} + +#if(NTDDI_VERSION >= NTDDI_WIN7) + +/** + @classify_function="ClassifyBasicPacketModification" + + Purpose: Blocks the current NET_BUFFER_LIST, modifies a clone of the NBL with the specified + data and injects the clone back to the stack's data path. <br> + <br> + Notes: Applies to the following layers: <br> + FWPS_LAYER_INBOUND_IPPACKET_V{4/6} <br> + FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6} <br> + FWPS_LAYER_IPFORWARD_V{4/6} <br> + FWPS_LAYER_INBOUND_TRANSPORT_V{4/6} <br> + FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6} <br> + FWPS_LAYER_DATAGRAM_DATA_V{4/6} <br> + FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6} <br> + FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6} <br> + FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6} <br> + FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} <br> + FWPS_LAYER_STREAM_PACKET_V{4/6} <br> + FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET <br> + FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET <br> + FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE <br> + FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE <br> + FWPS_LAYER_INGRESS_VSWITCH_ETHERNET <br> + FWPS_LAYER_EGRESS_VSWITCH_ETHERNET <br> + <br> + TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544893.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES0* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_opt_ const VOID* pClassifyContext, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + +#if(NTDDI_VERSION >= NTDDI_WIN8) + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE || + pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET || + pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET); + +#else + + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6); + +#endif /// (NTDDI_VERSION >= NTDDI_WIN8) + + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + if(pData) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + + if(pData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER && + (pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)) + { + UINT32 bytesRetreated = 0; + UINT8 version = 0; + IPPROTO protocol = IPPROTO_MAX; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = pMetadata->ipHeaderSize; + } + + if(bytesRetreated) + { + /// Initial offset is at the Transport Header for INBOUND_IPPACKET, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList); + + protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList, + version == IPV4 ? AF_INET : AF_INET6); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + FALSE, + 0); + } + + /// Exit if this isn't the protocol we are looking for + if(protocol != pData->originalTransportData.protocol) + HLPR_BAIL; + else + { + UINT16 sourcePort = 0; + UINT16 destinationPort = 0; + UINT32 bytesAdvanced = 0; + + if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesAdvanced = pMetadata->ipHeaderSize; + } + + if(bytesAdvanced) + { + /// Initial offset is at the IP Header for OUTBOUND_IPPACKET and IPFORWARD, so + /// advance the size of the IP Header ... + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesAdvanced, + FALSE, + 0); + } + + sourcePort = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + /// Exit if the ports are not what we are looking for + if(pData->originalTransportData.sourcePort && + sourcePort != pData->originalTransportData.sourcePort) + HLPR_BAIL; + + if(pData->originalTransportData.destinationPort && + destinationPort != pData->originalTransportData.destinationPort) + HLPR_BAIL; + + if(bytesAdvanced) + { + /// ... and retreat the offset back to the original position. + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesAdvanced, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + } + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ... + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if((pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6) + performOutOfBand = TRUE; + + /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups. + if(!performOutOfBand && + pInjectionData->direction == FWP_DIRECTION_INBOUND) + { + FWP_VALUE* pRemoteAddress = 0; + + pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_REMOTE_ADDRESS); + if(pRemoteAddress && + ((pRemoteAddress->type == FWP_UINT32 && + RtlCompareMemory(&(pRemoteAddress->uint32), + IPV4_LOOPBACK_ADDRESS, + IPV4_ADDRESS_SIZE)) || + (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE && + RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16), + IPV6_LOOPBACK_ADDRESS, + IPV6_ADDRESS_SIZE)))) + performOutOfBand = TRUE; + } + + if(performOutOfBand) + status = TriggerBasicPacketModificationOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicPacketModificationInline(pClassifyValues, + pMetadata, + pNetBufferList, + pClassifyContext, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification() [status: %#x]\n", + status); + } + } + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#else + +/** + @classify_function="ClassifyBasicPacketModification" + + Purpose: Blocks the current NET_BUFFER_LIST, modifies a clone of the NBL with the specified + data and injects the clone back to the stack's data path. <br> + <br> + Notes: Applies to the following layers: <br> + FWPS_LAYER_INBOUND_IPPACKET_V{4/6} <br> + FWPS_LAYER_OUTBOUND_IPPACKET_V{4/6} <br> + FWPS_LAYER_IPFORWARD_V{4/6} <br> + FWPS_LAYER_INBOUND_TRANSPORT_V{4/6} <br> + FWPS_LAYER_OUTBOUND_TRANSPORT_V{4/6} <br> + FWPS_LAYER_DATAGRAM_DATA_V{4/6} <br> + FWPS_LAYER_INBOUND_ICMP_ERROR_V{4/6} <br> + FWPS_LAYER_OUTBOUND_ICMP_ERROR_V{4/6} <br> + FWPS_LAYER_ALE_AUTH_CONNECT_V{4/6} <br> + FWPS_LAYER_ALE_FLOW_ESTABLISHED_V{4/6} <br> + <br> + TCP @ FWPM_LAYER_ALE_AUTH_CONNECT_V{4/6} has no NBL <br> + <br> + MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Hardware/FF544890.aspx <br> +*/ +_IRQL_requires_min_(PASSIVE_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES* pClassifyValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata, + _Inout_opt_ VOID* pNetBufferList, + _In_ const FWPS_FILTER* pFilter, + _In_ UINT64 flowContext, + _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut) +{ + NT_ASSERT(pClassifyValues); + NT_ASSERT(pMetadata); + NT_ASSERT(pFilter); + NT_ASSERT(pClassifyOut); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 || + pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 || + pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6); + NT_ASSERT(pFilter->providerContext); + NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT); + NT_ASSERT(pFilter->providerContext->dataBuffer); + NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_BASIC_PACKET_MODIFICATION_DATA)); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " ---> ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Rights: %#x]", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->rights); + + + if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE) + { + /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject + if(pNetBufferList) + { + NTSTATUS status = STATUS_SUCCESS; + FWP_VALUE* pFlags = 0; + PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + INJECTION_DATA* pInjectionData = 0; + + pClassifyOut->actionType = FWP_ACTION_CONTINUE; + + pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_FLAGS); + if(pFlags && + pFlags->type == FWP_UINT32) + { + /// For IPsec interop, if ALE classification is required, bypass the injection + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED && + +#if(NTDDI_VERSION >= NTDDI_WIN6SP1) + + FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED)) +#else + + pFlags->uint32 & FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY) + +#endif // (NTDDI_VERSION >= NTDDI_WIN6SP1) + + HLPR_BAIL; + + /// Inject the individual fragments, but not the fragment grouping of those fragments + if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP) + HLPR_BAIL; + } + + if(pData && + pData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER && + (pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 || + pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)) + { + UINT32 bytesRetreated = 0; + UINT8 version = 0; + IPPROTO protocol = IPPROTO_MAX; + + if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || + pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) + { + if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata, + FWPS_METADATA_FIELD_IP_HEADER_SIZE)) + bytesRetreated = pMetadata->ipHeaderSize; + } + + if(bytesRetreated) + { + /// Initial offset is at the Transport Header for INBOUND_IPPACKET, so retreat the size of the IP Header ... + status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + 0, + 0); + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n", + status); + + HLPR_BAIL; + } + } + + version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList); + + protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList, + version == IPV4 ? AF_INET : AF_INET6); + + if(bytesRetreated) + { + /// ... and advance the offset back to the original position. + NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList), + bytesRetreated, + FALSE, + 0); + } + + /// Exit if this isn't the protocol we are looking for + if(protocol != pData->originalTransportData.protocol) + HLPR_BAIL; + else + { + UINT16 sourcePort = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + UINT16 destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList, + protocol); + /// Exit if the ports are not what we are looking for + if((pData->originalTransportData.sourcePort && + sourcePort != pData->originalTransportData.sourcePort) || + (pData->originalTransportData.destinationPort && + destinationPort != pData->originalTransportData.destinationPort)) + HLPR_BAIL; + } + } + +#pragma warning(push) +#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy + + status = KrnlHlprInjectionDataCreate(&pInjectionData, + pClassifyValues, + pMetadata, + (NET_BUFFER_LIST*)pNetBufferList, + pFilter); + HLPR_BAIL_ON_FAILURE(status); + +#pragma warning(pop) + + if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF && + pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) + { + BOOLEAN performOutOfBand = TRUE; + FWP_VALUE* pProtocolValue = 0; + PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data; + + pClassifyOut->actionType = FWP_ACTION_BLOCK; + pClassifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; + pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; + + if(pFlags && + pFlags->type == FWP_UINT32 && + pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED) + pInjectionData->isIPsecSecured = TRUE; + + /// Override the default of performing Out of Band with the user's specified setting ... + if(pData->performInline) + performOutOfBand = FALSE; + + /// Due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent + pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues, + &FWPM_CONDITION_IP_PROTOCOL); + if(pProtocolValue && + pProtocolValue->uint8 == IPPROTO_TCP && + pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) + performOutOfBand = TRUE; + + if(performOutOfBand) + status = TriggerBasicPacketModificationOutOfBand(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + pInjectionData, + pData); + else + status = TriggerBasicPacketModificationInline(pClassifyValues, + pMetadata, + pNetBufferList, + 0, + pFilter, + flowContext, + pClassifyOut, + &pInjectionData); + } + else + { + pClassifyOut->actionType = FWP_ACTION_PERMIT; + + KrnlHlprInjectionDataDestroy(&pInjectionData); + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " -- Injection previously performed.\n"); + } + + HLPR_BAIL_LABEL: + + NT_ASSERT(status == STATUS_SUCCESS); + + if(status != STATUS_SUCCESS) + { + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_ERROR_LEVEL, + " !!!! ClassifyBasicPacketModification() [status: %#x]\n", + status); + + KrnlHlprInjectionDataDestroy(&pInjectionData); + } + } + else + pClassifyOut->actionType = FWP_ACTION_PERMIT; + } + + DbgPrintEx(DPFLTR_IHVNETWORK_ID, + DPFLTR_INFO_LEVEL, + " <--- ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n", + KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId), + pFilter->filterId, + pClassifyOut->actionType, + pClassifyOut->rights, + (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE"); + + return; +} + +#endif // (NTDDI_VERSION >= NTDDI_WIN7) |
