5 Commits

Author SHA1 Message Date
basil00 97101072db Fix previous commit 8bda0af
Fix #294

Previous commit was incomplete.
2022-04-10 06:51:38 +08:00
basil00 8bda0aff7b Fix handling of FwpmTransaction*() errors.
Fixes #294
2022-04-09 09:49:45 +08:00
basil00 34b565de65 Merge pull request #296 from StalkR/patch-1
windivert.html: fix 6.11 title: format not parse
2022-02-23 11:24:31 +08:00
StalkR 90396ffa2b windivert.html: fix 6.11 title: format not parse 2022-02-15 19:35:18 +01:00
basil00 8cdddce6ac Send an ICMP(V6) message if the packet is too big
Fix #278

The Miniport driver will reject any outbound
packet that is larger than the MTU.  However the
error flows back to the sending application as
an error code/condition, which is disrupted by
WinDivert, meaning the error is lost.

This change translates the error code into an
ICMP(V6) "packet too big" message, allowing for
the error to flow back to the origin in some
form.

This change required some refactoring.
2021-10-22 07:34:23 +08:00
8 changed files with 321 additions and 223 deletions
-6
View File
@@ -312,9 +312,3 @@ WinDivert 2.2.0
- Implement new packet parser that correctly handles IP fragments.
- Add a new "fragment" filter field that matches IP fragments.
- (Un)Loading the WinDivert driver will cause a system event to be logged.
WinDivert 2.2.1
- Fix potential driver deadlock on user-mode program crash.
- Fix filter language simplification bug.
- Fix Flow.EndpointId containing junk data.
WinDivert 2.2.2
- Fix potential WinDivertClose() BSOD for WINDIVERT_LAYER_FLOW handles.
+1 -1
View File
@@ -5,7 +5,7 @@ WinDivert 2.2: Windows Packet Divert
---------------
Windows Packet Divert (WinDivert) is a user-mode packet interception library
for Windows 10, Windows 11, and Windows Server.
for Windows 7, Windows 8 and Windows 10.
WinDivert enables user-mode capturing/modifying/dropping of network packets
sent to/from the Windows network stack. In summary, WinDivert can:
+1 -1
View File
@@ -1 +1 @@
2.2.2
2.2.0
+1 -1
View File
@@ -72,7 +72,7 @@
<p>
WinDivert is a powerful user-mode
capture/sniffing/modification/blocking/re-injection package for
Windows 10, Windows 11, and Windows Server.
Windows 7, Windows 8 and Windows 10.
WinDivert can be used to implement user-mode packet filters, packet sniffers,
firewalls, NAT, VPNs, tunneling applications, etc., without the need to
write kernel-mode code.
+1 -1
View File
@@ -4,7 +4,7 @@ Class = WFPCALLOUTS
ClassGuid = {57465043-616C-6C6F-7574-5F636C617373}
Provider = %Basil%
CatalogFile = WinDivert32.Cat
DriverVer = 01/09/2022,2.2.2
DriverVer = 08/08/2019,2.2.0
[SourceDisksNames]
1 = %DiskName%
+1 -1
View File
@@ -4,7 +4,7 @@ Class = WFPCALLOUTS
ClassGuid = {57465043-616C-6C6F-7574-5F636C617373}
Provider = %Basil%
CatalogFile = WinDivert64.Cat
DriverVer = 01/09/2022,2.2.2
DriverVer = 08/08/2019,2.2.0
[SourceDisksNames]
1 = %DiskName%
+314 -210
View File
@@ -242,8 +242,9 @@ struct packet_s
UINT32 ip_checksum:1; // Packet has IPv4 checksum?
UINT32 tcp_checksum:1; // Packet has TCP checksum?
UINT32 udp_checksum:1; // Packet has UDP checksum?
UINT32 icmp_checksum:1; // Packet has ICMP(V6) checksum?
UINT32 match:1; // Packet matches filter?
UINT32 padding:7; // Padding for alignment.
UINT32 padding:6; // Padding for alignment.
UINT32 packet_size; // Packet total size.
PVOID object; // Object associated with packet.
UINT32 priority; // Packet priority.
@@ -273,6 +274,8 @@ struct flow_s
UINT64 flow_id; // WFP flow ID.
UINT32 callout_id; // WFP callout ID.
UINT16 layer_id; // WFP layout ID.
BOOL inserted:1; // Flow inserted into context?
BOOL deleted:1; // Flow deleted from context?
BOOL outbound:1; // Flow is outound?
BOOL loopback:1; // Flow is loopback?
BOOL ipv6:1; // Flow is ipv6?
@@ -345,8 +348,7 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request,
req_context_t req_context);
static void NTAPI windivert_inject_complete(VOID *context,
NET_BUFFER_LIST *packets, BOOLEAN dispatch_level);
static void NTAPI windivert_reinject_complete(VOID *context,
NET_BUFFER_LIST *packets, BOOLEAN dispatch_level);
static void windivert_inject_packet_too_big(packet_t packet);
static NTSTATUS windivert_notify(IN FWPS_CALLOUT_NOTIFY_TYPE type,
IN const GUID *filter_key, IN const FWPS_FILTER0 *filter);
static void windivert_outbound_network_v4_classify(
@@ -467,7 +469,7 @@ static BOOL windivert_queue_work(context_t context, PVOID packet,
UINT64 flags, UINT32 priority, BOOL ipv4, BOOL outbound, BOOL loopback,
BOOL impostor, BOOL match, LONGLONG timestamp);
static void windivert_queue_packet(context_t context, packet_t packet);
static void windivert_reinject_packet(packet_t packet);
static NTSTATUS windivert_inject_packet(packet_t packet);
static void windivert_free_packet(packet_t packet);
static BOOL windivert_copy_data(PNET_BUFFER buffer, PVOID data, UINT size);
static BOOL windivert_get_data(PNET_BUFFER buffer, UINT length, INT min,
@@ -1979,20 +1981,17 @@ windivert_cleanup_error:
context->state = WINDIVERT_CONTEXT_STATE_CLOSING;
sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0);
forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD);
entry = context->flow_set.Flink;
if (entry != &context->flow_set)
while (!IsListEmpty(&context->flow_set))
{
entry = RemoveHeadList(&context->flow_set);
flow = CONTAINING_RECORD(entry, struct flow_s, entry);
flow->deleted = TRUE;
KeReleaseInStackQueuedSpinLock(&lock_handle);
for (; entry != &context->flow_set; entry = entry->Flink)
status = FwpsFlowRemoveContext0(flow->flow_id, flow->layer_id,
flow->callout_id);
if (!NT_SUCCESS(status))
{
flow = CONTAINING_RECORD(entry, struct flow_s, entry);
status = FwpsFlowRemoveContext0(flow->flow_id, flow->layer_id,
flow->callout_id);
if (!NT_SUCCESS(status) && status != STATUS_UNSUCCESSFUL)
{
// For STATUS_UNSUCCESSFUL, flow_delete() is still called.
WdfObjectDereference((WDFOBJECT)object);
}
windivert_free(flow);
}
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
}
@@ -2006,7 +2005,7 @@ windivert_cleanup_error:
timeout = WINDIVERT_TIMEOUT(context, packet->timestamp, timestamp);
if (!sniff_mode && !timeout)
{
windivert_reinject_packet(packet);
windivert_inject_packet(packet);
}
else
{
@@ -2028,7 +2027,7 @@ windivert_cleanup_error:
timeout = WINDIVERT_TIMEOUT(context, work->timestamp, timestamp);
if (!sniff_mode && !timeout)
{
windivert_reinject_packet(work);
windivert_inject_packet(work);
}
else
{
@@ -2087,8 +2086,6 @@ extern VOID windivert_destroy(IN WDFOBJECT object)
KLOCK_QUEUE_HANDLE lock_handle;
context_t context = windivert_context_get((WDFFILEOBJECT)object);
const WINDIVERT_FILTER *filter;
PLIST_ENTRY entry;
flow_t flow;
NTSTATUS status;
DEBUG("DESTROY: destroying WinDivert context (context=%p)", context);
@@ -2109,12 +2106,6 @@ extern VOID windivert_destroy(IN WDFOBJECT object)
FwpmEngineClose0(context->engine_handle);
}
windivert_free((PVOID)filter);
while (!IsListEmpty(&context->flow_set))
{
entry = RemoveHeadList(&context->flow_set);
flow = CONTAINING_RECORD(entry, struct flow_s, entry);
windivert_free(flow);
}
if (context->process != NULL)
{
ObDereferenceObject(context->process);
@@ -2599,19 +2590,20 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request,
req_context_t req_context)
{
KLOCK_QUEUE_HANDLE lock_handle;
PMDL mdl = NULL, mdl_copy = NULL;
PVOID data, data_copy = NULL;
UINT data_len, packet_len, inject_len;
PMDL mdl = NULL;
PVOID data, data_copy;
packet_t packet;
UINT data_len, packet_len, packet_size, inject_len;
PWINDIVERT_DATA_NETWORK network_data;
PWINDIVERT_IPHDR ip_header;
PWINDIVERT_IPV6HDR ipv6_header;
BOOL ipv4;
UINT8 layer;
UINT32 priority;
UINT64 flags, checksums;
HANDLE handle;
PNET_BUFFER_LIST buffers = NULL;
PWINDIVERT_ADDRESS addr;
UINT i, addr_len, addr_len_max;
UINT i, addr_len, addr_len_max, version;
NTSTATUS status = STATUS_SUCCESS, status_soft_error = STATUS_SUCCESS;
DEBUG("WRITE: writing/injecting a packet (context=%p, request=%p)",
@@ -2680,10 +2672,6 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request,
i < WINDIVERT_BATCH_MAX;
i++, addr_len += sizeof(WINDIVERT_ADDRESS))
{
buffers = NULL;
mdl_copy = NULL;
data_copy = NULL;
// Get the packet length:
if (data_len < sizeof(WINDIVERT_IPHDR))
{
@@ -2693,11 +2681,15 @@ windivert_write_too_small_packet:
goto windivert_write_hard_error;
}
ip_header = (PWINDIVERT_IPHDR)data;
switch (ip_header->Version)
version = ip_header->Version;
switch (version)
{
case 4:
packet_len = RtlUshortByteSwap(ip_header->Length);
ipv4 = TRUE;
if (packet_len < sizeof(WINDIVERT_IPHDR))
{
goto windivert_write_invalid_packet;
}
break;
case 6:
if (data_len < sizeof(WINDIVERT_IPV6HDR))
@@ -2707,11 +2699,11 @@ windivert_write_too_small_packet:
ipv6_header = (PWINDIVERT_IPV6HDR)data;
packet_len = RtlUshortByteSwap(ipv6_header->Length) +
sizeof(WINDIVERT_IPV6HDR);
ipv4 = FALSE;
break;
default:
windivert_write_invalid_packet:
status = STATUS_INVALID_PARAMETER;
DEBUG_ERROR("failed to inject non-IP packet", status);
DEBUG_ERROR("failed to inject invalid packet", status);
goto windivert_write_hard_error;
}
if (data_len < packet_len)
@@ -2719,100 +2711,77 @@ windivert_write_too_small_packet:
goto windivert_write_too_small_packet;
}
// Copy packet data:
data_copy = windivert_malloc(packet_len, FALSE);
if (data_copy == NULL)
// Copy packet & data:
packet_size = WINDIVERT_PACKET_SIZE(WINDIVERT_DATA_NETWORK,
packet_len);
packet = (packet_t)windivert_malloc(packet_size, FALSE);
if (packet == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
DEBUG_ERROR("failed to allocate memory for injected packet data",
DEBUG_ERROR("failed to allocate memory for injected packet",
status);
goto windivert_write_hard_error;
}
packet->layer = layer;
packet->event = WINDIVERT_EVENT_NETWORK_PACKET;
packet->sniffed = 0; // Unused
packet->outbound = addr[i].Outbound;
packet->loopback = 0; // Unused
packet->impostor = addr[i].Impostor;
packet->ipv6 = (version == 6? 1: 0);
packet->ip_checksum = addr[i].IPChecksum;
packet->tcp_checksum = addr[i].TCPChecksum;
packet->udp_checksum = addr[i].UDPChecksum;
packet->icmp_checksum = 1; // Assumed valid
packet->match = 0; // Unused
packet->packet_size = packet_size;
packet->packet_len = packet_len;
packet->priority = priority;
packet->timestamp = 0; // Unused
packet->object = NULL;
network_data =
(PWINDIVERT_DATA_NETWORK)WINDIVERT_LAYER_DATA_PTR(packet);
RtlCopyMemory(network_data, &addr[i].Network, sizeof(network_data));
data_copy = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_NETWORK, packet);
RtlCopyMemory(data_copy, data, packet_len);
switch (version)
{
case 4:
ip_header = (PWINDIVERT_IPHDR)data_copy;
if (ip_header->Version != 4 ||
packet_len != RtlUshortByteSwap(ip_header->Length))
{
windivert_free(packet);
goto windivert_write_invalid_packet;
}
break;
case 6:
ipv6_header = (PWINDIVERT_IPV6HDR)data_copy;
if (ipv6_header->Version != 6 ||
packet_len != RtlUshortByteSwap(ipv6_header->Length) +
sizeof(WINDIVERT_IPV6HDR))
{
windivert_free(packet);
goto windivert_write_invalid_packet;
}
break;
}
// Check bounds:
DEBUG_BOUNDS_CHECK((PVOID)addr, (UINT8 *)addr + addr_len_max,
(PVOID)&addr[i], (PVOID)&addr[i+1]);
// Fix checksums:
if (addr[i].IPChecksum == 0 || addr[i].TCPChecksum == 0 ||
addr[i].UDPChecksum == 0)
{
checksums =
(addr[i].IPChecksum == 0? 0:
WINDIVERT_HELPER_NO_IP_CHECKSUM) |
(addr[i].TCPChecksum == 0? 0:
WINDIVERT_HELPER_NO_TCP_CHECKSUM) |
(addr[i].UDPChecksum == 0? 0:
WINDIVERT_HELPER_NO_UDP_CHECKSUM) |
WINDIVERT_HELPER_NO_ICMP_CHECKSUM |
WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM;
WinDivertHelperCalcChecksums(data_copy, packet_len, NULL,
checksums);
}
// Decrement TTL for impostor packets:
if (addr[i].Impostor &&
!WinDivertHelperDecrementTTL(data_copy, packet_len))
{
status_soft_error = STATUS_HOPLIMIT_EXCEEDED;
windivert_free(data_copy);
goto windivert_write_loop;
}
// Allocate packet:
mdl_copy = IoAllocateMdl(data_copy, packet_len, FALSE, FALSE, NULL);
if (mdl_copy == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
DEBUG_ERROR("failed to allocate MDL for injected packet", status);
goto windivert_write_hard_error;
}
MmBuildMdlForNonPagedPool(mdl_copy);
status = FwpsAllocateNetBufferAndNetBufferList0(nbl_pool_handle, 0, 0,
mdl_copy, 0, packet_len, &buffers);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create NET_BUFFER_LIST for injected packet",
status);
goto windivert_write_hard_error;
}
// Inject packet:
if (layer == WINDIVERT_LAYER_NETWORK_FORWARD)
{
handle = (ipv4? inject_handle_forward: injectv6_handle_forward);
status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0,
(ipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID,
addr[i].Network.IfIdx, buffers, windivert_inject_complete,
data_copy);
}
else if (addr[i].Outbound != 0)
{
handle = (ipv4? inject_handle_out: injectv6_handle_out);
status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0,
UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete,
data_copy);
}
else
{
handle = (ipv4? inject_handle_in: injectv6_handle_in);
status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0,
UNSPECIFIED_COMPARTMENT_ID, addr[i].Network.IfIdx,
addr[i].Network.SubIfIdx, buffers, windivert_inject_complete,
data_copy);
}
status = windivert_inject_packet(packet);
if (!NT_SUCCESS(status))
{
if (status == STATUS_INSUFFICIENT_RESOURCES)
{
goto windivert_write_hard_error;
}
status_soft_error = status;
FwpsFreeNetBufferList0(buffers);
IoFreeMdl(mdl_copy);
windivert_free(data_copy);
}
windivert_write_loop:
// Reset state:
inject_len += packet_len;
data = (PVOID)((UINT8 *)data + packet_len);
@@ -2827,54 +2796,9 @@ windivert_write_loop:
windivert_write_hard_error:
// Request to be completed in windivert_ioctl()
if (buffers != NULL)
{
FwpsFreeNetBufferList0(buffers);
}
if (mdl_copy != NULL)
{
IoFreeMdl(mdl_copy);
}
windivert_free(data_copy);
return status;
}
/*
* WinDivert inject complete routine.
*/
static void NTAPI windivert_inject_complete(VOID *data,
NET_BUFFER_LIST *buffers, BOOLEAN dispatch_level)
{
PMDL mdl;
PNET_BUFFER buffer;
UNREFERENCED_PARAMETER(dispatch_level);
buffer = NET_BUFFER_LIST_FIRST_NB(buffers);
mdl = NET_BUFFER_FIRST_MDL(buffer);
windivert_free(data);
IoFreeMdl(mdl);
FwpsFreeNetBufferList0(buffers);
}
/*
* WinDivert reinject complete routine.
*/
static void NTAPI windivert_reinject_complete(VOID *context,
NET_BUFFER_LIST *buffers, BOOLEAN dispatch_level)
{
PMDL mdl;
PNET_BUFFER buffer;
packet_t packet;
UNREFERENCED_PARAMETER(dispatch_level);
buffer = NET_BUFFER_LIST_FIRST_NB(buffers);
packet = (packet_t)context;
mdl = NET_BUFFER_FIRST_MDL(buffer);
windivert_free_packet(packet);
IoFreeMdl(mdl);
FwpsFreeNetBufferList0(buffers);
}
/*
* WinDivert caller context preprocessing.
@@ -4188,11 +4112,22 @@ static void windivert_flow_established_classify(context_t context,
flow->flow_id = flow_id;
flow->callout_id = callout_id;
flow->layer_id = layer_id;
flow->inserted = FALSE;
flow->deleted = FALSE;
flow->outbound = outbound;
flow->loopback = loopback;
flow->ipv6 = !ipv4;
RtlCopyMemory(&flow->data, flow_data, sizeof(flow->data));
status = FwpsFlowAssociateContext0(flow_id, layer_id, callout_id,
(UINT64)flow);
if (!NT_SUCCESS(status))
{
windivert_free(flow);
WdfObjectDereference(object);
return;
}
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN ||
context->shutdown_recv)
@@ -4202,16 +4137,19 @@ static void windivert_flow_established_classify(context_t context,
WdfObjectDereference(object);
return;
}
status = FwpsFlowAssociateContext0(flow_id, layer_id, callout_id,
(UINT64)flow);
if (!NT_SUCCESS(status))
if (!flow->deleted)
{
InsertTailList(&context->flow_set, &flow->entry);
flow->inserted = TRUE;
}
else
{
// Flow was deleted before insertion; we are responsible for cleanup.
KeReleaseInStackQueuedSpinLock(&lock_handle);
windivert_free(flow);
WdfObjectDereference(object);
return;
}
InsertTailList(&context->flow_set, &flow->entry);
KeReleaseInStackQueuedSpinLock(&lock_handle);
}
@@ -4238,16 +4176,18 @@ static void windivert_flow_delete_notify(UINT16 layer_id, UINT32 callout_id,
{
return;
}
timestamp = KeQueryPerformanceCounter(NULL).QuadPart;
context = flow->context;
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
object = (WDFOBJECT)context->object; // referenced in flow_established.
cleanup = (context->state == WINDIVERT_CONTEXT_STATE_OPEN);
if (cleanup)
if (flow->inserted && !flow->deleted)
{
RemoveEntryList(&flow->entry);
}
flow->deleted = TRUE;
cleanup = flow->inserted;
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN ||
context->shutdown_recv)
{
@@ -4272,12 +4212,12 @@ static void windivert_flow_delete_notify(UINT16 layer_id, UINT32 callout_id,
}
windivert_flow_delete_notify_exit:
if (cleanup)
{
// If context->state != OPEN, then destroy() will free the flow.
windivert_free(flow);
WdfObjectDereference(object);
}
WdfObjectDereference(object);
}
/*
@@ -4904,7 +4844,7 @@ VOID windivert_worker(IN WDFWORKITEM item)
}
else
{
windivert_reinject_packet(work);
windivert_inject_packet(work);
}
KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle);
@@ -5065,21 +5005,22 @@ static BOOL windivert_queue_work(context_t context, PVOID packet,
return TRUE;
}
work->layer = layer;
work->event = event;
work->sniffed = (sniffed? 1: 0);
work->outbound = (outbound? 1: 0);
work->loopback = (loopback? 1: 0);
work->impostor = (impostor? 1: 0);
work->ipv6 = (!ipv4? 1: 0);
work->ip_checksum = (ip_checksum? 1: 0);
work->tcp_checksum = (tcp_checksum? 1: 0);
work->udp_checksum = (udp_checksum? 1: 0);
work->match = match;
work->packet_size = packet_size;
work->priority = priority;
work->timestamp = timestamp;
work->object = object;
work->layer = layer;
work->event = event;
work->sniffed = (sniffed? 1: 0);
work->outbound = (outbound? 1: 0);
work->loopback = (loopback? 1: 0);
work->impostor = (impostor? 1: 0);
work->ipv6 = (!ipv4? 1: 0);
work->ip_checksum = (ip_checksum? 1: 0);
work->tcp_checksum = (tcp_checksum? 1: 0);
work->udp_checksum = (udp_checksum? 1: 0);
work->icmp_checksum = 1;
work->match = match;
work->packet_size = packet_size;
work->priority = priority;
work->timestamp = timestamp;
work->object = object;
if (object != NULL)
{
ObfReferenceObject(object);
@@ -5141,7 +5082,7 @@ static void windivert_queue_packet(context_t context, packet_t packet)
if (context->state != WINDIVERT_CONTEXT_STATE_OPEN)
{
KeReleaseInStackQueuedSpinLock(&lock_handle);
windivert_reinject_packet(packet);
windivert_inject_packet(packet);
return;
}
if (packet->packet_size > context->packet_queue_maxsize)
@@ -5193,9 +5134,9 @@ static void windivert_queue_packet(context_t context, packet_t packet)
}
/*
* Re-inject a packet.
* Inject a packet.
*/
static void windivert_reinject_packet(packet_t packet)
static NTSTATUS windivert_inject_packet(packet_t packet)
{
UINT8 *packet_data;
UINT32 packet_len;
@@ -5211,7 +5152,7 @@ static void windivert_reinject_packet(packet_t packet)
packet->layer != WINDIVERT_LAYER_NETWORK_FORWARD)
{
windivert_free_packet(packet);
return;
return STATUS_INVALID_PARAMETER;
}
network_data = (PWINDIVERT_DATA_NETWORK)WINDIVERT_LAYER_DATA_PTR(packet);
@@ -5219,47 +5160,43 @@ static void windivert_reinject_packet(packet_t packet)
packet_len = packet->packet_len;
// Fix checksums:
if (packet->ip_checksum == 0 || packet->tcp_checksum == 0 ||
packet->udp_checksum == 0)
{
checksums =
(packet->ip_checksum == 0? 0: WINDIVERT_HELPER_NO_IP_CHECKSUM) |
(packet->tcp_checksum == 0? 0: WINDIVERT_HELPER_NO_TCP_CHECKSUM) |
(packet->udp_checksum == 0? 0: WINDIVERT_HELPER_NO_UDP_CHECKSUM) |
WINDIVERT_HELPER_NO_ICMP_CHECKSUM |
WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM;
WinDivertHelperCalcChecksums(packet_data, packet_len, NULL, checksums);
}
checksums =
(packet->ip_checksum == 0? 0: WINDIVERT_HELPER_NO_IP_CHECKSUM) |
(packet->tcp_checksum == 0? 0: WINDIVERT_HELPER_NO_TCP_CHECKSUM) |
(packet->udp_checksum == 0? 0: WINDIVERT_HELPER_NO_UDP_CHECKSUM) |
(packet->icmp_checksum == 0? 0: WINDIVERT_HELPER_NO_ICMP_CHECKSUM |
WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM);
WinDivertHelperCalcChecksums(packet_data, packet_len, NULL, checksums);
// Decrement TTL for impostor packets:
if (packet->impostor != 0 &&
!WinDivertHelperDecrementTTL(packet_data, packet_len))
{
status = STATUS_HOPLIMIT_EXCEEDED;
DEBUG_ERROR("failed to reinject ttl-exceeded impostor packet", status);
DEBUG_ERROR("failed to inject ttl-exceeded impostor packet", status);
windivert_free_packet(packet);
return;
return status;
}
// Reinject packet:
// Inject packet:
mdl = IoAllocateMdl(packet_data, packet_len, FALSE, FALSE, NULL);
if (mdl == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
DEBUG_ERROR("failed to allocate MDL for reinjected packet", status);
DEBUG_ERROR("failed to allocate MDL for injected packet", status);
windivert_free_packet(packet);
return;
return status;
}
MmBuildMdlForNonPagedPool(mdl);
status = FwpsAllocateNetBufferAndNetBufferList0(nbl_pool_handle, 0, 0,
mdl, 0, packet_len, &buffers);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to create NET_BUFFER_LIST for reinjected packet",
DEBUG_ERROR("failed to create NET_BUFFER_LIST for injected packet",
status);
IoFreeMdl(mdl);
windivert_free_packet(packet);
return;
return status;
}
priority = packet->priority;
if (packet->layer == WINDIVERT_LAYER_NETWORK_FORWARD)
@@ -5267,14 +5204,14 @@ static void windivert_reinject_packet(packet_t packet)
handle = (packet->ipv6? injectv6_handle_forward: inject_handle_forward);
status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0,
(packet->ipv6? AF_INET6: AF_INET), UNSPECIFIED_COMPARTMENT_ID,
network_data->IfIdx, buffers, windivert_reinject_complete,
network_data->IfIdx, buffers, windivert_inject_complete,
(HANDLE)packet);
}
else if (packet->outbound)
{
handle = (packet->ipv6? injectv6_handle_out: inject_handle_out);
status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0,
UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_reinject_complete,
UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete,
(HANDLE)packet);
}
else
@@ -5282,17 +5219,18 @@ static void windivert_reinject_packet(packet_t packet)
handle = (packet->ipv6? injectv6_handle_in: inject_handle_in);
status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0,
UNSPECIFIED_COMPARTMENT_ID, network_data->IfIdx,
network_data->SubIfIdx, buffers, windivert_reinject_complete,
network_data->SubIfIdx, buffers, windivert_inject_complete,
(HANDLE)packet);
}
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("failed to reinject (packet=%p)", status, packet);
DEBUG_ERROR("failed to inject (packet=%p)", status, packet);
FwpsFreeNetBufferList0(buffers);
IoFreeMdl(mdl);
windivert_free_packet(packet);
}
return status;
}
/*
@@ -5307,6 +5245,172 @@ static void windivert_free_packet(packet_t packet)
windivert_free(packet);
}
/*
* WinDivert inject complete routine.
*/
static void NTAPI windivert_inject_complete(VOID *context,
NET_BUFFER_LIST *buffers, BOOLEAN dispatch_level)
{
PMDL mdl;
PNET_BUFFER buffer;
packet_t packet;
UNREFERENCED_PARAMETER(dispatch_level);
packet = (packet_t)context;
if (buffers->Status == STATUS_INVALID_BUFFER_SIZE)
{
// STATUS_INVALID_BUFFER_SIZE indicates that the send failed because
// the packet was larger than the MTU. We generate an ICMP
// Fragmentation Needed (for IPv4) or an ICMPV6 Packet Too Big (for
// IPv6) message to allow for PMTU discovery.
windivert_inject_packet_too_big(packet);
}
buffer = NET_BUFFER_LIST_FIRST_NB(buffers);
mdl = NET_BUFFER_FIRST_MDL(buffer);
IoFreeMdl(mdl);
FwpsFreeNetBufferList0(buffers);
windivert_free_packet(packet);
}
/*
* WinDivert inject an ICMP(V6) Packet Too Big message.
*/
static void windivert_inject_packet_too_big(packet_t packet)
{
const UINT mtus[] =
{
568, 768, 1024, 1192, 1280, 1372, 1452, 1500, 4096, UINT16_MAX,
UINT32_MAX
};
PWINDIVERT_IPHDR ip_header, ip_header_2;
PWINDIVERT_IPV6HDR ipv6_header, ipv6_header_2;
PWINDIVERT_ICMPHDR icmp_header;
PWINDIVERT_ICMPV6HDR icmpv6_header;
packet_t icmp;
UINT version, packet_len, copy_len, icmp_len;
UINT icmp_size;
UINT i, min_mtu = /*ipv4 min MTU=*/568, mtu;
UINT32 flowlabel;
UINT8 *data;
if (packet->layer != WINDIVERT_LAYER_NETWORK || !packet->outbound ||
packet->loopback)
{
return;
}
ip_header = (PWINDIVERT_IPHDR)WINDIVERT_PACKET_DATA_PTR(
WINDIVERT_DATA_NETWORK, packet);
version = ip_header->Version;
switch (version)
{
case 4:
packet_len = RtlUshortByteSwap(ip_header->Length);
copy_len = ip_header->HdrLength * sizeof(UINT32) + 8;
copy_len = (packet_len < copy_len? packet_len: copy_len);
icmp_len = sizeof(WINDIVERT_IPHDR) + sizeof(WINDIVERT_ICMPHDR) +
copy_len;
break;
case 6:
ipv6_header = (PWINDIVERT_IPV6HDR)ip_header;
packet_len = RtlUshortByteSwap(ipv6_header->Length) +
sizeof(WINDIVERT_IPV6HDR);
min_mtu = /*ipv6 min MTU=*/1280;
copy_len = min_mtu - sizeof(WINDIVERT_IPV6HDR) -
sizeof(WINDIVERT_ICMPV6HDR);
copy_len = (packet_len < copy_len? packet_len: copy_len);
icmp_len = sizeof(WINDIVERT_IPV6HDR) +
sizeof(WINDIVERT_ICMPV6HDR) + copy_len;
break;
default:
return;
}
if (packet_len <= min_mtu)
{
return;
}
// We do not actually know the MTU value, so we make an educated guess.
for (i = 0; packet_len > mtus[i]; i++)
;
mtu = (i == 0? min_mtu: mtus[i-1]);
mtu = (mtu < min_mtu? min_mtu: mtu);
icmp_size = WINDIVERT_PACKET_SIZE(WINDIVERT_DATA_NETWORK, icmp_len);
icmp = (packet_t)windivert_malloc(icmp_size, FALSE);
if (icmp == NULL)
{
return;
}
icmp->layer = WINDIVERT_LAYER_NETWORK;
icmp->event = WINDIVERT_EVENT_NETWORK_PACKET;
icmp->sniffed = 0; // Unused
icmp->outbound = 0; // Inbound
icmp->loopback = 0; // Unused
icmp->impostor = 0; // Treat as non-impostor
icmp->ipv6 = (version == 6? 1: 0);
icmp->ip_checksum = 0; // IP checksum valid
icmp->tcp_checksum = 0; // Unused
icmp->udp_checksum = 0; // Unused
icmp->icmp_checksum = 0; // ICMP(V6) checksum invalid
icmp->match = 0; // Unused
icmp->packet_size = icmp_size;
icmp->packet_len = icmp_len;
icmp->priority = packet->priority;
icmp->timestamp = 0; // Unused
icmp->object = NULL;
RtlCopyMemory(WINDIVERT_LAYER_DATA_PTR(icmp),
WINDIVERT_LAYER_DATA_PTR(packet), sizeof(WINDIVERT_DATA_NETWORK));
data = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_NETWORK, icmp);
switch (version)
{
case 4:
ip_header_2 = (PWINDIVERT_IPHDR)data;
ip_header_2->Version = 4;
ip_header_2->HdrLength = sizeof(WINDIVERT_IPHDR) / sizeof(UINT32);
ip_header_2->TOS = 0x0;
ip_header_2->Length = RtlUshortByteSwap(icmp_len);
ip_header_2->Id = 0x0;
ip_header_2->TTL = 64;
ip_header_2->Protocol = IPPROTO_ICMP;
ip_header_2->SrcAddr = ip_header->DstAddr;
ip_header_2->DstAddr = ip_header->SrcAddr;
WINDIVERT_IPHDR_SET_FRAGOFF(ip_header_2, 0x0);
WINDIVERT_IPHDR_SET_MF(ip_header_2, 0);
WINDIVERT_IPHDR_SET_DF(ip_header_2, 1);
WINDIVERT_IPHDR_SET_RESERVED(ip_header_2, 0x0);
icmp_header = (PWINDIVERT_ICMPHDR)(ip_header_2 + 1);
icmp_header->Type = /*Destination Unreachable=*/3;
icmp_header->Code = /*Fragmentation required=*/4;
icmp_header->Body = ((UINT32)RtlUshortByteSwap(mtu)) << 16;
data = (UINT8 *)(icmp_header + 1);
RtlCopyMemory(data, ip_header, copy_len);
break;
case 6:
icmp_len -= sizeof(WINDIVERT_IPV6HDR);
ipv6_header_2 = (PWINDIVERT_IPV6HDR)data;
ipv6_header_2->Version = 6;
ipv6_header_2->Length = RtlUshortByteSwap(icmp_len);
ipv6_header_2->NextHdr = IPPROTO_ICMPV6;
ipv6_header_2->HopLimit = 64;
RtlCopyMemory(ipv6_header_2->SrcAddr, ipv6_header->DstAddr,
sizeof(ipv6_header_2->SrcAddr));
RtlCopyMemory(ipv6_header_2->DstAddr, ipv6_header->SrcAddr,
sizeof(ipv6_header_2->DstAddr));
WINDIVERT_IPV6HDR_SET_TRAFFICCLASS(ipv6_header_2, 0x0);
flowlabel = WINDIVERT_IPV6HDR_GET_FLOWLABEL(ipv6_header);
WINDIVERT_IPV6HDR_SET_FLOWLABEL(ipv6_header_2, flowlabel);
icmpv6_header = (PWINDIVERT_ICMPV6HDR)(ipv6_header_2 + 1);
icmpv6_header->Type = /*Packet Too Big=*/2;
icmpv6_header->Code = 0;
icmpv6_header->Body = RtlUlongByteSwap(mtu);
data = (UINT8 *)(icmpv6_header + 1);
RtlCopyMemory(data, ipv6_header, copy_len);
break;
}
windivert_inject_packet(icmp);
}
/*
* Copy data from a NET_BUFFER.
*/
+2 -2
View File
@@ -1,6 +1,6 @@
/*
* windivert.rc
* (C) 2022, all rights reserved,
* (C) 2019, all rights reserved,
*
* This file is part of WinDivert.
*
@@ -48,7 +48,7 @@
#define VER_PRODUCTVERSION 2.2
#define VER_PRODUCTVERSION_STR "2.2"
#define VER_COMPANYNAME_STR "Basil"
#define VER_LEGALCOPYRIGHT_YEARS "2011-2022"
#define VER_LEGALCOPYRIGHT_YEARS "2011-2019"
#define VER_LEGALCOPYRIGHT_STR \
"Copyright \251 " VER_COMPANYNAME_STR " " VER_LEGALCOPYRIGHT_YEARS
#define VER_FILEVERSION VER_PRODUCTVERSION