summaryrefslogtreecommitdiff
path: root/network/trans/ddproxy/sys
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /network/trans/ddproxy/sys
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'network/trans/ddproxy/sys')
-rw-r--r--network/trans/ddproxy/sys/DD_drv.c1059
-rw-r--r--network/trans/ddproxy/sys/DD_proxy.c1039
-rw-r--r--network/trans/ddproxy/sys/DD_proxy.h251
-rw-r--r--network/trans/ddproxy/sys/ddproxy.inf63
-rw-r--r--network/trans/ddproxy/sys/ddproxy.vcxproj185
-rw-r--r--network/trans/ddproxy/sys/ddproxy.vcxproj.Filters29
6 files changed, 2626 insertions, 0 deletions
diff --git a/network/trans/ddproxy/sys/DD_drv.c b/network/trans/ddproxy/sys/DD_drv.c
new file mode 100644
index 00000000..18509e53
--- /dev/null
+++ b/network/trans/ddproxy/sys/DD_drv.c
@@ -0,0 +1,1059 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+
+ Datagram-Data Transparent Proxy Callout Driver Sample.
+
+ This sample callout driver intercepts UDP and non-error ICMP traffic
+ of interest and proxies them to a new destination address and/or port
+ (for UDP); response traffic will be proxied back to have the original
+ tuple values. The proxying is transparent to the application.
+
+ Inspection parameters and proxy settings are configurable via the
+ following registry values --
+
+ HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ddproxy\Parameters
+
+ o InspectUdp (REG_DWORD) : 0 (ICMP); 1 (UDP, default)
+ o DestinationAddressToIntercept (REG_SZ) : literal IPv4/IPv6 string
+ (e.g. �10.0.0.1�)
+ o DestinationPortToIntercept (REG_DWORD) : applicable if InspectUdp is 1
+ o NewDestinationAddress(REG_SZ) : literal IPv4/IPv6 string
+ o NewDestinationPort(REG_DWORD)
+
+ The sample is IP version agnostic. It performs proxying for both IPv4
+ and IPv6 traffic.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include <ntddk.h>
+#include <wdf.h>
+
+#pragma warning(push)
+#pragma warning(disable:4201) // unnamed struct/union
+
+#include <fwpsk.h>
+
+#pragma warning(pop)
+
+#include <fwpmk.h>
+
+#include <ws2ipdef.h>
+#include <in6addr.h>
+#include <ip2string.h>
+
+#include "DD_proxy.h"
+
+#define INITGUID
+#include <guiddef.h>
+
+//
+// Configurable parameters (addresses and ports are in host order)
+//
+
+BOOLEAN configInspectUdp = TRUE;
+
+UINT16 configInspectDestPort = 5001;
+UINT8* configInspectDestAddrV4 = NULL;
+UINT8* configInspectDestAddrV6 = NULL;
+
+UINT16 configNewDestPort = 5001;
+UINT8* configNewDestAddrV4 = NULL;
+UINT8* configNewDestAddrV6 = NULL;
+
+SOCKADDR_STORAGE destAddr, newDestAddr;
+
+//
+// Callout and sublayer GUIDs
+//
+
+// b16b0a6e-2b2a-41a3-8b39-bd3ffc855ff8
+DEFINE_GUID(
+ DD_PROXY_CALLOUT_V4,
+ 0xb16b0a6e,
+ 0x2b2a,
+ 0x41a3,
+ 0x8b, 0x39, 0xbd, 0x3f, 0xfc, 0x85, 0x5f, 0xf8
+);
+// 2cebde39-1f59-48d1-a5d9-3e2458351476
+DEFINE_GUID(
+ DD_PROXY_CALLOUT_V6,
+ 0x2cebde39,
+ 0x1f59,
+ 0x48d1,
+ 0xa5, 0xd9, 0x3e, 0x24, 0x58, 0x35, 0x14, 0x76
+);
+// ee93719d-ad5d-48c9-ae46-7270367d205d
+DEFINE_GUID(
+ DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V4,
+ 0xee93719d,
+ 0xad5d,
+ 0x48c9,
+ 0xae, 0x46, 0x72, 0x70, 0x36, 0x7d, 0x20, 0x5d
+);
+
+// 1e3d3d13-0588-4167-82a3-14f68c98de86
+DEFINE_GUID(
+ DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V6,
+ 0x1e3d3d13,
+ 0x0588,
+ 0x4167,
+ 0x82, 0xa3, 0x14, 0xf6, 0x8c, 0x98, 0xde, 0x86
+);
+
+// 0104fd7e-c825-414e-94c9-f0d525bbc169
+DEFINE_GUID(
+ DD_PROXY_SUBLAYER,
+ 0x0104fd7e,
+ 0xc825,
+ 0x414e,
+ 0x94, 0xc9, 0xf0, 0xd5, 0x25, 0xbb, 0xc1, 0x69
+);
+
+//
+// Callout driver global variables
+//
+
+DEVICE_OBJECT* gWdmDevice;
+
+HANDLE gEngineHandle;
+UINT32 gFlowEstablishedCalloutIdV4, gCalloutIdV4;
+UINT32 gFlowEstablishedCalloutIdV6, gCalloutIdV6;
+
+HANDLE gInjectionHandle;
+
+LIST_ENTRY gFlowList;
+KSPIN_LOCK gFlowListLock;
+
+LIST_ENTRY gPacketQueue;
+KSPIN_LOCK gPacketQueueLock;
+KEVENT gPacketQueueEvent;
+
+BOOLEAN gDriverUnloading = FALSE;
+void* gThreadObj;
+
+DRIVER_INITIALIZE DriverEntry;
+EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
+
+//
+// Callout driver implementation
+//
+
+NTSTATUS
+DDProxyLoadIPAddress(
+ _In_ const WDFKEY key,
+ _In_ const UNICODE_STRING* valueName,
+ _Out_ SOCKADDR_STORAGE* result
+ )
+{
+ NTSTATUS status;
+ PWSTR terminator;
+ DECLARE_UNICODE_STRING_SIZE(value, INET6_ADDRSTRLEN);
+ IN_ADDR *resultV4 = &((SOCKADDR_IN*)result)->sin_addr;
+ IN6_ADDR *resultV6 = &((SOCKADDR_IN6*)result)->sin6_addr;
+
+ status = WdfRegistryQueryUnicodeString(key, valueName, NULL, &value);
+ result->ss_family = AF_UNSPEC;
+
+ if (NT_SUCCESS(status))
+ {
+ // The Registry API does not guarantee that the string will be
+ // null-terminated.
+ // Defensively null-terminate the string.
+ value.Length = min(value.Length, value.MaximumLength - sizeof(WCHAR));
+ value.Buffer[value.Length/sizeof(WCHAR)] = UNICODE_NULL;
+
+ status = RtlIpv4StringToAddressW(
+ value.Buffer,
+ TRUE,
+ &terminator,
+ resultV4
+ );
+
+ if (NT_SUCCESS(status))
+ {
+ resultV4->S_un.S_addr = RtlUlongByteSwap(resultV4->S_un.S_addr);
+ result->ss_family = AF_INET;
+ }
+ else
+ {
+ status = RtlIpv6StringToAddressW(
+ value.Buffer,
+ &terminator,
+ resultV6
+ );
+
+ if (NT_SUCCESS(status))
+ {
+ result->ss_family = AF_INET6;
+ }
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+DDProxyLoadConfig(
+ _In_ const WDFKEY key
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ DECLARE_CONST_UNICODE_STRING(inspectUdpValueName, L"InspectUdp");
+ DECLARE_CONST_UNICODE_STRING(destAddrValueName, L"DestinationAddressToIntercept");
+ DECLARE_CONST_UNICODE_STRING(destPortValueName, L"DestinationPortToIntercept");
+ DECLARE_CONST_UNICODE_STRING(newDestAddrValueName, L"NewDestinationAddress");
+ DECLARE_CONST_UNICODE_STRING(newDestPortValueName, L"NewDestinationPort");
+
+ ULONG ulongValue;
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(
+ key,
+ &inspectUdpValueName,
+ &ulongValue
+ )))
+ {
+ configInspectUdp = (ulongValue != 0);
+ }
+
+
+ if (NT_SUCCESS(DDProxyLoadIPAddress(
+ key,
+ &destAddrValueName,
+ &destAddr
+ )))
+ {
+ if (destAddr.ss_family == AF_INET)
+ {
+ configInspectDestAddrV4 = &((SOCKADDR_IN*)&destAddr)->sin_addr.S_un.S_un_b.s_b1;
+ }
+ else if (destAddr.ss_family == AF_INET6)
+ {
+ configInspectDestAddrV6 = (UINT8*)(&((SOCKADDR_IN6*)&destAddr)->sin6_addr.u.Byte[0]);
+ }
+ }
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(
+ key,
+ &destPortValueName,
+ &ulongValue
+ )))
+ {
+ configInspectDestPort = (USHORT) ulongValue;
+ }
+
+ if (NT_SUCCESS(DDProxyLoadIPAddress(
+ key,
+ &newDestAddrValueName,
+ &newDestAddr
+ )))
+ {
+ if (destAddr.ss_family == AF_INET)
+ {
+ configNewDestAddrV4 = &((SOCKADDR_IN*)&newDestAddr)->sin_addr.S_un.S_un_b.s_b1;
+ }
+ else if (destAddr.ss_family == AF_INET6)
+ {
+ configNewDestAddrV6 = (UINT8*)(&((SOCKADDR_IN6*)&newDestAddr)->sin6_addr.u.Byte[0]);
+ }
+ }
+
+ if (NT_SUCCESS(WdfRegistryQueryULong(
+ key,
+ &newDestPortValueName,
+ &ulongValue
+ )))
+ {
+ configNewDestPort = (USHORT) ulongValue;
+ }
+
+ return status;
+}
+
+NTSTATUS
+DDProxyAddFilter(
+ _In_ const PWSTR filterName,
+ _In_ const PWSTR filterDesc,
+ _In_reads_(16) const UINT8* remoteAddr,
+ _In_ USHORT remotePort,
+ _In_ FWP_DIRECTION direction,
+ _In_ UINT64 context,
+ _In_ const GUID* layerKey,
+ _In_ const GUID* calloutKey
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ FWPM_FILTER filter = {0};
+ FWPM_FILTER_CONDITION filterConditions[3] = {0};
+ UINT conditionIndex;
+
+ filter.layerKey = *layerKey;
+ filter.displayData.name = (wchar_t*)filterName;
+ filter.displayData.description = (wchar_t*)filterDesc;
+
+ filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
+ filter.action.calloutKey = *calloutKey;
+ filter.filterCondition = filterConditions;
+ filter.subLayerKey = DD_PROXY_SUBLAYER;
+ filter.weight.type = FWP_EMPTY; // auto-weight.
+ filter.rawContext = context;
+
+ conditionIndex = 0;
+
+ if (remoteAddr != NULL)
+ {
+ filterConditions[conditionIndex].fieldKey =
+ FWPM_CONDITION_IP_REMOTE_ADDRESS;
+ filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
+
+ if (IsEqualGUID(layerKey, &FWPM_LAYER_DATAGRAM_DATA_V4) ||
+ IsEqualGUID(layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4))
+ {
+ filterConditions[conditionIndex].conditionValue.type = FWP_UINT32;
+ filterConditions[conditionIndex].conditionValue.uint32 =
+ *(UINT32*)remoteAddr;
+ }
+ else
+ {
+ filterConditions[conditionIndex].conditionValue.type =
+ FWP_BYTE_ARRAY16_TYPE;
+ filterConditions[conditionIndex].conditionValue.byteArray16 =
+ (FWP_BYTE_ARRAY16*)remoteAddr;
+ }
+
+ conditionIndex++;
+ }
+
+ filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_DIRECTION;
+ filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
+ filterConditions[conditionIndex].conditionValue.type = FWP_UINT32;
+ filterConditions[conditionIndex].conditionValue.uint32 = direction;
+
+ conditionIndex++;
+
+ if (configInspectUdp)
+ {
+ filterConditions[conditionIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
+ filterConditions[conditionIndex].matchType = FWP_MATCH_EQUAL;
+ filterConditions[conditionIndex].conditionValue.type = FWP_UINT16;
+ filterConditions[conditionIndex].conditionValue.uint16 = remotePort;
+
+ conditionIndex++;
+ }
+
+ filter.numFilterConditions = conditionIndex;
+
+ status = FwpmFilterAdd(
+ gEngineHandle,
+ &filter,
+ NULL,
+ NULL);
+
+ return status;
+}
+
+NTSTATUS
+DDProxyRegisterFlowEstablishedCallouts(
+ _In_ const GUID* layerKey,
+ _In_ const GUID* calloutKey,
+ _Inout_ void* deviceObject,
+ _Out_ UINT32* calloutId
+ )
+/* ++
+
+ This function registers callouts and filters at the following layers
+ to intercept flow creations for the original and the proxy flows.
+
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6
+
+-- */
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ FWPS_CALLOUT sCallout = {0};
+ FWPM_CALLOUT mCallout = {0};
+
+ FWPM_DISPLAY_DATA displayData = {0};
+
+ BOOLEAN calloutRegistered = FALSE;
+
+ sCallout.calloutKey = *calloutKey;
+ sCallout.classifyFn = DDProxyFlowEstablishedClassify;
+ sCallout.notifyFn = DDProxyFlowEstablishedNotify;
+
+ status = FwpsCalloutRegister(
+ deviceObject,
+ &sCallout,
+ calloutId
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ calloutRegistered = TRUE;
+
+ displayData.name = L"Datagram-Data Proxy Flow-Established Callout";
+ displayData.description =
+ L"Intercepts flow creations for the original and the proxy flows";
+
+ mCallout.calloutKey = *calloutKey;
+ mCallout.displayData = displayData;
+ mCallout.applicableLayer = *layerKey;
+
+ status = FwpmCalloutAdd(
+ gEngineHandle,
+ &mCallout,
+ NULL,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyAddFilter(
+ L"Datagram-Data Proxy Flow-Established Filter (Original Flow)",
+ L"Intercepts flow creations for the original flow",
+ IsEqualGUID(layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4) ?
+ configInspectDestAddrV4 : configInspectDestAddrV6,
+ configInspectDestPort,
+ FWP_DIRECTION_OUTBOUND,
+ DD_PROXY_FLOW_ORIGINAL,
+ layerKey,
+ calloutKey
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyAddFilter(
+ L"Datagram-Data Proxy Flow-Established Filter (Proxy Flow)",
+ L"Intercepts flow creations for the proxy flow",
+ IsEqualGUID(layerKey, &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4) ?
+ configNewDestAddrV4 : configNewDestAddrV6,
+ configNewDestPort,
+ FWP_DIRECTION_OUTBOUND,
+ DD_PROXY_FLOW_PROXY,
+ layerKey,
+ calloutKey
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ if (calloutRegistered)
+ {
+ FwpsCalloutUnregisterById(*calloutId);
+ *calloutId = 0;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+DDProxyRegisterDatagramDataCallouts(
+ _In_ const GUID* layerKey,
+ _In_ const GUID* calloutKey,
+ _Inout_ void* deviceObject,
+ _Out_ UINT32* calloutId
+ )
+/* ++
+
+ This function registers callouts and filters that intercept TCP traffic at
+ WFP FWPM_LAYER_DATAGRAM_DATA_V4 or FWPM_LAYER_DATAGRAM_DATA_V6 layer.
+
+-- */
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ FWPS_CALLOUT sCallout = {0};
+ FWPM_CALLOUT mCallout = {0};
+
+ FWPM_DISPLAY_DATA displayData = {0};
+
+ BOOLEAN calloutRegistered = FALSE;
+
+ sCallout.calloutKey = *calloutKey;
+ sCallout.classifyFn = DDProxyClassify;
+ sCallout.notifyFn = DDProxyNotify;
+ sCallout.flowDeleteFn = DDProxyFlowDelete;
+ sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW;
+
+ status = FwpsCalloutRegister(
+ deviceObject,
+ &sCallout,
+ calloutId
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ calloutRegistered = TRUE;
+
+ displayData.name = L"Datagram-Data Proxy Callout";
+ displayData.description = L"Proxies destination address/port for UDP/ICMP";
+
+ mCallout.calloutKey = *calloutKey;
+ mCallout.displayData = displayData;
+ mCallout.applicableLayer = *layerKey;
+
+ status = FwpmCalloutAdd(
+ gEngineHandle,
+ &mCallout,
+ NULL,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyAddFilter(
+ L"Datagram-Data Proxy Filter (Outbound)",
+ L"Proxies destination address/port for UDP/ICMP",
+ IsEqualGUID(layerKey, &FWPM_LAYER_DATAGRAM_DATA_V4) ?
+ configInspectDestAddrV4 : configInspectDestAddrV6,
+ configInspectDestPort,
+ FWP_DIRECTION_OUTBOUND,
+ 0,
+ layerKey,
+ calloutKey
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyAddFilter(
+ L"Datagram-Data Proxy Filter (Inbound)",
+ L"Proxies destination address/port for UDP/ICMP",
+ IsEqualGUID(layerKey, &FWPM_LAYER_DATAGRAM_DATA_V4) ?
+ configNewDestAddrV4 : configNewDestAddrV6,
+ configNewDestPort,
+ FWP_DIRECTION_INBOUND,
+ 0,
+ layerKey,
+ calloutKey
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+Exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ if (calloutRegistered)
+ {
+ FwpsCalloutUnregisterById(*calloutId);
+ *calloutId = 0;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+DDProxyRegisterCallouts(
+ _Inout_ void* deviceObject
+ )
+/* ++
+
+ This function registers dynamic callouts and filters that intercept UDP or
+ non-error ICMP traffic at WFP FWPM_LAYER_DATAGRAM_DATA_V{4|6} and
+ FWPM_LAYER_ALE_FLOW_ESTABLISHED_V{4|6} layers.
+
+ Callouts and filters will be removed during DriverUnload.
+
+-- */
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ FWPM_SUBLAYER DDProxySubLayer;
+
+ BOOLEAN engineOpened = FALSE;
+ BOOLEAN inTransaction = FALSE;
+
+ FWPM_SESSION session = {0};
+
+ session.flags = FWPM_SESSION_FLAG_DYNAMIC;
+
+ status = FwpmEngineOpen(
+ NULL,
+ RPC_C_AUTHN_WINNT,
+ NULL,
+ &session,
+ &gEngineHandle
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ engineOpened = TRUE;
+
+ status = FwpmTransactionBegin(gEngineHandle, 0);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ inTransaction = TRUE;
+
+ RtlZeroMemory(&DDProxySubLayer, sizeof(FWPM_SUBLAYER));
+
+ DDProxySubLayer.subLayerKey = DD_PROXY_SUBLAYER;
+ DDProxySubLayer.displayData.name = L"Datagram-Data Proxy Sub-Layer";
+ DDProxySubLayer.displayData.description =
+ L"Sub-Layer for use by Datagram-Data Proxy callouts";
+ DDProxySubLayer.flags = 0;
+ DDProxySubLayer.weight = FWP_EMPTY; // auto-weight.;
+
+ status = FwpmSubLayerAdd(gEngineHandle, &DDProxySubLayer, NULL);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyRegisterFlowEstablishedCallouts(
+ &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4,
+ &DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V4,
+ deviceObject,
+ &gFlowEstablishedCalloutIdV4
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyRegisterFlowEstablishedCallouts(
+ &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6,
+ &DD_PROXY_FLOW_ESTABLISHED_CALLOUT_V6,
+ deviceObject,
+ &gFlowEstablishedCalloutIdV6
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyRegisterDatagramDataCallouts(
+ &FWPM_LAYER_DATAGRAM_DATA_V4,
+ &DD_PROXY_CALLOUT_V4,
+ deviceObject,
+ &gCalloutIdV4
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyRegisterDatagramDataCallouts(
+ &FWPM_LAYER_DATAGRAM_DATA_V6,
+ &DD_PROXY_CALLOUT_V6,
+ deviceObject,
+ &gCalloutIdV6
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = FwpmTransactionCommit(gEngineHandle);
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ inTransaction = FALSE;
+
+Exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ if (inTransaction)
+ {
+ FwpmTransactionAbort(gEngineHandle);
+ _Analysis_assume_lock_not_held_(gEngineHandle); // Potential leak if "FwpmTransactionAbort" fails
+ }
+ if (engineOpened)
+ {
+ FwpmEngineClose(gEngineHandle);
+ gEngineHandle = NULL;
+ }
+ }
+
+ return status;
+}
+
+void
+DDProxyUnregisterCallouts(void)
+{
+ FwpmEngineClose(gEngineHandle);
+ gEngineHandle = NULL;
+
+ FwpsCalloutUnregisterById(gCalloutIdV6);
+ FwpsCalloutUnregisterById(gCalloutIdV4);
+
+ FwpsCalloutUnregisterById(gFlowEstablishedCalloutIdV6);
+ FwpsCalloutUnregisterById(gFlowEstablishedCalloutIdV4);
+}
+
+void
+DDProxyRemoveFlows(void)
+{
+ while (!IsListEmpty(&gFlowList))
+ {
+ KLOCK_QUEUE_HANDLE flowListLockHandle;
+ LIST_ENTRY* listEntry = NULL;
+ DD_PROXY_FLOW_CONTEXT* flowContext;
+
+ KeAcquireInStackQueuedSpinLock(
+ &gFlowListLock,
+ &flowListLockHandle
+ );
+
+ if (!IsListEmpty(&gFlowList))
+ {
+ listEntry = RemoveHeadList(&gFlowList);
+ }
+
+ //
+ // Releasing the lock here since removing the flow context
+ // will invoke the callout's flowDeleteFn synchronously
+ // if there are no active classifications in progress.
+ //
+ KeReleaseInStackQueuedSpinLock(&flowListLockHandle);
+
+ if (listEntry != NULL)
+ {
+ flowContext = CONTAINING_RECORD(
+ listEntry,
+ DD_PROXY_FLOW_CONTEXT,
+ listEntry
+ );
+
+ flowContext->deleted = TRUE;
+
+ FwpsFlowRemoveContext(
+ flowContext->flowId,
+ flowContext->layerId,
+ flowContext->calloutId
+ );
+ }
+ }
+}
+
+_Function_class_(EVT_WDF_DRIVER_UNLOAD)
+_IRQL_requires_same_
+_IRQL_requires_max_(PASSIVE_LEVEL)
+void
+EvtDriverUnload(
+ _In_ WDFDRIVER driverObject
+ )
+{
+ KLOCK_QUEUE_HANDLE packetQueueLockHandle;
+ KLOCK_QUEUE_HANDLE flowListLockHandle;
+
+ UNREFERENCED_PARAMETER(driverObject);
+
+ KeAcquireInStackQueuedSpinLock(
+ &gPacketQueueLock,
+ &packetQueueLockHandle
+ );
+
+ KeAcquireInStackQueuedSpinLock(
+ &gFlowListLock,
+ &flowListLockHandle
+ );
+
+ gDriverUnloading = TRUE;
+
+ KeReleaseInStackQueuedSpinLock(&flowListLockHandle);
+
+ //
+ // Any associated flow contexts must be removed before
+ // a callout can be successfully unregistered.
+ //
+ DDProxyRemoveFlows();
+
+ if (IsListEmpty(&gPacketQueue))
+ {
+ KeSetEvent(
+ &gPacketQueueEvent,
+ IO_NO_INCREMENT,
+ FALSE
+ );
+ }
+
+ KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle);
+
+ NT_ASSERT(gThreadObj != NULL);
+
+ KeWaitForSingleObject(
+ gThreadObj,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ ObDereferenceObject(gThreadObj);
+
+ DDProxyUnregisterCallouts();
+
+ FwpsInjectionHandleDestroy(gInjectionHandle);
+}
+
+//
+// Create the minimal WDF Driver and Device objects required for a WFP callout
+// driver.
+//
+NTSTATUS
+DDProxyInitDriverObjects(
+ _Inout_ DRIVER_OBJECT* driverObject,
+ _In_ const UNICODE_STRING* registryPath,
+ _Out_ WDFDRIVER* pDriver,
+ _Out_ WDFDEVICE* pDevice
+ )
+{
+ NTSTATUS status;
+ WDF_DRIVER_CONFIG config;
+ PWDFDEVICE_INIT pInit = NULL;
+
+ WDF_DRIVER_CONFIG_INIT(
+ &config,
+ WDF_NO_EVENT_CALLBACK
+ );
+
+ config.DriverInitFlags |= WdfDriverInitNonPnpDriver;
+ config.EvtDriverUnload = EvtDriverUnload;
+
+ status = WdfDriverCreate(
+ driverObject,
+ registryPath,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &config,
+ pDriver
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ pInit = WdfControlDeviceInitAllocate(
+ *pDriver,
+ &SDDL_DEVOBJ_KERNEL_ONLY
+ );
+
+ if (!pInit)
+ {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Exit;
+ }
+
+ WdfDeviceInitSetDeviceType(
+ pInit,
+ FILE_DEVICE_NETWORK
+ );
+
+ WdfDeviceInitSetCharacteristics(
+ pInit,
+ FILE_DEVICE_SECURE_OPEN,
+ FALSE
+ );
+
+ WdfDeviceInitSetCharacteristics(
+ pInit,
+ FILE_AUTOGENERATED_DEVICE_NAME,
+ TRUE
+ );
+
+ status = WdfDeviceCreate(
+ &pInit,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ pDevice
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ WdfDeviceInitFree(pInit);
+ goto Exit;
+ }
+
+ WdfControlFinishInitializing(*pDevice);
+
+Exit:
+ return status;
+}
+
+
+NTSTATUS
+DriverEntry(
+ DRIVER_OBJECT* driverObject,
+ UNICODE_STRING* registryPath
+ )
+{
+ NTSTATUS status;
+ WDFDRIVER driver;
+ WDFDEVICE device;
+ WDFKEY configKey;
+ HANDLE threadHandle;
+
+ // Request NX Non-Paged Pool when available
+ ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
+
+ status = DDProxyInitDriverObjects(
+ driverObject,
+ registryPath,
+ &driver,
+ &device
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = WdfDriverOpenParametersRegistryKey(
+ driver,
+ KEY_READ,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &configKey
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = DDProxyLoadConfig(configKey);
+
+ if (!NT_SUCCESS(status))
+ {
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto Exit;
+ }
+
+ //
+ // To proxy UDP traffic, a new destination port or a pair of inspect and
+ // proxy ip address need to be pre-configured. To proxy UDP traffic, a
+ // pair of inspect and proxy ip addresses must be pre-configured.
+ //
+ if (configInspectUdp)
+ {
+ if ((configInspectDestPort == configNewDestPort) &&
+ (((configInspectDestAddrV4 == NULL) ||
+ (configNewDestAddrV4 == NULL)) &&
+ ((configInspectDestAddrV6 == NULL) ||
+ (configNewDestAddrV6 == NULL))))
+ {
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto Exit;
+ }
+ }
+ else
+ {
+ if (((configInspectDestAddrV4 == NULL) ||
+ (configNewDestAddrV4 == NULL)) &&
+ ((configInspectDestAddrV6 == NULL) ||
+ (configNewDestAddrV6 == NULL)))
+ {
+ status = STATUS_DEVICE_CONFIGURATION_ERROR;
+ goto Exit;
+ }
+ }
+
+ status = FwpsInjectionHandleCreate(
+ AF_UNSPEC,
+ FWPS_INJECTION_TYPE_TRANSPORT,
+ &gInjectionHandle
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ InitializeListHead(&gFlowList);
+ KeInitializeSpinLock(&gFlowListLock);
+
+ InitializeListHead(&gPacketQueue);
+ KeInitializeSpinLock(&gPacketQueueLock);
+ KeInitializeEvent(
+ &gPacketQueueEvent,
+ NotificationEvent,
+ FALSE
+ );
+
+ gWdmDevice = WdfDeviceWdmGetDeviceObject(device);
+
+ status = DDProxyRegisterCallouts(gWdmDevice);
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = PsCreateSystemThread(
+ &threadHandle,
+ THREAD_ALL_ACCESS,
+ NULL,
+ NULL,
+ NULL,
+ DDProxyWorker,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ status = ObReferenceObjectByHandle(
+ threadHandle,
+ 0,
+ NULL,
+ KernelMode,
+ &gThreadObj,
+ NULL
+ );
+ NT_ASSERT(NT_SUCCESS(status));
+
+ ZwClose(threadHandle);
+
+Exit:
+
+ if (!NT_SUCCESS(status))
+ {
+ if (gEngineHandle != NULL)
+ {
+ DDProxyUnregisterCallouts();
+ }
+ if (gInjectionHandle != NULL)
+ {
+ FwpsInjectionHandleDestroy(gInjectionHandle);
+ }
+ }
+
+ return status;
+}
diff --git a/network/trans/ddproxy/sys/DD_proxy.c b/network/trans/ddproxy/sys/DD_proxy.c
new file mode 100644
index 00000000..a75e2cf9
--- /dev/null
+++ b/network/trans/ddproxy/sys/DD_proxy.c
@@ -0,0 +1,1039 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+
+ This file implements the classifyFn, notifiFn, and flowDeleteFn callout
+ functions for the flow-established and datagram-data callouts. In addition
+ the system worker thread that performs the actual packet modifications
+ is also implemented here along with the eventing mechanisms shared between
+ the classify function and the worker thread.
+
+ Packet modification is done out-of-band by a system worker thread using
+ the reference-drop-clone-modify-reinject mechanism. Therefore the sample
+ can serve as a base in scenarios where filtering/modification decision
+ cannot be made within the classifyFn() callout and instead must be made,
+ for example, by an user-mode application.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include <ntddk.h>
+
+#pragma warning(push)
+#pragma warning(disable:4201) // unnamed struct/union
+
+#include <fwpsk.h>
+
+#pragma warning(pop)
+
+#include <fwpmk.h>
+
+#include "DD_proxy.h"
+
+__inline
+void
+DDProxyFreePendedPacket(
+ _Inout_ __drv_freesMem(Mem) DD_PROXY_PENDED_PACKET* packet,
+ _Inout_opt_ __drv_freesMem(Mem) WSACMSGHDR* controlData
+ )
+{
+ FwpsDereferenceNetBufferList(packet->netBufferList, FALSE);
+ DDProxyDereferenceFlowContext(packet->belongingFlow);
+ if (controlData != NULL)
+ {
+ ExFreePoolWithTag(controlData, DD_PROXY_CONTROL_DATA_POOL_TAG);
+ }
+ ExFreePoolWithTag(packet, DD_PROXY_PENDED_PACKET_POOL_TAG);
+}
+
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+
+void
+DDProxyFlowEstablishedClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_opt_ const void* classifyContext,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ )
+
+#else
+
+void
+DDProxyFlowEstablishedClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ )
+
+#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
+
+/* ++
+
+ This is the classifyFn function of the flow-established callout. It
+ allocates flow context for the original and the proxy flow and associates
+ them with the indicated flow-id. This function also stores information
+ common to both flows in the context. The flow context is inserted into the
+ global flow list.
+
+-- */
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ BOOLEAN locked = FALSE;
+
+ KLOCK_QUEUE_HANDLE flowListLockHandle;
+
+ DD_PROXY_FLOW_CONTEXT* flowContextLocal = NULL;
+
+ UNREFERENCED_PARAMETER(layerData);
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+ UNREFERENCED_PARAMETER(classifyContext);
+#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
+ UNREFERENCED_PARAMETER(flowContext);
+
+ flowContextLocal = ExAllocatePoolWithTag(
+ NonPagedPool,
+ sizeof(DD_PROXY_FLOW_CONTEXT),
+ DD_PROXY_FLOW_CONTEXT_POOL_TAG
+ );
+
+ if (flowContextLocal == NULL)
+ {
+ status = STATUS_NO_MEMORY;
+ goto Exit;
+ }
+
+ RtlZeroMemory(flowContextLocal, sizeof(DD_PROXY_FLOW_CONTEXT));
+
+ flowContextLocal->refCount = 1;
+ flowContextLocal->flowType = (DD_PROXY_FLOW_TYPE)(filter->context);
+ flowContextLocal->addressFamily =
+ (inFixedValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4) ?
+ AF_INET : AF_INET6;
+ NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues,
+ FWPS_METADATA_FIELD_FLOW_HANDLE));
+ flowContextLocal->flowId = inMetaValues->flowHandle;
+
+ //
+ // Note that since the consumer of the flow context is the datagram-data
+ // layer classifyFn, layerId and calloutId are set to those of DD and not
+ // flow-established.
+ //
+ flowContextLocal->layerId =
+ (flowContextLocal->addressFamily == AF_INET) ?
+ FWPS_LAYER_DATAGRAM_DATA_V4 : FWPS_LAYER_DATAGRAM_DATA_V6;
+ flowContextLocal->calloutId =
+ (flowContextLocal->addressFamily == AF_INET) ?
+ gCalloutIdV4 : gCalloutIdV6;
+
+ if (flowContextLocal->addressFamily == AF_INET)
+ {
+ // Prefast thinks we are ignoring this return value.
+ // If driver is unloading, we give up and ignore it on purpose.
+ // Otherwise, we put the pointer onto the list, but we make it opaque
+ // by casting it as a UINT64, and this tricks Prefast.
+ flowContextLocal->ipv4LocalAddr =
+ RtlUlongByteSwap(
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32
+ );
+ flowContextLocal->protocol =
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8;
+ }
+ else
+ {
+ RtlCopyMemory(
+ (UINT8*)&flowContextLocal->localAddr,
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_ADDRESS].value.byteArray16,
+ sizeof(FWP_BYTE_ARRAY16)
+ );
+ flowContextLocal->protocol =
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8;
+ }
+
+ if (flowContextLocal->flowType == DD_PROXY_FLOW_ORIGINAL)
+ {
+ flowContextLocal->toRemoteAddr =
+ (flowContextLocal->addressFamily == AF_INET) ?
+ configNewDestAddrV4 : configNewDestAddrV6;
+ // host-order -> network-order conversion for port.
+ flowContextLocal->toRemotePort = RtlUshortByteSwap(configNewDestPort);
+ }
+ else
+ {
+ NT_ASSERT(flowContextLocal->flowType == DD_PROXY_FLOW_PROXY);
+ flowContextLocal->toRemoteAddr =
+ (flowContextLocal->addressFamily == AF_INET) ?
+ configInspectDestAddrV4 : configInspectDestAddrV6;
+ // host-order -> network-order conversion for port.
+ // See PREfast comments above. Opaque pointer tricks PREfast.
+ flowContextLocal->toRemotePort = RtlUshortByteSwap(configInspectDestPort);
+ }
+ if ((flowContextLocal->toRemoteAddr != NULL) &&
+ (flowContextLocal->addressFamily == AF_INET))
+ {
+ // host-order -> network-order conversion for Ipv4 address.
+ // See PREfast comments above. Opaque pointer tricks PREfast.
+ flowContextLocal->ipv4NetworkOrderStorage =
+ RtlUlongByteSwap(*(ULONG*)(flowContextLocal->toRemoteAddr));
+ flowContextLocal->toRemoteAddr =
+ (UINT8*)&flowContextLocal->ipv4NetworkOrderStorage;
+ }
+
+ KeAcquireInStackQueuedSpinLock(
+ &gFlowListLock,
+ &flowListLockHandle
+ );
+
+ locked = TRUE;
+
+ if (!gDriverUnloading)
+ {
+ //
+ // Associate DD_PROXY_FLOW_CONTEXT with the indicated flow-id to be
+ // accessible by the Datagram-Data classifyFn. (i.e. when a packet
+ // belongs to the same flow being classified at Datagram-Data layer,
+ // DD_PROXY_FLOW_CONTEXT will be passed onto the classifyFn as the
+ // "flowContext" parameter.
+ //
+ status = FwpsFlowAssociateContext(
+ flowContextLocal->flowId,
+ flowContextLocal->layerId,
+ flowContextLocal->calloutId,
+ (UINT64)flowContextLocal
+ );
+ if(!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ InsertHeadList(&gFlowList, &flowContextLocal->listEntry);
+ flowContextLocal = NULL; // ownership transferred
+ }
+
+ classifyOut->actionType = FWP_ACTION_PERMIT;
+
+ if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
+ {
+ classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
+ }
+
+
+Exit:
+
+ if(locked)
+ {
+ KeReleaseInStackQueuedSpinLock(&flowListLockHandle);
+ }
+
+ if (flowContextLocal != NULL)
+ {
+ ExFreePoolWithTag(flowContextLocal, DD_PROXY_FLOW_CONTEXT_POOL_TAG);
+ }
+
+ if(!NT_SUCCESS(status))
+ {
+ classifyOut->actionType = FWP_ACTION_BLOCK;
+ classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
+ }
+
+ return;
+}
+
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+
+void
+DDProxyClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_opt_ const void* classifyContext,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ )
+
+#else
+
+void
+DDProxyClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ )
+
+#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
+/* ++
+
+ This is the classifyFn function of the datagram-data callout. It
+ allocates a packet structure to store the classify and meta data and
+ it references the net buffer list for out-of-band modification and
+ re-injection. The packet structure will be queued to the global packet
+ queue. The worker thread will then be signaled, if idle, to process
+ the queue.
+
+-- */
+{
+ DD_PROXY_PENDED_PACKET* packet = NULL;
+ DD_PROXY_FLOW_CONTEXT* flowContextLocal = (DD_PROXY_FLOW_CONTEXT*)(DWORD_PTR)flowContext;
+
+ FWPS_PACKET_INJECTION_STATE packetState;
+ KLOCK_QUEUE_HANDLE packetQueueLockHandle;
+ BOOLEAN signalWorkerThread;
+
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+ UNREFERENCED_PARAMETER(classifyContext);
+#endif
+ UNREFERENCED_PARAMETER(filter);
+
+ _Analysis_assume_(layerData != NULL);
+
+ //
+ // We don't have the necessary right to alter the packet.
+ //
+ if ((classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) == 0)
+ {
+ goto Exit;
+ }
+
+ //
+ // We don't re-inspect packets that we've inspected earlier.
+ //
+ packetState = FwpsQueryPacketInjectionState(
+ gInjectionHandle,
+ layerData,
+ NULL
+ );
+
+ if ((packetState == FWPS_PACKET_INJECTED_BY_SELF) ||
+ (packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF))
+ {
+ classifyOut->actionType = FWP_ACTION_PERMIT;
+ if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
+ {
+ classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
+ }
+
+ goto Exit;
+ }
+
+ packet = ExAllocatePoolWithTag(
+ NonPagedPool,
+ sizeof(DD_PROXY_PENDED_PACKET),
+ DD_PROXY_PENDED_PACKET_POOL_TAG
+ );
+
+ if (packet == NULL)
+ {
+ classifyOut->actionType = FWP_ACTION_BLOCK;
+ classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
+ goto Exit;
+ }
+
+ RtlZeroMemory(packet, sizeof(DD_PROXY_PENDED_PACKET));
+
+ NT_ASSERT(flowContextLocal != NULL);
+
+ packet->belongingFlow = flowContextLocal;
+ DDProxyReferenceFlowContext(packet->belongingFlow);
+ if (flowContextLocal->addressFamily == AF_INET)
+ {
+ NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4);
+ packet->direction =
+ inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V4_DIRECTION].\
+ value.uint32;
+ }
+ else
+ {
+ NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6);
+ packet->direction =
+ inFixedValues->incomingValue[FWPS_FIELD_DATAGRAM_DATA_V6_DIRECTION].\
+ value.uint32;
+ }
+ packet->netBufferList = layerData;
+
+ //
+ // Reference the net buffer list to make it accessible outside of
+ // classifyFn.
+ //
+ FwpsReferenceNetBufferList(packet->netBufferList, TRUE);
+
+ NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues,
+ FWPS_METADATA_FIELD_COMPARTMENT_ID));
+ packet->compartmentId = inMetaValues->compartmentId;
+
+ if (packet->direction == FWP_DIRECTION_OUTBOUND)
+ {
+ NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
+ inMetaValues,
+ FWPS_METADATA_FIELD_TRANSPORT_ENDPOINT_HANDLE));
+ packet->endpointHandle = inMetaValues->transportEndpointHandle;
+
+ if (flowContextLocal->addressFamily == AF_INET)
+ {
+ // See PREfast comments above. Opaque pointer tricks PREfast.
+ packet->ipv4RemoteAddr =
+ RtlUlongByteSwap( /* host-order -> network-order conversion */
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_DATAGRAM_DATA_V4_IP_REMOTE_ADDRESS].value.uint32
+ );
+ }
+ else
+ {
+ RtlCopyMemory(
+ (UINT8*)&packet->remoteAddr,
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_DATAGRAM_DATA_V6_IP_REMOTE_ADDRESS].value.byteArray16,
+ sizeof(FWP_BYTE_ARRAY16)
+ );
+
+ }
+ packet->remoteScopeId = inMetaValues->remoteScopeId;
+
+ if (FWPS_IS_METADATA_FIELD_PRESENT(
+ inMetaValues,
+ FWPS_METADATA_FIELD_TRANSPORT_CONTROL_DATA))
+ {
+ NT_ASSERT(inMetaValues->controlDataLength > 0);
+
+ packet->controlData = ExAllocatePoolWithTag(
+ NonPagedPool,
+ inMetaValues->controlDataLength,
+ DD_PROXY_CONTROL_DATA_POOL_TAG
+ );
+ if (packet->controlData == NULL)
+ {
+ classifyOut->actionType = FWP_ACTION_BLOCK;
+ classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
+ goto Exit;
+ }
+
+ RtlCopyMemory(
+ packet->controlData,
+ inMetaValues->controlData,
+ inMetaValues->controlDataLength
+ );
+
+ packet->controlDataLength = inMetaValues->controlDataLength;
+ }
+ }
+ else
+ {
+ NT_ASSERT(packet->direction == FWP_DIRECTION_INBOUND);
+
+ if (flowContextLocal->addressFamily == AF_INET)
+ {
+ NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4);
+ packet->interfaceIndex =
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_DATAGRAM_DATA_V4_INTERFACE_INDEX].value.uint32;
+ packet->subInterfaceIndex =
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_DATAGRAM_DATA_V4_SUB_INTERFACE_INDEX].value.uint32;
+ }
+ else
+ {
+ NT_ASSERT(inFixedValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6);
+ packet->interfaceIndex =
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_DATAGRAM_DATA_V6_INTERFACE_INDEX].value.uint32;
+ packet->subInterfaceIndex =
+ inFixedValues->incomingValue\
+ [FWPS_FIELD_DATAGRAM_DATA_V6_SUB_INTERFACE_INDEX].value.uint32;
+ }
+
+ NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
+ inMetaValues,
+ FWPS_METADATA_FIELD_IP_HEADER_SIZE));
+ NT_ASSERT(FWPS_IS_METADATA_FIELD_PRESENT(
+ inMetaValues,
+ FWPS_METADATA_FIELD_TRANSPORT_HEADER_SIZE));
+ packet->ipHeaderSize = inMetaValues->ipHeaderSize;
+ packet->transportHeaderSize = inMetaValues->transportHeaderSize;
+
+ packet->nblOffset =
+ NET_BUFFER_DATA_OFFSET(NET_BUFFER_LIST_FIRST_NB(packet->netBufferList));
+ }
+
+ KeAcquireInStackQueuedSpinLock(
+ &gPacketQueueLock,
+ &packetQueueLockHandle
+ );
+
+ if (!gDriverUnloading)
+ {
+ signalWorkerThread = IsListEmpty(&gPacketQueue);
+
+ InsertTailList(&gPacketQueue, &packet->listEntry);
+ packet = NULL; // ownership transferred
+
+ classifyOut->actionType = FWP_ACTION_BLOCK;
+ classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
+ classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
+ }
+ else
+ {
+ //
+ // Driver is being unloaded, permit any incoming packets.
+ //
+ signalWorkerThread = FALSE;
+
+ classifyOut->actionType = FWP_ACTION_PERMIT;
+ if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
+ {
+ classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
+ }
+ }
+
+ if (signalWorkerThread)
+ {
+ KeSetEvent(
+ &gPacketQueueEvent,
+ 0,
+ FALSE
+ );
+ }
+
+ KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle);
+
+Exit:
+
+ if (packet != NULL)
+ {
+ DDProxyFreePendedPacket(packet, packet->controlData);
+ }
+
+ return;
+}
+
+NTSTATUS
+DDProxyFlowEstablishedNotify(
+ _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
+ _In_ const GUID* filterKey,
+ _Inout_ const FWPS_FILTER* filter
+ )
+{
+ UNREFERENCED_PARAMETER(notifyType);
+ UNREFERENCED_PARAMETER(filterKey);
+ UNREFERENCED_PARAMETER(filter);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+DDProxyNotify(
+ _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
+ _In_ const GUID* filterKey,
+ _Inout_ const FWPS_FILTER* filter
+ )
+{
+ UNREFERENCED_PARAMETER(notifyType);
+ UNREFERENCED_PARAMETER(filterKey);
+ UNREFERENCED_PARAMETER(filter);
+
+ return STATUS_SUCCESS;
+}
+
+void
+DDProxyFlowDelete(
+ _In_ UINT16 layerId,
+ _In_ UINT32 calloutId,
+ _In_ UINT64 flowContext
+ )
+/* ++
+
+ This is the flowDeleteFn function of the datagram-data callout. It
+ removes the flow context from the global flow list and dereference the
+ context.
+
+-- */
+{
+ DD_PROXY_FLOW_CONTEXT* flowContextLocal = (DD_PROXY_FLOW_CONTEXT*)(DWORD_PTR)flowContext;
+
+ KLOCK_QUEUE_HANDLE flowListLockHandle;
+
+ UNREFERENCED_PARAMETER(layerId);
+ UNREFERENCED_PARAMETER(calloutId);
+
+ KeAcquireInStackQueuedSpinLock(
+ &gFlowListLock,
+ &flowListLockHandle
+ );
+
+ if (!flowContextLocal->deleted)
+ {
+ RemoveEntryList(&flowContextLocal->listEntry);
+ }
+
+ KeReleaseInStackQueuedSpinLock(&flowListLockHandle);
+
+ DDProxyDereferenceFlowContext(flowContextLocal);
+}
+
+typedef struct UDP_HEADER_ {
+ UINT16 srcPort;
+ UINT16 destPort;
+ UINT16 length;
+ UINT16 checksum;
+} UDP_HEADER;
+
+void DDProxyInjectComplete(
+ _Inout_ void* context,
+ _Inout_ NET_BUFFER_LIST* netBufferList,
+ _In_ BOOLEAN dispatchLevel
+ )
+{
+ DD_PROXY_PENDED_PACKET* packet = context;
+ UNREFERENCED_PARAMETER(dispatchLevel);
+
+ FwpsFreeCloneNetBufferList(netBufferList, 0);
+
+ DDProxyFreePendedPacket(packet, packet->controlData);
+}
+
+NTSTATUS
+DDProxyCloneModifyReinjectOutbound(
+ _In_ DD_PROXY_PENDED_PACKET* packet
+ )
+/* ++
+
+ This function clones the outbound net buffer list and, if needed,
+ modifies the destination port of all indicated packets (i.e. NET_BUFFER)
+ and/or send-injects the clone to a new destination address.
+
+-- */
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ NET_BUFFER_LIST* clonedNetBufferList = NULL;
+ UDP_HEADER* udpHeader;
+ FWPS_TRANSPORT_SEND_PARAMS sendArgs = {0};
+
+ status = FwpsAllocateCloneNetBufferList(
+ packet->netBufferList,
+ NULL,
+ NULL,
+ 0,
+ &clonedNetBufferList
+ );
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ //
+ // Check to see if port modification is required.
+ //
+ if ((packet->belongingFlow->protocol == IPPROTO_UDP) &&
+ (packet->belongingFlow->toRemotePort != 0))
+ {
+ NET_BUFFER* netBuffer;
+
+ //
+ // The data offset of outbound transport packets is the beginning of
+ // transport header (e.g. UDP header). The IP header has not yet been
+ // constructed at Datagram-Data (or outbound Transport) layer.
+ //
+ // Note the packet offset is inherited by the clone.
+ //
+
+ //
+ // Outbound net buffer list can contain more than one net buffer (e.g.
+ // one UDP packet).
+ //
+
+ for (netBuffer = NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList);
+ netBuffer != NULL;
+ netBuffer = NET_BUFFER_NEXT_NB(netBuffer))
+ {
+ udpHeader = NdisGetDataBuffer(
+ netBuffer,
+ sizeof(UDP_HEADER),
+ NULL,
+ sizeof(UINT16),
+ 0
+ );
+ NT_ASSERT(udpHeader != NULL); // We can assume UDP header in a net buffer
+ // is contiguous and 2-byte aligned.
+ _Analysis_assume_(udpHeader != NULL);
+
+ udpHeader->destPort = packet->belongingFlow->toRemotePort;
+ udpHeader->checksum = 0;
+ }
+ }
+
+ //
+ // Determine whehter we need to proxy the destination address. If not,
+ // we set the remoteAddress to the same address that was initially
+ // classified.
+ //
+ sendArgs.remoteAddress =
+ (packet->belongingFlow->toRemoteAddr ? packet->belongingFlow->toRemoteAddr
+ : (UINT8*)&packet->remoteAddr);
+ sendArgs.remoteScopeId = packet->remoteScopeId;
+ sendArgs.controlData = packet->controlData;
+ sendArgs.controlDataLength = packet->controlDataLength;
+
+ //
+ // Send-inject the modified net buffer list to the new destination address.
+ //
+
+ status = FwpsInjectTransportSendAsync(
+ gInjectionHandle,
+ NULL,
+ packet->endpointHandle,
+ 0,
+ &sendArgs,
+ packet->belongingFlow->addressFamily,
+ packet->compartmentId,
+ clonedNetBufferList,
+ DDProxyInjectComplete,
+ packet
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ clonedNetBufferList = NULL; // ownership transferred to the
+ // completion function.
+
+Exit:
+
+ if (clonedNetBufferList != NULL)
+ {
+ FwpsFreeCloneNetBufferList(clonedNetBufferList, 0);
+ }
+
+ return status;
+}
+
+NTSTATUS
+DDProxyCloneModifyReinjectInbound(
+ _In_ DD_PROXY_PENDED_PACKET* packet
+ )
+/* ++
+
+ This function clones the inbound net buffer list and, if needed,
+ modifies the source port and/or source address and receive-injects
+ the clone back to the tcpip stack.
+
+-- */
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ NET_BUFFER_LIST* clonedNetBufferList = NULL;
+ NET_BUFFER* netBuffer;
+ UDP_HEADER* udpHeader;
+ ULONG nblOffset;
+ NDIS_STATUS ndisStatus;
+
+ //
+ // For inbound net buffer list, we can assume it contains only one
+ // net buffer.
+ //
+ netBuffer = NET_BUFFER_LIST_FIRST_NB(packet->netBufferList);
+
+ nblOffset = NET_BUFFER_DATA_OFFSET(netBuffer);
+
+ //
+ // The TCP/IP stack could have retreated the net buffer list by the
+ // transportHeaderSize amount; detect the condition here to avoid
+ // retreating twice.
+ //
+ if (nblOffset != packet->nblOffset)
+ {
+ NT_ASSERT(packet->nblOffset - nblOffset == packet->transportHeaderSize);
+ packet->transportHeaderSize = 0;
+ }
+
+ //
+ // Adjust the net buffer list offset to the start of the IP header.
+ //
+ ndisStatus = NdisRetreatNetBufferDataStart(
+ netBuffer,
+ packet->ipHeaderSize + packet->transportHeaderSize,
+ 0,
+ NULL
+ );
+ _Analysis_assume_(ndisStatus == NDIS_STATUS_SUCCESS);
+
+ //
+ // Note that the clone will inherit the original net buffer list's offset.
+ //
+
+ status = FwpsAllocateCloneNetBufferList(
+ packet->netBufferList,
+ NULL,
+ NULL,
+ 0,
+ &clonedNetBufferList
+ );
+
+ //
+ // Undo the adjustment on the original net buffer list.
+ //
+
+ NdisAdvanceNetBufferDataStart(
+ netBuffer,
+ packet->ipHeaderSize + packet->transportHeaderSize,
+ FALSE,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ //
+ // Check to see if port modification is required.
+ //
+ if ((packet->belongingFlow->protocol == IPPROTO_UDP) &&
+ (packet->belongingFlow->toRemotePort != 0))
+ {
+ netBuffer = NET_BUFFER_LIST_FIRST_NB(clonedNetBufferList);
+
+ //
+ // Advance to the beginning of the transport header (i.e. UDP header).
+ //
+ NdisAdvanceNetBufferDataStart(
+ netBuffer,
+ packet->ipHeaderSize,
+ FALSE,
+ NULL
+ );
+
+ udpHeader = NdisGetDataBuffer(
+ netBuffer,
+ sizeof(UDP_HEADER),
+ NULL,
+ sizeof(UINT16),
+ 0
+ );
+ NT_ASSERT(udpHeader != NULL); // We can assume UDP header in a net buffer
+ // is contiguous and 2-byte aligned.
+ _Analysis_assume_(udpHeader != NULL);
+
+ udpHeader->destPort =
+ packet->belongingFlow->toRemotePort;
+ // This is our new source port -- or
+ // the destination port of the original
+ // outbound traffic.
+ udpHeader->checksum = 0;
+
+ //
+ // Undo the advance. Net buffer list needs to be positioned at the
+ // beginning of IP header for address modification and/or receive-
+ // injection.
+ //
+ ndisStatus = NdisRetreatNetBufferDataStart(
+ netBuffer,
+ packet->ipHeaderSize,
+ 0,
+ NULL
+ );
+ _Analysis_assume_(ndisStatus == NDIS_STATUS_SUCCESS);
+
+ }
+
+ if (packet->belongingFlow->toRemoteAddr != NULL)
+ {
+ status = FwpsConstructIpHeaderForTransportPacket(
+ clonedNetBufferList,
+ packet->ipHeaderSize,
+ packet->belongingFlow->addressFamily,
+ packet->belongingFlow->toRemoteAddr,
+ // This is our new source address --
+ // or the destination address of the
+ // original outbound traffic.
+ (UINT8*)&packet->belongingFlow->localAddr,
+ // This is the destination address of
+ // the clone -- or the source of the
+ // original outbound traffic.
+ packet->belongingFlow->protocol,
+ 0,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ 0,
+ 0
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+ }
+
+ status = FwpsInjectTransportReceiveAsync(
+ gInjectionHandle,
+ NULL,
+ NULL,
+ 0,
+ packet->belongingFlow->addressFamily,
+ packet->compartmentId,
+ packet->interfaceIndex,
+ packet->subInterfaceIndex,
+ clonedNetBufferList,
+ DDProxyInjectComplete,
+ packet
+ );
+
+ if (!NT_SUCCESS(status))
+ {
+ goto Exit;
+ }
+
+ clonedNetBufferList = NULL; // ownership transferred to the
+ // completion function.
+
+Exit:
+
+ if (clonedNetBufferList != NULL)
+ {
+ FwpsFreeCloneNetBufferList(clonedNetBufferList, 0);
+ }
+
+ return status;
+}
+
+void
+DDProxyWorker(
+ _In_ void* StartContext
+ )
+/* ++
+
+ This worker thread waits for the packet queue event when the queue is
+ empty; and it will be woken up when there are packets queued needing to
+ be proxied to or from the new destination address/port. Once awaking,
+ It will run in a loop to clone-modify-reinject packets until the packet
+ queue is exhausted (and it will go to sleep waiting for more work).
+
+ The worker thread will end once it detected the driver is unloading.
+
+-- */
+{
+ DD_PROXY_PENDED_PACKET* packet;
+ LIST_ENTRY* listEntry;
+ KLOCK_QUEUE_HANDLE packetQueueLockHandle;
+
+ UNREFERENCED_PARAMETER(StartContext);
+
+ for(;;)
+ {
+ KeWaitForSingleObject(
+ &gPacketQueueEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ if (gDriverUnloading)
+ {
+ break;
+ }
+
+ NT_ASSERT(!IsListEmpty(&gPacketQueue));
+
+ KeAcquireInStackQueuedSpinLock(
+ &gPacketQueueLock,
+ &packetQueueLockHandle
+ );
+
+ listEntry = RemoveHeadList(&gPacketQueue);
+
+ KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle);
+
+ packet = CONTAINING_RECORD(
+ listEntry,
+ DD_PROXY_PENDED_PACKET,
+ listEntry
+ );
+
+ if (!packet->belongingFlow->deleted)
+ {
+ NTSTATUS status;
+
+ if (packet->direction == FWP_DIRECTION_OUTBOUND)
+ {
+ status = DDProxyCloneModifyReinjectOutbound(packet);
+ }
+ else
+ {
+ status = DDProxyCloneModifyReinjectInbound(packet);
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ packet = NULL; // ownership transferred.
+ }
+ }
+
+ if (packet != NULL)
+ {
+ DDProxyFreePendedPacket(packet, packet->controlData);
+ }
+
+ KeAcquireInStackQueuedSpinLock(
+ &gPacketQueueLock,
+ &packetQueueLockHandle
+ );
+
+ if (IsListEmpty(&gPacketQueue) && !gDriverUnloading)
+ {
+ KeClearEvent(&gPacketQueueEvent);
+ }
+
+ KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle);
+ }
+
+ NT_ASSERT(gDriverUnloading);
+
+ //
+ // Discard all the pended packets if driver is being unloaded.
+ //
+
+ KeAcquireInStackQueuedSpinLock(
+ &gPacketQueueLock,
+ &packetQueueLockHandle
+ );
+
+ while (!IsListEmpty(&gPacketQueue))
+ {
+ listEntry = RemoveHeadList(&gPacketQueue);
+
+ packet = CONTAINING_RECORD(
+ listEntry,
+ DD_PROXY_PENDED_PACKET,
+ listEntry
+ );
+
+ DDProxyFreePendedPacket(packet, packet->controlData);
+ }
+
+ KeReleaseInStackQueuedSpinLock(&packetQueueLockHandle);
+ PsTerminateSystemThread(STATUS_SUCCESS);
+
+
+}
diff --git a/network/trans/ddproxy/sys/DD_proxy.h b/network/trans/ddproxy/sys/DD_proxy.h
new file mode 100644
index 00000000..197b0ff9
--- /dev/null
+++ b/network/trans/ddproxy/sys/DD_proxy.h
@@ -0,0 +1,251 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved
+
+Abstract:
+
+ This header files declares common data types and function prototypes used
+ throughout the Datagram-Data transparent proxy sample.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#ifndef _DD_PROXY_H_
+#define _DD_PROXY_H_
+
+typedef enum DD_PROXY_FLOW_TYPE_
+{
+ DD_PROXY_FLOW_ORIGINAL,
+ DD_PROXY_FLOW_PROXY
+} DD_PROXY_FLOW_TYPE;
+
+//
+// DD_PROXY_FLOW_CONTEXT is the object type we used to stored information
+// specific flow. This callout driver maintains two kind of flow contexts --
+// the original flow and the flow being proxied to.
+//
+
+typedef struct DD_PROXY_FLOW_CONTEXT_
+{
+ LIST_ENTRY listEntry;
+
+ BOOLEAN deleted;
+
+ DD_PROXY_FLOW_TYPE flowType;
+ ADDRESS_FAMILY addressFamily;
+
+ #pragma warning(push)
+ #pragma warning(disable: 4201) //NAMELESS_STRUCT_UNION
+ union
+ {
+ FWP_BYTE_ARRAY16 localAddr;
+ UINT32 ipv4LocalAddr;
+ };
+ #pragma warning(pop)
+
+
+ UINT8 protocol;
+
+ UINT64 flowId;
+ UINT16 layerId;
+ UINT32 calloutId;
+
+ UINT32 ipv4NetworkOrderStorage;
+
+ //
+ // For DD_PROXY_FLOW_ORIGINAL type, toRemote* is the new address/port
+ // we are proxing to. For DD_PROXY_FLOW_PROXY type, it is the address/
+ // port that we will need to revert to.
+ //
+ UINT8* toRemoteAddr;
+ UINT16 toRemotePort;
+
+ LONG refCount;
+} DD_PROXY_FLOW_CONTEXT;
+
+//
+// DD_PROXY_PENDED_PACKET is the object type we used to store all information
+// needed for out-of-band packet modification and re-injection. This type
+// also points back to the flow context the packet belongs to.
+
+typedef struct DD_PROXY_PENDED_PACKET_
+{
+ LIST_ENTRY listEntry;
+
+ DD_PROXY_FLOW_CONTEXT* belongingFlow;
+ FWP_DIRECTION direction;
+
+ //
+ // Common fields for inbound and outbound traffic.
+ //
+ NET_BUFFER_LIST* netBufferList;
+ COMPARTMENT_ID compartmentId;
+
+ //
+ // Data fields for outbound packet re-injection.
+ //
+ UINT64 endpointHandle;
+
+ #pragma warning(push)
+ #pragma warning(disable: 4201) //NAMELESS_STRUCT_UNION
+ union
+ {
+ FWP_BYTE_ARRAY16 remoteAddr;
+ UINT32 ipv4RemoteAddr;
+ };
+ #pragma warning(pop)
+
+ SCOPE_ID remoteScopeId;
+ WSACMSGHDR* controlData;
+ ULONG controlDataLength;
+
+ //
+ // Data fields for inbound packet re-injection.
+ //
+ ULONG nblOffset;
+ UINT32 ipHeaderSize;
+ UINT32 transportHeaderSize;
+ IF_INDEX interfaceIndex;
+ IF_INDEX subInterfaceIndex;
+} DD_PROXY_PENDED_PACKET;
+
+//
+// Pooltags used by this callout driver.
+//
+#define DD_PROXY_FLOW_CONTEXT_POOL_TAG 'olfD'
+#define DD_PROXY_PENDED_PACKET_POOL_TAG 'kppD'
+#define DD_PROXY_CONTROL_DATA_POOL_TAG 'dcdD'
+
+//
+// Shared global data.
+//
+extern UINT16 configInspectDestPort;
+extern UINT8* configInspectDestAddrV4;
+extern UINT8* configInspectDestAddrV6;
+
+extern UINT16 configNewDestPort;
+extern UINT8* configNewDestAddrV4;
+extern UINT8* configNewDestAddrV6;
+
+extern HANDLE gInjectionHandle;
+
+extern LIST_ENTRY gFlowList;
+extern KSPIN_LOCK gFlowListLock;
+
+extern LIST_ENTRY gPacketQueue;
+extern KSPIN_LOCK gPacketQueueLock;
+extern KEVENT gPacketQueueEvent;
+
+extern UINT32 gCalloutIdV4;
+extern UINT32 gCalloutIdV6;
+
+extern BOOLEAN gDriverUnloading;
+
+//
+// Utility functions
+//
+
+__inline void
+DDProxyReferenceFlowContext(
+ _Inout_ DD_PROXY_FLOW_CONTEXT* flowContext
+ )
+{
+ NT_ASSERT(flowContext->refCount > 0);
+ InterlockedIncrement(&flowContext->refCount);
+}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+__inline
+void
+DDProxyDereferenceFlowContext(
+ _Inout_ DD_PROXY_FLOW_CONTEXT* flowContext
+ )
+{
+ NT_ASSERT(flowContext->refCount > 0);
+ InterlockedDecrement(&flowContext->refCount);
+ if (flowContext->refCount == 0)
+ {
+ ExFreePoolWithTag(flowContext, DD_PROXY_FLOW_CONTEXT_POOL_TAG);
+ }
+}
+
+//
+// Shared function prototypes
+//
+
+#if(NTDDI_VERSION >= NTDDI_WIN7)
+
+void
+DDProxyFlowEstablishedClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_opt_ const void* classifyContext,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ );
+
+void
+DDProxyClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_opt_ const void* classifyContext,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ );
+
+#else
+
+void
+DDProxyFlowEstablishedClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ );
+
+void
+DDProxyClassify(
+ _In_ const FWPS_INCOMING_VALUES* inFixedValues,
+ _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues,
+ _Inout_opt_ void* layerData,
+ _In_ const FWPS_FILTER* filter,
+ _In_ UINT64 flowContext,
+ _Inout_ FWPS_CLASSIFY_OUT* classifyOut
+ );
+
+#endif /// (NTDDI_VERSION >= NTDDI_WIN7)
+
+void
+DDProxyFlowDelete(
+ _In_ UINT16 layerId,
+ _In_ UINT32 calloutId,
+ _In_ UINT64 flowContext
+ );
+
+NTSTATUS
+DDProxyFlowEstablishedNotify(
+ _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
+ _In_ const GUID* filterKey,
+ _Inout_ const FWPS_FILTER* filter
+ );
+
+
+NTSTATUS
+DDProxyNotify(
+ _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
+ _In_ const GUID* filterKey,
+ _Inout_ const FWPS_FILTER* filter
+ );
+
+KSTART_ROUTINE DDProxyWorker;
+
+#endif // _DD_PROXY_H_
diff --git a/network/trans/ddproxy/sys/ddproxy.inf b/network/trans/ddproxy/sys/ddproxy.inf
new file mode 100644
index 00000000..dd504967
--- /dev/null
+++ b/network/trans/ddproxy/sys/ddproxy.inf
@@ -0,0 +1,63 @@
+;;;
+;;; Copyright (c) Microsoft Corporation. All rights reserved
+;;;
+;;; Abstract:
+;;; DatagramData Proxy Callout sample driver install configuration.
+;;;
+
+[Version]
+ Signature = "$Windows NT$"
+ Class = WFPCALLOUTS
+ ClassGuid = {57465043-616C-6C6F-7574-5F636C617373}
+ Provider = %Contoso%
+ CatalogFile = DDProxy.cat
+ DriverVer = 11/24/2014,14.24.55.836
+
+[SourceDisksNames]
+ 1 = %DDProxyDisk%,,,""
+
+[SourceDisksFiles]
+ DDProxy.sys = 1,,
+
+[DestinationDirs]
+ DefaultDestDir = 12 ; %WinDir%\System32\Drivers
+ DDProxy.DriverFiles = 12 ; %WinDir%\System32\Drivers
+
+[DefaultInstall]
+ OptionDesc = %DDProxyServiceDesc%
+ CopyFiles = DDProxy.DriverFiles
+
+[DefaultInstall.Services]
+ AddService = %DDProxyServiceName%,,DDProxy.Service
+
+[DefaultUninstall]
+ DelFiles = DDProxy.DriverFiles
+
+[DefaultUninstall.Services]
+ DelService = %DDProxyServiceName%,0x200 ; SPSVCINST_STOPSERVICE
+ DelReg = DDProxy.DelRegistry
+
+[DDProxy.DriverFiles]
+ DDProxy.sys,,,0x00000040 ; COPYFLG_OVERWRITE_OLDER_ONLY
+
+[DDProxy.Service]
+ DisplayName = %DDProxyServiceName%
+ Description = %DDProxyServiceDesc%
+ ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+ StartType = 3 ; SERVICE_DEMAND_START
+ ErrorControl = 1 ; SERVICE_ERROR_NORMAL
+ ServiceBinary = %12%\DDProxy.sys ; %WinDir%\System32\Drivers\DDProxy.sys
+ AddReg = DDProxy.AddRegistry
+
+[DDProxy.AddRegistry]
+ HKR,"Parameters","DestinationAddressToIntercept",0x00000000,"10.0.0.1" ; FLG_ADDREG_TYPE_SZ
+ HKR,"Parameters","NewDestinationAddress",0x00000000,"10.0.0.2" ; FLG_ADDREG_TYPE_SZ
+
+[DDProxy.DelRegistry]
+ HKR,"Parameters",,,
+
+[Strings]
+ Contoso = "Contoso Ltd."
+ DDProxyDisk = "DatagramData Proxy Installation Disk"
+ DDProxyServiceDesc = "DatagramData Proxy Callout Driver"
+ DDProxyServiceName = "DDProxy" \ No newline at end of file
diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj b/network/trans/ddproxy/sys/ddproxy.vcxproj
new file mode 100644
index 00000000..9fd6e6af
--- /dev/null
+++ b/network/trans/ddproxy/sys/ddproxy.vcxproj
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{F5ED1745-0947-474A-924D-CB5D3D2D6C5E}</ProjectGuid>
+ <RootNamespace>$(MSBuildProjectName)</RootNamespace>
+ <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR>
+ <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
+ <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+ <SampleGuid>{91B8C3EC-EC68-40D0-BDAE-FECDC2A5A69E}</SampleGuid>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>False</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetVersion>Windows10</TargetVersion>
+ <UseDebugLibraries>True</UseDebugLibraries>
+ <DriverTargetPlatform>Universal</DriverTargetPlatform>
+ <DriverType>KMDF</DriverType>
+ <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <PropertyGroup>
+ <OutDir>$(IntDir)</OutDir>
+ </PropertyGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
+ </ImportGroup>
+ <ItemGroup Label="WrappedTaskItems" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>ddproxy</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>ddproxy</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>ddproxy</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>ddproxy</TargetName>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </ResourceCompile>
+ <ClCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(DDK_LIB_PATH)\ntoskrnl.lib;$(DDK_LIB_PATH)\ndis.lib;$(DDK_LIB_PATH)\wdmsec.lib;$(DDK_LIB_PATH)\fwpkclnt.lib;$(SDK_LIB_PATH)\uuid.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="dd_drv.c" />
+ <ClCompile Include="dd_proxy.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Exclude="@(Inf)" Include="*.inf" />
+ <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Exclude="@(None)" Include="*.txt;*.htm;*.html" />
+ <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" />
+ <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project> \ No newline at end of file
diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters
new file mode 100644
index 00000000..9728a6bb
--- /dev/null
+++ b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions>
+ <UniqueIdentifier>{667070D4-ABD3-4134-83D9-C2E8E3B4D990}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{55D8461C-9BC4-4CBA-8364-1C9E8628AD2E}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Resource Files">
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions>
+ <UniqueIdentifier>{89C687B6-C04D-48DD-BFC4-36AB091E7E94}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{3E15A537-E520-445D-AFC4-4CFCEF6234E6}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dd_drv.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dd_proxy.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file