- Refactor the WFP interface:

* WinDivert sublayers are created and inserted when the driver is loaded.
  * All WinDivert callouts are installed at the same sublayer.
- Clean-up the implementation of priorities.
- Re-introduce deep copying for SNIFF mode.  This avoids referencing the
  sniffed packets.
- Fix-up the netfilter example:
  * Don't send RSTs to RST/FINs, this can start a RST war.
  * Don't inject ICMP outbound - this may not be a problem despite 1233 errors.
This commit is contained in:
basil00
2013-12-23 11:03:02 +08:00
parent 4f81c315d6
commit 57934712c6
4 changed files with 359 additions and 174 deletions
+8 -1
View File
@@ -495,6 +495,13 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer,
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
priority32 = WINDIVERT_PRIORITY(priority);
if (priority32 < WINDIVERT_PRIORITY_MIN ||
priority32 > WINDIVERT_PRIORITY_MAX)
{
SetLastError(ERROR_INVALID_PARAMETER);
return INVALID_HANDLE_VALUE;
}
// Parse the filter:
if (!WinDivertCompileFilter(filter, layer, ioctl_filter, &filter_len))
@@ -503,6 +510,7 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer,
return INVALID_HANDLE_VALUE;
}
#ifdef WINDIVERT_DEBUG
WinDivertFilterDump(ioctl_filter, filter_len);
#endif
@@ -568,7 +576,6 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer,
}
// Set the priority:
priority32 = WINDIVERT_PRIORITY(priority);
if (priority32 != WINDIVERT_PRIORITY_DEFAULT)
{
if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_PRIORITY, 0,
+3 -6
View File
@@ -254,7 +254,7 @@ int __cdecl main(int argc, char **argv)
putchar(' ');
if (ip_header != NULL)
if (ip_header != NULL && !tcp_header->Rst && !tcp_header->Fin)
{
reset->ip.SrcAddr = ip_header->DstAddr;
reset->ip.DstAddr = ip_header->SrcAddr;
@@ -280,7 +280,7 @@ int __cdecl main(int argc, char **argv)
}
}
if (ipv6_header != NULL)
if (ipv6_header != NULL && !tcp_header->Rst && !tcp_header->Fin)
{
memcpy(resetv6->ipv6.SrcAddr, ipv6_header->DstAddr,
sizeof(resetv6->ipv6.SrcAddr));
@@ -315,9 +315,6 @@ int __cdecl main(int argc, char **argv)
if (ip_header != NULL)
{
// NOTE: For some ICMP error messages, WFP does not seem to
// support INBOUND injection. As a work-around, we
// always inject OUTBOUND.
UINT icmp_length = ip_header->HdrLength*sizeof(UINT32) + 8;
memcpy(dnr->data, ip_header, icmp_length);
icmp_length += sizeof(ICMPPACKET);
@@ -328,7 +325,7 @@ int __cdecl main(int argc, char **argv)
WinDivertHelperCalcChecksums((PVOID)dnr, icmp_length, 0);
memcpy(&send_addr, &recv_addr, sizeof(send_addr));
send_addr.Direction = WINDIVERT_DIRECTION_OUTBOUND;
send_addr.Direction = !recv_addr.Direction;
if (!WinDivertSend(handle, (PVOID)dnr, icmp_length, &send_addr,
NULL))
{
+1
View File
@@ -146,6 +146,7 @@
((UINT32)((INT32)(priority16) + 0x7FFF + 1))
#define WINDIVERT_PRIORITY_DEFAULT WINDIVERT_PRIORITY(0)
#define WINDIVERT_PRIORITY_MAX WINDIVERT_PRIORITY(1000)
#define WINDIVERT_PRIORITY_MIN WINDIVERT_PRIORITY(-1000)
/*
* WinDivert parameters.
+347 -167
View File
@@ -22,6 +22,8 @@
#include <wdf.h>
#include <stdarg.h>
#include <ntstrsafe.h>
#define INITGUID
#include <guiddef.h>
#include "windivert_device.h"
@@ -140,16 +142,14 @@ struct context_s
UINT64 flags; // Context's flags.
UINT32 priority_0; // Context's priority (initial).
UINT32 priority; // Context's priority.
GUID sublayer_guid[WINDIVERT_CONTEXT_MAXLAYERS];
// Sublayer GUIDs.
GUID callout_guid[WINDIVERT_CONTEXT_MAXLAYERS];
// Callout GUIDs.
GUID filter_guid[WINDIVERT_CONTEXT_MAXLAYERS];
// Filter GUIDs.
BOOL registered[WINDIVERT_CONTEXT_MAXLAYERS];
// What is registered?
HANDLE engine_handle; // WFP engine handle.
BOOL installed[WINDIVERT_CONTEXT_MAXLAYERS];
// What is installed?
LONG filter_on; // Is filter on?
HANDLE engine_handle; // WFP engine handle.
filter_t filter; // Packet filter.
};
typedef struct context_s context_s;
@@ -172,7 +172,8 @@ struct layer_s
wchar_t *callout_desc; // Call-out description.
wchar_t *filter_name; // Filter name.
wchar_t *filter_desc; // Filter description.
GUID guid; // WFP layer GUID.
GUID layer_guid; // WFP layer GUID.
GUID sublayer_guid; // Sub-layer GUID.
windivert_callout_t callout; // Call-out.
};
typedef struct layer_s *layer_t;
@@ -196,6 +197,8 @@ struct packet_s
{
LIST_ENTRY entry; // Entry for queue.
PNET_BUFFER_LIST net_buffer_list; // Clone of the net buffer list.
PVOID data; // Copy of the packet data.
size_t data_len; // Length of `data'.
UINT8 direction; // Packet direction.
UINT32 if_idx; // Interface index.
UINT32 sub_if_idx; // Sub-interface index.
@@ -301,13 +304,32 @@ struct udphdr
*/
#define UINT8_MAX 0xFF
#define UINT16_MAX 0xFFFF
#define UINT32_MAX 0xFFFFFFFF
#define INT16_MAX 0x7FFF
/*
* Global handles.
* Global state.
*/
HANDLE inject_handle;
HANDLE injectv6_handle;
NDIS_HANDLE pool_handle;
HANDLE inject_handle = NULL;
HANDLE injectv6_handle = NULL;
NDIS_HANDLE pool_handle = NULL;
HANDLE engine_handle = NULL;
LONG priority_counter = 0;
/*
* Priorities.
*/
#define WINDIVERT_CONTEXT_PRIORITY(priority0) \
windivert_context_priority(priority0)
static UINT32 windivert_context_priority(UINT32 priority0)
{
UINT16 priority1 = (UINT16)InterlockedIncrement(&priority_counter);
priority0 -= WINDIVERT_PRIORITY_MIN;
return ((priority0 << 16) | ((UINT32)priority1 & 0x0000FFFF));
}
#define WINDIVERT_FILTER_WEIGHT(priority) \
((UINT64)(UINT32_MAX - (priority)))
/*
* Prototypes.
@@ -321,10 +343,12 @@ static BOOLEAN windivert_context_verify(context_t context,
context_state_t state);
extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
IN WDFFILEOBJECT object);
static NTSTATUS windivert_register_callouts(context_t context, BOOL is_inbound,
static NTSTATUS windivert_install_sublayer(layer_t layer);
static NTSTATUS windivert_install_callouts(context_t context, BOOL is_inbound,
BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6);
static NTSTATUS windivert_register_callout(context_t context, UINT idx,
static NTSTATUS windivert_install_callout(context_t context, UINT idx,
layer_t layer);
static void windivert_uninstall_callouts(context_t context);
extern VOID windivert_timer(IN WDFTIMER timer);
extern VOID windivert_cleanup(IN WDFFILEOBJECT object);
extern VOID windivert_close(IN WDFFILEOBJECT object);
@@ -387,7 +411,29 @@ static BOOL windivert_filter_test(filter_t filter, UINT16 ip, UINT8 protocol,
UINT8 field, UINT32 arg);
/*
* Defined layers.
* WinDivert sublayer GUIDs
*/
DEFINE_GUID(WINDIVERT_SUBLAYER_INBOUND_IPV4_GUID,
0xAC32F237, 0x1408, 0x435A,
0x90, 0xF1, 0xF1, 0xAA, 0x58, 0x4C, 0xCA, 0x7F);
DEFINE_GUID(WINDIVERT_SUBLAYER_OUTBOUND_IPV4_GUID,
0xF229AF90, 0xC7CC, 0x4351,
0x8A, 0x5E, 0x51, 0xB1, 0xE1, 0xB5, 0x97, 0x0A);
DEFINE_GUID(WINDIVERT_SUBLAYER_INBOUND_IPV6_GUID,
0x0C25C18F, 0x4F3E, 0x4F3E,
0xBA, 0xB4, 0x26, 0xBD, 0x1E, 0x6E, 0x41, 0x31);
DEFINE_GUID(WINDIVERT_SUBLAYER_OUTBOUND_IPV6_GUID,
0x26966F53, 0x4BCD, 0x4DE2,
0xAA, 0x0E, 0xA6, 0x70, 0x59, 0x20, 0x37, 0xC0);
DEFINE_GUID(WINDIVERT_SUBLAYER_FORWARD_IPV4_GUID,
0x2F472BD8, 0x346F, 0x4D9C,
0x8A, 0xEE, 0x68, 0xCA, 0xFD, 0x00, 0x71, 0x11);
DEFINE_GUID(WINDIVERT_SUBLAYER_FORWARD_IPV6_GUID,
0xA196BAC6, 0x6546, 0x4FD5,
0x96, 0xD3, 0x18, 0xD5, 0x7D, 0x5C, 0x3D, 0x78);
/*
* WinDivert supported layers.
*/
static struct layer_s layer_inbound_network_ipv4_0 =
{
@@ -398,6 +444,7 @@ static struct layer_s layer_inbound_network_ipv4_0 =
L"" WINDIVERT_DEVICE_NAME L"_FilterInboundNetworkIPv4",
L"" WINDIVERT_DEVICE_NAME L" filter network (inbound IPv4)",
{0},
{0},
windivert_classify_inbound_network_v4_callout,
};
static layer_t layer_inbound_network_ipv4 = &layer_inbound_network_ipv4_0;
@@ -411,6 +458,7 @@ static struct layer_s layer_outbound_network_ipv4_0 =
L"" WINDIVERT_DEVICE_NAME L"_FilterOutboundNetworkIPv4",
L"" WINDIVERT_DEVICE_NAME L" filter network (outbound IPv4)",
{0},
{0},
windivert_classify_outbound_network_v4_callout,
};
static layer_t layer_outbound_network_ipv4 = &layer_outbound_network_ipv4_0;
@@ -424,6 +472,7 @@ static struct layer_s layer_inbound_network_ipv6_0 =
L"" WINDIVERT_DEVICE_NAME L"_FilterInboundNetworkIPv6",
L"" WINDIVERT_DEVICE_NAME L" filter network (inbound IPv6)",
{0},
{0},
windivert_classify_inbound_network_v6_callout,
};
static layer_t layer_inbound_network_ipv6 = &layer_inbound_network_ipv6_0;
@@ -437,6 +486,7 @@ static struct layer_s layer_outbound_network_ipv6_0 =
L"" WINDIVERT_DEVICE_NAME L"_FilterOutboundNetworkIPv6",
L"" WINDIVERT_DEVICE_NAME L" filter network (outbound IPv6)",
{0},
{0},
windivert_classify_outbound_network_v6_callout,
};
static layer_t layer_outbound_network_ipv6 = &layer_outbound_network_ipv6_0;
@@ -450,6 +500,7 @@ static struct layer_s layer_forward_network_ipv4_0 =
L"" WINDIVERT_DEVICE_NAME L"_FilterForwardNetworkIPv4",
L"" WINDIVERT_DEVICE_NAME L" filter network (forward IPv4)",
{0},
{0},
windivert_classify_forward_network_v4_callout,
};
static layer_t layer_forward_network_ipv4 = &layer_forward_network_ipv4_0;
@@ -463,6 +514,7 @@ static struct layer_s layer_forward_network_ipv6_0 =
L"" WINDIVERT_DEVICE_NAME L"_FilterForwardNetworkIPv6",
L"" WINDIVERT_DEVICE_NAME L" filter network (forward IPv6)",
{0},
{0},
windivert_classify_forward_network_v6_callout,
};
static layer_t layer_forward_network_ipv6 = &layer_forward_network_ipv6_0;
@@ -491,12 +543,24 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
DEBUG("LOAD: loading WinDivert driver");
// Initialize the layers.
layer_inbound_network_ipv4->guid = FWPM_LAYER_INBOUND_IPPACKET_V4;
layer_outbound_network_ipv4->guid = FWPM_LAYER_OUTBOUND_IPPACKET_V4;
layer_inbound_network_ipv6->guid = FWPM_LAYER_INBOUND_IPPACKET_V6;
layer_outbound_network_ipv6->guid = FWPM_LAYER_OUTBOUND_IPPACKET_V6;
layer_forward_network_ipv4->guid = FWPM_LAYER_IPFORWARD_V4;
layer_forward_network_ipv6->guid = FWPM_LAYER_IPFORWARD_V6;
layer_inbound_network_ipv4->layer_guid = FWPM_LAYER_INBOUND_IPPACKET_V4;
layer_outbound_network_ipv4->layer_guid = FWPM_LAYER_OUTBOUND_IPPACKET_V4;
layer_inbound_network_ipv6->layer_guid = FWPM_LAYER_INBOUND_IPPACKET_V6;
layer_outbound_network_ipv6->layer_guid = FWPM_LAYER_OUTBOUND_IPPACKET_V6;
layer_forward_network_ipv4->layer_guid = FWPM_LAYER_IPFORWARD_V4;
layer_forward_network_ipv6->layer_guid = FWPM_LAYER_IPFORWARD_V6;
layer_inbound_network_ipv4->sublayer_guid =
WINDIVERT_SUBLAYER_INBOUND_IPV4_GUID;
layer_outbound_network_ipv4->sublayer_guid =
WINDIVERT_SUBLAYER_OUTBOUND_IPV4_GUID;
layer_inbound_network_ipv6->sublayer_guid =
WINDIVERT_SUBLAYER_INBOUND_IPV6_GUID;
layer_outbound_network_ipv6->sublayer_guid =
WINDIVERT_SUBLAYER_OUTBOUND_IPV6_GUID;
layer_forward_network_ipv4->sublayer_guid =
WINDIVERT_SUBLAYER_FORWARD_IPV4_GUID;
layer_forward_network_ipv6->sublayer_guid =
WINDIVERT_SUBLAYER_FORWARD_IPV6_GUID;
// Configure ourself as a non-PnP driver:
WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK);
@@ -507,7 +571,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create WDF driver", status);
return status;
goto driver_entry_exit;
}
device_init = WdfControlDeviceInitAllocate(driver,
&SDDL_DEVOBJ_SYS_ALL_ADM_ALL);
@@ -516,7 +580,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
status = STATUS_INSUFFICIENT_RESOURCES;
DEBUG_ERROR("failed to allocate WDF control device init structure",
status);
return status;
goto driver_entry_exit;
}
WdfDeviceInitSetDeviceType(device_init, FILE_DEVICE_NETWORK);
WdfDeviceInitSetIoType(device_init, WdfDeviceIoDirect);
@@ -525,7 +589,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
{
DEBUG_ERROR("failed to create WDF device name", status);
WdfDeviceInitFree(device_init);
return status;
goto driver_entry_exit;
}
WDF_FILEOBJECT_CONFIG_INIT(&file_config, windivert_create, windivert_close,
windivert_cleanup);
@@ -539,7 +603,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
{
DEBUG_ERROR("failed to create WDF control device", status);
WdfDeviceInitFree(device_init);
return status;
goto driver_entry_exit;
}
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queue_config,
WdfIoQueueDispatchParallel);
@@ -553,13 +617,13 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create default WDF queue", status);
return status;
goto driver_entry_exit;
}
status = WdfDeviceCreateSymbolicLink(device, &dos_device_name);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create device symbolic link", status);
return status;
goto driver_entry_exit;
}
WdfControlFinishInitializing(device);
@@ -570,7 +634,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create WFP packet injection handle", status);
return status;
goto driver_entry_exit;
}
status = FwpsInjectionHandleCreate0(AF_INET6,
FWPS_INJECTION_TYPE_NETWORK | FWPS_INJECTION_TYPE_FORWARD,
@@ -579,8 +643,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
{
DEBUG_ERROR("failed to create WFP ipv6 packet injection handle",
status);
FwpsInjectionHandleDestroy0(inject_handle);
return status;
goto driver_entry_exit;
}
// Create the packet pool handle.
@@ -596,12 +659,75 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
{
status = STATUS_INSUFFICIENT_RESOURCES;
DEBUG_ERROR("failed to allocate net buffer list pool", status);
FwpsInjectionHandleDestroy0(inject_handle);
FwpsInjectionHandleDestroy0(injectv6_handle);
return status;
goto driver_entry_exit;
}
return STATUS_SUCCESS;
// Open a handle to the filter engine:
status = FwpmEngineOpen0(NULL, RPC_C_AUTHN_DEFAULT, NULL, NULL,
&engine_handle);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create WFP engine handle", status);
goto driver_entry_exit;
}
// Register WFP sub-layers:
status = windivert_install_sublayer(layer_inbound_network_ipv4);
if (!NT_SUCCESS(status))
{
driver_entry_sublayer_error:
DEBUG_ERROR("failed to install WFP sub-layer", status);
goto driver_entry_exit;
}
status = windivert_install_sublayer(layer_outbound_network_ipv4);
if (!NT_SUCCESS(status))
{
goto driver_entry_sublayer_error;
}
status = windivert_install_sublayer(layer_inbound_network_ipv6);
if (!NT_SUCCESS(status))
{
goto driver_entry_sublayer_error;
}
status = windivert_install_sublayer(layer_outbound_network_ipv6);
if (!NT_SUCCESS(status))
{
goto driver_entry_sublayer_error;
}
status = windivert_install_sublayer(layer_forward_network_ipv4);
if (!NT_SUCCESS(status))
{
goto driver_entry_sublayer_error;
}
status = windivert_install_sublayer(layer_forward_network_ipv6);
if (!NT_SUCCESS(status))
{
goto driver_entry_sublayer_error;
}
driver_entry_exit:
if (!NT_SUCCESS(status))
{
if (inject_handle != NULL)
{
FwpsInjectionHandleDestroy0(inject_handle);
}
if (injectv6_handle != NULL)
{
FwpsInjectionHandleDestroy0(injectv6_handle);
}
if (pool_handle != NULL)
{
NdisFreeNetBufferListPool(pool_handle);
}
if (engine_handle != NULL)
{
FwpmEngineClose0(engine_handle);
}
}
return status;
}
/*
@@ -610,9 +736,46 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
extern VOID windivert_unload(IN WDFDRIVER Driver)
{
DEBUG("UNLOAD: unloading the WinDivert driver");
FwpmSubLayerDeleteByKey0(engine_handle,
&layer_inbound_network_ipv4->sublayer_guid);
FwpmSubLayerDeleteByKey0(engine_handle,
&layer_outbound_network_ipv4->sublayer_guid);
FwpmSubLayerDeleteByKey0(engine_handle,
&layer_inbound_network_ipv6->sublayer_guid);
FwpmSubLayerDeleteByKey0(engine_handle,
&layer_outbound_network_ipv6->sublayer_guid);
FwpmSubLayerDeleteByKey0(engine_handle,
&layer_forward_network_ipv4->sublayer_guid);
FwpmSubLayerDeleteByKey0(engine_handle,
&layer_forward_network_ipv6->sublayer_guid);
FwpsInjectionHandleDestroy0(inject_handle);
FwpsInjectionHandleDestroy0(injectv6_handle);
NdisFreeNetBufferListPool(pool_handle);
FwpmEngineClose0(engine_handle);
}
/*
* Register a sub-layer.
*/
static NTSTATUS windivert_install_sublayer(layer_t layer)
{
FWPM_SUBLAYER0 sublayer;
NTSTATUS status;
RtlZeroMemory(&sublayer, sizeof(sublayer));
sublayer.subLayerKey = layer->sublayer_guid;
sublayer.displayData.name = layer->sublayer_name;
sublayer.displayData.description = layer->sublayer_desc;
sublayer.weight = INT16_MAX;
status = FwpmSubLayerAdd0(engine_handle, &sublayer, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to add WFP sub-layer", status);
}
return status;
}
/*
@@ -654,7 +817,6 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
WDF_WORKITEM_CONFIG item_config;
WDF_OBJECT_ATTRIBUTES obj_attrs;
FWPM_SESSION0 session;
HANDLE thread;
NTSTATUS status = STATUS_SUCCESS;
UINT8 i;
context_t context = windivert_context_get(object);
@@ -673,8 +835,9 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
context->layer = WINDIVERT_LAYER_DEFAULT;
context->flags_0 = 0;
context->flags = 0;
context->priority_0 = WINDIVERT_PRIORITY_DEFAULT;
context->priority = WINDIVERT_PRIORITY_DEFAULT;
context->priority_0 =
WINDIVERT_CONTEXT_PRIORITY(WINDIVERT_PRIORITY_DEFAULT);
context->priority = context->priority_0;
context->filter = NULL;
for (i = 0; i < WINDIVERT_CONTEXT_MAXWORKERS; i++)
{
@@ -683,19 +846,13 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
context->worker_curr = 0;
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
{
context->registered[i] = FALSE;
context->installed[i] = FALSE;
}
context->filter_on = FALSE;
KeInitializeSpinLock(&context->lock);
InitializeListHead(&context->packet_queue);
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
{
status = ExUuidCreate(&context->sublayer_guid[i]);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create sub-layer GUID", status);
goto windivert_create_exit;
}
status = ExUuidCreate(&context->callout_guid[i]);
if (!NT_SUCCESS(status))
{
@@ -785,7 +942,7 @@ windivert_create_exit:
/*
* Register all WFP callouts.
*/
static NTSTATUS windivert_register_callouts(context_t context, BOOL is_inbound,
static NTSTATUS windivert_install_callouts(context_t context, BOOL is_inbound,
BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6)
{
UINT8 i, j;
@@ -829,40 +986,20 @@ static NTSTATUS windivert_register_callouts(context_t context, BOOL is_inbound,
return STATUS_INVALID_PARAMETER;
}
status = FwpmTransactionBegin0(context->engine_handle, 0);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to begin WFP transaction", status);
goto windivert_register_callouts_exit;
}
for (j = 0; j < i; j++)
{
status = windivert_register_callout(context, j, layers[j]);
status = windivert_install_callout(context, j, layers[j]);
if (!NT_SUCCESS(status))
{
FwpmTransactionAbort0(context->engine_handle);
goto windivert_register_callouts_exit;
goto windivert_install_callouts_exit;
}
}
status = FwpmTransactionCommit0(context->engine_handle);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to commit WFP transaction", status);
goto windivert_register_callouts_exit;
}
windivert_register_callouts_exit:
windivert_install_callouts_exit:
if (!NT_SUCCESS(status))
{
for (j = 0; j < i; j++)
{
if (context->registered[j])
{
FwpsCalloutUnregisterByKey0(&context->callout_guid[j]);
context->registered[j] = FALSE;
}
}
windivert_uninstall_callouts(context);
}
return status;
@@ -871,22 +1008,17 @@ windivert_register_callouts_exit:
/*
* Register a WFP callout.
*/
static NTSTATUS windivert_register_callout(context_t context, UINT idx,
static NTSTATUS windivert_install_callout(context_t context, UINT idx,
layer_t layer)
{
FWPM_SUBLAYER0 sublayer;
FWPS_CALLOUT0 scallout;
FWPM_CALLOUT0 mcallout;
FWPM_FILTER0 filter;
BOOL registered = FALSE;
UINT64 weight;
NTSTATUS status;
RtlZeroMemory(&sublayer, sizeof(sublayer));
sublayer.subLayerKey = context->sublayer_guid[idx];
sublayer.displayData.name = layer->sublayer_name;
sublayer.displayData.description = layer->sublayer_desc;
sublayer.weight =
(UINT16)(WINDIVERT_PRIORITY_MAX - context->priority);
weight = WINDIVERT_FILTER_WEIGHT(context->priority);
RtlZeroMemory(&scallout, sizeof(scallout));
scallout.calloutKey = context->callout_guid[idx];
scallout.classifyFn = layer->callout;
@@ -896,53 +1028,95 @@ static NTSTATUS windivert_register_callout(context_t context, UINT idx,
mcallout.calloutKey = context->callout_guid[idx];
mcallout.displayData.name = layer->callout_name;
mcallout.displayData.description = layer->callout_desc;
mcallout.applicableLayer = layer->guid;
mcallout.applicableLayer = layer->layer_guid;
RtlZeroMemory(&filter, sizeof(filter));
filter.filterKey = context->filter_guid[idx];
filter.layerKey = layer->guid;
filter.layerKey = layer->layer_guid;
filter.displayData.name = layer->filter_name;
filter.displayData.description = layer->filter_desc;
filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;
filter.action.calloutKey = context->callout_guid[idx];
filter.subLayerKey = context->sublayer_guid[idx];
filter.weight.type = FWP_EMPTY;
filter.subLayerKey = layer->sublayer_guid;
filter.weight.type = FWP_UINT64;
filter.weight.uint64 = &weight;
filter.rawContext = (UINT64)context;
status = FwpmSubLayerAdd0(context->engine_handle, &sublayer, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to add WFP sub-layer", status);
goto windivert_register_callout_error;
}
status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(context->device),
&scallout, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to register WFP callout", status);
goto windivert_register_callout_error;
DEBUG_ERROR("failed to install WFP callout", status);
return status;
}
registered = TRUE;
status = FwpmCalloutAdd0(context->engine_handle, &mcallout, NULL, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to add WFP callout", status);
goto windivert_register_callout_error;
FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]);
return status;
}
status = FwpmFilterAdd0(context->engine_handle, &filter, NULL, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to add WFP filter", status);
goto windivert_register_callout_error;
FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]);
return status;
}
context->registered[idx] = TRUE;
context->installed[idx] = TRUE;
return STATUS_SUCCESS;
}
windivert_register_callout_error:
if (registered)
/*
* WinDivert uninstall callouts routine.
*/
static void windivert_uninstall_callouts(context_t context)
{
LARGE_INTEGER timeout;
int retries;
UINT i;
NTSTATUS status;
timeout.HighPart = 0;
timeout.LowPart = -640; // 64ms
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
{
FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]);
if (!context->installed[i])
{
continue;
}
// It is essential that the filter/callout be deleted, otherwise
// networking will break. Sometimes these calls fail with generic
// RPC errors (e.g. RPC_NT_CALL_FAILED), so we retry until they work.
// There should be a better solution?
for (retries = 8; retries > 0; retries--)
{
status = FwpmFilterDeleteByKey0(context->engine_handle,
&context->filter_guid[i]);
if (NT_SUCCESS(status))
{
break;
}
retries--;
KeDelayExecutionThread(KernelMode, FALSE, &timeout);
}
for (retries = 8; retries > 0; retries--)
{
status = FwpmCalloutDeleteByKey0(context->engine_handle,
&context->callout_guid[i]);
if (NT_SUCCESS(status))
{
break;
}
retries--;
KeDelayExecutionThread(KernelMode, FALSE, &timeout);
}
FwpsCalloutUnregisterByKey0(&context->callout_guid[i]);
context->installed[i] = FALSE;
}
return status;
}
/*
@@ -1030,52 +1204,8 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object)
WdfWorkItemFlush(context->workers[i]);
WdfObjectDelete(context->workers[i]);
}
status = FwpmTransactionBegin0(context->engine_handle, 0);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to begin WFP transaction", status);
goto windivert_cleanup_exit;
}
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
{
if (!context->registered[i])
{
continue;
}
status = FwpmFilterDeleteByKey0(context->engine_handle,
context->filter_guid+i);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed delete WFP filter", status);
FwpmTransactionAbort0(context->engine_handle);
goto windivert_cleanup_exit;
}
status = FwpmSubLayerDeleteByKey0(context->engine_handle,
context->sublayer_guid+i);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed delete WFP sub-layer", status);
FwpmTransactionAbort0(context->engine_handle);
goto windivert_cleanup_exit;
}
}
status = FwpmTransactionCommit0(context->engine_handle);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to commit WFP transaction", status);
goto windivert_cleanup_exit;
}
windivert_cleanup_exit:
windivert_uninstall_callouts(context);
FwpmEngineClose0(context->engine_handle);
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
{
if (context->registered[i])
{
FwpsCalloutUnregisterByKey0(&context->callout_guid[i]);
}
}
if (context->filter != NULL)
{
ExFreePoolWithTag(context->filter, WINDIVERT_TAG);
@@ -1191,16 +1321,26 @@ static void windivert_read_service(context_t context)
goto windivert_read_service_complete;
}
dst_len = MmGetMdlByteCount(dst_mdl);
buffer = NET_BUFFER_LIST_FIRST_NB(packet->net_buffer_list);
src_len = NET_BUFFER_DATA_LENGTH(buffer);
dst_len = (src_len < dst_len? src_len: dst_len);
src = NdisGetDataBuffer(buffer, dst_len, NULL, 1, 0);
if (src == NULL)
if (packet->net_buffer_list != NULL)
{
NdisGetDataBuffer(buffer, dst_len, dst, 1, 0);
buffer = NET_BUFFER_LIST_FIRST_NB(packet->net_buffer_list);
src_len = NET_BUFFER_DATA_LENGTH(buffer);
dst_len = (src_len < dst_len? src_len: dst_len);
src = NdisGetDataBuffer(buffer, dst_len, NULL, 1, 0);
if (src == NULL)
{
NdisGetDataBuffer(buffer, dst_len, dst, 1, 0);
}
else
{
RtlCopyMemory(dst, src, dst_len);
}
}
else
{
src_len = packet->data_len;
dst_len = (src_len < dst_len? src_len: dst_len);
src = packet->data;
RtlCopyMemory(dst, src, dst_len);
}
@@ -1649,7 +1789,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
windivert_filter_analyze(context->filter, &is_inbound,
&is_outbound, &is_ipv4, &is_ipv6);
status = windivert_register_callouts(context, is_inbound,
status = windivert_install_callouts(context, is_inbound,
is_outbound, is_ipv4, is_ipv6);
// Start the timer.
@@ -1672,13 +1812,16 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
case IOCTL_WINDIVERT_SET_PRIORITY:
ioctl = (windivert_ioctl_t)inbuf;
if (ioctl->arg > WINDIVERT_PRIORITY_MAX)
if (ioctl->arg < WINDIVERT_PRIORITY_MIN ||
ioctl->arg > WINDIVERT_PRIORITY_MAX)
{
status = STATUS_INVALID_DEVICE_REQUEST;
DEBUG_ERROR("failed to set priority; value too big", status);
DEBUG_ERROR("failed to set priority; value out of range",
status);
goto windivert_ioctl_exit;
}
context->priority_0 = (UINT16)ioctl->arg;
context->priority_0 =
WINDIVERT_CONTEXT_PRIORITY((UINT32)ioctl->arg);
break;
case IOCTL_WINDIVERT_SET_FLAGS:
@@ -1836,7 +1979,7 @@ static void windivert_classify_inbound_network_v4_callout(
0, NULL);
if (!NT_SUCCESS(status))
{
result->actionType = FWP_ACTION_PERMIT;
result->actionType = FWP_ACTION_CONTINUE;
return;
}
windivert_classify_callout(WINDIVERT_DIRECTION_INBOUND,
@@ -1875,7 +2018,7 @@ static void windivert_classify_inbound_network_v6_callout(
0, NULL);
if (!NT_SUCCESS(status))
{
result->actionType = FWP_ACTION_PERMIT;
result->actionType = FWP_ACTION_CONTINUE;
return;
}
windivert_classify_callout(WINDIVERT_DIRECTION_INBOUND,
@@ -1951,7 +2094,7 @@ static void windivert_classify_callout(IN UINT8 direction, IN UINT32 if_idx,
context = (context_t)filter->context;
if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_OPEN))
{
result->actionType = FWP_ACTION_PERMIT;
result->actionType = FWP_ACTION_CONTINUE;
return;
}
buffers = (PNET_BUFFER_LIST)data;
@@ -1962,7 +2105,7 @@ static void windivert_classify_callout(IN UINT8 direction, IN UINT32 if_idx,
* This is a fragment group. This can be ignored since each
* fragment should already have been indicated.
*/
result->actionType = FWP_ACTION_PERMIT;
result->actionType = FWP_ACTION_CONTINUE;
return;
}
if (isipv4)
@@ -1979,9 +2122,9 @@ static void windivert_classify_callout(IN UINT8 direction, IN UINT32 if_idx,
packet_state == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
{
priority = (UINT32)packet_context;
if (priority <= context->priority)
if (priority >= context->priority)
{
result->actionType = FWP_ACTION_PERMIT;
result->actionType = FWP_ACTION_CONTINUE;
return;
}
}
@@ -2007,7 +2150,7 @@ static void windivert_classify_callout(IN UINT8 direction, IN UINT32 if_idx,
}
else
{
result->actionType = FWP_ACTION_PERMIT;
result->actionType = FWP_ACTION_CONTINUE;
return;
}
@@ -2037,7 +2180,7 @@ windivert_classify_callout_exit:
if ((context->flags & WINDIVERT_FLAG_SNIFF) != 0)
{
result->actionType = FWP_ACTION_PERMIT;
result->actionType = FWP_ACTION_CONTINUE;
}
else
{
@@ -2055,6 +2198,8 @@ static BOOL windivert_queue_packet(context_t context, PNET_BUFFER_LIST buffers,
{
KLOCK_QUEUE_HANDLE lock_handle;
NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksum_info;
PNET_BUFFER buffer;
PVOID data;
PLIST_ENTRY entry;
packet_t packet;
NTSTATUS status;
@@ -2065,13 +2210,41 @@ static BOOL windivert_queue_packet(context_t context, PNET_BUFFER_LIST buffers,
{
return FALSE;
}
status = FwpsAllocateCloneNetBufferList0(buffers, pool_handle, NULL,
0, &packet->net_buffer_list);
if (!NT_SUCCESS(status))
packet->net_buffer_list = NULL;
packet->data = NULL;
packet->data_len = 0;
if ((context->flags & WINDIVERT_FLAG_SNIFF) != 0)
{
ExFreePoolWithTag(packet, WINDIVERT_TAG);
return FALSE;
// Deep copy for sniff-mode:
buffer = NET_BUFFER_LIST_FIRST_NB(buffers);
packet->data_len = NET_BUFFER_DATA_LENGTH(buffer);
packet->data = ExAllocatePoolWithTag(NonPagedPool,
packet->data_len, WINDIVERT_TAG);
if (packet->data == NULL)
{
ExFreePoolWithTag(packet, WINDIVERT_TAG);
return FALSE;
}
data = NdisGetDataBuffer(buffer, packet->data_len, NULL, 1, 0);
if (data == NULL)
{
NdisGetDataBuffer(buffer, packet->data_len, packet->data, 1, 0);
}
else
{
RtlCopyMemory(packet->data, data, packet->data_len);
}
}
else
{
// Shallow copy for divert-mode:
status = FwpsAllocateCloneNetBufferList0(buffers, pool_handle, NULL,
0, &packet->net_buffer_list);
if (!NT_SUCCESS(status))
{
ExFreePoolWithTag(packet, WINDIVERT_TAG);
return FALSE;
}
}
checksum_info.Value = NET_BUFFER_LIST_INFO(buffers,
@@ -2128,7 +2301,14 @@ static BOOL windivert_queue_packet(context_t context, PNET_BUFFER_LIST buffers,
*/
static void windivert_free_packet(packet_t packet)
{
FwpsFreeCloneNetBufferList0(packet->net_buffer_list, 0);
if (packet->net_buffer_list != NULL)
{
FwpsFreeCloneNetBufferList0(packet->net_buffer_list, 0);
}
if (packet->data != NULL)
{
ExFreePoolWithTag(packet->data, WINDIVERT_TAG);
}
ExFreePoolWithTag(packet, WINDIVERT_TAG);
}