Various improvements

- Support NonPagedPoolNx for Windows 8, 10.
- More aggressive context locking
- Set MAXWORKERS=1 (seems to perform best).
This commit is contained in:
basil00
2017-10-16 22:56:38 +08:00
parent d778639cbe
commit f5f3559ae9
+304 -199
View File
@@ -60,10 +60,6 @@ static void DEBUG(PCCH format, ...)
DbgPrint("WINDIVERT: %s\n", buf);
va_end(args);
}
#else // DEBUG_ON
#define DEBUG(format, ...)
#endif
static void DEBUG_ERROR(PCCH format, NTSTATUS status, ...)
{
va_list args;
@@ -77,6 +73,10 @@ static void DEBUG_ERROR(PCCH format, NTSTATUS status, ...)
DbgPrint("WINDIVERT: *** ERROR ***: (status = %x): %s\n", status, buf);
va_end(args);
}
#else // DEBUG_ON
#define DEBUG(format, ...)
#define DEBUG_ERROR(format, status, ...)
#endif
#define WINDIVERT_TAG 'viDW'
@@ -104,10 +104,9 @@ typedef struct filter_s *filter_t;
/*
* WinDivert context information.
*/
#define WINDIVERT_CONTEXT_MAGIC 0x1C1E48D280D7F487ull
#define WINDIVERT_CONTEXT_SIZE (sizeof(struct context_s))
#define WINDIVERT_CONTEXT_MAXLAYERS 4
#define WINDIVERT_CONTEXT_MAXWORKERS 2
#define WINDIVERT_CONTEXT_MAXWORKERS 1
#define WINDIVERT_CONTEXT_OUTBOUND_IPV4_LAYER 0
#define WINDIVERT_CONTEXT_INBOUND_IPV4_LAYER 1
#define WINDIVERT_CONTEXT_OUTBOUND_IPV6_LAYER 2
@@ -122,7 +121,6 @@ typedef enum
} context_state_t;
struct context_s
{
UINT64 magic; // WINDIVERT_CONTEXT_MAGIC
context_state_t state; // Context's state.
KSPIN_LOCK lock; // Context-wide lock.
WDFDEVICE device; // Context's device.
@@ -139,11 +137,8 @@ struct context_s
WDFWORKITEM workers[WINDIVERT_CONTEXT_MAXWORKERS];
// Read workers.
UINT8 worker_curr; // Current read worker.
UINT8 layer_0; // Context's layer (initial).
UINT8 layer; // Context's layer.
UINT64 flags_0; // Context's flags (initial).
UINT64 flags; // Context's flags.
UINT32 priority_0; // Context's priority (initial).
UINT32 priority; // Context's priority.
GUID callout_guid[WINDIVERT_CONTEXT_MAXLAYERS];
// Callout GUIDs.
@@ -151,7 +146,7 @@ struct context_s
// Filter GUIDs.
BOOL installed[WINDIVERT_CONTEXT_MAXLAYERS];
// What is installed?
LONG filter_on; // Is filter on?
BOOL on; // Is filtering on?
HANDLE engine_handle; // WFP engine handle.
filter_t filter; // Packet filter.
};
@@ -339,6 +334,7 @@ static NDIS_HANDLE nb_pool_handle = NULL;
static HANDLE engine_handle = NULL;
static LONG priority_counter = 0;
static ULONGLONG counts_per_ms = 0;
static POOL_TYPE non_paged_pool = NonPagedPool;
/*
* Priorities.
@@ -364,16 +360,15 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
static NTSTATUS windivert_read(context_t context, WDFREQUEST request);
extern VOID windivert_worker(IN WDFWORKITEM item);
static void windivert_read_service(context_t context);
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_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_install_callouts(context_t context, UINT8 layer,
BOOL is_inbound, BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6);
static NTSTATUS windivert_install_callout(context_t context, UINT idx,
layer_t layer);
static void windivert_uninstall_callouts(context_t context);
static void windivert_uninstall_callouts(context_t context,
context_state_t state);
extern VOID windivert_cleanup(IN WDFFILEOBJECT object);
extern VOID windivert_close(IN WDFFILEOBJECT object);
extern VOID windivert_destroy(IN WDFOBJECT object);
@@ -551,6 +546,22 @@ static struct layer_s layer_forward_network_ipv6_0 =
};
static layer_t layer_forward_network_ipv6 = &layer_forward_network_ipv6_0;
/*
* WinDivert malloc/free.
*/
static PVOID windivert_malloc(SIZE_T size, BOOL paged)
{
POOL_TYPE pool = (paged? PagedPool: non_paged_pool);
return ExAllocatePoolWithTag(pool, size, WINDIVERT_TAG);
}
static VOID windivert_free(PVOID ptr)
{
if (ptr != NULL)
{
ExFreePoolWithTag(ptr, WINDIVERT_TAG);
}
}
/*
* WinDivert driver entry routine.
*/
@@ -567,6 +578,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
WDF_OBJECT_ATTRIBUTES obj_attrs;
NET_BUFFER_LIST_POOL_PARAMETERS nbl_pool_params;
NET_BUFFER_POOL_PARAMETERS nb_pool_params;
RTL_OSVERSIONINFOW version;
LARGE_INTEGER freq;
NTSTATUS status;
DECLARE_CONST_UNICODE_STRING(device_name,
@@ -576,6 +588,17 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj,
DEBUG("LOAD: loading WinDivert driver");
// Use the "no execute" pool if available:
status = RtlGetVersion(&version);
if (NT_SUCCESS(status))
{
if (version.dwMajorVersion > 6 ||
(version.dwMajorVersion == 6 && version.dwMinorVersion >= 2))
{
non_paged_pool = (POOL_TYPE)512; // NonPagedPoolNx (documented)
}
}
// Initialize timer info.
KeQueryPerformanceCounter(&freq);
counts_per_ms = (ULONGLONG)freq.QuadPart / 1000;
@@ -873,34 +896,6 @@ static NTSTATUS windivert_install_sublayer(layer_t layer)
return status;
}
/*
* WinDivert context verify.
*/
static BOOLEAN windivert_context_verify(context_t context,
context_state_t state)
{
if (context == NULL)
{
DEBUG_ERROR("failed to verify context; context is NULL",
STATUS_INVALID_HANDLE);
return FALSE;
}
if (context->magic != WINDIVERT_CONTEXT_MAGIC)
{
DEBUG_ERROR("failed to verify context; invalid magic number",
STATUS_INVALID_HANDLE);
return FALSE;
}
if (context->state != state)
{
DEBUG_ERROR("failed to verify context; expected context state %x, "
"found context state %x", STATUS_INVALID_HANDLE, state,
context->state);
return FALSE;
}
return TRUE;
}
/*
* WinDivert create routine.
*/
@@ -918,7 +913,6 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
DEBUG("CREATE: creating a new WinDivert context (context=%p)", context);
// Initialise the new context:
context->magic = WINDIVERT_CONTEXT_MAGIC;
context->state = WINDIVERT_CONTEXT_STATE_OPENING;
context->device = device;
context->object = object;
@@ -929,14 +923,10 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
context->packet_queue_maxcounts =
WINDIVERT_PARAM_QUEUE_TIME_DEFAULT * counts_per_ms;
context->packet_queue_maxtime = WINDIVERT_PARAM_QUEUE_TIME_DEFAULT;
context->layer_0 = WINDIVERT_LAYER_DEFAULT;
context->layer = WINDIVERT_LAYER_DEFAULT;
context->flags_0 = 0;
context->flags = 0;
context->priority_0 =
WINDIVERT_CONTEXT_PRIORITY(WINDIVERT_PRIORITY_DEFAULT);
context->priority = context->priority_0;
context->filter = NULL;
context->layer = WINDIVERT_LAYER_DEFAULT;
context->flags = 0;
context->priority = WINDIVERT_CONTEXT_PRIORITY(WINDIVERT_PRIORITY_DEFAULT);
context->filter = NULL;
for (i = 0; i < WINDIVERT_CONTEXT_MAXWORKERS; i++)
{
context->workers[i] = NULL;
@@ -946,7 +936,7 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request,
{
context->installed[i] = FALSE;
}
context->filter_on = FALSE;
context->on = FALSE;
KeInitializeSpinLock(&context->lock);
InitializeListHead(&context->work_queue);
InitializeListHead(&context->packet_queue);
@@ -1027,15 +1017,15 @@ windivert_create_exit:
/*
* Register all WFP callouts.
*/
static NTSTATUS windivert_install_callouts(context_t context, BOOL is_inbound,
BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6)
static NTSTATUS windivert_install_callouts(context_t context, UINT8 layer,
BOOL is_inbound, BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6)
{
UINT8 i, j;
layer_t layers[WINDIVERT_CONTEXT_MAXLAYERS];
NTSTATUS status;
i = 0;
switch (context->layer)
switch (layer)
{
case WINDIVERT_LAYER_NETWORK:
if (is_inbound && is_ipv4)
@@ -1084,7 +1074,7 @@ windivert_install_callouts_exit:
if (!NT_SUCCESS(status))
{
windivert_uninstall_callouts(context);
windivert_uninstall_callouts(context, WINDIVERT_CONTEXT_STATE_OPEN);
}
return status;
@@ -1096,87 +1086,133 @@ windivert_install_callouts_exit:
static NTSTATUS windivert_install_callout(context_t context, UINT idx,
layer_t layer)
{
KLOCK_QUEUE_HANDLE lock_handle;
FWPS_CALLOUT0 scallout;
FWPM_CALLOUT0 mcallout;
FWPM_FILTER0 filter;
UINT64 weight;
UINT32 priority;
GUID callout_guid, filter_guid;
WDFDEVICE device;
HANDLE engine_handle;
NTSTATUS status;
weight = WINDIVERT_FILTER_WEIGHT(context->priority);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
return status;
}
priority = context->priority;
callout_guid = context->callout_guid[idx];
filter_guid = context->filter_guid[idx];
device = context->device;
engine_handle = context->engine_handle;
KeReleaseInStackQueuedSpinLock(&lock_handle);
weight = WINDIVERT_FILTER_WEIGHT(priority);
RtlZeroMemory(&scallout, sizeof(scallout));
scallout.calloutKey = context->callout_guid[idx];
scallout.calloutKey = callout_guid;
scallout.classifyFn = layer->callout;
scallout.notifyFn = windivert_notify_callout;
scallout.flowDeleteFn = NULL;
RtlZeroMemory(&mcallout, sizeof(mcallout));
mcallout.calloutKey = context->callout_guid[idx];
mcallout.calloutKey = callout_guid;
mcallout.displayData.name = layer->callout_name;
mcallout.displayData.description = layer->callout_desc;
mcallout.applicableLayer = layer->layer_guid;
RtlZeroMemory(&filter, sizeof(filter));
filter.filterKey = context->filter_guid[idx];
filter.filterKey = filter_guid;
filter.layerKey = layer->layer_guid;
filter.displayData.name = layer->filter_name;
filter.displayData.description = layer->filter_desc;
filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN;
filter.action.calloutKey = context->callout_guid[idx];
filter.action.calloutKey = callout_guid;
filter.subLayerKey = layer->sublayer_guid;
filter.weight.type = FWP_UINT64;
filter.weight.uint64 = &weight;
filter.rawContext = (UINT64)context;
status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(context->device),
status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(device),
&scallout, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to install WFP callout", status);
return status;
}
status = FwpmTransactionBegin0(context->engine_handle, 0);
status = FwpmTransactionBegin0(engine_handle, 0);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to begin WFP transaction", status);
FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]);
FwpsCalloutUnregisterByKey0(&callout_guid);
return status;
}
status = FwpmCalloutAdd0(context->engine_handle, &mcallout, NULL, NULL);
status = FwpmCalloutAdd0(engine_handle, &mcallout, NULL, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to add WFP callout", status);
goto windivert_install_callout_error;
}
status = FwpmFilterAdd0(context->engine_handle, &filter, NULL, NULL);
status = FwpmFilterAdd0(engine_handle, &filter, NULL, NULL);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to add WFP filter", status);
goto windivert_install_callout_error;
}
status = FwpmTransactionCommit0(context->engine_handle);
status = FwpmTransactionCommit0(engine_handle);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to commit WFP transaction", status);
FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]);
FwpsCalloutUnregisterByKey0(&callout_guid);
return status;
}
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
FwpsCalloutUnregisterByKey0(&callout_guid);
status = STATUS_INVALID_DEVICE_STATE;
return status;
}
context->installed[idx] = TRUE;
KeReleaseInStackQueuedSpinLock(&lock_handle);
return STATUS_SUCCESS;
windivert_install_callout_error:
FwpmTransactionAbort0(context->engine_handle);
FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]);
FwpmTransactionAbort0(engine_handle);
FwpsCalloutUnregisterByKey0(&callout_guid);
return status;
}
/*
* WinDivert uninstall callouts routine.
*/
static void windivert_uninstall_callouts(context_t context)
static void windivert_uninstall_callouts(context_t context,
context_state_t state)
{
KLOCK_QUEUE_HANDLE lock_handle;
UINT i;
HANDLE engine_handle;
BOOL installed;
GUID callout_guid, filter_guid;
NTSTATUS status;
status = FwpmTransactionBegin0(context->engine_handle, 0);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != state)
{
windivert_uninstall_callouts_error:
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
DEBUG_ERROR("failed to delete filters and callouts", status);
return;
}
engine_handle = context->engine_handle;
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = FwpmTransactionBegin0(engine_handle, 0);
if (!NT_SUCCESS(status))
{
// If the userspace app closes without closing the handle to
@@ -1188,21 +1224,31 @@ static void windivert_uninstall_callouts(context_t context)
}
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
{
if (!context->installed[i])
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != state)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
FwpmTransactionAbort0(engine_handle);
status = STATUS_INVALID_DEVICE_STATE;
DEBUG_ERROR("failed to delete filters and callouts", status);
return;
}
installed = context->installed[i];
callout_guid = context->callout_guid[i];
filter_guid = context->filter_guid[i];
KeReleaseInStackQueuedSpinLock(&lock_handle);
if (!installed)
{
continue;
}
status = FwpmFilterDeleteByKey0(context->engine_handle,
&context->filter_guid[i]);
status = FwpmFilterDeleteByKey0(engine_handle, &filter_guid);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to delete filter", status);
break;
}
status = FwpmCalloutDeleteByKey0(context->engine_handle,
&context->callout_guid[i]);
status = FwpmCalloutDeleteByKey0(engine_handle, &callout_guid);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to delete callout", status);
@@ -1211,10 +1257,10 @@ static void windivert_uninstall_callouts(context_t context)
}
if (!NT_SUCCESS(status))
{
FwpmTransactionAbort0(context->engine_handle);
FwpmTransactionAbort0(engine_handle);
goto unregister_callouts;
}
status = FwpmTransactionCommit0(context->engine_handle);
status = FwpmTransactionCommit0(engine_handle);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to commit WFP transaction", status);
@@ -1223,8 +1269,20 @@ static void windivert_uninstall_callouts(context_t context)
unregister_callouts:
for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++)
{
FwpsCalloutUnregisterByKey0(&context->callout_guid[i]);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != state)
{
goto windivert_uninstall_callouts_error;
}
installed = context->installed[i];
callout_guid = context->callout_guid[i];
context->installed[i] = FALSE;
KeReleaseInStackQueuedSpinLock(&lock_handle);
if (!installed)
{
continue;
}
FwpsCalloutUnregisterByKey0(&callout_guid);
}
}
@@ -1239,15 +1297,21 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object)
context_t context = windivert_context_get(object);
work_t work;
packet_t packet;
WDFQUEUE read_queue;
WDFWORKITEM worker;
NTSTATUS status;
DEBUG("CLEANUP: cleaning up WinDivert context (context=%p)", context);
if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_OPEN))
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
windivert_cleanup_error:
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
DEBUG_ERROR("failed to verify state for cleanup routine", status);
return;
}
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
context->state = WINDIVERT_CONTEXT_STATE_CLOSING;
while (!IsListEmpty(&context->work_queue))
{
@@ -1255,26 +1319,44 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object)
KeReleaseInStackQueuedSpinLock(&lock_handle);
work = CONTAINING_RECORD(entry, struct work_s, entry);
FwpsDereferenceNetBufferList(work->buffers, FALSE);
ExFreePoolWithTag(work, WINDIVERT_TAG);
windivert_free(work);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING)
{
goto windivert_cleanup_error;
}
}
while (!IsListEmpty(&context->packet_queue))
{
entry = RemoveHeadList(&context->packet_queue);
KeReleaseInStackQueuedSpinLock(&lock_handle);
packet = CONTAINING_RECORD(entry, struct packet_s, entry);
context->packet_queue_length--;
context->packet_queue_size -= packet->data_len;
KeReleaseInStackQueuedSpinLock(&lock_handle);
windivert_free_packet(packet);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING)
{
goto windivert_cleanup_error;
}
}
read_queue = context->read_queue;
KeReleaseInStackQueuedSpinLock(&lock_handle);
WdfIoQueuePurge(context->read_queue, NULL, NULL);
WdfObjectDelete(context->read_queue);
WdfIoQueuePurge(read_queue, NULL, NULL);
WdfObjectDelete(read_queue);
for (i = 0; i < WINDIVERT_CONTEXT_MAXWORKERS; i++)
{
WdfWorkItemFlush(context->workers[i]);
WdfObjectDelete(context->workers[i]);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING)
{
goto windivert_cleanup_error;
}
worker = context->workers[i];
KeReleaseInStackQueuedSpinLock(&lock_handle);
WdfWorkItemFlush(worker);
WdfObjectDelete(worker);
}
windivert_uninstall_callouts(context);
windivert_uninstall_callouts(context, WINDIVERT_CONTEXT_STATE_CLOSING);
FwpmEngineClose0(context->engine_handle);
}
@@ -1285,14 +1367,18 @@ extern VOID windivert_close(IN WDFFILEOBJECT object)
{
KLOCK_QUEUE_HANDLE lock_handle;
context_t context = windivert_context_get(object);
NTSTATUS status;
DEBUG("CLOSE: closing WinDivert context (context=%p)", context);
if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_CLOSING))
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
DEBUG_ERROR("failed to verify state for close routine", status);
return;
}
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
context->state = WINDIVERT_CONTEXT_STATE_CLOSED;
KeReleaseInStackQueuedSpinLock(&lock_handle);
}
@@ -1302,18 +1388,24 @@ extern VOID windivert_close(IN WDFFILEOBJECT object)
*/
extern VOID windivert_destroy(IN WDFOBJECT object)
{
KLOCK_QUEUE_HANDLE lock_handle;
context_t context = windivert_context_get((WDFFILEOBJECT)object);
filter_t filter;
NTSTATUS status;
DEBUG("DESTROY: destroying WinDivert context (context=%p)", context);
if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_CLOSED))
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_CLOSED)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
DEBUG_ERROR("failed to verify state for destroy routine", status);
return;
}
if (context->filter != NULL)
{
ExFreePoolWithTag(context->filter, WINDIVERT_TAG);
}
filter = context->filter;
KeReleaseInStackQueuedSpinLock(&lock_handle);
windivert_free(filter);
}
/*
@@ -1481,12 +1573,15 @@ static void windivert_read_service(context_t context)
static NTSTATUS windivert_write(context_t context, WDFREQUEST request,
windivert_addr_t addr)
{
KLOCK_QUEUE_HANDLE lock_handle;
PMDL mdl = NULL, mdl_copy = NULL;
PVOID data, data_copy = NULL;
UINT data_len;
struct iphdr *ip_header;
struct ipv6hdr *ipv6_header;
BOOL isipv4;
UINT8 layer;
UINT32 priority;
HANDLE handle;
PNET_BUFFER_LIST buffers = NULL;
NTSTATUS status = STATUS_SUCCESS;
@@ -1494,12 +1589,6 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request,
DEBUG("WRITE: writing/injecting a packet (context=%p, request=%p)",
context, request);
if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_OPEN))
{
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_write_exit;
}
if (addr->Direction != WINDIVERT_DIRECTION_INBOUND &&
addr->Direction != WINDIVERT_DIRECTION_OUTBOUND)
{
@@ -1532,7 +1621,7 @@ windivert_write_bad_packet:
goto windivert_write_exit;
}
data_copy = ExAllocatePoolWithTag(NonPagedPool, data_len, WINDIVERT_TAG);
data_copy = windivert_malloc(data_len, FALSE);
if (data_copy == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1587,25 +1676,35 @@ windivert_write_bad_packet:
goto windivert_write_exit;
}
handle = (isipv4? inject_handle: injectv6_handle);
if (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD)
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
status = FwpsInjectForwardAsync0(handle, (HANDLE)context->priority,
0, (isipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID,
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_write_exit;
}
layer = context->layer;
priority = context->priority;
KeReleaseInStackQueuedSpinLock(&lock_handle);
handle = (isipv4? inject_handle: injectv6_handle);
if (layer == WINDIVERT_LAYER_NETWORK_FORWARD)
{
status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0,
(isipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID,
addr->IfIdx, buffers, windivert_inject_complete, NULL);
}
else if (addr->Direction == WINDIVERT_DIRECTION_OUTBOUND)
{
status = FwpsInjectNetworkSendAsync0(handle,
(HANDLE)context->priority, 0, UNSPECIFIED_COMPARTMENT_ID, buffers,
windivert_inject_complete, NULL);
status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0,
UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete,
NULL);
}
else
{
status = FwpsInjectNetworkReceiveAsync0(handle,
(HANDLE)context->priority, 0, UNSPECIFIED_COMPARTMENT_ID,
addr->IfIdx, addr->SubIfIdx, buffers, windivert_inject_complete,
NULL);
status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0,
UNSPECIFIED_COMPARTMENT_ID, addr->IfIdx, addr->SubIfIdx, buffers,
windivert_inject_complete, NULL);
}
windivert_write_exit:
@@ -1625,10 +1724,7 @@ windivert_write_exit:
{
IoFreeMdl(mdl_copy);
}
if (data_copy != NULL)
{
ExFreePoolWithTag(data_copy, WINDIVERT_TAG);
}
windivert_free(data_copy);
}
return status;
@@ -1649,10 +1745,7 @@ static void NTAPI windivert_inject_complete(VOID *context,
buffer = NET_BUFFER_LIST_FIRST_NB(buffers);
mdl = NET_BUFFER_FIRST_MDL(buffer);
data = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority);
if (data != NULL)
{
ExFreePoolWithTag(data, WINDIVERT_TAG);
}
windivert_free(data);
IoFreeMdl(mdl);
FwpsFreeNetBufferList0(buffers);
}
@@ -1787,9 +1880,13 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
{
KLOCK_QUEUE_HANDLE lock_handle;
PCHAR inbuf, outbuf;
size_t inbuflen, outbuflen, filter_len;
size_t inbuflen, outbuflen, filter0_len;
windivert_ioctl_t ioctl;
windivert_ioctl_filter_t filter;
windivert_ioctl_filter_t filter0;
filter_t filter;
UINT8 layer;
UINT32 priority;
UINT64 flags;
windivert_addr_t addr;
req_context_t req_context;
NTSTATUS status = STATUS_SUCCESS;
@@ -1800,12 +1897,6 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
DEBUG("IOCTL: I/O control request (context=%p)", context);
if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_OPEN))
{
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_ioctl_exit;
}
// Get the buffers and do sanity checks.
status = WdfRequestRetrieveInputBuffer(request, 0, &inbuf, &inbuflen);
if (!NT_SUCCESS(status))
@@ -1856,30 +1947,32 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
{
BOOL is_inbound, is_outbound, is_ipv4, is_ipv6;
if (InterlockedExchange(&context->filter_on, TRUE) == TRUE)
{
status = STATUS_INVALID_DEVICE_STATE;
DEBUG_ERROR("duplicate START_FILTER ioctl", status);
goto windivert_ioctl_exit;
}
context->layer = context->layer_0;
context->flags = context->flags_0;
context->priority = context->priority_0;
filter = (windivert_ioctl_filter_t)outbuf;
filter_len = outbuflen;
context->filter = windivert_filter_compile(filter, filter_len);
if (context->filter == NULL)
filter0 = (windivert_ioctl_filter_t)outbuf;
filter0_len = outbuflen;
filter = windivert_filter_compile(filter0, filter0_len);
if (filter == NULL)
{
status = STATUS_INVALID_PARAMETER;
DEBUG_ERROR("failed to compile filter", status);
goto windivert_ioctl_exit;
}
windivert_filter_analyze(context->filter, &is_inbound,
&is_outbound, &is_ipv4, &is_ipv6);
status = windivert_install_callouts(context, is_inbound,
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
windivert_free(filter);
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_ioctl_exit;
}
context->on = TRUE;
context->filter = filter;
layer = context->layer;
KeReleaseInStackQueuedSpinLock(&lock_handle);
windivert_filter_analyze(filter, &is_inbound, &is_outbound,
&is_ipv4, &is_ipv6);
status = windivert_install_callouts(context, layer, is_inbound,
is_outbound, is_ipv4, is_ipv6);
break;
@@ -1893,7 +1986,16 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
DEBUG_ERROR("failed to set layer; value too big", status);
goto windivert_ioctl_exit;
}
context->layer_0 = (UINT8)ioctl->arg;
layer = (UINT8)ioctl->arg;
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_ioctl_exit;
}
context->layer = layer;
KeReleaseInStackQueuedSpinLock(&lock_handle);
break;
case IOCTL_WINDIVERT_SET_PRIORITY:
@@ -1906,8 +2008,16 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
status);
goto windivert_ioctl_exit;
}
context->priority_0 =
WINDIVERT_CONTEXT_PRIORITY((UINT32)ioctl->arg);
priority = WINDIVERT_CONTEXT_PRIORITY((UINT32)ioctl->arg);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_ioctl_exit;
}
context->priority = priority;
KeReleaseInStackQueuedSpinLock(&lock_handle);
break;
case IOCTL_WINDIVERT_SET_FLAGS:
@@ -1919,7 +2029,16 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
status);
goto windivert_ioctl_exit;
}
context->flags_0 = ioctl->arg;
flags = ioctl->arg;
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_ioctl_exit;
}
context->flags = flags;
KeReleaseInStackQueuedSpinLock(&lock_handle);
break;
case IOCTL_WINDIVERT_SET_PARAM:
@@ -1929,7 +2048,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_PARAMETER;
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_ioctl_exit;
}
switch ((WINDIVERT_PARAM)ioctl->arg8)
@@ -1999,7 +2118,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request,
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
status = STATUS_INVALID_PARAMETER;
status = STATUS_INVALID_DEVICE_STATE;
goto windivert_ioctl_exit;
}
switch ((WINDIVERT_PARAM)ioctl->arg8)
@@ -2184,11 +2303,10 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction,
PNET_BUFFER buffer, buffer_fst, buffer_itr;
NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksums_info;
UINT8 checksums;
BOOL outbound, queued;
filter_t filter;
BOOL outbound;
WDFOBJECT object;
work_t work;
ULONG read_queue_len;
filter_t filter;
ULONGLONG timestamp;
NTSTATUS status;
@@ -2244,7 +2362,7 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction,
}
timestamp = (ULONGLONG)KeQueryPerformanceCounter(NULL).QuadPart;
timestamp--;
timestamp = (timestamp > 0? timestamp-1: timestamp);
// Determine which checksum fields are present or not.
if (isloopback)
@@ -2320,8 +2438,7 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction,
// At least one packet matches the filter. Delay all further processing
// until windivert_worker() at IRQL=PASSIVE_LEVEL.
work = (work_t)ExAllocatePoolWithTag(NonPagedPool, sizeof(struct work_s),
WINDIVERT_TAG);
work = (work_t)windivert_malloc(sizeof(struct work_s), FALSE);
if (work == NULL)
{
goto windivert_classify_callout_exit;
@@ -2338,28 +2455,21 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction,
work->sub_if_idx = sub_if_idx;
work->priority = priority;
work->timestamp = timestamp;
queued = FALSE;
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state == WINDIVERT_CONTEXT_STATE_OPEN)
{
InsertTailList(&context->work_queue, &work->entry);
WdfWorkItemEnqueue(context->workers[context->worker_curr]);
context->worker_curr++;
if (context->worker_curr >= WINDIVERT_CONTEXT_MAXWORKERS)
{
context->worker_curr = 0;
}
queued = TRUE;
}
KeReleaseInStackQueuedSpinLock(&lock_handle);
if (!queued)
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
WdfObjectDereference(object);
FwpsDereferenceNetBufferList(buffers, FALSE);
ExFreePoolWithTag(work, WINDIVERT_TAG);
windivert_free(work);
result->actionType = FWP_ACTION_CONTINUE;
return;
}
InsertTailList(&context->work_queue, &work->entry);
WdfWorkItemEnqueue(context->workers[context->worker_curr]);
context->worker_curr =
(context->worker_curr + 1) % WINDIVERT_CONTEXT_MAXWORKERS;
KeReleaseInStackQueuedSpinLock(&lock_handle);
windivert_classify_callout_exit:
@@ -2387,9 +2497,12 @@ VOID windivert_worker(IN WDFWORKITEM item)
NTSTATUS status;
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0);
forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD);
filter = context->filter;
if (context->state == WINDIVERT_CONTEXT_STATE_OPEN)
{
sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0);
forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD);
filter = context->filter;
}
while (context->state == WINDIVERT_CONTEXT_STATE_OPEN &&
!IsListEmpty(&context->work_queue))
{
@@ -2483,7 +2596,7 @@ windivert_worker_complete:
NdisAdvanceNetBufferDataStart(work->buffer, advance, 0, 0);
}
FwpsDereferenceNetBufferList(work->buffers, FALSE);
ExFreePoolWithTag(work, WINDIVERT_TAG);
windivert_free(work);
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
}
KeReleaseInStackQueuedSpinLock(&lock_handle);
@@ -2546,13 +2659,12 @@ static BOOL windivert_queue_packet(context_t context, PNET_BUFFER buffer,
// SLOW PATH: queue the packet.
data_len = NET_BUFFER_DATA_LENGTH(buffer);
packet = (packet_t)ExAllocatePoolWithTag(NonPagedPool,
sizeof(struct packet_s), WINDIVERT_TAG);
packet = (packet_t)windivert_malloc(sizeof(struct packet_s), FALSE);
if (packet == NULL)
{
return FALSE;
}
packet->data = ExAllocatePoolWithTag(PagedPool, data_len, WINDIVERT_TAG);
packet->data = windivert_malloc(data_len, TRUE);
if (packet->data == NULL)
{
windivert_free_packet(packet);
@@ -2755,11 +2867,8 @@ static void NTAPI windivert_reinject_clone_complete(VOID *context,
*/
static void windivert_free_packet(packet_t packet)
{
if (packet->data != NULL)
{
ExFreePoolWithTag(packet->data, WINDIVERT_TAG);
}
ExFreePoolWithTag(packet, WINDIVERT_TAG);
windivert_free(packet->data);
windivert_free(packet);
}
/*
@@ -3552,8 +3661,8 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter,
}
// Do NOT use the stack (size = 12Kb on x86) for filter0.
filter0 = (filter_t)ExAllocatePoolWithTag(NonPagedPool,
WINDIVERT_FILTER_MAXLEN*sizeof(struct filter_s), WINDIVERT_TAG);
filter0 = (filter_t)windivert_malloc(
WINDIVERT_FILTER_MAXLEN*sizeof(struct filter_s), TRUE);
if (filter0 == NULL)
{
goto windivert_filter_compile_exit;
@@ -3776,8 +3885,7 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter,
}
}
result = (filter_t)ExAllocatePoolWithTag(NonPagedPool,
i*sizeof(struct filter_s), WINDIVERT_TAG);
result = (filter_t)windivert_malloc(i*sizeof(struct filter_s), FALSE);
if (result != NULL)
{
RtlMoveMemory(result, filter0, i*sizeof(struct filter_s));
@@ -3785,10 +3893,7 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter,
windivert_filter_compile_exit:
if (filter0 != NULL)
{
ExFreePoolWithTag(filter0, WINDIVERT_TAG);
}
windivert_free(filter0);
return result;
}