BOOL WinDivertHelperCalcChecksums(
- __inout PVOID pPacket,
+ __inout VOID *pPacket,
__in UINT packetLen,
__out_opt WINDIVERT_ADDRESS *pAddr,
__in UINT64 flags
@@ -2874,7 +2880,65 @@ find the optimal solution.
-8. Samples
+8. Performance
+
+
+Using WinDivert to redirect network traffic to/from a user application incurs
+performance overheads, such as copying packet data and user/kernel mode
+context switching.
+Under heavy load (≥1Gbps) these overheads can be significant.
+The following techniques can be used to
+reduce overheads (in order of importance):
+
+
+- Selective Filter: Only select the subset of network traffic
+ the user application is interested in.
+ Non-matching traffic will continue to use the default path without
+ incurring additional overheads.
+- Batch Mode: The
+ WinDivertRecvEx() and
+ WinDivertSendEx() functions
+ support batching that allows several packets to be
+ received/sent at once.
+ This can significantly reduce the overheads relating to
+ user/kernel mode context switching.
+- Multi-threading: It is possible to spread packet processing
+ over multiple threads ensuring that the user application does not
+ become a bottleneck.
+ That said, sometimes spawning too many threads can degrade performance.
+
+- Small Buffers: Large buffers generally incur more overhead
+ compared to smaller buffers.
+ In general, the buffer size should reflect the expected usage
+ as closely as possible.
+- Simple Filters:
+ Currently WinDivert does not optimize the filter compilation, so it
+ is up to the user application to ensure the filter is simple/optimized.
+
+- Overlapped I/O: This allows the user application to do
+ additional tasks at the same time as receive/send operations, which may
+ improve performance for some applications.
+ It is also possible for a single thread to initiate several
+ receive/send operations at once.
+ However, using overlapped I/O can be tricky, and it is important
+ that all buffers passed to
+ WinDivertRecvEx() or
+ WinDivertSendEx()
+ (including the
OVERLAPPED structure)
+ are not modified by the user application until the operation
+ completes.
+- Queue length/size/time: If these values are too small then some
+ packets may be dropped under heavy load.
+ These values can be controlled using the
+ WinDivertSetParam() function.
+
+
+The passthru.exe sample program can
+be used to experiment with different batch sizes and thread counts.
+
+
+
+9. Samples
Some samples have been provided to demonstrate the WinDivert API.
@@ -2905,9 +2969,8 @@ The sample programs are:
-j REJECT option.
passthru.exe: A simple program that simply re-injects every
packet it captures.
- This example is multi-threaded, where multiple threads are processing
- packets from a single handle.
- This example is useful for performance testing, and as a starting point
+ This example has a configurable batch-size and thread count,
+ and so is useful for performance testing or as a starting point
for more interesting applications.
streamdump.exe: A simple program that demonstrates how to
handle streams using WinDivert.
@@ -2924,9 +2987,11 @@ The sample programs are:
The socketdump sample demonstrates the
WINDIVERT_LAYER_SOCKET layer.
windivertctl.exe allows the user to query which processes
- are using WinDivert via the list or monitor
+ are using WinDivert via the list or watch
commands, or to terminate all such processes using the
- killall command.
+ kill command.
+ The windivertctl.exe can also forcibly remove the
+ WinDivert driver using the uninstall command.
The windivertctl sample demonstrates the
WINDIVERT_LAYER_REFLECT layer.
@@ -2956,7 +3021,7 @@ capture-modify-reinject loop:
// Main capture-modify-inject loop:
while (TRUE)
{
- if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packetLen))
+ if (!WinDivertRecv(handle, packet, sizeof(packet), &packetLen, &addr))
{
// Handle recv error
continue;
@@ -2965,7 +3030,7 @@ capture-modify-reinject loop:
// Modify packet.
WinDivertHelperCalcChecksums(packet, packetLen, &addr, 0);
- if (!WinDivertSend(handle, packet, packetLen, &addr, NULL))
+ if (!WinDivertSend(handle, packet, packetLen, NULL, &addr))
{
// Handle send error
continue;
@@ -2981,7 +3046,7 @@ See the netdump.exe sample program for an example of this usage.
-9. Known Issues
+10. Known Issues
WinDivert has some known limitations listed below:
@@ -3038,8 +3103,8 @@ WinDivert has some known limitations listed below:
WinDivertRecv(),
it is possible that the process responsible for the event has already
terminated.
- Furthermore, it is also possible that the processId has been
- reassigned to an unrelated process.
+ Furthermore, it is theoretically possible that the
+ processId has been reassigned to an unrelated process.
This problem can be partly mitigated by comparing the timestamp
(addr.Timestamp) with the creation time of the process.
If the process is newer, then the ID has been reassigned.
@@ -3048,13 +3113,13 @@ WinDivert has some known limitations listed below:
In this special case, the addr.Reflect.processId is
guaranteed to be valid until the corresponding
WINDIVERT_EVENT_REFLECT_CLOSE event is
- received by the user application or dropped
+ received by the user application or is dropped
(filter mismatch or timeout).
-10. License
+11. License
WinDivert is dual-licensed under your choice of either the
GNU Lesser General
diff --git a/examples/flowtrack/flowtrack.c b/examples/flowtrack/flowtrack.c
index 624bec2..a6e37fb 100644
--- a/examples/flowtrack/flowtrack.c
+++ b/examples/flowtrack/flowtrack.c
@@ -263,7 +263,7 @@ int __cdecl main(int argc, char **argv)
// Main loop:
while (TRUE)
{
- if (!WinDivertRecv(handle, NULL, 0, &addr, &packet_len))
+ if (!WinDivertRecv(handle, NULL, 0, NULL, &addr))
{
fprintf(stderr, "failed to read packet (%d)\n", GetLastError());
continue;
diff --git a/examples/netdump/netdump.c b/examples/netdump/netdump.c
index 8811c03..9bd76cd 100644
--- a/examples/netdump/netdump.c
+++ b/examples/netdump/netdump.c
@@ -148,7 +148,7 @@ int __cdecl main(int argc, char **argv)
while (TRUE)
{
// Read a matching packet.
- if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len))
+ if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
{
fprintf(stderr, "warning: failed to read packet (%d)\n",
GetLastError());
@@ -156,9 +156,9 @@ int __cdecl main(int argc, char **argv)
}
// Print info about the matching packet.
- WinDivertHelperParsePacket(packet, packet_len, NULL, &ip_header,
- &ipv6_header, &icmp_header, &icmpv6_header, &tcp_header,
- &udp_header, NULL, NULL, NULL, NULL);
+ WinDivertHelperParsePacket(packet, packet_len, &ip_header, &ipv6_header,
+ NULL, &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL,
+ NULL, NULL, NULL);
if (ip_header == NULL && ipv6_header == NULL)
{
fprintf(stderr, "warning: junk packet\n");
diff --git a/examples/netfilter/netfilter.c b/examples/netfilter/netfilter.c
index cfeb061..fccb265 100644
--- a/examples/netfilter/netfilter.c
+++ b/examples/netfilter/netfilter.c
@@ -193,17 +193,17 @@ int __cdecl main(int argc, char **argv)
while (TRUE)
{
// Read a matching packet.
- if (!WinDivertRecv(handle, packet, sizeof(packet), &recv_addr,
- &packet_len))
+ if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len,
+ &recv_addr))
{
fprintf(stderr, "warning: failed to read packet\n");
continue;
}
// Print info about the matching packet.
- WinDivertHelperParsePacket(packet, packet_len, NULL, &ip_header,
- &ipv6_header, &icmp_header, &icmpv6_header, &tcp_header,
- &udp_header, NULL, &payload_len, NULL, NULL);
+ WinDivertHelperParsePacket(packet, packet_len, &ip_header, &ipv6_header,
+ NULL, &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL,
+ &payload_len, NULL, NULL);
if (ip_header == NULL && ipv6_header == NULL)
{
continue;
@@ -292,7 +292,7 @@ int __cdecl main(int argc, char **argv)
WinDivertHelperCalcChecksums((PVOID)reset, sizeof(TCPPACKET),
&send_addr, 0);
if (!WinDivertSend(handle, (PVOID)reset, sizeof(TCPPACKET),
- &send_addr, NULL))
+ NULL, &send_addr))
{
fprintf(stderr, "warning: failed to send TCP reset (%d)\n",
GetLastError());
@@ -319,7 +319,7 @@ int __cdecl main(int argc, char **argv)
WinDivertHelperCalcChecksums((PVOID)resetv6,
sizeof(TCPV6PACKET), &send_addr, 0);
if (!WinDivertSend(handle, (PVOID)resetv6, sizeof(TCPV6PACKET),
- &send_addr, NULL))
+ NULL, &send_addr))
{
fprintf(stderr, "warning: failed to send TCP (IPV6) "
"reset (%d)\n", GetLastError());
@@ -344,8 +344,8 @@ int __cdecl main(int argc, char **argv)
send_addr.Outbound = !recv_addr.Outbound;
WinDivertHelperCalcChecksums((PVOID)dnr, icmp_length,
&send_addr, 0);
- if (!WinDivertSend(handle, (PVOID)dnr, icmp_length, &send_addr,
- NULL))
+ if (!WinDivertSend(handle, (PVOID)dnr, icmp_length, NULL,
+ &send_addr))
{
fprintf(stderr, "warning: failed to send ICMP message "
"(%d)\n", GetLastError());
@@ -368,7 +368,7 @@ int __cdecl main(int argc, char **argv)
WinDivertHelperCalcChecksums((PVOID)dnrv6, icmpv6_length,
&send_addr, 0);
if (!WinDivertSend(handle, (PVOID)dnrv6, icmpv6_length,
- &send_addr, NULL))
+ NULL, &send_addr))
{
fprintf(stderr, "warning: failed to send ICMPv6 message "
"(%d)\n", GetLastError());
diff --git a/examples/socketdump/socketdump.c b/examples/socketdump/socketdump.c
index a33d539..52c717a 100644
--- a/examples/socketdump/socketdump.c
+++ b/examples/socketdump/socketdump.c
@@ -61,7 +61,6 @@ int __cdecl main(int argc, char **argv)
char local_str[INET6_ADDRSTRLEN+1], remote_str[INET6_ADDRSTRLEN+1];
char *filename;
DWORD path_len;
- UINT packet_len;
WINDIVERT_ADDRESS addr;
switch (argc)
@@ -97,7 +96,7 @@ int __cdecl main(int argc, char **argv)
console = GetStdHandle(STD_OUTPUT_HANDLE);
while (TRUE)
{
- if (!WinDivertRecv(handle, NULL, 0, &addr, &packet_len))
+ if (!WinDivertRecv(handle, NULL, 0, NULL, &addr))
{
fprintf(stderr, "failed to read packet (%d)\n", GetLastError());
continue;
diff --git a/examples/streamdump/streamdump.c b/examples/streamdump/streamdump.c
index fe96de5..7d84019 100644
--- a/examples/streamdump/streamdump.c
+++ b/examples/streamdump/streamdump.c
@@ -187,13 +187,13 @@ int __cdecl main(int argc, char **argv)
// Main loop:
while (TRUE)
{
- if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len))
+ if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
{
warning("failed to read packet (%d)", GetLastError());
continue;
}
- WinDivertHelperParsePacket(packet, packet_len, NULL, &ip_header, NULL,
+ WinDivertHelperParsePacket(packet, packet_len, &ip_header, NULL, NULL,
NULL, NULL, &tcp_header, NULL, NULL, NULL, NULL, NULL);
if (ip_header == NULL || tcp_header == NULL)
{
@@ -237,7 +237,7 @@ int __cdecl main(int argc, char **argv)
}
WinDivertHelperCalcChecksums(packet, packet_len, &addr, 0);
- if (!WinDivertSend(handle, packet, packet_len, &addr, NULL))
+ if (!WinDivertSend(handle, packet, packet_len, NULL, &addr))
{
warning("failed to send packet (%d)", GetLastError());
continue;
diff --git a/examples/webfilter/webfilter.c b/examples/webfilter/webfilter.c
index 2b9578e..bd320a8 100644
--- a/examples/webfilter/webfilter.c
+++ b/examples/webfilter/webfilter.c
@@ -197,21 +197,21 @@ int __cdecl main(int argc, char **argv)
// Main loop:
while (TRUE)
{
- if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len))
+ if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
{
fprintf(stderr, "warning: failed to read packet (%d)\n",
GetLastError());
continue;
}
- WinDivertHelperParsePacket(packet, packet_len, NULL, &ip_header,
+ WinDivertHelperParsePacket(packet, packet_len, &ip_header, NULL,
NULL, NULL, NULL, &tcp_header, NULL, &payload, &payload_len,
NULL, NULL);
if (ip_header == NULL || tcp_header == NULL || payload == NULL ||
!BlackListPayloadMatch(blacklist, payload, (UINT16)payload_len))
{
// Packet does not match the blacklist; simply reinject it.
- if (!WinDivertSend(handle, packet, packet_len, &addr, NULL))
+ if (!WinDivertSend(handle, packet, packet_len, NULL, &addr))
{
fprintf(stderr, "warning: failed to reinject packet (%d)\n",
GetLastError());
@@ -231,7 +231,7 @@ int __cdecl main(int argc, char **argv)
reset->tcp.SeqNum = tcp_header->SeqNum;
reset->tcp.AckNum = tcp_header->AckNum;
WinDivertHelperCalcChecksums((PVOID)reset, sizeof(PACKET), &addr, 0);
- if (!WinDivertSend(handle, (PVOID)reset, sizeof(PACKET), &addr, NULL))
+ if (!WinDivertSend(handle, (PVOID)reset, sizeof(PACKET), NULL, &addr))
{
fprintf(stderr, "warning: failed to send reset packet (%d)\n",
GetLastError());
@@ -246,8 +246,8 @@ int __cdecl main(int argc, char **argv)
htonl(ntohl(tcp_header->SeqNum) + payload_len);
addr.Outbound = !addr.Outbound; // Reverse direction.
WinDivertHelperCalcChecksums((PVOID)blockpage, blockpage_len, &addr, 0);
- if (!WinDivertSend(handle, (PVOID)blockpage, blockpage_len, &addr,
- NULL))
+ if (!WinDivertSend(handle, (PVOID)blockpage, blockpage_len, NULL,
+ &addr))
{
fprintf(stderr, "warning: failed to send block page packet (%d)\n",
GetLastError());
@@ -264,7 +264,7 @@ int __cdecl main(int argc, char **argv)
finish->tcp.AckNum =
htonl(ntohl(tcp_header->SeqNum) + payload_len);
WinDivertHelperCalcChecksums((PVOID)finish, sizeof(PACKET), &addr, 0);
- if (!WinDivertSend(handle, (PVOID)finish, sizeof(PACKET), &addr, NULL))
+ if (!WinDivertSend(handle, (PVOID)finish, sizeof(PACKET), NULL, &addr))
{
fprintf(stderr, "warning: failed to send finish packet (%d)\n",
GetLastError());
diff --git a/examples/windivertctl/windivertctl.c b/examples/windivertctl/windivertctl.c
index c88a5c9..cb884a1 100644
--- a/examples/windivertctl/windivertctl.c
+++ b/examples/windivertctl/windivertctl.c
@@ -66,7 +66,7 @@ typedef enum
*/
int __cdecl main(int argc, char **argv)
{
- HANDLE handle, process, console;
+ HANDLE handle, process, console, mutex;
INT16 priority = -333; // Arbitrary.
UINT packet_len;
static UINT8 packet[MAX_PACKET];
@@ -128,7 +128,7 @@ usage:
// Open WinDivert REFLECT handle:
handle = WinDivertOpen(filter, WINDIVERT_LAYER_REFLECT, priority,
WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_RECV_ONLY |
- (mode == WATCH || mode == UNINSTALL? 0: WINDIVERT_FLAG_NO_INSTALL));
+ (mode == WATCH? 0: WINDIVERT_FLAG_NO_INSTALL));
if (handle == INVALID_HANDLE_VALUE)
{
if (mode != WATCH && GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
@@ -165,36 +165,11 @@ usage:
return EXIT_FAILURE;
}
- // Stop the WinDivert service.
- if (mode == UNINSTALL)
- {
- manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
- if (manager == NULL)
- {
- fprintf(stderr, "error: failed to open service manager (%d)\n",
- GetLastError());
- return EXIT_FAILURE;
- }
- service = OpenService(manager, "WinDivert", SERVICE_ALL_ACCESS);
- if (service == NULL)
- {
- fprintf(stderr, "error: failed to open WinDivert service (%d)\n",
- GetLastError());
- return EXIT_FAILURE;
- }
- if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
- {
- fprintf(stderr, "error: failed to stop WinDivert service (%d)\n",
- GetLastError());
- return EXIT_FAILURE;
- }
- }
-
// Main loop:
console = GetStdHandle(STD_OUTPUT_HANDLE);
while (TRUE)
{
- if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len))
+ if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len, &addr))
{
if (mode != WATCH && GetLastError() == ERROR_NO_DATA)
{
@@ -344,19 +319,6 @@ usage:
putchar('\n');
}
- if (mode == UNINSTALL)
- {
- SetConsoleTextAttribute(console, FOREGROUND_RED);
- fputs("UNINSTALL", stdout);
- SetConsoleTextAttribute(console,
- FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
- puts(" WinDivert");
-
- CloseServiceHandle(service);
- CloseServiceHandle(manager);
- }
-
-
if (!WinDivertClose(handle))
{
fprintf(stderr, "error: failed to close WinDivert handle (%d)\n",
@@ -364,6 +326,70 @@ usage:
return EXIT_FAILURE;
}
+ if (mode == UNINSTALL)
+ {
+ // Stop & delete the WinDivert service:
+ mutex = CreateMutex(NULL, FALSE, "WinDivertDriverInstallMutex");
+ if (mutex == NULL)
+ {
+ fprintf(stderr, "error: failed to create WinDivert driver "
+ "install mutex (%d)\n", GetLastError());
+ return EXIT_FAILURE;
+ }
+ switch (WaitForSingleObject(mutex, INFINITE))
+ {
+ case WAIT_OBJECT_0: case WAIT_ABANDONED:
+ break;
+ default:
+ fprintf(stderr, "error: failed to acquire WinDivert driver "
+ "install mutex (%d)\n", GetLastError());
+ return EXIT_FAILURE;
+ }
+ manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (manager == NULL)
+ {
+ fprintf(stderr, "error: failed to open service manager (%d)\n",
+ GetLastError());
+ return EXIT_FAILURE;
+ }
+ service = OpenService(manager, "WinDivert", SERVICE_ALL_ACCESS);
+ if (service == NULL)
+ {
+ fprintf(stderr, "error: failed to open WinDivert service (%d)\n",
+ GetLastError());
+ return EXIT_FAILURE;
+ }
+ if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
+ {
+ fprintf(stderr, "error: failed to stop WinDivert service (%d)\n",
+ GetLastError());
+ return EXIT_FAILURE;
+ }
+ if (status.dwCurrentState != SERVICE_STOPPED)
+ {
+ fprintf(stderr, "error: failed to stop WinDivert service");
+ return EXIT_FAILURE;
+ }
+ if (!DeleteService(service) &&
+ GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
+ {
+ fprintf(stderr, "error: failed to delete WinDivert service (%d)\n",
+ GetLastError());
+ return EXIT_FAILURE;
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(manager);
+
+ SetConsoleTextAttribute(console, FOREGROUND_GREEN);
+ fputs("UNINSTALL", stdout);
+ SetConsoleTextAttribute(console,
+ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+ puts(" WinDivert");
+
+ ReleaseMutex(mutex);
+ CloseHandle(mutex);
+ }
+
return 0;
}
diff --git a/include/windivert.h b/include/windivert.h
index e930c54..8e71ccb 100644
--- a/include/windivert.h
+++ b/include/windivert.h
@@ -227,8 +227,8 @@ extern WINDIVERTEXPORT BOOL WinDivertRecv(
__in HANDLE handle,
__out_opt VOID *pPacket,
__in UINT packetLen,
- __out_opt WINDIVERT_ADDRESS *pAddr,
- __out_opt UINT *pReadLen);
+ __out_opt UINT *pRecvLen,
+ __out_opt WINDIVERT_ADDRESS *pAddr);
/*
* Receive (read) a packet from a WinDivert handle.
@@ -237,7 +237,7 @@ extern WINDIVERTEXPORT BOOL WinDivertRecvEx(
__in HANDLE handle,
__out_opt VOID *pPacket,
__in UINT packetLen,
- __out_opt UINT *pReadLen,
+ __out_opt UINT *pRecvLen,
__in UINT64 flags,
__out WINDIVERT_ADDRESS *pAddr,
__inout_opt UINT *pAddrLen,
@@ -250,8 +250,8 @@ extern WINDIVERTEXPORT BOOL WinDivertSend(
__in HANDLE handle,
__in const VOID *pPacket,
__in UINT packetLen,
- __in const WINDIVERT_ADDRESS *pAddr,
- __out_opt UINT *pWriteLen);
+ __out_opt UINT *pSendLen,
+ __in const WINDIVERT_ADDRESS *pAddr);
/*
* Send (write/inject) a packet to a WinDivert handle.
@@ -260,7 +260,7 @@ extern WINDIVERTEXPORT BOOL WinDivertSendEx(
__in HANDLE handle,
__in const VOID *pPacket,
__in UINT packetLen,
- __out_opt UINT *pWriteLen,
+ __out_opt UINT *pSendLen,
__in UINT64 flags,
__in const WINDIVERT_ADDRESS *pAddr,
__in UINT addrLen,
@@ -480,9 +480,9 @@ extern WINDIVERTEXPORT UINT64 WinDivertHelperHashPacket(
extern WINDIVERTEXPORT BOOL WinDivertHelperParsePacket(
__in const VOID *pPacket,
__in UINT packetLen,
- __out_opt UINT8 *pProtocol,
__out_opt PWINDIVERT_IPHDR *ppIpHdr,
__out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr,
+ __out_opt UINT8 *pProtocol,
__out_opt PWINDIVERT_ICMPHDR *ppIcmpHdr,
__out_opt PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
__out_opt PWINDIVERT_TCPHDR *ppTcpHdr,
|