From 62505689740c419bb91e83b1d08fe3be58e46d09 Mon Sep 17 00:00:00 2001 From: basil00 Date: Sun, 30 Sep 2018 09:07:16 +0800 Subject: [PATCH] Add a new FLOW layer into WinDivert. Adds a new WINDIVERT_LAYER_FLOW layer that can: - Track when flows are established - Track when existing flows are deleted --- VERSION | 2 +- dll/windivert.c | 17 +- dll/windivert_helper.c | 283 ++++- doc/windivert.html | 23 +- examples/flowtrack/flowtrack.c | 350 ++++++ examples/netdump/netdump.c | 5 +- examples/netfilter/netfilter.c | 8 +- examples/streamdump/streamdump.c | 66 +- examples/webfilter/webfilter.c | 2 +- include/windivert.h | 86 +- include/windivert_device.h | 43 +- mingw-build.sh | 4 + release-build.sh | 4 + sys/windivert.c | 1742 +++++++++++++++++++++--------- 14 files changed, 1964 insertions(+), 671 deletions(-) create mode 100644 examples/flowtrack/flowtrack.c diff --git a/VERSION b/VERSION index 1c99cf0..227cea2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.4 +2.0.0 diff --git a/dll/windivert.c b/dll/windivert.c index 3e773c7..e593e31 100644 --- a/dll/windivert.c +++ b/dll/windivert.c @@ -411,11 +411,26 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, UINT32 priority32; // Parameter checking. - if (!WINDIVERT_FLAGS_VALID(flags) || layer > WINDIVERT_LAYER_MAX) + if (layer == 0) + { + layer = WINDIVERT_LAYER_NETWORK; + } + switch (layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + case WINDIVERT_LAYER_FLOW: + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + if (!WINDIVERT_FLAGS_VALID(flags)) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } + priority32 = WINDIVERT_PRIORITY(priority); if (priority32 < WINDIVERT_PRIORITY_MIN || priority32 > WINDIVERT_PRIORITY_MAX) diff --git a/dll/windivert_helper.c b/dll/windivert_helper.c index 917c803..2b0c40a 100644 --- a/dll/windivert_helper.c +++ b/dll/windivert_helper.c @@ -116,6 +116,12 @@ typedef enum TOKEN_SUB_IF_IDX, TOKEN_LOOPBACK, TOKEN_IMPOSTOR, + TOKEN_PROCESS_ID, + TOKEN_LOCAL_ADDR, + TOKEN_REMOTE_ADDR, + TOKEN_LOCAL_PORT, + TOKEN_REMOTE_PORT, + TOKEN_PROTOCOL, TOKEN_OPEN, TOKEN_CLOSE, TOKEN_EQ, @@ -504,7 +510,7 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, tcp_header->Checksum = WinDivertHelperCalcChecksum( pseudo_header, pseudo_header_len, tcp_header, checksum_len); } - else if (pAddr->Direction == WINDIVERT_DIRECTION_OUTBOUND) + else if (pAddr->Outbound) { // Pseudo TCP checksum tcp_header->Checksum = 0; @@ -533,7 +539,7 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, udp_header->Checksum = 0xFFFF; } } - else if (pAddr->Direction == WINDIVERT_DIRECTION_OUTBOUND) + else if (pAddr->Outbound) { // Pseudo UDP checksum udp_header->Checksum = 0; @@ -748,7 +754,7 @@ extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) l = k + 1; k = (k >= 8? k - 8: k); l = (l >= 8? l - 8: l); - addr_ptr[i] = + addr_ptr[3 - i] = (UINT32)laddr[2 * i + 1] | (UINT32)laddr[2 * i] << 16 | (UINT32)raddr[l] | @@ -785,6 +791,91 @@ static PTOKEN_NAME WinDivertTokenLookup(PTOKEN_NAME token_names, return NULL; } +/* + * Validate token for layer. + */ +static BOOL WinDivertCheckTokenKindForLayer(WINDIVERT_LAYER layer, KIND kind) +{ + switch (layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + switch (kind) + { + case TOKEN_INBOUND: + case TOKEN_OUTBOUND: + return (layer != WINDIVERT_LAYER_NETWORK_FORWARD); + case TOKEN_PROCESS_ID: + case TOKEN_LOCAL_ADDR: + case TOKEN_REMOTE_ADDR: + case TOKEN_LOCAL_PORT: + case TOKEN_REMOTE_PORT: + return FALSE; + default: + return TRUE; + } + case WINDIVERT_LAYER_FLOW: + switch (kind) + { + case TOKEN_ICMP_BODY: + case TOKEN_ICMP_CHECKSUM: + case TOKEN_ICMP_CODE: + case TOKEN_ICMP_TYPE: + case TOKEN_ICMPV6_BODY: + case TOKEN_ICMPV6_CHECKSUM: + case TOKEN_ICMPV6_CODE: + case TOKEN_ICMPV6_TYPE: + case TOKEN_IP_CHECKSUM: + case TOKEN_IP_DF: + case TOKEN_IP_DST_ADDR: + case TOKEN_IP_FRAG_OFF: + case TOKEN_IP_HDR_LENGTH: + case TOKEN_IP_ID: + case TOKEN_IP_LENGTH: + case TOKEN_IP_MF: + case TOKEN_IP_PROTOCOL: + case TOKEN_IP_SRC_ADDR: + case TOKEN_IP_TOS: + case TOKEN_IP_TTL: + case TOKEN_IPV6_DST_ADDR: + case TOKEN_IPV6_FLOW_LABEL: + case TOKEN_IPV6_HOP_LIMIT: + case TOKEN_IPV6_LENGTH: + case TOKEN_IPV6_NEXT_HDR: + case TOKEN_IPV6_SRC_ADDR: + case TOKEN_IPV6_TRAFFIC_CLASS: + case TOKEN_TCP_ACK: + case TOKEN_TCP_ACK_NUM: + case TOKEN_TCP_CHECKSUM: + case TOKEN_TCP_DST_PORT: + case TOKEN_TCP_FIN: + case TOKEN_TCP_HDR_LENGTH: + case TOKEN_TCP_PAYLOAD_LENGTH: + case TOKEN_TCP_PSH: + case TOKEN_TCP_RST: + case TOKEN_TCP_SEQ_NUM: + case TOKEN_TCP_SRC_PORT: + case TOKEN_TCP_SYN: + case TOKEN_TCP_URG: + case TOKEN_TCP_URG_PTR: + case TOKEN_TCP_WINDOW: + case TOKEN_UDP_CHECKSUM: + case TOKEN_UDP_DST_PORT: + case TOKEN_UDP_LENGTH: + case TOKEN_UDP_PAYLOAD_LENGTH: + case TOKEN_UDP_SRC_PORT: + case TOKEN_IF_IDX: + case TOKEN_SUB_IF_IDX: + case TOKEN_IMPOSTOR: + return FALSE; + default: + return TRUE; + } + default: + return FALSE; + } +} + /* * Tokenize the given filter string. */ @@ -829,10 +920,16 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, {"ipv6.NextHdr", TOKEN_IPV6_NEXT_HDR}, {"ipv6.SrcAddr", TOKEN_IPV6_SRC_ADDR}, {"ipv6.TrafficClass", TOKEN_IPV6_TRAFFIC_CLASS}, + {"localAddr", TOKEN_LOCAL_ADDR}, + {"localPort", TOKEN_LOCAL_PORT}, {"loopback", TOKEN_LOOPBACK}, {"not", TOKEN_NOT}, {"or", TOKEN_OR}, {"outbound", TOKEN_OUTBOUND}, + {"processId", TOKEN_PROCESS_ID}, + {"protocol", TOKEN_PROTOCOL}, + {"remoteAddr", TOKEN_REMOTE_ADDR}, + {"remotePort", TOKEN_REMOTE_PORT}, {"subIfIdx", TOKEN_SUB_IF_IDX}, {"tcp", TOKEN_TCP}, {"tcp.Ack", TOKEN_TCP_ACK}, @@ -986,18 +1083,9 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, sizeof(token_names) / sizeof(TOKEN_NAME), token); if (result != NULL) { - switch (layer) + if (!WinDivertCheckTokenKindForLayer(layer, result->kind)) { - case WINDIVERT_LAYER_NETWORK_FORWARD: - if (result->kind == TOKEN_INBOUND || - result->kind == TOKEN_OUTBOUND) - { - return MAKE_ERROR( - WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER, i-j); - } - break; - default: - break; + return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER, i-j); } tokens[tp++].kind = result->kind; continue; @@ -1023,6 +1111,7 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, // Check for IPv4 address: if (WinDivertHelperParseIPv4Address(token, tokens[tp].val)) { + tokens[tp].val[1] = 0x0000FFFF; tokens[tp].kind = TOKEN_NUMBER; tp++; continue; @@ -1032,16 +1121,6 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, SetLastError(0); if (WinDivertHelperParseIPv6Address(token, tokens[tp].val)) { - // Work-around the different word orderings between the - // DLL vs SYS. - UINT32 tmp; - tmp = tokens[tp].val[0]; - tokens[tp].val[0] = tokens[tp].val[3]; - tokens[tp].val[3] = tmp; - tmp = tokens[tp].val[1]; - tokens[tp].val[1] = tokens[tp].val[2]; - tokens[tp].val[2] = tmp; - tokens[tp].kind = TOKEN_NUMBER; tp++; continue; @@ -1140,7 +1219,13 @@ static PEXPR WinDivertMakeVar(PPOOL pool, KIND kind) {{{0}}, TOKEN_IF_IDX}, {{{0}}, TOKEN_SUB_IF_IDX}, {{{0}}, TOKEN_LOOPBACK}, - {{{0}}, TOKEN_IMPOSTOR} + {{{0}}, TOKEN_IMPOSTOR}, + {{{0}}, TOKEN_PROCESS_ID}, + {{{0}}, TOKEN_LOCAL_ADDR}, + {{{0}}, TOKEN_REMOTE_ADDR}, + {{{0}}, TOKEN_LOCAL_PORT}, + {{{0}}, TOKEN_REMOTE_PORT}, + {{{0}}, TOKEN_PROTOCOL}, }; // Binary search: @@ -1268,6 +1353,12 @@ static PEXPR WinDivertParseTest(PPOOL pool, TOKEN *toks, UINT *i) case TOKEN_ICMPV6: case TOKEN_TCP: case TOKEN_UDP: + case TOKEN_PROCESS_ID: + case TOKEN_LOCAL_ADDR: + case TOKEN_REMOTE_ADDR: + case TOKEN_LOCAL_PORT: + case TOKEN_REMOTE_PORT: + case TOKEN_PROTOCOL: case TOKEN_IP_HDR_LENGTH: case TOKEN_IP_TOS: case TOKEN_IP_LENGTH: @@ -1524,6 +1615,7 @@ static BOOL WinDivertEvalTest(PEXPR test, BOOL *res) case TOKEN_ICMP_CODE: case TOKEN_ICMPV6_TYPE: case TOKEN_ICMPV6_CODE: + case TOKEN_PROCESS_ID: lb = 0; ub = 0xFF; break; case TOKEN_IP_FRAG_OFF: @@ -1547,13 +1639,19 @@ static BOOL WinDivertEvalTest(PEXPR test, BOOL *res) case TOKEN_UDP_LENGTH: case TOKEN_UDP_CHECKSUM: case TOKEN_UDP_PAYLOAD_LENGTH: + case TOKEN_LOCAL_PORT: + case TOKEN_REMOTE_PORT: lb = 0; ub = 0xFFFF; break; case TOKEN_IPV6_FLOW_LABEL: lb = 0; ub = 0x000FFFFF; break; + case TOKEN_IP_SRC_ADDR: + case TOKEN_IP_DST_ADDR: case TOKEN_IPV6_SRC_ADDR: case TOKEN_IPV6_DST_ADDR: + case TOKEN_LOCAL_ADDR: + case TOKEN_REMOTE_ADDR: return FALSE; default: lb = 0; ub = 0xFFFFFFFF; @@ -1735,6 +1833,21 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset, case TOKEN_IMPOSTOR: object->field = WINDIVERT_FILTER_FIELD_IMPOSTOR; break; + case TOKEN_PROCESS_ID: + object->field = WINDIVERT_FILTER_FIELD_PROCESSID; + break; + case TOKEN_LOCAL_ADDR: + object->field = WINDIVERT_FILTER_FIELD_LOCALADDR; + break; + case TOKEN_REMOTE_ADDR: + object->field = WINDIVERT_FILTER_FIELD_REMOTEADDR; + break; + case TOKEN_LOCAL_PORT: + object->field = WINDIVERT_FILTER_FIELD_LOCALPORT; + break; + case TOKEN_REMOTE_PORT: + object->field = WINDIVERT_FILTER_FIELD_REMOTEPORT; + break; case TOKEN_IP: object->field = WINDIVERT_FILTER_FIELD_IP; break; @@ -2118,18 +2231,17 @@ static int WinDivertBigNumCompare(const UINT32 *a, const UINT32 *b) /* * Evaluate the given filter with the given packet as input. */ -extern BOOL WinDivertHelperEvalFilter(const char *filter, - WINDIVERT_LAYER layer, PVOID packet, UINT packet_len, - PWINDIVERT_ADDRESS addr) +extern BOOL WinDivertHelperEvalFilter(const char *filter, PVOID packet, + UINT packet_len, PWINDIVERT_ADDRESS addr) { UINT16 pc; ERROR err; - PWINDIVERT_IPHDR iphdr; - PWINDIVERT_IPV6HDR ipv6hdr; - PWINDIVERT_ICMPHDR icmphdr; - PWINDIVERT_ICMPV6HDR icmpv6hdr; - PWINDIVERT_TCPHDR tcphdr; - PWINDIVERT_UDPHDR udphdr; + PWINDIVERT_IPHDR iphdr = NULL; + PWINDIVERT_IPV6HDR ipv6hdr = NULL; + PWINDIVERT_ICMPHDR icmphdr = NULL; + PWINDIVERT_ICMPV6HDR icmpv6hdr = NULL; + PWINDIVERT_TCPHDR tcphdr = NULL; + PWINDIVERT_UDPHDR udphdr = NULL; UINT payload_len; UINT32 val[4]; BOOL pass; @@ -2137,22 +2249,48 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, struct windivert_ioctl_filter_s object[WINDIVERT_FILTER_MAXLEN]; UINT obj_len; - if (filter == NULL || packet == NULL || addr == NULL) + if (filter == NULL || addr == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + if (packet == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + WinDivertHelperParsePacket(packet, packet_len, &iphdr, &ipv6hdr, + &icmphdr, &icmpv6hdr, &tcphdr, &udphdr, NULL, &payload_len); + if ((addr->IPv6 && ipv6hdr == NULL) || + (!addr->IPv6 && iphdr == NULL)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_LAYER_FLOW: + if (packet != NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } - err = WinDivertCompileFilter(filter, layer, object, &obj_len); + err = WinDivertCompileFilter(filter, addr->Layer, object, &obj_len); if (IS_ERROR(err)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - WinDivertHelperParsePacket(packet, packet_len, &iphdr, &ipv6hdr, &icmphdr, - &icmpv6hdr, &tcphdr, &udphdr, NULL, &payload_len); - pc = 0; while (TRUE) { @@ -2232,6 +2370,23 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: pass = (udphdr != NULL); break; + case WINDIVERT_FILTER_FIELD_INBOUND: + case WINDIVERT_FILTER_FIELD_OUTBOUND: + pass = (addr->Layer != WINDIVERT_LAYER_NETWORK_FORWARD); + break; + case WINDIVERT_FILTER_FIELD_IFIDX: + case WINDIVERT_FILTER_FIELD_SUBIFIDX: + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + break; + case WINDIVERT_FILTER_FIELD_PROCESSID: + case WINDIVERT_FILTER_FIELD_LOCALADDR: + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + case WINDIVERT_FILTER_FIELD_LOCALPORT: + case WINDIVERT_FILTER_FIELD_REMOTEPORT: + case WINDIVERT_FILTER_FIELD_PROTOCOL: + pass = (addr->Layer == WINDIVERT_LAYER_FLOW); + break; default: pass = TRUE; break; @@ -2248,16 +2403,16 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = 0; break; case WINDIVERT_FILTER_FIELD_INBOUND: - val[0] = (addr->Direction == WINDIVERT_DIRECTION_INBOUND); + val[0] = !addr->Outbound; break; case WINDIVERT_FILTER_FIELD_OUTBOUND: - val[0] = (addr->Direction == WINDIVERT_DIRECTION_OUTBOUND); + val[0] = addr->Outbound; break; case WINDIVERT_FILTER_FIELD_IFIDX: - val[0] = addr->IfIdx; + val[0] = addr->Network.IfIdx; break; case WINDIVERT_FILTER_FIELD_SUBIFIDX: - val[0] = addr->SubIfIdx; + val[0] = addr->Network.SubIfIdx; break; case WINDIVERT_FILTER_FIELD_LOOPBACK: val[0] = addr->Loopback; @@ -2266,22 +2421,26 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = addr->Impostor; break; case WINDIVERT_FILTER_FIELD_IP: - val[0] = (iphdr != NULL); + val[0] = !addr->IPv6; break; case WINDIVERT_FILTER_FIELD_IPV6: - val[0] = (ipv6hdr != NULL); + val[0] = addr->IPv6; break; case WINDIVERT_FILTER_FIELD_ICMP: - val[0] = (icmphdr != NULL); + val[0] = (addr->Layer == WINDIVERT_LAYER_FLOW? + addr->Flow.Protocol == IPPROTO_ICMP: icmphdr != NULL); break; case WINDIVERT_FILTER_FIELD_ICMPV6: - val[0] = (icmpv6hdr != NULL); + val[0] = (addr->Layer == WINDIVERT_LAYER_FLOW? + addr->Flow.Protocol == IPPROTO_ICMPV6: icmpv6hdr != NULL); break; case WINDIVERT_FILTER_FIELD_TCP: - val[0] = (tcphdr != NULL); + val[0] = (addr->Layer == WINDIVERT_LAYER_FLOW? + addr->Flow.Protocol == IPPROTO_TCP: tcphdr != NULL); break; case WINDIVERT_FILTER_FIELD_UDP: - val[0] = (udphdr != NULL); + val[0] = (addr->Layer == WINDIVERT_LAYER_FLOW? + addr->Flow.Protocol == IPPROTO_UDP: udphdr != NULL); break; case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: val[0] = iphdr->HdrLength; @@ -2314,9 +2473,11 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = ntohs(iphdr->Checksum); break; case WINDIVERT_FILTER_FIELD_IP_SRCADDR: + val[1] = 0x0000FFFF; val[0] = ntohl(iphdr->SrcAddr); break; case WINDIVERT_FILTER_FIELD_IP_DSTADDR: + val[1] = 0x0000FFFF; val[0] = ntohl(iphdr->DstAddr); break; case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: @@ -2430,6 +2591,30 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: val[0] = payload_len; break; + case WINDIVERT_FILTER_FIELD_PROCESSID: + val[0] = addr->Flow.ProcessId; + break; + case WINDIVERT_FILTER_FIELD_LOCALADDR: + val[0] = addr->Flow.LocalAddr[0]; + val[1] = addr->Flow.LocalAddr[1]; + val[2] = addr->Flow.LocalAddr[2]; + val[3] = addr->Flow.LocalAddr[3]; + break; + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + val[0] = addr->Flow.RemoteAddr[0]; + val[1] = addr->Flow.RemoteAddr[1]; + val[2] = addr->Flow.RemoteAddr[2]; + val[3] = addr->Flow.RemoteAddr[3]; + break; + case WINDIVERT_FILTER_FIELD_LOCALPORT: + val[0] = addr->Flow.LocalPort; + break; + case WINDIVERT_FILTER_FIELD_REMOTEPORT: + val[0] = addr->Flow.RemotePort; + break; + case WINDIVERT_FILTER_FIELD_PROTOCOL: + val[0] = addr->Flow.Protocol; + break; default: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; diff --git a/doc/windivert.html b/doc/windivert.html index 0f7b1e0..67d36d0 100644 --- a/doc/windivert.html +++ b/doc/windivert.html @@ -1260,8 +1260,8 @@ Use GetLastError() to get the reason for the error.

Remarks
Parses an IPv4 address stored in addrStr. -If non-NULL, the result is stored in pAddr -in host-byte-order. +If pAddr is non-NULL, the is be stored in +host-byte-order. Use htonl() to convert the result into network-byte-order.

@@ -1289,21 +1289,22 @@ Use GetLastError() to get the reason for the error.

Remarks
Parses an IPv6 address stored in addrStr. -If non-NULL, the result is stored in pAddr. -The pAddr parameter is assumed to point to a buffer large enough -to hold a 16-byte IPv6 address. -Given an IPv6 address of the form +If pAddr is non-NULL, the buffer assumed +to be large enough to hold a 16-byte IPv6 address. +The result is stored in host-byte-order. +For example, given an IPv6 address of the form 0011:2233:4455:6677:8899:aabb:ccdd:eeff, then the result is ordered as follows:

-pAddr[0] = 0x00112233
-pAddr[1] = 0x44556677
-pAddr[2] = 0x8899aabb
-pAddr[3] = 0xccddeeff
+pAddr[0] = 0xccddeeff
+pAddr[1] = 0x8899aabb
+pAddr[2] = 0x44556677
+pAddr[3] = 0x00112233
 
where each pAddr[i] is in host-byte-order. The result can be converted into network-byte-order by setting -pAddr[i] = htonl(pAddr[i]) for each i. +pAddr[i] = htonl(pAddr[i]) for each i and +swapping the array indexes.

diff --git a/examples/flowtrack/flowtrack.c b/examples/flowtrack/flowtrack.c new file mode 100644 index 0000000..f705cae --- /dev/null +++ b/examples/flowtrack/flowtrack.c @@ -0,0 +1,350 @@ +/* + * streamdump.c + * (C) 2018, all rights reserved, + * + * This file is part of WinDivert. + * + * WinDivert is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * WinDivert is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * DESCRIPTION: + * + * usage: flowtrack.exe [filter] + */ + +#include +#include +#include +#include +#include +#include + +#include "windivert.h" + +#define MAX_FLOWS 256 + +/* + * Flow tracking. + */ +typedef struct FLOW +{ + WINDIVERT_ADDRESS addr; + struct FLOW *next; +} FLOW, *PFLOW; + +static HANDLE lock; +static PFLOW flows = NULL; + +/* + * Print an IP address. + */ +static void print_address(const UINT32 *addr) +{ + if (addr[3] == 0 && addr[2] == 0 && addr[1] == 0x0000FFFF) + { + // IPv4 address: + UINT32 a, b, c, d; + a = (addr[0] >> 24) & 0xFF; + b = (addr[0] >> 16) & 0xFF; + c = (addr[0] >> 8) & 0xFF; + d = (addr[0] >> 0) & 0xFF; + printf("%u.%u.%u.%u", a, b, c, d); + } + else + { + // IPv6 address: + int i; + for (i = 3; i >= 0; i--) + { + UINT32 a, b; + a = (addr[i] >> 16) & 0xFFFF; + b = (addr[i] >> 0) & 0xFFFF; + printf("%x:%x", a, b); + if (i != 0) + { + putchar(':'); + } + } + } +} + +/* + * Draw flows to console in a delayed loop. + * + * This function does minimal error checking. + */ +static DWORD draw(LPVOID arg) +{ + const COORD top_left = {0, 0}; + HANDLE process, console = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO screen; + char path[MAX_PATH+1]; + char *filename; + const char header[] = "PID PROGRAM PROT FLOW"; + DWORD rows, columns, written, fill_len, path_len, i; + PFLOW flow; + WINDIVERT_ADDRESS addrs[MAX_FLOWS], *addr; + UINT num_addrs; + + while (TRUE) + { + GetConsoleScreenBufferInfo(console, &screen); + SetConsoleCursorPosition(console, top_left); + + rows = screen.srWindow.Bottom - screen.srWindow.Top + 1; + columns = screen.srWindow.Right - screen.srWindow.Left + 1; + + // Copy a snapshot of the current flows: + WaitForSingleObject(lock, INFINITE); + flow = flows; + num_addrs = 0; + for (i = 0; flow != NULL && i < rows && i < MAX_FLOWS; i++) + { + memcpy(&addrs[i], &flow->addr, sizeof(addrs[i])); + num_addrs++; + flow = flow->next; + } + ReleaseMutex(lock); + + // Print the flows: + SetConsoleTextAttribute(console, BACKGROUND_RED | BACKGROUND_GREEN | + BACKGROUND_BLUE); + WriteConsole(console, header, sizeof(header)-1, &written, NULL); + fill_len = columns - (sizeof(header)-1); + if (fill_len > 0) + { + COORD pos = {sizeof(header)-1, 0}; + FillConsoleOutputCharacterA(console, ' ', fill_len, pos, + &written); + FillConsoleOutputAttribute(console, + BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE, + fill_len, pos, &written); + } + putchar('\n'); + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + for (i = 0; i < num_addrs && i < rows-1; i++) + { + COORD pos = {0, i+1}; + addr = &addrs[i]; + FillConsoleOutputCharacterA(console, ' ', columns, pos, &written); + FillConsoleOutputAttribute(console, + FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE, + columns, pos, &written); + SetConsoleCursorPosition(console, pos); + if (i == rows-2 && (i+1) < num_addrs) + { + fputs("...", stdout); + fflush(stdout); + continue; + } + + printf("%-10d ", addr->Flow.ProcessId); + + process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, + addr->Flow.ProcessId); + path_len = 0; + if (process != NULL) + { + path_len = GetProcessImageFileName(process, path, sizeof(path)); + CloseHandle(process); + } + SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN); + if (path_len != 0) + { + filename = PathFindFileName(path); + printf("%-20.20s ", filename); + } + else if (addr->Flow.ProcessId == 4) + { + fputs("Windows ", stdout); + } + else + { + fputs("??? ", stdout); + } + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + switch (addr->Flow.Protocol) + { + case IPPROTO_TCP: + SetConsoleTextAttribute(console, FOREGROUND_GREEN); + printf("TCP "); + break; + case IPPROTO_UDP: + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN); + printf("UDP "); + break; + case IPPROTO_ICMP: + SetConsoleTextAttribute(console, FOREGROUND_RED); + printf("ICMP "); + break; + case IPPROTO_ICMPV6: + SetConsoleTextAttribute(console, FOREGROUND_RED); + printf("ICMPV6 "); + break; + default: + printf("%-6u ", addr->Flow.Protocol); + break; + } + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + print_address(addr->Flow.LocalAddr); + printf(":%u %s ", addr->Flow.LocalPort, + (addr->Outbound? "---->": "<----")); + print_address(addr->Flow.RemoteAddr); + printf(":%u", addr->Flow.RemotePort); + fflush(stdout); + } + for (; i < rows-1; i++) + { + COORD pos = {0, i+1}; + FillConsoleOutputCharacterA(console, ' ', columns, pos, &written); + FillConsoleOutputAttribute(console, + FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE, + columns, pos, &written); + } + + Sleep(1000); + } +} + +/* + * Entry. + */ +int __cdecl main(int argc, char **argv) +{ + HANDLE handle, thread; + INT16 priority = 776; // Arbitrary. + const char *filter = "true", *err_str; + UINT packet_len; + WINDIVERT_ADDRESS addr; + PFLOW flow, prev; + + switch (argc) + { + case 1: + break; + case 2: + filter = argv[1]; + break; + default: + fprintf(stderr, "usage: %s [filter]\n"); + exit(EXIT_FAILURE); + } + + // Spawn the draw() thread. + lock = CreateMutex(NULL, FALSE, NULL); + thread = CreateThread(NULL, 1, (LPTHREAD_START_ROUTINE)draw, NULL, 0, + NULL); + if (thread == NULL) + { + fprintf(stderr, "error: failed to create thread (%d)\n", + GetLastError()); + return EXIT_FAILURE; + } + CloseHandle(thread); + + // Open WinDivert FLOW handle: + handle = WinDivertOpen(filter, WINDIVERT_LAYER_FLOW, priority, + WINDIVERT_FLAGS_LAYER_FLOW); + if (handle == INVALID_HANDLE_VALUE) + { + if (GetLastError() == ERROR_INVALID_PARAMETER && + !WinDivertHelperCheckFilter(filter, WINDIVERT_LAYER_FLOW, + &err_str, NULL)) + { + fprintf(stderr, "error: invalid filter \"%s\"\n", err_str); + exit(EXIT_FAILURE); + } + fprintf(stderr, "error: failed to open the WinDivert device (%d)\n", + GetLastError()); + return EXIT_FAILURE; + } + + // Main loop: + while (TRUE) + { + if (!WinDivertRecv(handle, NULL, 0, &addr, &packet_len)) + { + fprintf(stderr, "failed to read packet (%d)\n", GetLastError()); + continue; + } + + switch (addr.Event) + { + case WINDIVERT_EVENT_FLOW_ESTABLISHED: + + // Flow established: + flow = (PFLOW)malloc(sizeof(FLOW)); + if (flow == NULL) + { + fprintf(stderr, "error: failed to allocate memory\n"); + exit(EXIT_FAILURE); + } + memcpy(&flow->addr, &addr, sizeof(flow->addr)); + WaitForSingleObject(lock, INFINITE); + flow->next = flows; + flows = flow; + ReleaseMutex(lock); + break; + + case WINDIVERT_EVENT_FLOW_DELETED: + + // Flow deleted: + prev = NULL; + WaitForSingleObject(lock, INFINITE); + flow = flows; + while (flow != NULL) + { + if (memcmp(&addr.Flow, &flow->addr.Flow, + sizeof(addr.Flow)) == 0) + { + if (prev != NULL) + { + prev->next = flow->next; + } + else + { + flows = flow->next; + } + break; + } + prev = flow; + flow = flow->next; + } + ReleaseMutex(lock); + free(flow); + } + } + + return 0; +} + diff --git a/examples/netdump/netdump.c b/examples/netdump/netdump.c index 6c51848..ee1f46d 100644 --- a/examples/netdump/netdump.c +++ b/examples/netdump/netdump.c @@ -156,9 +156,8 @@ int __cdecl main(int argc, char **argv) (double)freq.QuadPart; printf("Packet [Timestamp=%.8g, Direction=%s IfIdx=%u SubIfIdx=%u " "Loopback=%u]\n", - time_passed, (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND? - "outbound": "inbound"), addr.IfIdx, addr.SubIfIdx, - addr.Loopback); + time_passed, (addr.Outbound? "outbound": "inbound"), + addr.Network.IfIdx, addr.Network.SubIfIdx, addr.Loopback); if (ip_header != NULL) { UINT8 *src_addr = (UINT8 *)&ip_header->SrcAddr; diff --git a/examples/netfilter/netfilter.c b/examples/netfilter/netfilter.c index 0e990ba..b8e3fc1 100644 --- a/examples/netfilter/netfilter.c +++ b/examples/netfilter/netfilter.c @@ -287,7 +287,7 @@ int __cdecl main(int argc, char **argv) htonl(ntohl(tcp_header->SeqNum) + payload_len)); memcpy(&send_addr, &recv_addr, sizeof(send_addr)); - send_addr.Direction = !recv_addr.Direction; + send_addr.Outbound = !recv_addr.Outbound; WinDivertHelperCalcChecksums((PVOID)reset, sizeof(TCPPACKET), &send_addr, 0); if (!WinDivertSend(handle, (PVOID)reset, sizeof(TCPPACKET), @@ -314,7 +314,7 @@ int __cdecl main(int argc, char **argv) htonl(ntohl(tcp_header->SeqNum) + payload_len)); memcpy(&send_addr, &recv_addr, sizeof(send_addr)); - send_addr.Direction = !recv_addr.Direction; + send_addr.Outbound = !recv_addr.Outbound; WinDivertHelperCalcChecksums((PVOID)resetv6, sizeof(TCPV6PACKET), &send_addr, 0); if (!WinDivertSend(handle, (PVOID)resetv6, sizeof(TCPV6PACKET), @@ -340,7 +340,7 @@ int __cdecl main(int argc, char **argv) dnr->ip.DstAddr = ip_header->SrcAddr; memcpy(&send_addr, &recv_addr, sizeof(send_addr)); - send_addr.Direction = !recv_addr.Direction; + send_addr.Outbound = !recv_addr.Outbound; WinDivertHelperCalcChecksums((PVOID)dnr, icmp_length, &send_addr, 0); if (!WinDivertSend(handle, (PVOID)dnr, icmp_length, &send_addr, @@ -363,7 +363,7 @@ int __cdecl main(int argc, char **argv) sizeof(dnrv6->ipv6.DstAddr)); memcpy(&send_addr, &recv_addr, sizeof(send_addr)); - send_addr.Direction = !recv_addr.Direction; + send_addr.Outbound = !recv_addr.Outbound; WinDivertHelperCalcChecksums((PVOID)dnrv6, icmpv6_length, &send_addr, 0); if (!WinDivertSend(handle, (PVOID)dnrv6, icmpv6_length, diff --git a/examples/streamdump/streamdump.c b/examples/streamdump/streamdump.c index c8c9164..48315fa 100644 --- a/examples/streamdump/streamdump.c +++ b/examples/streamdump/streamdump.c @@ -257,41 +257,39 @@ read_failed: continue; } - switch (addr.Direction) + if (addr.Outbound) { - case WINDIVERT_DIRECTION_OUTBOUND: - if (tcp_header->DstPort == htons(port)) - { - // Reflect: PORT ---> PROXY - UINT32 dst_addr = ip_header->DstAddr; - tcp_header->DstPort = htons(proxy_port); - ip_header->DstAddr = ip_header->SrcAddr; - ip_header->SrcAddr = dst_addr; - addr.Direction = WINDIVERT_DIRECTION_INBOUND; - } - else if (tcp_header->SrcPort == htons(proxy_port)) - { - // Reflect: PROXY ---> PORT - UINT32 dst_addr = ip_header->DstAddr; - tcp_header->SrcPort = htons(port); - ip_header->DstAddr = ip_header->SrcAddr; - ip_header->SrcAddr = dst_addr; - addr.Direction = WINDIVERT_DIRECTION_INBOUND; - } - else if (tcp_header->DstPort == htons(alt_port)) - { - // Redirect: ALT ---> PORT - tcp_header->DstPort = htons(port); - } - break; - - case WINDIVERT_DIRECTION_INBOUND: - if (tcp_header->SrcPort == htons(port)) - { - // Redirect: PORT ---> ALT - tcp_header->SrcPort = htons(alt_port); - } - break; + if (tcp_header->DstPort == htons(port)) + { + // Reflect: PORT ---> PROXY + UINT32 dst_addr = ip_header->DstAddr; + tcp_header->DstPort = htons(proxy_port); + ip_header->DstAddr = ip_header->SrcAddr; + ip_header->SrcAddr = dst_addr; + addr.Outbound = FALSE; + } + else if (tcp_header->SrcPort == htons(proxy_port)) + { + // Reflect: PROXY ---> PORT + UINT32 dst_addr = ip_header->DstAddr; + tcp_header->SrcPort = htons(port); + ip_header->DstAddr = ip_header->SrcAddr; + ip_header->SrcAddr = dst_addr; + addr.Outbound = FALSE; + } + else if (tcp_header->DstPort == htons(alt_port)) + { + // Redirect: ALT ---> PORT + tcp_header->DstPort = htons(port); + } + } + else + { + if (tcp_header->SrcPort == htons(port)) + { + // Redirect: PORT ---> ALT + tcp_header->SrcPort = htons(alt_port); + } } WinDivertHelperCalcChecksums(packet, packet_len, &addr, 0); diff --git a/examples/webfilter/webfilter.c b/examples/webfilter/webfilter.c index 2f04b00..860970b 100644 --- a/examples/webfilter/webfilter.c +++ b/examples/webfilter/webfilter.c @@ -238,7 +238,7 @@ int __cdecl main(int argc, char **argv) blockpage->header.tcp.SeqNum = tcp_header->AckNum; blockpage->header.tcp.AckNum = htonl(ntohl(tcp_header->SeqNum) + payload_len); - addr.Direction = !addr.Direction; // Reverse direction. + addr.Outbound = !addr.Outbound; // Reverse direction. WinDivertHelperCalcChecksums((PVOID)blockpage, blockpage_len, &addr, 0); if (!WinDivertSend(handle, (PVOID)blockpage, blockpage_len, &addr, NULL)) diff --git a/include/windivert.h b/include/windivert.h index 6f344fa..49029f2 100644 --- a/include/windivert.h +++ b/include/windivert.h @@ -70,43 +70,90 @@ extern "C" { /****************************************************************************/ /* - * Divert address. + * WinDivert NETWORK and NETWORK_FORWARD layer data. + */ +typedef struct +{ + UINT32 IfIdx; /* Packet's interface index. */ + UINT32 SubIfIdx; /* Packet's sub-interface index. */ +} WINDIVERT_NETWORK_DATA, *PWINDIVERT_NETWORK_DATA; + +/* + * WinDivert FLOW layer data. + */ +typedef struct +{ + UINT32 ProcessId; /* Process ID. */ + UINT32 LocalAddr[4]; /* Local address. */ + UINT32 RemoteAddr[4]; /* Remote address. */ + UINT16 LocalPort; /* Local port. */ + UINT16 RemotePort; /* Remote port. */ + UINT8 Protocol; /* Protocol. */ +} WINDIVERT_FLOW_DATA, *PWINDIVERT_FLOW_DATA; + +/* + * WinDivert address. */ typedef struct { INT64 Timestamp; /* Packet's timestamp. */ - UINT32 IfIdx; /* Packet's interface index. */ - UINT32 SubIfIdx; /* Packet's sub-interface index. */ - UINT8 Direction:1; /* Packet's direction. */ - UINT8 Loopback:1; /* Packet is loopback? */ - UINT8 Impostor:1; /* Packet is impostor? */ - UINT8 PseudoIPChecksum:1; /* Packet has pseudo IPv4 checksum? */ - UINT8 PseudoTCPChecksum:1; /* Packet has pseudo TCP checksum? */ - UINT8 PseudoUDPChecksum:1; /* Packet has pseudo UDP checksum? */ - UINT8 Reserved:2; + UINT32 Layer:8; /* Packet's layer. */ + UINT32 Event:8; /* Packet event. */ + UINT32 Outbound:1; /* Packet is outound? */ + UINT32 Loopback:1; /* Packet is loopback? */ + UINT32 Impostor:1; /* Packet is impostor? */ + UINT32 IPv6:1; /* Packet is IPv6? */ + UINT32 PseudoIPChecksum:1; /* Packet has pseudo IPv4 checksum? */ + UINT32 PseudoTCPChecksum:1; /* Packet has pseudo TCP checksum? */ + UINT32 PseudoUDPChecksum:1; /* Packet has pseudo UDP checksum? */ + UINT32 Reserved:9; + union + { + WINDIVERT_NETWORK_DATA Network; /* Network layer data. */ + WINDIVERT_FLOW_DATA Flow; /* Flow layer data. */ + }; } WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS; -#define WINDIVERT_DIRECTION_OUTBOUND 0 -#define WINDIVERT_DIRECTION_INBOUND 1 - /* - * Divert layers. + * WinDivert layers. */ typedef enum { - WINDIVERT_LAYER_NETWORK = 0, /* Network layer. */ - WINDIVERT_LAYER_NETWORK_FORWARD = 1 /* Network layer (forwarded packets) */ + WINDIVERT_LAYER_NETWORK = 1, /* Network layer. */ + WINDIVERT_LAYER_NETWORK_FORWARD = 2,/* Network layer (forwarded packets) */ + WINDIVERT_LAYER_FLOW = 3 /* Flow layer. */ } WINDIVERT_LAYER, *PWINDIVERT_LAYER; /* - * Divert flags. + * WinDivert events. + */ +typedef enum +{ + WINDIVERT_EVENT_NETWORK_PACKET = 0, /* Network packet. */ + WINDIVERT_EVENT_FLOW_ESTABLISHED = 1, + /* Flow established. */ + WINDIVERT_EVENT_FLOW_DELETED = 2, /* Flow deleted. */ +} WINDIVERT_EVENT, *PWINDIVERT_EVENT; + +/* + * WinDivert flags. */ #define WINDIVERT_FLAG_SNIFF 1 #define WINDIVERT_FLAG_DROP 2 -#define WINDIVERT_FLAG_DEBUG 4 +#define WINDIVERT_FLAG_RECV_ONLY 4 +#define WINDIVERT_FLAG_READ_ONLY WINDIVERT_FLAG_RECV_ONLY +#define WINDIVERT_FLAG_SEND_ONLY 8 +#define WINDIVERT_FLAG_WRITE_ONLY WINDIVERT_FLAG_SEND_ONLY +#define WINDIVERT_FLAG_DEBUG 16 + +#define WINDIVERT_FLAGS_LAYER_NETWORK 0 +#define WINDIVERT_FLAGS_LAYER_NETWORK_FORWARD \ + 0 +#define WINDIVERT_FLAGS_LAYER_FLOW \ + (WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_RECV_ONLY) /* - * Divert parameters. + * WinDivert parameters. */ typedef enum { @@ -396,7 +443,6 @@ extern WINDIVERTEXPORT BOOL WinDivertHelperCheckFilter( */ extern WINDIVERTEXPORT BOOL WinDivertHelperEvalFilter( __in const char *filter, - __in WINDIVERT_LAYER layer, __in PVOID pPacket, __in UINT packetLen, __in PWINDIVERT_ADDRESS pAddr); diff --git a/include/windivert_device.h b/include/windivert_device.h index 0036461..73cc45f 100644 --- a/include/windivert_device.h +++ b/include/windivert_device.h @@ -44,8 +44,8 @@ #define WINDIVERT_KERNEL #include "windivert.h" -#define WINDIVERT_VERSION 1 -#define WINDIVERT_VERSION_MINOR 4 +#define WINDIVERT_VERSION 2 +#define WINDIVERT_VERSION_MINOR 0 #define WINDIVERT_STR2(s) #s #define WINDIVERT_STR(s) WINDIVERT_STR2(s) @@ -59,8 +59,8 @@ #define WINDIVERT_DEVICE_NAME \ L"WinDivert" WINDIVERT_VERSION_LSTR -#define WINDIVERT_IOCTL_VERSION 6 -#define WINDIVERT_IOCTL_MAGIC 0xA2BF +#define WINDIVERT_IOCTL_VERSION 7 +#define WINDIVERT_IOCTL_MAGIC 0xC7C9 #define WINDIVERT_FILTER_FIELD_ZERO 0 #define WINDIVERT_FILTER_FIELD_INBOUND 1 @@ -122,8 +122,14 @@ #define WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH 57 #define WINDIVERT_FILTER_FIELD_LOOPBACK 58 #define WINDIVERT_FILTER_FIELD_IMPOSTOR 59 +#define WINDIVERT_FILTER_FIELD_PROCESSID 60 +#define WINDIVERT_FILTER_FIELD_LOCALADDR 61 +#define WINDIVERT_FILTER_FIELD_REMOTEADDR 62 +#define WINDIVERT_FILTER_FIELD_LOCALPORT 63 +#define WINDIVERT_FILTER_FIELD_REMOTEPORT 64 +#define WINDIVERT_FILTER_FIELD_PROTOCOL 65 #define WINDIVERT_FILTER_FIELD_MAX \ - WINDIVERT_FILTER_FIELD_IMPOSTOR + WINDIVERT_FILTER_FIELD_PROTOCOL #define WINDIVERT_FILTER_TEST_EQ 0 #define WINDIVERT_FILTER_TEST_NEQ 1 @@ -142,20 +148,21 @@ * WinDivert layers. */ #define WINDIVERT_LAYER_DEFAULT WINDIVERT_LAYER_NETWORK -#define WINDIVERT_LAYER_MAX \ - WINDIVERT_LAYER_NETWORK_FORWARD /* * WinDivert flags. */ #define WINDIVERT_FLAGS_ALL \ - (WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_DROP | WINDIVERT_FLAG_DEBUG) + (WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_DROP | WINDIVERT_FLAG_RECV_ONLY |\ + WINDIVERT_FLAG_SEND_ONLY | WINDIVERT_FLAG_DEBUG) #define WINDIVERT_FLAGS_EXCLUDE(flags, flag1, flag2) \ (((flags) & ((flag1) | (flag2))) != ((flag1) | (flag2))) #define WINDIVERT_FLAGS_VALID(flags) \ ((((flags) & ~WINDIVERT_FLAGS_ALL) == 0) && \ WINDIVERT_FLAGS_EXCLUDE(flags, WINDIVERT_FLAG_SNIFF, \ - WINDIVERT_FLAG_DROP)) + WINDIVERT_FLAG_DROP) && \ + WINDIVERT_FLAGS_EXCLUDE(flags, WINDIVERT_FLAG_RECV_ONLY, \ + WINDIVERT_FLAG_SEND_ONLY)) /* * WinDivert priorities. @@ -197,7 +204,7 @@ typedef struct windivert_ioctl_s *windivert_ioctl_t; */ struct windivert_ioctl_filter_s { - UINT8 field; // WINDIVERT_FILTER_FIELD_IP_* + UINT8 field; // WINDIVERT_FILTER_FIELD_* UINT8 test; // WINDIVERT_FILTER_TEST_* UINT16 success; // Success continuation. UINT16 failure; // Fail continuation. @@ -210,20 +217,20 @@ typedef struct windivert_ioctl_filter_s *windivert_ioctl_filter_t; * IOCTL codes. */ #define IOCTL_WINDIVERT_RECV \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x908, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x918, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINDIVERT_SEND \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x909, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x919, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINDIVERT_START_FILTER \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x90A, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x91A, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINDIVERT_SET_LAYER \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x90B, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x91B, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINDIVERT_SET_PRIORITY \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x90C, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x91C, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINDIVERT_SET_FLAGS \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x90D, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x91D, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINDIVERT_SET_PARAM \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x90E, METHOD_IN_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x91E, METHOD_IN_DIRECT, FILE_ANY_ACCESS) #define IOCTL_WINDIVERT_GET_PARAM \ - CTL_CODE(FILE_DEVICE_NETWORK, 0x90F, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + CTL_CODE(FILE_DEVICE_NETWORK, 0x91F, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #endif /* __WINDIVERT_DEVICE_H */ diff --git a/mingw-build.sh b/mingw-build.sh index 1fcbee4..74ca900 100644 --- a/mingw-build.sh +++ b/mingw-build.sh @@ -97,6 +97,10 @@ do $CC -s -O2 -Iinclude/ examples/streamdump/streamdump.c \ -o "install/MINGW/$CPU/streamdump.exe" -lWinDivert -lws2_32 \ -L"install/MINGW/$CPU/" + echo "\tcopy install/MINGW/$CPU/flowtrack.exe..." + $CC -s -O2 -Iinclude/ examples/flowtrack/flowtrack.c \ + -o "install/MINGW/$CPU/flowtrack.exe" -lWinDivert -lws2_32 -lpsapi \ + -lshlwapi -L"install/MINGW/$CPU/" echo "\tcopy install/MINGW/$CPU/WinDivert$BITS.sys..." cp install/WDDK/$CPU/WinDivert$BITS.sys install/MINGW/$CPU else diff --git a/release-build.sh b/release-build.sh index 80cd0fe..21a7750 100644 --- a/release-build.sh +++ b/release-build.sh @@ -90,6 +90,8 @@ do cp install/$TARGET/i386/webfilter.exe $INSTALL/x86 echo "\tcopy $INSTALL/x86/streamdump.exe..." cp install/$TARGET/i386/streamdump.exe $INSTALL/x86 + echo "\tcopy $INSTALL/i386/flowtrack.exe..." + cp install/$TARGET/i386/flowtrack.exe $INSTALL/amd64 if [ -d "install/$TARGET/amd64" ] then echo "\tmake $INSTALL/amd64..." @@ -115,6 +117,8 @@ do cp install/$TARGET/amd64/webfilter.exe $INSTALL/amd64 echo "\tcopy $INSTALL/amd64/streamdump.exe..." cp install/$TARGET/amd64/streamdump.exe $INSTALL/amd64 + echo "\tcopy $INSTALL/amd64/flowtrack.exe..." + cp install/$TARGET/amd64/flowtrack.exe $INSTALL/amd64 else echo "\tWARNING: skipping missing AMD64 build..." fi diff --git a/sys/windivert.c b/sys/windivert.c index 2ab6476..9d4bcc2 100644 --- a/sys/windivert.c +++ b/sys/windivert.c @@ -59,7 +59,7 @@ EVT_WDF_WORKITEM windivert_worker; /* * Debugging macros. */ -// #define DEBUG_ON +#define DEBUG_ON #define DEBUG_BUFSIZE 256 #ifdef DEBUG_ON @@ -116,6 +116,8 @@ typedef struct filter_s *filter_t; #define WINDIVERT_FILTER_PROTOCOL_ICMPV6 4 #define WINDIVERT_FILTER_PROTOCOL_TCP 5 #define WINDIVERT_FILTER_PROTOCOL_UDP 6 +#define WINDIVERT_FILTER_PROTOCOL_NETWORK 7 +#define WINDIVERT_FILTER_PROTOCOL_FLOW 8 /* * WinDivert context information. @@ -123,10 +125,6 @@ typedef struct filter_s *filter_t; #define WINDIVERT_CONTEXT_SIZE (sizeof(struct context_s)) #define WINDIVERT_CONTEXT_MAXLAYERS 4 #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 -#define WINDIVERT_CONTEXT_INBOUND_IPV6_LAYER 3 typedef enum { WINDIVERT_CONTEXT_STATE_OPENING = 0xA0, // Context is opening. @@ -141,6 +139,9 @@ struct context_s KSPIN_LOCK lock; // Context-wide lock. WDFDEVICE device; // Context's device. WDFFILEOBJECT object; // Context's parent object. + LIST_ENTRY flow_set; // All active flows. + UINT32 flow_v4_callout_id; // Flow established callout id. + UINT32 flow_v6_callout_id; // Flow established callout id. LIST_ENTRY work_queue; // Work queue. ULONG work_queue_length; // Work queue length. LIST_ENTRY packet_queue; // Packet queue. @@ -161,9 +162,7 @@ struct context_s // Callout GUIDs. GUID filter_guid[WINDIVERT_CONTEXT_MAXLAYERS]; // Filter GUIDs. - BOOL installed[WINDIVERT_CONTEXT_MAXLAYERS]; - // What is installed? - BOOL on; // Is filtering on? + BOOL installed[WINDIVERT_CONTEXT_MAXLAYERS];// What is installed? HANDLE engine_handle; // WFP engine handle. filter_t filter; // Packet filter. }; @@ -178,11 +177,13 @@ WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(context_s, windivert_context_get); /* * WinDivert Layer information. */ -typedef void (*windivert_callout_t)( +typedef void (*windivert_classify_t)( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); +typedef void (*windivert_flow_delete_notify_t)( + IN UINT16 layer_id, IN UINT32 callout_id, IN UINT64 flow_context); struct layer_s { wchar_t *sublayer_name; // Sub-layer name. @@ -193,7 +194,10 @@ struct layer_s wchar_t *filter_desc; // Filter description. GUID layer_guid; // WFP layer GUID. GUID sublayer_guid; // Sub-layer GUID. - windivert_callout_t callout; // Call-out. + windivert_classify_t classify; // Classify function. + windivert_flow_delete_notify_t flow_delete; + // Flow delete function. + UINT16 sublayer_weight; // Sub-layer weight. }; typedef struct layer_s *layer_t; @@ -209,29 +213,72 @@ typedef struct req_context_s *req_context_t; WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(req_context_s, windivert_req_context_get); /* - * WinDivert packet structure. + * WinDivert packet structure. Layout is as follows: + * + * +-----------------+------------+-------------+ + * | struct packet_s | layer data | packet data | + * +-----------------+------------+-------------+ + * + * Note the packet data must be pointer-aligned. */ -#define WINDIVERT_WORK_QUEUE_LEN_MAX 2048 +#define WINDIVERT_WORK_QUEUE_LEN_MAX 4096 +#ifdef _WIN64 +#define WINDIVERT_ALIGN_SIZE 8 +#define WINDIVERT_DATA_ALIGN __declspec(align(8)) +#else +#define WINDIVERT_ALIGN_SIZE 4 +#define WINDIVERT_DATA_ALIGN __declspec(align(4)) +#endif struct packet_s { LIST_ENTRY entry; // Entry for queue. - UINT8 direction; // Packet direction. - BOOL is_ipv4:1; // Is IPv4? - BOOL forward:1; // Is forward? - BOOL impostor:1; // Is Impostor? - BOOL loopback:1; // Is loopback? - BOOL match:1; // Matches filter? - UINT32 if_idx; // Interface index. - UINT32 sub_if_idx; // Sub-interface index. - UINT32 priority; // Packet priority. LONGLONG timestamp; // Packet timestamp. - NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksums; - // Checksum information. - size_t data_len; // Length of `data'. - char *data; // Packet data. + UINT32 layer:8; // Layer. + UINT32 event:8; // Event. + UINT32 outbound:1; // Packet is outound? + UINT32 loopback:1; // Packet is loopback? + UINT32 impostor:1; // Packet is impostor? + UINT32 ipv6:1; // Packet is IPv6? + UINT32 pseudo_ip_checksum:1; // Packet has pseudo IPv4 check? + UINT32 pseudo_tcp_checksum:1; // Packet has pseudo TCP check? + UINT32 pseudo_udp_checksum:1; // Packet has pseudo UDP check? + UINT32 match:1; // Packet matches filter? + UINT32 priority; // Packet priority. + UINT32 packet_len; // Length of the packet. + WINDIVERT_DATA_ALIGN UINT8 data[]; // Packet/layer data. }; typedef struct packet_s *packet_t; +#define WINDIVERT_DATA_SIZE(size) \ + ((((size) + WINDIVERT_ALIGN_SIZE - 1) / WINDIVERT_ALIGN_SIZE) * \ + WINDIVERT_ALIGN_SIZE) +#define WINDIVERT_PACKET_SIZE(layer_type, packet_len) \ + (sizeof(struct packet_s) + WINDIVERT_DATA_SIZE(sizeof(layer_type)) + \ + (packet_len)) +#define WINDIVERT_LAYER_DATA_PTR(packet) \ + ((packet)->data) +#define WINDIVERT_PACKET_DATA_PTR(layer_type, packet) \ + ((packet)->data + WINDIVERT_DATA_SIZE(sizeof(layer_type))) + +/* + * WinDivert flow structure. + */ +struct flow_s +{ + LIST_ENTRY entry; // Entry for tracking. + context_t context; // Context. + 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? + WINDIVERT_FLOW_DATA data; // Flow data. +}; +typedef struct flow_s *flow_t; + /* * IPv4/IPv6 pseudo headers. */ @@ -300,9 +347,9 @@ 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, UINT8 layer, - BOOL is_inbound, BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6); + BOOL inbound, BOOL outbound, BOOL ipv4, BOOL ipv6); static NTSTATUS windivert_install_callout(context_t context, UINT idx, - layer_t layer); + layer_t layer, UINT32 *callout_id_ptr); static void windivert_uninstall_callouts(context_t context, context_state_t state); extern VOID windivert_cleanup(IN WDFFILEOBJECT object); @@ -312,60 +359,77 @@ extern NTSTATUS windivert_write(context_t context, WDFREQUEST request, PWINDIVERT_ADDRESS addr); extern void NTAPI windivert_inject_complete(VOID *context, NET_BUFFER_LIST *packets, BOOLEAN dispatch_level); -static NTSTATUS windivert_notify_callout(IN FWPS_CALLOUT_NOTIFY_TYPE type, +extern void NTAPI windivert_reinject_complete(VOID *context, + NET_BUFFER_LIST *packets, BOOLEAN dispatch_level); +static NTSTATUS windivert_notify(IN FWPS_CALLOUT_NOTIFY_TYPE type, IN const GUID *filter_key, IN const FWPS_FILTER0 *filter); -static void windivert_classify_outbound_network_v4_callout( +static void windivert_outbound_network_v4_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); -static void windivert_classify_inbound_network_v4_callout( +static void windivert_inbound_network_v4_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); -static void windivert_classify_outbound_network_v6_callout( +static void windivert_outbound_network_v6_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); -static void windivert_classify_inbound_network_v6_callout( +static void windivert_inbound_network_v6_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); -static void windivert_classify_forward_network_v4_callout( +static void windivert_forward_network_v4_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); -static void windivert_classify_forward_network_v6_callout( +static void windivert_forward_network_v6_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); -static void windivert_classify_callout(context_t context, IN UINT8 direction, - IN UINT32 if_idx, IN UINT32 sub_if_idx, IN BOOL is_ipv4, +static void windivert_flow_established_v4_classify( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); +static void windivert_flow_established_v6_classify( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result); +static void windivert_flow_established_classify(context_t context, + IN UINT64 flow_id, IN PWINDIVERT_FLOW_DATA flow_data, IN BOOL ipv4, + IN BOOL outbound, IN BOOL loopback, OUT FWPS_CLASSIFY_OUT0 *result); +static void windivert_flow_delete_notify(UINT16 layer_id, UINT32 callout_id, + UINT64 flow_context); +static void windivert_network_classify(context_t context, + IN PWINDIVERT_NETWORK_DATA network_data, IN BOOL ipv4, IN BOOL outbound, IN BOOL loopback, IN UINT advance, IN OUT void *data, - IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result); -static BOOL windivert_queue_work(context_t context, BOOL sniff_mode, - BOOL drop_mode, PNET_BUFFER_LIST buffers, PNET_BUFFER buffer, - UINT8 direction, UINT32 if_idx, UINT32 sub_if_idx, BOOL is_ipv4, - BOOL forward, BOOL impostor, BOOL loopback, BOOL match, UINT32 priority, + OUT FWPS_CLASSIFY_OUT0 *result); +static BOOL windivert_queue_work(context_t context, PNET_BUFFER buffer, + PNET_BUFFER_LIST buffers, PWINDIVERT_NETWORK_DATA network_data, + PWINDIVERT_FLOW_DATA flow_data, WINDIVERT_LAYER layer, + WINDIVERT_EVENT event, 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 void windivert_free_packet(packet_t packet); -static BOOL windivert_decrement_ttl(PVOID data, BOOL is_ipv4, BOOL checksum); -static UINT8 windivert_skip_headers(UINT8 proto, UINT8 **header, size_t *len); +static BOOL windivert_decrement_ttl(PVOID data, BOOL ipv4, BOOL checksum); static int windivert_big_num_compare(const UINT32 *a, const UINT32 *b); -static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, - UINT32 sub_if_idx, BOOL outbound, BOOL is_ipv4, BOOL impostor, - BOOL loopback, filter_t filter); +static BOOL windivert_filter(PNET_BUFFER buffer, + PWINDIVERT_NETWORK_DATA network_data, PWINDIVERT_FLOW_DATA flow_data, + BOOL ipv4, BOOL outbound, BOOL loopback, BOOL impostor, filter_t filter); static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, size_t ioctl_filter_len); -static void windivert_filter_analyze(filter_t filter, BOOL *is_inbound, - BOOL *is_outbound, BOOL *ip_ipv4, BOOL *is_ipv6); +static void windivert_filter_analyze(filter_t filter, UINT64 flags, + BOOL *inbound, BOOL *outbound, BOOL *ipv4, BOOL *ipv6); static BOOL windivert_filter_test(filter_t filter, UINT16 ip, UINT8 protocol, UINT8 field, UINT32 arg); @@ -390,6 +454,12 @@ DEFINE_GUID(WINDIVERT_SUBLAYER_FORWARD_IPV4_GUID, DEFINE_GUID(WINDIVERT_SUBLAYER_FORWARD_IPV6_GUID, 0xE70D0973, 0x935F, 0x4790, 0x8E, 0x64, 0xF7, 0xF7, 0x36, 0x27, 0xA5, 0x8F); +DEFINE_GUID(WINDIVERT_SUBLAYER_FLOW_ESTABLISHED_IPV4_GUID, + 0x168A6CF7, 0x6080, 0x4905, + 0xAA, 0x72, 0x03, 0x74, 0x67, 0xCD, 0x4F, 0x19); +DEFINE_GUID(WINDIVERT_SUBLAYER_FLOW_ESTABLISHED_IPV6_GUID, + 0x9EAAEB28, 0xC1BB, 0x41F5, + 0x9F, 0x27, 0x18, 0xD4, 0xDF, 0xE1, 0x31, 0xEE); /* * WinDivert supported layers. @@ -404,7 +474,9 @@ static struct layer_s layer_inbound_network_ipv4_0 = L"" WINDIVERT_DEVICE_NAME L" filter network (inbound IPv4)", {0}, {0}, - windivert_classify_inbound_network_v4_callout, + windivert_inbound_network_v4_classify, + NULL, + UINT16_MAX }; static layer_t layer_inbound_network_ipv4 = &layer_inbound_network_ipv4_0; @@ -418,7 +490,9 @@ static struct layer_s layer_outbound_network_ipv4_0 = L"" WINDIVERT_DEVICE_NAME L" filter network (outbound IPv4)", {0}, {0}, - windivert_classify_outbound_network_v4_callout, + windivert_outbound_network_v4_classify, + NULL, + UINT16_MAX }; static layer_t layer_outbound_network_ipv4 = &layer_outbound_network_ipv4_0; @@ -432,7 +506,9 @@ static struct layer_s layer_inbound_network_ipv6_0 = L"" WINDIVERT_DEVICE_NAME L" filter network (inbound IPv6)", {0}, {0}, - windivert_classify_inbound_network_v6_callout, + windivert_inbound_network_v6_classify, + NULL, + UINT16_MAX }; static layer_t layer_inbound_network_ipv6 = &layer_inbound_network_ipv6_0; @@ -446,7 +522,9 @@ static struct layer_s layer_outbound_network_ipv6_0 = L"" WINDIVERT_DEVICE_NAME L" filter network (outbound IPv6)", {0}, {0}, - windivert_classify_outbound_network_v6_callout, + windivert_outbound_network_v6_classify, + NULL, + UINT16_MAX }; static layer_t layer_outbound_network_ipv6 = &layer_outbound_network_ipv6_0; @@ -460,7 +538,9 @@ static struct layer_s layer_forward_network_ipv4_0 = L"" WINDIVERT_DEVICE_NAME L" filter network (forward IPv4)", {0}, {0}, - windivert_classify_forward_network_v4_callout, + windivert_forward_network_v4_classify, + NULL, + UINT16_MAX }; static layer_t layer_forward_network_ipv4 = &layer_forward_network_ipv4_0; @@ -474,16 +554,54 @@ static struct layer_s layer_forward_network_ipv6_0 = L"" WINDIVERT_DEVICE_NAME L" filter network (forward IPv6)", {0}, {0}, - windivert_classify_forward_network_v6_callout, + windivert_forward_network_v6_classify, + NULL, + UINT16_MAX }; static layer_t layer_forward_network_ipv6 = &layer_forward_network_ipv6_0; +static struct layer_s layer_flow_established_ipv4_0 = +{ + L"" WINDIVERT_DEVICE_NAME L"_SubLayerFlowEstablishedIPv4", + L"" WINDIVERT_DEVICE_NAME L" sublayer flow established (IPv4)", + L"" WINDIVERT_DEVICE_NAME L"_CalloutFlowEstablishedIPv4", + L"" WINDIVERT_DEVICE_NAME L" callout flow established (IPv4)", + L"" WINDIVERT_DEVICE_NAME L"_FilterFlowEstablishedIPv4", + L"" WINDIVERT_DEVICE_NAME L" filter flow established (IPv4)", + {0}, + {0}, + windivert_flow_established_v4_classify, + windivert_flow_delete_notify, + 0 +}; +static layer_t layer_flow_established_ipv4 = &layer_flow_established_ipv4_0; + +static struct layer_s layer_flow_established_ipv6_0 = +{ + L"" WINDIVERT_DEVICE_NAME L"_SubLayerFlowEstablishedIPv6", + L"" WINDIVERT_DEVICE_NAME L" sublayer flow established (IPv6)", + L"" WINDIVERT_DEVICE_NAME L"_CalloutFlowEstablishedIPv6", + L"" WINDIVERT_DEVICE_NAME L" callout flow established (IPv6)", + L"" WINDIVERT_DEVICE_NAME L"_FilterFlowEstablishedIPv6", + L"" WINDIVERT_DEVICE_NAME L" filter flow established (IPv6)", + {0}, + {0}, + windivert_flow_established_v6_classify, + windivert_flow_delete_notify, + 0 +}; +static layer_t layer_flow_established_ipv6 = &layer_flow_established_ipv6_0; + /* * WinDivert malloc/free. */ static PVOID windivert_malloc(SIZE_T size, BOOL paged) { POOL_TYPE pool = (paged? PagedPool: non_paged_pool); + if (size == 0) + { + return NULL; + } return ExAllocatePoolWithTag(pool, size, WINDIVERT_TAG); } static VOID windivert_free(PVOID ptr) @@ -543,6 +661,10 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj, 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_flow_established_ipv4->layer_guid = + FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4; + layer_flow_established_ipv6->layer_guid = + FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6; layer_inbound_network_ipv4->sublayer_guid = WINDIVERT_SUBLAYER_INBOUND_IPV4_GUID; layer_outbound_network_ipv4->sublayer_guid = @@ -555,6 +677,10 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj, WINDIVERT_SUBLAYER_FORWARD_IPV4_GUID; layer_forward_network_ipv6->sublayer_guid = WINDIVERT_SUBLAYER_FORWARD_IPV6_GUID; + layer_flow_established_ipv4->sublayer_guid = + WINDIVERT_SUBLAYER_FLOW_ESTABLISHED_IPV4_GUID; + layer_flow_established_ipv6->sublayer_guid = + WINDIVERT_SUBLAYER_FLOW_ESTABLISHED_IPV6_GUID; // Configure ourself as a non-PnP driver: WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); @@ -725,6 +851,16 @@ driver_entry_sublayer_error: { goto driver_entry_sublayer_error; } + status = windivert_install_sublayer(layer_flow_established_ipv4); + if (!NT_SUCCESS(status)) + { + goto driver_entry_sublayer_error; + } + status = windivert_install_sublayer(layer_flow_established_ipv6); + if (!NT_SUCCESS(status)) + { + goto driver_entry_sublayer_error; + } status = FwpmTransactionCommit0(engine_handle); if (!NT_SUCCESS(status)) { @@ -796,6 +932,10 @@ static void windivert_driver_unload(void) &layer_forward_network_ipv4->sublayer_guid); FwpmSubLayerDeleteByKey0(engine_handle, &layer_forward_network_ipv6->sublayer_guid); + FwpmSubLayerDeleteByKey0(engine_handle, + &layer_flow_established_ipv4->sublayer_guid); + FwpmSubLayerDeleteByKey0(engine_handle, + &layer_flow_established_ipv6->sublayer_guid); status = FwpmTransactionCommit0(engine_handle); if (!NT_SUCCESS(status)) { @@ -814,10 +954,10 @@ static NTSTATUS windivert_install_sublayer(layer_t layer) NTSTATUS status; RtlZeroMemory(&sublayer, sizeof(sublayer)); - sublayer.subLayerKey = layer->sublayer_guid; + sublayer.subLayerKey = layer->sublayer_guid; sublayer.displayData.name = layer->sublayer_name; sublayer.displayData.description = layer->sublayer_desc; - sublayer.weight = UINT16_MAX; + sublayer.weight = layer->sublayer_weight; status = FwpmSubLayerAdd0(engine_handle, &sublayer, NULL); if (!NT_SUCCESS(status)) @@ -869,8 +1009,10 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request, { context->installed[i] = FALSE; } - context->on = FALSE; KeInitializeSpinLock(&context->lock); + InitializeListHead(&context->flow_set); + context->flow_v4_callout_id = 0; + context->flow_v6_callout_id = 0; InitializeListHead(&context->work_queue); InitializeListHead(&context->packet_queue); for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++) @@ -919,7 +1061,6 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request, DEBUG_ERROR("failed to create WFP engine handle", status); goto windivert_create_exit; } - context->state = WINDIVERT_CONTEXT_STATE_OPEN; windivert_create_exit: @@ -951,44 +1092,58 @@ windivert_create_exit: * Register all WFP callouts. */ static NTSTATUS windivert_install_callouts(context_t context, UINT8 layer, - BOOL is_inbound, BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6) + BOOL inbound, BOOL outbound, BOOL ipv4, BOOL ipv6) { UINT8 i, j; layer_t layers[WINDIVERT_CONTEXT_MAXLAYERS]; + UINT32 *callout_ids[WINDIVERT_CONTEXT_MAXLAYERS] = {NULL}; NTSTATUS status = STATUS_SUCCESS; i = 0; switch (layer) { case WINDIVERT_LAYER_NETWORK: - if (is_inbound && is_ipv4) + if (inbound && ipv4) { layers[i++] = layer_inbound_network_ipv4; } - if (is_outbound && is_ipv4) + if (outbound && ipv4) { layers[i++] = layer_outbound_network_ipv4; } - if (is_inbound && is_ipv6) + if (inbound && ipv6) { layers[i++] = layer_inbound_network_ipv6; } - if (is_outbound && is_ipv6) + if (outbound && ipv6) { layers[i++] = layer_outbound_network_ipv6; } break; case WINDIVERT_LAYER_NETWORK_FORWARD: - if (is_ipv4) + if (ipv4) { layers[i++] = layer_forward_network_ipv4; } - if (is_ipv6) + if (ipv6) { layers[i++] = layer_forward_network_ipv6; } break; + + case WINDIVERT_LAYER_FLOW: + if (ipv4) + { + callout_ids[i] = &context->flow_v4_callout_id; + layers[i++] = layer_flow_established_ipv4; + } + if (ipv6) + { + callout_ids[i] = &context->flow_v6_callout_id; + layers[i++] = layer_flow_established_ipv6; + } + break; default: return STATUS_INVALID_PARAMETER; @@ -996,7 +1151,8 @@ static NTSTATUS windivert_install_callouts(context_t context, UINT8 layer, for (j = 0; j < i; j++) { - status = windivert_install_callout(context, j, layers[j]); + status = windivert_install_callout(context, j, layers[j], + callout_ids[j]); if (!NT_SUCCESS(status)) { goto windivert_install_callouts_exit; @@ -1017,7 +1173,7 @@ windivert_install_callouts_exit: * Register a WFP callout. */ static NTSTATUS windivert_install_callout(context_t context, UINT idx, - layer_t layer) + layer_t layer, UINT32 *callout_id_ptr) { KLOCK_QUEUE_HANDLE lock_handle; FWPS_CALLOUT0 scallout; @@ -1026,6 +1182,7 @@ static NTSTATUS windivert_install_callout(context_t context, UINT idx, UINT64 weight; UINT32 priority; GUID callout_guid, filter_guid; + UINT32 callout_id; WDFDEVICE device; HANDLE engine_handle; NTSTATUS status; @@ -1048,9 +1205,9 @@ static NTSTATUS windivert_install_callout(context_t context, UINT idx, RtlZeroMemory(&scallout, sizeof(scallout)); scallout.calloutKey = callout_guid; - scallout.classifyFn = layer->callout; - scallout.notifyFn = windivert_notify_callout; - scallout.flowDeleteFn = NULL; + scallout.classifyFn = layer->classify; + scallout.notifyFn = windivert_notify; + scallout.flowDeleteFn = layer->flow_delete; RtlZeroMemory(&mcallout, sizeof(mcallout)); mcallout.calloutKey = callout_guid; mcallout.displayData.name = layer->callout_name; @@ -1068,12 +1225,18 @@ static NTSTATUS windivert_install_callout(context_t context, UINT idx, filter.weight.uint64 = &weight; filter.rawContext = (UINT64)context; status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(device), - &scallout, NULL); + &scallout, &callout_id); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to install WFP callout", status); return status; } + if (callout_id_ptr != NULL) + { + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + *callout_id_ptr = callout_id; + KeReleaseInStackQueuedSpinLock(&lock_handle); + } status = FwpmTransactionBegin0(engine_handle, 0); if (!NT_SUCCESS(status)) { @@ -1153,7 +1316,7 @@ windivert_uninstall_callouts_error: // RPC handle was closed first. So, this path is "normal" if // the user's app crashed or never closed the WinDivert handle. DEBUG_ERROR("failed to begin WFP transaction", status); - goto unregister_callouts; + goto windivert_uninstall_callouts_unregister; } for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++) { @@ -1171,10 +1334,10 @@ windivert_uninstall_callouts_error: filter_guid = context->filter_guid[i]; KeReleaseInStackQueuedSpinLock(&lock_handle); - if (!installed) - { - continue; - } + if (!installed) + { + continue; + } status = FwpmFilterDeleteByKey0(engine_handle, &filter_guid); if (!NT_SUCCESS(status)) { @@ -1191,15 +1354,16 @@ windivert_uninstall_callouts_error: if (!NT_SUCCESS(status)) { FwpmTransactionAbort0(engine_handle); - goto unregister_callouts; + goto windivert_uninstall_callouts_unregister; } status = FwpmTransactionCommit0(engine_handle); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to commit WFP transaction", status); - //fallthrough + // continue } -unregister_callouts: + +windivert_uninstall_callouts_unregister: for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++) { KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); @@ -1215,7 +1379,12 @@ unregister_callouts: { continue; } - FwpsCalloutUnregisterByKey0(&callout_guid); + status = FwpsCalloutUnregisterByKey0(&callout_guid); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to delete callout", status); + continue; + } } } @@ -1228,6 +1397,7 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object) PLIST_ENTRY entry; UINT i; context_t context = windivert_context_get(object); + flow_t flow; packet_t work, packet; WDFQUEUE read_queue; WDFWORKITEM worker; @@ -1240,7 +1410,8 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object) timestamp = KeQueryPerformanceCounter(NULL).QuadPart; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) + if (context->state != WINDIVERT_CONTEXT_STATE_OPENING && + context->state != WINDIVERT_CONTEXT_STATE_OPEN) { windivert_cleanup_error: KeReleaseInStackQueuedSpinLock(&lock_handle); @@ -1252,15 +1423,29 @@ windivert_cleanup_error: sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0); forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD); priority = context->priority; + while (!IsListEmpty(&context->flow_set)) + { + entry = RemoveHeadList(&context->flow_set); + flow = CONTAINING_RECORD(entry, struct flow_s, entry); + flow->deleted = TRUE; + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = FwpsFlowRemoveContext0(flow->flow_id, flow->layer_id, + flow->callout_id); + if (!NT_SUCCESS(status)) + { + windivert_free(flow); + } + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + } while (!IsListEmpty(&context->packet_queue)) { entry = RemoveHeadList(&context->packet_queue); packet = CONTAINING_RECORD(entry, struct packet_s, entry); context->packet_queue_length--; - context->packet_queue_size -= packet->data_len; + context->packet_queue_size -= packet->packet_len; KeReleaseInStackQueuedSpinLock(&lock_handle); timeout = WINDIVERT_TIMEOUT(context, packet->timestamp, timestamp); - if (!timeout) + if (!sniff_mode && !timeout) { windivert_reinject_packet(packet); } @@ -1282,7 +1467,7 @@ windivert_cleanup_error: KeReleaseInStackQueuedSpinLock(&lock_handle); work = CONTAINING_RECORD(entry, struct packet_s, entry); timeout = WINDIVERT_TIMEOUT(context, work->timestamp, timestamp); - if (!timeout) + if (!sniff_mode && !timeout) { windivert_reinject_packet(work); } @@ -1313,8 +1498,6 @@ windivert_cleanup_error: WdfWorkItemFlush(worker); WdfObjectDelete(worker); } - windivert_uninstall_callouts(context, WINDIVERT_CONTEXT_STATE_CLOSING); - FwpmEngineClose0(context->engine_handle); } /* @@ -1362,6 +1545,8 @@ extern VOID windivert_destroy(IN WDFOBJECT object) } filter = context->filter; KeReleaseInStackQueuedSpinLock(&lock_handle); + windivert_uninstall_callouts(context, WINDIVERT_CONTEXT_STATE_CLOSED); + FwpmEngineClose0(context->engine_handle); windivert_free(filter); } @@ -1383,6 +1568,13 @@ static NTSTATUS windivert_read(context_t context, WDFREQUEST request) KeReleaseInStackQueuedSpinLock(&lock_handle); return STATUS_INVALID_DEVICE_STATE; } + if ((context->flags & WINDIVERT_FLAG_SEND_ONLY) != 0) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("failed to inject; send-only flag is set", status); + return status; + } status = WdfRequestForwardToIoQueue(request, context->read_queue); KeReleaseInStackQueuedSpinLock(&lock_handle); if (!NT_SUCCESS(status)) @@ -1403,7 +1595,7 @@ static NTSTATUS windivert_read(context_t context, WDFREQUEST request) static void windivert_read_service_request(packet_t packet, WDFREQUEST request) { PMDL dst_mdl; - PVOID dst, src; + UINT8 *layer_data, *src, *dst; ULONG dst_len, src_len; req_context_t req_context; PWINDIVERT_ADDRESS addr; @@ -1412,69 +1604,81 @@ static void windivert_read_service_request(packet_t packet, WDFREQUEST request) DEBUG("SERVICE: servicing read request (request=%p, packet=%p)", request, packet); - status = WdfRequestRetrieveOutputWdmMdl(request, &dst_mdl); - if (!NT_SUCCESS(status)) + layer_data = (PVOID)packet->data; + switch (packet->layer) { - DEBUG_ERROR("failed to retrieve output MDL", status); - goto windivert_read_service_request_exit; - } - dst = MmGetSystemAddressForMdlSafe(dst_mdl, NormalPagePriority); - if (dst == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - DEBUG_ERROR("failed to get address of output MDL", status); - goto windivert_read_service_request_exit; - } + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: - dst_len = MmGetMdlByteCount(dst_mdl); - src_len = packet->data_len; - dst_len = (src_len < dst_len? src_len: dst_len); - src = packet->data; - RtlCopyMemory(dst, src, dst_len); + status = WdfRequestRetrieveOutputWdmMdl(request, &dst_mdl); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to retrieve output MDL", status); + goto windivert_read_service_request_exit; + } + dst = MmGetSystemAddressForMdlSafe(dst_mdl, NormalPagePriority); + if (dst == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to get address of output MDL", status); + goto windivert_read_service_request_exit; + } + + src = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_NETWORK_DATA, packet); + src_len = packet->packet_len; + dst_len = MmGetMdlByteCount(dst_mdl); + dst_len = (src_len < dst_len? src_len: dst_len); + RtlCopyMemory(dst, src, dst_len); + break; + + case WINDIVERT_LAYER_FLOW: + + status = STATUS_SUCCESS; + dst_len = 0; + break; + + default: + status = STATUS_INVALID_DEVICE_STATE; + DEBUG_ERROR("invalid packet layer", status); + goto windivert_read_service_request_exit; + } // Write the address information. req_context = windivert_req_context_get(request); addr = req_context->addr; if (addr != NULL) { - addr->Timestamp = (INT64)packet->timestamp; - addr->IfIdx = packet->if_idx; - addr->SubIfIdx = packet->sub_if_idx; - addr->Direction = packet->direction; - addr->Loopback = (packet->loopback? 1: 0); - addr->Impostor = (packet->impostor? 1: 0); - if (packet->loopback) + addr->Timestamp = (INT64)packet->timestamp; + addr->Layer = packet->layer; + addr->Event = packet->event; + addr->Outbound = (packet->outbound? 1: 0); + addr->Loopback = (packet->loopback? 1: 0); + addr->Impostor = (packet->impostor? 1: 0); + addr->IPv6 = (packet->ipv6? 1: 0); + addr->PseudoIPChecksum = (packet->pseudo_ip_checksum? 1: 0); + addr->PseudoTCPChecksum = (packet->pseudo_tcp_checksum? 1: 0); + addr->PseudoUDPChecksum = (packet->pseudo_udp_checksum? 1: 0); + addr->Reserved = 0; + switch (packet->layer) { - addr->PseudoIPChecksum = addr->PseudoTCPChecksum = - addr->PseudoUDPChecksum = 1; + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + RtlCopyMemory(&addr->Network, layer_data, + sizeof(WINDIVERT_NETWORK_DATA)); + break; + + case WINDIVERT_LAYER_FLOW: + RtlCopyMemory(&addr->Flow, layer_data, + sizeof(WINDIVERT_FLOW_DATA)); + break; + + default: + break; } - else if (packet->forward) - { - addr->PseudoIPChecksum = addr->PseudoTCPChecksum = - addr->PseudoUDPChecksum = 0; - } - else if (packet->direction == WINDIVERT_DIRECTION_OUTBOUND) - { - addr->PseudoIPChecksum = - (UINT8)packet->checksums.Transmit.IpHeaderChecksum; - addr->PseudoTCPChecksum = - (UINT8)packet->checksums.Transmit.TcpChecksum; - addr->PseudoUDPChecksum = - (UINT8)packet->checksums.Transmit.UdpChecksum; - } - else - { - addr->PseudoIPChecksum = - (UINT8)packet->checksums.Receive.IpChecksumSucceeded; - addr->PseudoTCPChecksum = - (UINT8)packet->checksums.Receive.TcpChecksumSucceeded; - addr->PseudoUDPChecksum = - (UINT8)packet->checksums.Receive.UdpChecksumSucceeded; - } - addr->Reserved = 0; } windivert_read_service_request_exit: + if (NT_SUCCESS(status)) { WdfRequestCompleteWithInformation(request, status, dst_len); @@ -1523,7 +1727,7 @@ static void windivert_read_service(context_t context) } } context->packet_queue_length--; - context->packet_queue_size -= packet->data_len; + context->packet_queue_size -= packet->packet_len; KeReleaseInStackQueuedSpinLock(&lock_handle); if (!timeout) @@ -1550,23 +1754,40 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, UINT data_len; PWINDIVERT_IPHDR ip_header; PWINDIVERT_IPV6HDR ipv6_header; - BOOL is_ipv4; + BOOL ipv4; UINT8 layer; UINT32 priority; UINT64 flags; HANDLE handle, compl_handle; PNET_BUFFER_LIST buffers = NULL; - NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksums_info; NTSTATUS status = STATUS_SUCCESS; DEBUG("WRITE: writing/injecting a packet (context=%p, request=%p)", context, request); + + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + goto windivert_write_exit; + } + layer = context->layer; + priority = context->priority; + flags = context->flags; + KeReleaseInStackQueuedSpinLock(&lock_handle); - if (addr->Direction != WINDIVERT_DIRECTION_INBOUND && - addr->Direction != WINDIVERT_DIRECTION_OUTBOUND) + if ((flags & WINDIVERT_FLAG_RECV_ONLY) != 0) { status = STATUS_INVALID_PARAMETER; - DEBUG_ERROR("failed to inject packet; invalid direction", status); + DEBUG_ERROR("failed to inject; recv-only flag is set", status); + goto windivert_write_exit; + } + + if (layer == WINDIVERT_LAYER_FLOW) + { + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("failed to inject at FLOW layer", status); goto windivert_write_exit; } @@ -1612,7 +1833,7 @@ windivert_write_bad_packet: { goto windivert_write_bad_packet; } - is_ipv4 = TRUE; + ipv4 = TRUE; break; case 6: if (data_len < sizeof(WINDIVERT_IPV6HDR)) @@ -1625,7 +1846,7 @@ windivert_write_bad_packet: { goto windivert_write_bad_packet; } - is_ipv4 = FALSE; + ipv4 = FALSE; break; default: goto windivert_write_bad_packet; @@ -1636,7 +1857,7 @@ windivert_write_bad_packet: (char *)data + sizeof(WINDIVERT_IPHDR), data_len - sizeof(WINDIVERT_IPHDR)); } - if (addr->Impostor && !windivert_decrement_ttl(data_copy, is_ipv4, + if (addr->Impostor && !windivert_decrement_ttl(data_copy, ipv4, (addr->PseudoIPChecksum == 0))) { status = STATUS_HOPLIMIT_EXCEEDED; @@ -1661,62 +1882,16 @@ windivert_write_bad_packet: goto windivert_write_exit; } - KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) - { - KeReleaseInStackQueuedSpinLock(&lock_handle); - status = STATUS_INVALID_DEVICE_STATE; - goto windivert_write_exit; - } - layer = context->layer; - priority = context->priority; - flags = context->flags; - KeReleaseInStackQueuedSpinLock(&lock_handle); - - if (layer != WINDIVERT_LAYER_NETWORK_FORWARD) - { - checksums_info.Value = NET_BUFFER_LIST_INFO(buffers, - TcpIpChecksumNetBufferListInfo); - if (addr->Direction == WINDIVERT_DIRECTION_OUTBOUND) - { - checksums_info.Transmit.TcpChecksum = - (addr->PseudoTCPChecksum == 0? 0: 1); - checksums_info.Transmit.UdpChecksum = - (addr->PseudoUDPChecksum == 0? 0: 1); - checksums_info.Transmit.IpHeaderChecksum = - (addr->PseudoIPChecksum == 0? 0: 1); - } - else - { - checksums_info.Receive.TcpChecksumSucceeded = - (addr->PseudoTCPChecksum == 0? 0: 1); - checksums_info.Receive.UdpChecksumSucceeded = - (addr->PseudoUDPChecksum == 0? 0: 1); - checksums_info.Receive.IpChecksumSucceeded = - (addr->PseudoIPChecksum == 0? 0: 1); - } - NET_BUFFER_LIST_INFO(buffers, TcpIpChecksumNetBufferListInfo) = - checksums_info.Value; - } - else - { - if (addr->PseudoTCPChecksum != 0 || addr->PseudoUDPChecksum != 0 || - addr->PseudoIPChecksum != 0) - { - status = STATUS_INVALID_PARAMETER; - goto windivert_write_exit; - } - } - - handle = (is_ipv4? inject_handle: injectv6_handle); + handle = (ipv4? inject_handle: injectv6_handle); compl_handle = ((flags & WINDIVERT_FLAG_DEBUG) != 0? (HANDLE)request: NULL); if (layer == WINDIVERT_LAYER_NETWORK_FORWARD) { status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0, - (is_ipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID, - addr->IfIdx, buffers, windivert_inject_complete, compl_handle); + (ipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID, + addr->Network.IfIdx, buffers, windivert_inject_complete, + compl_handle); } - else if (addr->Direction == WINDIVERT_DIRECTION_OUTBOUND) + else if (addr->Outbound != 0) { status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0, UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete, @@ -1725,8 +1900,9 @@ windivert_write_bad_packet: else { status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0, - UNSPECIFIED_COMPARTMENT_ID, addr->IfIdx, addr->SubIfIdx, buffers, - windivert_inject_complete, compl_handle); + UNSPECIFIED_COMPARTMENT_ID, addr->Network.IfIdx, + addr->Network.SubIfIdx, buffers, windivert_inject_complete, + compl_handle); } windivert_write_exit: @@ -1740,7 +1916,7 @@ windivert_write_exit: } else { - DEBUG_ERROR("failed to inject packet", status); + // Request completed in windivert_ioctl() if (buffers != NULL) { FwpsFreeNetBufferList0(buffers); @@ -1788,6 +1964,26 @@ static void NTAPI windivert_inject_complete(VOID *context, 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; + size_t length; + 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. */ @@ -1983,7 +2179,19 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, case IOCTL_WINDIVERT_START_FILTER: { - BOOL is_inbound, is_outbound, is_ipv4, is_ipv6; + BOOL inbound, outbound, ipv4, ipv6; + + filter = NULL; + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPENING) + { +windivert_ioctl_bad_start_state: + KeReleaseInStackQueuedSpinLock(&lock_handle); + windivert_free(filter); + status = STATUS_INVALID_DEVICE_STATE; + goto windivert_ioctl_exit; + } + KeReleaseInStackQueuedSpinLock(&lock_handle); filter0 = (windivert_ioctl_filter_t)outbuf; filter0_len = outbuflen; @@ -1996,37 +2204,52 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, } KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + if (context->state != WINDIVERT_CONTEXT_STATE_OPENING) { - KeReleaseInStackQueuedSpinLock(&lock_handle); - windivert_free(filter); - status = STATUS_INVALID_DEVICE_STATE; - goto windivert_ioctl_exit; + goto windivert_ioctl_bad_start_state; } - context->on = TRUE; - context->filter = filter; layer = context->layer; + flags = context->flags; + switch (layer) + { + case WINDIVERT_LAYER_FLOW: + if ((flags & WINDIVERT_FLAG_SNIFF) == 0 || + (flags & WINDIVERT_FLAG_RECV_ONLY) == 0) + { + goto windivert_ioctl_bad_start_state; + } + break; + default: + break; + } + context->state = WINDIVERT_CONTEXT_STATE_OPEN; + context->filter = filter; 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); + windivert_filter_analyze(filter, flags, &inbound, &outbound, + &ipv4, &ipv6); + status = windivert_install_callouts(context, layer, inbound, + outbound, ipv4, ipv6); break; } case IOCTL_WINDIVERT_SET_LAYER: ioctl = (windivert_ioctl_t)inbuf; - if (ioctl->arg > WINDIVERT_LAYER_MAX) + switch (ioctl->arg) { - status = STATUS_INVALID_PARAMETER; - DEBUG_ERROR("failed to set layer; value too big", status); - goto windivert_ioctl_exit; + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + case WINDIVERT_LAYER_FLOW: + break; + default: + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("failed to set layer; invalid value", status); + goto windivert_ioctl_exit; } layer = (UINT8)ioctl->arg; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + if (context->state != WINDIVERT_CONTEXT_STATE_OPENING) { KeReleaseInStackQueuedSpinLock(&lock_handle); status = STATUS_INVALID_DEVICE_STATE; @@ -2048,7 +2271,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, } priority = WINDIVERT_CONTEXT_PRIORITY((UINT32)ioctl->arg); KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + if (context->state != WINDIVERT_CONTEXT_STATE_OPENING) { KeReleaseInStackQueuedSpinLock(&lock_handle); status = STATUS_INVALID_DEVICE_STATE; @@ -2069,7 +2292,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, } flags = ioctl->arg; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + if (context->state != WINDIVERT_CONTEXT_STATE_OPENING) { KeReleaseInStackQueuedSpinLock(&lock_handle); status = STATUS_INVALID_DEVICE_STATE; @@ -2192,9 +2415,9 @@ windivert_ioctl_exit: } /* - * WinDivert notify callout. + * WinDivert notify function. */ -static NTSTATUS windivert_notify_callout(IN FWPS_CALLOUT_NOTIFY_TYPE type, +static NTSTATUS windivert_notify(IN FWPS_CALLOUT_NOTIFY_TYPE type, IN const GUID *filter_key, IN const FWPS_FILTER0 *filter) { UNREFERENCED_PARAMETER(type); @@ -2204,154 +2427,212 @@ static NTSTATUS windivert_notify_callout(IN FWPS_CALLOUT_NOTIFY_TYPE type, } /* - * WinDivert classify outbound IPv4 callout. + * WinDivert classify outbound IPv4 function. */ -static void windivert_classify_outbound_network_v4_callout( +static void windivert_outbound_network_v4_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result) { - windivert_classify_callout((context_t)filter->context, - WINDIVERT_DIRECTION_OUTBOUND, - fixed_vals->incomingValue[ - FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32, - fixed_vals->incomingValue[ - FWPS_FIELD_OUTBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32, - TRUE, - (fixed_vals->incomingValue[ - FWPS_FIELD_OUTBOUND_IPPACKET_V4_FLAGS].value.uint32 & - FWP_CONDITION_FLAG_IS_LOOPBACK) != 0, - 0, data, flow_context, result); + WINDIVERT_NETWORK_DATA network_data; + BOOL loopback; + + if ((result->rights & FWPS_RIGHT_ACTION_WRITE) == 0 || data == NULL) + { + return; + } + + network_data.IfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32, + network_data.SubIfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32, + loopback = ((fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V4_FLAGS].value.uint32 & + FWP_CONDITION_FLAG_IS_LOOPBACK) != 0); + + windivert_network_classify((context_t)filter->context, &network_data, + /*ipv4=*/TRUE, /*outbound=*/TRUE, loopback, /*advance=*/0, data, + result); } /* - * WinDivert classify outbound IPv6 callout. + * WinDivert classify outbound IPv6 function. */ -static void windivert_classify_outbound_network_v6_callout( +static void windivert_outbound_network_v6_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result) { - windivert_classify_callout((context_t)filter->context, - WINDIVERT_DIRECTION_OUTBOUND, - fixed_vals->incomingValue[ - FWPS_FIELD_OUTBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32, - fixed_vals->incomingValue[ - FWPS_FIELD_OUTBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32, - FALSE, - (fixed_vals->incomingValue[ - FWPS_FIELD_OUTBOUND_IPPACKET_V6_FLAGS].value.uint32 & - FWP_CONDITION_FLAG_IS_LOOPBACK) != 0, - 0, data, flow_context, result); + WINDIVERT_NETWORK_DATA network_data; + BOOL loopback; + + if ((result->rights & FWPS_RIGHT_ACTION_WRITE) == 0 || data == NULL) + { + return; + } + + network_data.IfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32, + network_data.SubIfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32, + loopback = ((fixed_vals->incomingValue[ + FWPS_FIELD_OUTBOUND_IPPACKET_V6_FLAGS].value.uint32 & + FWP_CONDITION_FLAG_IS_LOOPBACK) != 0); + + windivert_network_classify((context_t)filter->context, &network_data, + /*ipv4=*/FALSE, /*outbound=*/TRUE, loopback, /*advance=*/0, + data, result); } /* - * WinDivert classify inbound IPv4 callout. + * WinDivert classify inbound IPv4 function. */ -static void windivert_classify_inbound_network_v4_callout( +static void windivert_inbound_network_v4_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result) { - UINT advance = meta_vals->ipHeaderSize; - windivert_classify_callout((context_t)filter->context, - WINDIVERT_DIRECTION_INBOUND, - fixed_vals->incomingValue[ - FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32, - fixed_vals->incomingValue[ - FWPS_FIELD_INBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32, - TRUE, - (fixed_vals->incomingValue[ - FWPS_FIELD_INBOUND_IPPACKET_V4_FLAGS].value.uint32 & - FWP_CONDITION_FLAG_IS_LOOPBACK) != 0, - advance, data, flow_context, result); + WINDIVERT_NETWORK_DATA network_data; + UINT advance; + BOOL loopback; + + if ((result->rights & FWPS_RIGHT_ACTION_WRITE) == 0 || data == NULL) + { + return; + } + + loopback = ((fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V4_FLAGS].value.uint32 & + FWP_CONDITION_FLAG_IS_LOOPBACK) != 0); + if (loopback) + { + result->actionType = FWP_ACTION_CONTINUE; + return; + } + + network_data.IfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V4_INTERFACE_INDEX].value.uint32; + network_data.SubIfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V4_SUB_INTERFACE_INDEX].value.uint32; + advance = meta_vals->ipHeaderSize; + + windivert_network_classify((context_t)filter->context, &network_data, + /*ipv4=*/TRUE, /*outbound=*/FALSE, loopback, advance, data, result); } /* - * WinDivert classify inbound IPv6 callout. + * WinDivert classify inbound IPv6 function. */ -static void windivert_classify_inbound_network_v6_callout( +static void windivert_inbound_network_v6_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result) { - UINT advance = meta_vals->ipHeaderSize; - windivert_classify_callout((context_t)filter->context, - WINDIVERT_DIRECTION_INBOUND, - fixed_vals->incomingValue[ - FWPS_FIELD_INBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32, - fixed_vals->incomingValue[ - FWPS_FIELD_INBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32, - FALSE, - (fixed_vals->incomingValue[ - FWPS_FIELD_INBOUND_IPPACKET_V6_FLAGS].value.uint32 & - FWP_CONDITION_FLAG_IS_LOOPBACK) != 0, - advance, data, flow_context, result); + WINDIVERT_NETWORK_DATA network_data; + UINT advance; + BOOL loopback; + + if ((result->rights & FWPS_RIGHT_ACTION_WRITE) == 0 || data == NULL) + { + return; + } + + loopback = ((fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V6_FLAGS].value.uint32 & + FWP_CONDITION_FLAG_IS_LOOPBACK) != 0); + if (loopback) + { + result->actionType = FWP_ACTION_CONTINUE; + return; + } + + network_data.IfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V6_INTERFACE_INDEX].value.uint32; + network_data.SubIfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_INBOUND_IPPACKET_V6_SUB_INTERFACE_INDEX].value.uint32; + advance = meta_vals->ipHeaderSize; + + windivert_network_classify((context_t)filter->context, &network_data, + /*ipv4=*/FALSE, /*outbound=*/FALSE, loopback, advance, data, result); } /* - * WinDivert classify forward IPv4 callout. + * WinDivert classify forward IPv4 function. */ -static void windivert_classify_forward_network_v4_callout( +static void windivert_forward_network_v4_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result) { - windivert_classify_callout((context_t)filter->context, - WINDIVERT_DIRECTION_OUTBOUND, - fixed_vals->incomingValue[ - FWPS_FIELD_IPFORWARD_V4_DESTINATION_INTERFACE_INDEX].value.uint32, - 0, TRUE, FALSE, 0, data, flow_context, result); + WINDIVERT_NETWORK_DATA network_data; + + if ((result->rights & FWPS_RIGHT_ACTION_WRITE) == 0 || data == NULL) + { + return; + } + + network_data.IfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_IPFORWARD_V4_DESTINATION_INTERFACE_INDEX].value.uint32; + network_data.SubIfIdx = 0; + + windivert_network_classify((context_t)filter->context, &network_data, + /*ipv4=*/TRUE, /*outbound=*/TRUE, /*loopback=*/FALSE, /*advance=*/0, + data, result); } /* - * WinDivert classify forward IPv6 callout. + * WinDivert classify forward IPv6 function. */ -static void windivert_classify_forward_network_v6_callout( +static void windivert_forward_network_v6_classify( IN const FWPS_INCOMING_VALUES0 *fixed_vals, IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, const FWPS_FILTER0 *filter, IN UINT64 flow_context, OUT FWPS_CLASSIFY_OUT0 *result) { - windivert_classify_callout((context_t)filter->context, - WINDIVERT_DIRECTION_OUTBOUND, - fixed_vals->incomingValue[ - FWPS_FIELD_IPFORWARD_V6_DESTINATION_INTERFACE_INDEX].value.uint32, - 0, FALSE, FALSE, 0, data, flow_context, result); + WINDIVERT_NETWORK_DATA network_data; + + if ((result->rights & FWPS_RIGHT_ACTION_WRITE) == 0 || data == NULL) + { + return; + } + + network_data.IfIdx = fixed_vals->incomingValue[ + FWPS_FIELD_IPFORWARD_V6_DESTINATION_INTERFACE_INDEX].value.uint32; + network_data.SubIfIdx = 0; + + windivert_network_classify((context_t)filter->context, &network_data, + /*ipv4=*/FALSE, /*outbound=*/TRUE, /*loopback=*/FALSE, /*advance=*/0, + data, result); } /* - * WinDivert classify callout. + * WinDivert network classify function. */ -static void windivert_classify_callout(context_t context, IN UINT8 direction, - IN UINT32 if_idx, IN UINT32 sub_if_idx, IN BOOL is_ipv4, IN BOOL loopback, - IN UINT advance, IN OUT void *data, IN UINT64 flow_context, +static void windivert_network_classify(context_t context, + IN PWINDIVERT_NETWORK_DATA network_data, IN BOOL ipv4, IN BOOL outbound, + IN BOOL loopback, IN UINT advance, IN OUT void *data, OUT FWPS_CLASSIFY_OUT0 *result) { KLOCK_QUEUE_HANDLE lock_handle; FWPS_PACKET_INJECTION_STATE packet_state; HANDLE packet_context; UINT32 priority, packet_priority; + UINT64 flags; + WINDIVERT_LAYER layer; PNET_BUFFER_LIST buffers; PNET_BUFFER buffer, buffer_fst, buffer_itr; - BOOL outbound, impostor, sniff_mode, drop_mode, forward, ok; + BOOL impostor, sniff_mode, ok; WDFOBJECT object; PLIST_ENTRY old_entry; filter_t filter; LONGLONG timestamp; NTSTATUS status; - // Basic checks: - if (!(result->rights & FWPS_RIGHT_ACTION_WRITE) || data == NULL) - { - return; - } - result->actionType = FWP_ACTION_CONTINUE; buffers = (PNET_BUFFER_LIST)data; buffer = NET_BUFFER_LIST_FIRST_NB(buffers); @@ -2361,7 +2642,7 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, // should have already been indicated. return; } - if (is_ipv4) + if (ipv4) { packet_state = FwpsQueryPacketInjectionState0(inject_handle, buffers, &packet_context); @@ -2378,11 +2659,10 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, KeReleaseInStackQueuedSpinLock(&lock_handle); return; } - sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0); - drop_mode = ((context->flags & WINDIVERT_FLAG_DROP) != 0); - forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD); + flags = context->flags; priority = context->priority; filter = context->filter; + layer = context->layer; object = (WDFOBJECT)context->object; WdfObjectReference(object); KeReleaseInStackQueuedSpinLock(&lock_handle); @@ -2406,13 +2686,6 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, impostor = TRUE; } - // Loopback packets are considered outbound only. - if (loopback && direction == WINDIVERT_DIRECTION_INBOUND) - { - WdfObjectDereference(object); - return; - } - // Get the timestamp. timestamp = KeQueryPerformanceCounter(NULL).QuadPart; @@ -2441,11 +2714,10 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, // Find the first NET_BUFFER we need to queue: buffer_fst = buffer; - outbound = (direction == WINDIVERT_DIRECTION_OUTBOUND); do { - BOOL match = windivert_filter(buffer_fst, if_idx, sub_if_idx, - outbound, is_ipv4, impostor, loopback, filter); + BOOL match = windivert_filter(buffer_fst, network_data, + /*flow_data=*/NULL, ipv4, outbound, loopback, impostor, filter); if (match) { break; @@ -2470,22 +2742,25 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, // STEP (1): Queue all non-matching packets up to buffer_fst. buffer_itr = buffer; + sniff_mode = ((flags & WINDIVERT_FLAG_SNIFF) != 0); while (!sniff_mode && buffer_itr != buffer_fst) { - ok = windivert_queue_work(context, sniff_mode, drop_mode, buffers, - buffer_itr, direction, if_idx, sub_if_idx, is_ipv4, forward, - impostor, loopback, FALSE, priority, timestamp); + ok = windivert_queue_work(context, buffer_itr, buffers, network_data, + /*flow_data=*/NULL, layer, /*event=*/WINDIVERT_EVENT_NETWORK_PACKET, + flags, priority, ipv4, outbound, loopback, impostor, + /*match=*/FALSE, timestamp); if (!ok) { - goto windivert_classify_callout_exit; + goto windivert_network_classify_exit; } buffer_itr = NET_BUFFER_NEXT_NB(buffer_itr); } // STEP (2): Queue the first matching packet buffer_fst: - ok = windivert_queue_work(context, sniff_mode, drop_mode, buffers, - buffer_fst, direction, if_idx, sub_if_idx, is_ipv4, forward, impostor, - loopback, TRUE, priority, timestamp); + ok = windivert_queue_work(context, buffer_itr, buffers, network_data, + /*flow_data=*/NULL, layer, /*event=*/WINDIVERT_EVENT_NETWORK_PACKET, + flags, priority, ipv4, outbound, loopback, impostor, /*match=*/TRUE, + timestamp); if (advance != 0) { // Advance the NET_BUFFER to its original position. Note that we can @@ -2496,26 +2771,27 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, } if (!ok) { - goto windivert_classify_callout_exit; + goto windivert_network_classify_exit; } // STEP (3): Queue all remaining packets: buffer_itr = NET_BUFFER_NEXT_NB(buffer_fst); while (buffer_itr != NULL) { - BOOL match = windivert_filter(buffer_itr, if_idx, sub_if_idx, - outbound, is_ipv4, impostor, loopback, filter); - ok = windivert_queue_work(context, sniff_mode, drop_mode, buffers, - buffer_itr, direction, if_idx, sub_if_idx, is_ipv4, forward, - impostor, loopback, match, priority, timestamp); + BOOL match = windivert_filter(buffer_itr, network_data, + /*flow_data=*/NULL, ipv4, outbound, loopback, impostor, filter); + ok = windivert_queue_work(context, buffer_itr, buffers, network_data, + /*flow_data=*/NULL, layer, /*event=*/WINDIVERT_EVENT_NETWORK_PACKET, + flags, priority, ipv4, outbound, loopback, impostor, match, + timestamp); if (!ok) { - goto windivert_classify_callout_exit; + goto windivert_network_classify_exit; } buffer_itr = NET_BUFFER_NEXT_NB(buffer_itr); } -windivert_classify_callout_exit: +windivert_network_classify_exit: WdfObjectDereference(object); if (!sniff_mode) @@ -2526,6 +2802,276 @@ windivert_classify_callout_exit: } } +/* + * WinDivert classify flow established IPv4 function. + */ +static void windivert_flow_established_v4_classify( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result) +{ + WINDIVERT_FLOW_DATA flow_data; + BOOL outbound, loopback; + UINT64 flow_id; + + flow_data.ProcessId = (UINT32)meta_vals->processId; + flow_data.LocalAddr[0] = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_ADDRESS].value.uint32; + flow_data.LocalAddr[1] = 0x0000FFFF; + flow_data.LocalAddr[2] = 0; + flow_data.LocalAddr[3] = 0; + flow_data.RemoteAddr[0] = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_ADDRESS].value.uint32; + flow_data.RemoteAddr[1] = 0x0000FFFF; + flow_data.RemoteAddr[2] = 0; + flow_data.RemoteAddr[3] = 0; + flow_data.LocalPort = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_LOCAL_PORT].value.uint16; + flow_data.RemotePort = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_REMOTE_PORT].value.uint16; + flow_data.Protocol = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL].value.uint8; + + outbound = (fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.uint32 == + FWP_DIRECTION_OUTBOUND); + loopback = ((fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_FLAGS].value.uint32 & + FWP_CONDITION_FLAG_IS_LOOPBACK) != 0); + flow_id = meta_vals->flowHandle; + + windivert_flow_established_classify((context_t)filter->context, + flow_id, &flow_data, /*ipv4=*/TRUE, outbound, loopback, result); +} + +/* + * WinDivert classify flow established IPv6 function. + */ +static void windivert_flow_established_v6_classify( + IN const FWPS_INCOMING_VALUES0 *fixed_vals, + IN const FWPS_INCOMING_METADATA_VALUES0 *meta_vals, IN OUT void *data, + const FWPS_FILTER0 *filter, IN UINT64 flow_context, + OUT FWPS_CLASSIFY_OUT0 *result) +{ + WINDIVERT_FLOW_DATA flow_data; + BOOL outbound, loopback; + UINT64 flow_id; + UINT8 *addr; + INT i; + + // IPv6 assumes host byte order, so convert: + flow_data.ProcessId = (UINT32)meta_vals->processId; + addr = (UINT8 *)&flow_data.LocalAddr; + for (i = sizeof(flow_data.LocalAddr)-1; i >= 0; i--) + { + addr[sizeof(flow_data.LocalAddr)-i-1] = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_ADDRESS] + .value.byteArray16->byteArray16[i]; + } + for (i = sizeof(flow_data.RemoteAddr)-1; i >= 0; i--) + { + addr[sizeof(flow_data.RemoteAddr)-i-1] = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_ADDRESS] + .value.byteArray16->byteArray16[i]; + } + flow_data.LocalPort = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_LOCAL_PORT].value.uint16; + flow_data.RemotePort = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_REMOTE_PORT].value.uint16; + flow_data.Protocol = fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL].value.uint8; + + outbound = (fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_DIRECTION].value.uint32 == + FWP_DIRECTION_OUTBOUND); + loopback = ((fixed_vals->incomingValue[ + FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_FLAGS].value.uint32 & + FWP_CONDITION_FLAG_IS_LOOPBACK) != 0); + flow_id = meta_vals->flowHandle; + + windivert_flow_established_classify((context_t)filter->context, + flow_id, &flow_data, /*ipv4=*/FALSE, outbound, loopback, result); +} + +/* + * WinDivert flow established classify function. + */ +static void windivert_flow_established_classify(context_t context, + IN UINT64 flow_id, IN PWINDIVERT_FLOW_DATA flow_data, IN BOOL ipv4, + IN BOOL outbound, IN BOOL loopback, OUT FWPS_CLASSIFY_OUT0 *result) +{ + KLOCK_QUEUE_HANDLE lock_handle; + UINT64 flags; + UINT32 callout_id; + UINT16 layer_id; + BOOL match, ok; + WDFOBJECT object; + filter_t filter; + LONGLONG timestamp; + flow_t flow; + NTSTATUS status; + + // Basic checks: + if (!(result->rights & FWPS_RIGHT_ACTION_WRITE)) + { + return; + } + + // Get the timestamp. + timestamp = KeQueryPerformanceCounter(NULL).QuadPart; + + result->actionType = FWP_ACTION_CONTINUE; + + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + return; + } + filter = context->filter; + flags = context->flags; + callout_id = (ipv4? context->flow_v4_callout_id: + context->flow_v6_callout_id); + object = (WDFOBJECT)context->object; + + // Reference only released once the flow has been deleted. This is to + // prevent the callout being unregistered while flow deletions are still + // pending, causing the operation to fail with STATUS_DEVICE_BUSY. + WdfObjectReference(object); + KeReleaseInStackQueuedSpinLock(&lock_handle); + + match = windivert_filter(/*buffer=*/NULL, /*network_data=*/NULL, + flow_data, ipv4, outbound, loopback, /*impostor=*/FALSE, filter); + if (match) + { + ok = windivert_queue_work(context, /*buffer=*/NULL, /*buffers=*/NULL, + /*network_data=*/NULL, flow_data, /*layer=*/WINDIVERT_LAYER_FLOW, + /*event=*/WINDIVERT_EVENT_FLOW_ESTABLISHED, flags, /*priority=*/0, + ipv4, outbound, loopback, /*impostor=*/FALSE, match, timestamp); + if (!ok) + { + WdfObjectDereference(object); + return; + } + } + + // Associate a context with the flow. This is so we can detect when + // the flow is deleted. + flow = windivert_malloc(sizeof(struct flow_s), FALSE); + if (flow == NULL) + { + WdfObjectDereference(object); + return; + } + layer_id = (ipv4? FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4: + FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6); + flow->context = 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) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + windivert_free(flow); + WdfObjectDereference(object); + return; + } + 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; + } + KeReleaseInStackQueuedSpinLock(&lock_handle); +} + +/* + * WinDivert flow delete notify function. + */ +static void windivert_flow_delete_notify(UINT16 layer_id, UINT32 callout_id, + UINT64 flow_context) +{ + KLOCK_QUEUE_HANDLE lock_handle; + UINT64 flags; + BOOL match, cleanup; + WDFOBJECT object; + context_t context; + filter_t filter; + LONGLONG timestamp; + flow_t flow; + + flow = (flow_t)flow_context; + if (flow == NULL) + { + return; + } + + timestamp = KeQueryPerformanceCounter(NULL).QuadPart; + context = flow->context; + + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + object = (WDFOBJECT)context->object; // referenced in flow_established. + if (flow->inserted && !flow->deleted) + { + RemoveEntryList(&flow->entry); + } + flow->deleted = TRUE; + cleanup = flow->inserted; + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + goto windivert_flow_delete_notify_exit; + } + filter = context->filter; + flags = context->flags; + KeReleaseInStackQueuedSpinLock(&lock_handle); + + match = windivert_filter(/*buffer=*/NULL, /*network_data=*/NULL, + &flow->data, !flow->ipv6, flow->outbound, flow->loopback, + /*impostor=*/FALSE, filter); + if (match) + { + (VOID)windivert_queue_work(context, /*buffer=*/NULL, /*buffers=*/NULL, + /*network_data=*/NULL, &flow->data, /*layer=*/WINDIVERT_LAYER_FLOW, + /*event=*/WINDIVERT_EVENT_FLOW_DELETED, flags, /*priority=*/0, + !flow->ipv6, flow->outbound, flow->loopback, /*impostor=*/FALSE, + match, timestamp); + } + +windivert_flow_delete_notify_exit: + + if (cleanup) + { + windivert_free(flow); + WdfObjectDereference(object); + } +} + /* * WinDivert work item routine for out-of-band filtering. */ @@ -2563,63 +3109,113 @@ VOID windivert_worker(IN WDFWORKITEM item) /* * Queue work. */ -static BOOL windivert_queue_work(context_t context, BOOL sniff_mode, - BOOL drop_mode, PNET_BUFFER_LIST buffers, PNET_BUFFER buffer, - UINT8 direction, UINT32 if_idx, UINT32 sub_if_idx, BOOL is_ipv4, - BOOL forward, BOOL impostor, BOOL loopback, BOOL match, UINT32 priority, +static BOOL windivert_queue_work(context_t context, PNET_BUFFER buffer, + PNET_BUFFER_LIST buffers, PWINDIVERT_NETWORK_DATA network_data, + PWINDIVERT_FLOW_DATA flow_data, WINDIVERT_LAYER layer, + WINDIVERT_EVENT event, UINT64 flags, UINT32 priority, BOOL ipv4, + BOOL outbound, BOOL loopback, BOOL impostor, BOOL match, LONGLONG timestamp) { KLOCK_QUEUE_HANDLE lock_handle; packet_t work; - UINT data_len; - PVOID data; + ULONG packet_len; + PVOID packet_data; + UINT8 *data; PLIST_ENTRY old_entry; + NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksums; + BOOL pseudo_ip_checksum, pseudo_tcp_checksum, pseudo_udp_checksum; - if (!match && sniff_mode) + if (!match && (flags & WINDIVERT_FLAG_SNIFF) != 0) { return TRUE; } - if (match && drop_mode) + if (match && (flags & WINDIVERT_FLAG_DROP) != 0) { return TRUE; } - work = (packet_t)windivert_malloc(sizeof(struct packet_s), FALSE); - if (work == NULL) + // Copy the packet & layer data. + switch (layer) { - return TRUE; + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + packet_len = NET_BUFFER_DATA_LENGTH(buffer); + if (packet_len > UINT16_MAX) + { + // Cannot handle oversized packet + return TRUE; + } + work = (packet_t)windivert_malloc( + WINDIVERT_PACKET_SIZE(WINDIVERT_NETWORK_DATA, packet_len), + FALSE); + if (work == NULL) + { + return TRUE; + } + work->packet_len = (UINT32)packet_len; + data = WINDIVERT_LAYER_DATA_PTR(work); + RtlCopyMemory(data, network_data, sizeof(WINDIVERT_NETWORK_DATA)); + data = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_NETWORK_DATA, work); + packet_data = NdisGetDataBuffer(buffer, packet_len, NULL, 1, 0); + if (packet_data == NULL) + { + NdisGetDataBuffer(buffer, packet_len, data, 1, 0); + } + else + { + RtlCopyMemory(data, packet_data, packet_len); + } + checksums.Value = NET_BUFFER_LIST_INFO(buffers, + TcpIpChecksumNetBufferListInfo); + if (outbound) + { + pseudo_ip_checksum = (checksums.Transmit.IpHeaderChecksum != 0); + pseudo_tcp_checksum = (checksums.Transmit.TcpChecksum != 0); + pseudo_udp_checksum = (checksums.Transmit.UdpChecksum != 0); + } + else + { + pseudo_ip_checksum = + (checksums.Receive.IpChecksumSucceeded != 0); + pseudo_tcp_checksum = + (checksums.Receive.TcpChecksumSucceeded != 0); + pseudo_udp_checksum = + (checksums.Receive.UdpChecksumSucceeded != 0); + } + break; + + case WINDIVERT_LAYER_FLOW: + work = (packet_t)windivert_malloc( + WINDIVERT_PACKET_SIZE(WINDIVERT_FLOW_DATA, 0), FALSE); + if (work == NULL) + { + return TRUE; + } + work->packet_len = 0; + data = WINDIVERT_LAYER_DATA_PTR(work); + RtlCopyMemory(data, flow_data, sizeof(WINDIVERT_FLOW_DATA)); + pseudo_ip_checksum = pseudo_tcp_checksum = pseudo_udp_checksum = + FALSE; + break; + + default: + return TRUE; } - data_len = NET_BUFFER_DATA_LENGTH(buffer); - work->data = windivert_malloc(data_len, FALSE); - if (work->data == NULL) - { - windivert_free_packet(work); - return TRUE; - } - work->data_len = data_len; - data = NdisGetDataBuffer(buffer, data_len, NULL, 1, 0); - if (data == NULL) - { - NdisGetDataBuffer(buffer, data_len, work->data, 1, 0); - } - else - { - RtlCopyMemory(work->data, data, data_len); - } - work->is_ipv4 = is_ipv4; - work->forward = forward; - work->impostor = impostor; - work->loopback = loopback; - work->match = match; - work->direction = direction; - work->if_idx = if_idx; - work->sub_if_idx = sub_if_idx; - work->priority = priority; - work->timestamp = timestamp; - work->checksums.Value = NET_BUFFER_LIST_INFO(buffers, - TcpIpChecksumNetBufferListInfo); + + work->layer = layer; + work->event = event; + work->outbound = (outbound? 1: 0); + work->loopback = (loopback? 1: 0); + work->impostor = (impostor? 1: 0); + work->ipv6 = (!ipv4? 1: 0); + work->pseudo_ip_checksum = (pseudo_ip_checksum? 1: 0); + work->pseudo_tcp_checksum = (pseudo_tcp_checksum? 1: 0); + work->pseudo_udp_checksum = (pseudo_udp_checksum? 1: 0); + work->match = match; + work->priority = priority; + work->timestamp = timestamp; + old_entry = NULL; - KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { @@ -2667,10 +3263,10 @@ static void windivert_queue_packet(context_t context, packet_t packet) if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { KeReleaseInStackQueuedSpinLock(&lock_handle); - windivert_free_packet(packet); + windivert_reinject_packet(packet); return; } - if (packet->data_len > context->packet_queue_maxsize) + if (packet->packet_len > context->packet_queue_maxsize) { // (Corner case) the packet is larger than the max queue size: KeReleaseInStackQueuedSpinLock(&lock_handle); @@ -2686,7 +3282,7 @@ static void windivert_queue_packet(context_t context, packet_t packet) return; } - if (context->packet_queue_size + packet->data_len > + if (context->packet_queue_size + packet->packet_len > context->packet_queue_maxsize || context->packet_queue_length + 1 > context->packet_queue_maxlength) { @@ -2694,7 +3290,7 @@ static void windivert_queue_packet(context_t context, packet_t packet) old_entry = RemoveHeadList(&context->packet_queue); old_packet = CONTAINING_RECORD(old_entry, struct packet_s, entry); context->packet_queue_length--; - context->packet_queue_size -= old_packet->data_len; + context->packet_queue_size -= old_packet->packet_len; KeReleaseInStackQueuedSpinLock(&lock_handle); DEBUG("DROP: packet queue is full, dropping packet"); windivert_free_packet(old_packet); @@ -2707,7 +3303,7 @@ static void windivert_queue_packet(context_t context, packet_t packet) // Queue the packet: InsertTailList(&context->packet_queue, &packet->entry); context->packet_queue_length++; - context->packet_queue_size += packet->data_len; + context->packet_queue_size += packet->packet_len; break; } } @@ -2726,13 +3322,26 @@ static void windivert_queue_packet(context_t context, packet_t packet) */ static void windivert_reinject_packet(packet_t packet) { + UINT8 *packet_data; + UINT32 packet_len; + PWINDIVERT_NETWORK_DATA network_data; PMDL mdl; PNET_BUFFER_LIST buffers; HANDLE handle; UINT32 priority; NTSTATUS status; - mdl = IoAllocateMdl(packet->data, packet->data_len, FALSE, FALSE, NULL); + if (packet->layer != WINDIVERT_LAYER_NETWORK && + packet->layer != WINDIVERT_LAYER_NETWORK_FORWARD) + { + windivert_free_packet(packet); + return; + } + + network_data = (PWINDIVERT_NETWORK_DATA)WINDIVERT_LAYER_DATA_PTR(packet); + packet_data = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_NETWORK_DATA, packet); + packet_len = packet->packet_len; + mdl = IoAllocateMdl(packet_data, packet_len, FALSE, FALSE, NULL); if (mdl == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -2742,7 +3351,7 @@ static void windivert_reinject_packet(packet_t packet) } MmBuildMdlForNonPagedPool(mdl); status = FwpsAllocateNetBufferAndNetBufferList0(nbl_pool_handle, 0, 0, - mdl, 0, packet->data_len, &buffers); + mdl, 0, packet_len, &buffers); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to create NET_BUFFER_LIST for injected packet", @@ -2752,39 +3361,35 @@ static void windivert_reinject_packet(packet_t packet) return; } priority = packet->priority; - NET_BUFFER_LIST_INFO(buffers, TcpIpChecksumNetBufferListInfo) = - packet->checksums.Value; - handle = (packet->is_ipv4? inject_handle: injectv6_handle); - if (packet->forward) + handle = (packet->ipv6? injectv6_handle: inject_handle); + if (packet->layer == WINDIVERT_LAYER_NETWORK_FORWARD) { status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0, - (packet->is_ipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID, - packet->if_idx, buffers, windivert_inject_complete, NULL); + (packet->ipv6? AF_INET6: AF_INET), UNSPECIFIED_COMPARTMENT_ID, + network_data->IfIdx, buffers, windivert_reinject_complete, + (HANDLE)packet); } - else if (packet->direction == WINDIVERT_DIRECTION_OUTBOUND) + else if (packet->outbound) { - status = FwpsInjectNetworkSendAsync0(handle, - (HANDLE)priority, 0, UNSPECIFIED_COMPARTMENT_ID, buffers, - windivert_inject_complete, NULL); + status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0, + UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_reinject_complete, + (HANDLE)packet); } else { - status = FwpsInjectNetworkReceiveAsync0(handle, - (HANDLE)priority, 0, UNSPECIFIED_COMPARTMENT_ID, packet->if_idx, - packet->sub_if_idx, buffers, windivert_inject_complete, NULL); + status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0, + UNSPECIFIED_COMPARTMENT_ID, network_data->IfIdx, + network_data->SubIfIdx, buffers, windivert_reinject_complete, + (HANDLE)packet); } - if (NT_SUCCESS(status)) - { - packet->data = NULL; // Data is now owned by injected NET_BUFFER. - } - else + if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to re-inject (packet=%p)", status, packet); FwpsFreeNetBufferList0(buffers); IoFreeMdl(mdl); + windivert_free_packet(packet); } - windivert_free_packet(packet); } /* @@ -2792,19 +3397,18 @@ static void windivert_reinject_packet(packet_t packet) */ static void windivert_free_packet(packet_t packet) { - windivert_free(packet->data); windivert_free(packet); } /* * Decrement the TTL of a packet. */ -static BOOL windivert_decrement_ttl(PVOID data, BOOL is_ipv4, BOOL checksum) +static BOOL windivert_decrement_ttl(PVOID data, BOOL ipv4, BOOL checksum) { PWINDIVERT_IPHDR ip_header; PWINDIVERT_IPV6HDR ipv6_header; - if (is_ipv4) + if (ipv4) { ip_header = (PWINDIVERT_IPHDR)data; if (ip_header->TTL <= 1) @@ -2838,53 +3442,6 @@ static BOOL windivert_decrement_ttl(PVOID data, BOOL is_ipv4, BOOL checksum) return TRUE; } -/* - * Skip well-known IPv6 extension headers. - */ -static UINT8 windivert_skip_headers(UINT8 proto, UINT8 **header, size_t *len) -{ - size_t hdrlen; - - while (TRUE) - { - if (*len <= 2) - { - return IPPROTO_NONE; - } - - hdrlen = (size_t)*(*header + 1); - switch (proto) - { - case IPPROTO_FRAGMENT: - hdrlen = 8; - break; - case IPPROTO_AH: - hdrlen += 2; - hdrlen *= 4; - break; - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - hdrlen++; - hdrlen *= 8; - break; - case IPPROTO_NONE: - return proto; - default: - return proto; - } - - if (hdrlen >= *len) - { - return IPPROTO_NONE; - } - - proto = **header; - *header += hdrlen; - *len -= hdrlen; - } -} - /* * Big number comparison. */ @@ -2926,13 +3483,16 @@ static int windivert_big_num_compare(const UINT32 *a, const UINT32 *b) } /* - * Checks if the given packet is of interest. + * Parse packet headers. */ -static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, - UINT32 sub_if_idx, BOOL outbound, BOOL is_ipv4, BOOL impostor, - BOOL loopback, filter_t filter) +static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4, + PWINDIVERT_IPHDR *ip_header_ptr, PWINDIVERT_IPV6HDR *ipv6_header_ptr, + PWINDIVERT_ICMPHDR *icmp_header_ptr, + PWINDIVERT_ICMPV6HDR *icmpv6_header_ptr, + PWINDIVERT_TCPHDR *tcp_header_ptr, PWINDIVERT_UDPHDR *udp_header_ptr, + UINT *payload_len_ptr) { - size_t tot_len, ip_header_len; + UINT tot_len, ip_header_len; PWINDIVERT_IPHDR ip_header = NULL; PWINDIVERT_IPV6HDR ipv6_header = NULL; PWINDIVERT_ICMPHDR icmp_header = NULL; @@ -2940,7 +3500,8 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, PWINDIVERT_TCPHDR tcp_header = NULL; PWINDIVERT_UDPHDR udp_header = NULL; UINT16 ip, ttl; - UINT8 proto; + UINT8 proto = 0; + UINT payload_len = 0; NTSTATUS status; // Parse the headers: @@ -2952,7 +3513,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, } // Get the IP header. - if (is_ipv4) + if (ipv4) { // IPv4: if (tot_len < sizeof(WINDIVERT_IPHDR)) @@ -3010,7 +3571,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, while (TRUE) { UINT8 *ext_header; - size_t ext_header_len; + UINT ext_header_len; BOOL isexthdr = TRUE; ext_header = (UINT8 *)NdisGetDataBuffer(buffer, 2, NULL, 1, 0); @@ -3019,7 +3580,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, break; } - ext_header_len = (size_t)ext_header[1]; + ext_header_len = (UINT)ext_header[1]; switch (proto) { case IPPROTO_FRAGMENT: @@ -3065,10 +3626,13 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, case IPPROTO_TCP: tcp_header = (PWINDIVERT_TCPHDR)NdisGetDataBuffer(buffer, sizeof(WINDIVERT_TCPHDR), NULL, 1, 0); + payload_len = tot_len - ip_header_len - + tcp_header->HdrLength*sizeof(UINT32); break; case IPPROTO_UDP: udp_header = (PWINDIVERT_UDPHDR)NdisGetDataBuffer(buffer, sizeof(WINDIVERT_UDPHDR), NULL, 1, 0); + payload_len = tot_len - ip_header_len - sizeof(WINDIVERT_UDPHDR); break; default: break; @@ -3082,12 +3646,56 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, return FALSE; } + *ip_header_ptr = ip_header; + *ipv6_header_ptr = ipv6_header; + *icmp_header_ptr = icmp_header; + *icmpv6_header_ptr = icmpv6_header; + *tcp_header_ptr = tcp_header; + *udp_header_ptr = udp_header; + *payload_len_ptr = payload_len; + + return TRUE; +} + +/* + * Checks if the given network packet is of interest. + */ +static BOOL windivert_filter(PNET_BUFFER buffer, + PWINDIVERT_NETWORK_DATA network_data, PWINDIVERT_FLOW_DATA flow_data, + BOOL ipv4, BOOL outbound, BOOL loopback, BOOL impostor, filter_t filter) +{ + PWINDIVERT_IPHDR ip_header = NULL; + PWINDIVERT_IPV6HDR ipv6_header = NULL; + PWINDIVERT_ICMPHDR icmp_header = NULL; + PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; + PWINDIVERT_TCPHDR tcp_header = NULL; + PWINDIVERT_UDPHDR udp_header = NULL; + UINT payload_len = 0; + UINT16 ip, ttl; + NTSTATUS status; + + if (network_data != NULL) + { + if (!windivert_parse_headers(buffer, ipv4, &ip_header, &ipv6_header, + &icmp_header, &icmpv6_header, &tcp_header, &udp_header, + &payload_len)) + { + return FALSE; + } + } + else if (flow_data == NULL) + { + DEBUG("FILTER: REJECT (invalid parameter)"); + return FALSE; + } + // Execute the filter: ip = 0; ttl = WINDIVERT_FILTER_MAXLEN+1; // Additional safety while (ttl-- != 0) { - BOOL result; + BOOL result = FALSE; + BOOL error = FALSE; int cmp; UINT32 field[4]; field[1] = 0; @@ -3098,6 +3706,12 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, case WINDIVERT_FILTER_PROTOCOL_NONE: result = TRUE; break; + case WINDIVERT_FILTER_PROTOCOL_NETWORK: + result = (network_data != NULL); + break; + case WINDIVERT_FILTER_PROTOCOL_FLOW: + result = (flow_data != NULL); + break; case WINDIVERT_FILTER_PROTOCOL_IP: result = (ip_header != NULL); break; @@ -3117,6 +3731,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, result = (udp_header != NULL); break; default: + error = TRUE; result = FALSE; break; } @@ -3128,16 +3743,16 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, field[0] = 0; break; case WINDIVERT_FILTER_FIELD_INBOUND: - field[0] = (UINT32)(!outbound); + field[0] = (UINT32)!outbound; break; case WINDIVERT_FILTER_FIELD_OUTBOUND: field[0] = (UINT32)outbound; break; case WINDIVERT_FILTER_FIELD_IFIDX: - field[0] = (UINT32)if_idx; + field[0] = network_data->IfIdx; break; case WINDIVERT_FILTER_FIELD_SUBIFIDX: - field[0] = (UINT32)sub_if_idx; + field[0] = network_data->SubIfIdx; break; case WINDIVERT_FILTER_FIELD_LOOPBACK: field[0] = (UINT32)loopback; @@ -3146,22 +3761,30 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, field[0] = (UINT32)impostor; break; case WINDIVERT_FILTER_FIELD_IP: - field[0] = (UINT32)(ip_header != NULL); + field[0] = (UINT32)ipv4; break; case WINDIVERT_FILTER_FIELD_IPV6: - field[0] = (UINT32)(ipv6_header != NULL); + field[0] = (UINT32)!ipv4; break; case WINDIVERT_FILTER_FIELD_ICMP: - field[0] = (UINT32)(icmp_header != NULL); + field[0] = (UINT32)(flow_data != NULL? + flow_data->Protocol == IPPROTO_ICMP: + icmp_header != NULL); break; case WINDIVERT_FILTER_FIELD_ICMPV6: - field[0] = (UINT32)(icmpv6_header != NULL); + field[0] = (UINT32)(flow_data != NULL? + flow_data->Protocol == IPPROTO_ICMPV6: + icmpv6_header != NULL); break; case WINDIVERT_FILTER_FIELD_TCP: - field[0] = (UINT32)(tcp_header != NULL); + field[0] = (UINT32)(flow_data != NULL? + flow_data->Protocol == IPPROTO_TCP: + tcp_header != NULL); break; case WINDIVERT_FILTER_FIELD_UDP: - field[0] = (UINT32)(udp_header != NULL); + field[0] = (UINT32)(flow_data != NULL? + flow_data->Protocol == IPPROTO_UDP: + udp_header != NULL); break; case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: field[0] = (UINT32)ip_header->HdrLength; @@ -3195,9 +3818,11 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, field[0] = (UINT32)RtlUshortByteSwap(ip_header->Checksum); break; case WINDIVERT_FILTER_FIELD_IP_SRCADDR: + field[1] = 0x0000FFFF; field[0] = (UINT32)RtlUlongByteSwap(ip_header->SrcAddr); break; case WINDIVERT_FILTER_FIELD_IP_DSTADDR: + field[1] = 0x0000FFFF; field[0] = (UINT32)RtlUlongByteSwap(ip_header->DstAddr); break; case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: @@ -3307,8 +3932,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, field[0] = (UINT32)RtlUshortByteSwap(tcp_header->UrgPtr); break; case WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: - field[0] = (UINT32)(tot_len - ip_header_len - - tcp_header->HdrLength*sizeof(UINT32)); + field[0] = (UINT32)payload_len; break; case WINDIVERT_FILTER_FIELD_UDP_SRCPORT: field[0] = (UINT32)RtlUshortByteSwap(udp_header->SrcPort); @@ -3323,13 +3947,40 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, field[0] = (UINT32)RtlUshortByteSwap(udp_header->Checksum); break; case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: - field[0] = (UINT32)(tot_len - ip_header_len - - sizeof(WINDIVERT_UDPHDR)); + field[0] = (UINT32)payload_len; + break; + case WINDIVERT_FILTER_FIELD_LOCALADDR: + field[0] = flow_data->LocalAddr[0]; + field[1] = flow_data->LocalAddr[1]; + field[2] = flow_data->LocalAddr[2]; + field[3] = flow_data->LocalAddr[3]; + break; + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + field[0] = flow_data->RemoteAddr[0]; + field[1] = flow_data->RemoteAddr[1]; + field[2] = flow_data->RemoteAddr[2]; + field[3] = flow_data->RemoteAddr[3]; + break; + case WINDIVERT_FILTER_FIELD_LOCALPORT: + field[0] = (UINT32)flow_data->LocalPort; + break; + case WINDIVERT_FILTER_FIELD_REMOTEPORT: + field[0] = (UINT32)flow_data->RemotePort; + break; + case WINDIVERT_FILTER_FIELD_PROTOCOL: + field[0] = (UINT32)flow_data->Protocol; + break; + case WINDIVERT_FILTER_FIELD_PROCESSID: + field[0] = flow_data->ProcessId; break; default: - field[0] = 0; + error = TRUE; + result = FALSE; break; } + } + if (result) + { cmp = windivert_big_num_compare(field, filter[ip].arg); switch (filter[ip].test) { @@ -3352,10 +4003,16 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, result = (cmp >= 0); break; default: + error = TRUE; result = FALSE; break; } } + if (error) + { + DEBUG("FILTER: REJECT (bad filter)"); + return FALSE; + } ip = (result? filter[ip].success: filter[ip].failure); if (ip == WINDIVERT_FILTER_RESULT_ACCEPT) { @@ -3366,6 +4023,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, return FALSE; } } + DEBUG("FILTER: REJECT (filter TTL exceeded)"); return FALSE; } @@ -3373,21 +4031,28 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, /* * Analyze the given filter. */ -static void windivert_filter_analyze(filter_t filter, BOOL *is_inbound, - BOOL *is_outbound, BOOL *is_ipv4, BOOL *is_ipv6) +static void windivert_filter_analyze(filter_t filter, UINT64 flags, + BOOL *inbound, BOOL *outbound, BOOL *ipv4, BOOL *ipv6) { BOOL result; + // Send-only? + if ((flags & WINDIVERT_FLAG_SEND_ONLY) != 0) + { +windivert_filter_analyze_send_only: + *inbound = FALSE; + *outbound = FALSE; + *ipv4 = FALSE; + *ipv6 = FALSE; + return; + } + // False filter? result = windivert_filter_test(filter, 0, WINDIVERT_FILTER_PROTOCOL_NONE, WINDIVERT_FILTER_FIELD_ZERO, 0); if (!result) { - *is_inbound = FALSE; - *is_outbound = FALSE; - *is_ipv4 = FALSE; - *is_ipv6 = FALSE; - return; + goto windivert_filter_analyze_send_only; } // Inbound? @@ -3399,7 +4064,7 @@ static void windivert_filter_analyze(filter_t filter, BOOL *is_inbound, WINDIVERT_FILTER_PROTOCOL_NONE, WINDIVERT_FILTER_FIELD_OUTBOUND, 0); } - *is_inbound = result; + *inbound = result; // Outbound? result = windivert_filter_test(filter, 0, WINDIVERT_FILTER_PROTOCOL_NONE, @@ -3409,7 +4074,7 @@ static void windivert_filter_analyze(filter_t filter, BOOL *is_inbound, result = windivert_filter_test(filter, 0, WINDIVERT_FILTER_PROTOCOL_NONE, WINDIVERT_FILTER_FIELD_INBOUND, 0); } - *is_outbound = result; + *outbound = result; // IPv4? result = windivert_filter_test(filter, 0, WINDIVERT_FILTER_PROTOCOL_NONE, @@ -3419,7 +4084,7 @@ static void windivert_filter_analyze(filter_t filter, BOOL *is_inbound, result = windivert_filter_test(filter, 0, WINDIVERT_FILTER_PROTOCOL_NONE, WINDIVERT_FILTER_FIELD_IPV6, 0); } - *is_ipv4 = result; + *ipv4 = result; // Ipv6? result = windivert_filter_test(filter, 0, WINDIVERT_FILTER_PROTOCOL_NONE, @@ -3429,7 +4094,7 @@ static void windivert_filter_analyze(filter_t filter, BOOL *is_inbound, result = windivert_filter_test(filter, 0, WINDIVERT_FILTER_PROTOCOL_NONE, WINDIVERT_FILTER_FIELD_IP, 0); } - *is_ipv6 = result; + *ipv6 = result; } /* @@ -3508,26 +4173,24 @@ static BOOL windivert_filter_test(filter_t filter, UINT16 ip, UINT8 protocol, static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, size_t ioctl_filter_len) { - filter_t filter0 = NULL, result = NULL; + filter_t filter = NULL; UINT16 i; size_t length; if (ioctl_filter_len % sizeof(struct windivert_ioctl_filter_s) != 0) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } length = ioctl_filter_len / sizeof(struct windivert_ioctl_filter_s); - if (length >= WINDIVERT_FILTER_MAXLEN) + if (length >= WINDIVERT_FILTER_MAXLEN || length == 0) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } - // Do NOT use the stack (size = 12Kb on x86) for filter0. - filter0 = (filter_t)windivert_malloc( - WINDIVERT_FILTER_MAXLEN*sizeof(struct filter_s), TRUE); - if (filter0 == NULL) + filter = (filter_t)windivert_malloc(length*sizeof(struct filter_s), FALSE); + if (filter == NULL) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } for (i = 0; i < length; i++) @@ -3535,7 +4198,7 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, if (ioctl_filter[i].field > WINDIVERT_FILTER_FIELD_MAX || ioctl_filter[i].test > WINDIVERT_FILTER_TEST_MAX) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } switch (ioctl_filter[i].success) { @@ -3546,7 +4209,7 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, if (ioctl_filter[i].success <= i || ioctl_filter[i].success >= length) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; } @@ -3559,20 +4222,33 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, if (ioctl_filter[i].failure <= i || ioctl_filter[i].failure >= length) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; } // Enforce size limits: if (ioctl_filter[i].field != WINDIVERT_FILTER_FIELD_IPV6_SRCADDR && - ioctl_filter[i].field != WINDIVERT_FILTER_FIELD_IPV6_DSTADDR) + ioctl_filter[i].field != WINDIVERT_FILTER_FIELD_IPV6_DSTADDR && + ioctl_filter[i].field != WINDIVERT_FILTER_FIELD_LOCALADDR && + ioctl_filter[i].field != WINDIVERT_FILTER_FIELD_REMOTEADDR) { - if (ioctl_filter[i].arg[1] != 0 || - ioctl_filter[i].arg[2] != 0 || + if (ioctl_filter[i].arg[2] != 0 || ioctl_filter[i].arg[3] != 0) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; + } + if ((ioctl_filter[i].field == WINDIVERT_FILTER_FIELD_IP_SRCADDR || + ioctl_filter[i].field == WINDIVERT_FILTER_FIELD_IP_DSTADDR)) + { + if (ioctl_filter[i].arg[1] != 0x0000FFFF) + { + goto windivert_filter_compile_error; + } + } + else if (ioctl_filter[i].arg[1] != 0) + { + goto windivert_filter_compile_error; } } switch (ioctl_filter[i].field) @@ -3596,14 +4272,14 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_TCP_FIN: if (ioctl_filter[i].arg[0] > 1) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: case WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH: if (ioctl_filter[i].arg[0] > 0x0F) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; case WINDIVERT_FILTER_FIELD_IP_TOS: @@ -3616,15 +4292,16 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_ICMP_CODE: case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: case WINDIVERT_FILTER_FIELD_ICMPV6_CODE: + case WINDIVERT_FILTER_FIELD_PROTOCOL: if (ioctl_filter[i].arg[0] > UINT8_MAX) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; case WINDIVERT_FILTER_FIELD_IP_FRAGOFF: if (ioctl_filter[i].arg[0] > 0x1FFF) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; case WINDIVERT_FILTER_FIELD_IP_LENGTH: @@ -3644,28 +4321,30 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_UDP_LENGTH: case WINDIVERT_FILTER_FIELD_UDP_CHECKSUM: case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + case WINDIVERT_FILTER_FIELD_LOCALPORT: + case WINDIVERT_FILTER_FIELD_REMOTEPORT: if (ioctl_filter[i].arg[0] > UINT16_MAX) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; case WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL: if (ioctl_filter[i].arg[0] > 0x000FFFFF) { - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } break; default: break; } - filter0[i].field = ioctl_filter[i].field; - filter0[i].test = ioctl_filter[i].test; - filter0[i].success = ioctl_filter[i].success; - filter0[i].failure = ioctl_filter[i].failure; - filter0[i].arg[0] = ioctl_filter[i].arg[0]; - filter0[i].arg[1] = ioctl_filter[i].arg[1]; - filter0[i].arg[2] = ioctl_filter[i].arg[2]; - filter0[i].arg[3] = ioctl_filter[i].arg[3]; + filter[i].field = ioctl_filter[i].field; + filter[i].test = ioctl_filter[i].test; + filter[i].success = ioctl_filter[i].success; + filter[i].failure = ioctl_filter[i].failure; + filter[i].arg[0] = ioctl_filter[i].arg[0]; + filter[i].arg[1] = ioctl_filter[i].arg[1]; + filter[i].arg[2] = ioctl_filter[i].arg[2]; + filter[i].arg[3] = ioctl_filter[i].arg[3]; // Protocol selection: switch (ioctl_filter[i].field) @@ -3673,8 +4352,6 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_ZERO: case WINDIVERT_FILTER_FIELD_INBOUND: case WINDIVERT_FILTER_FIELD_OUTBOUND: - case WINDIVERT_FILTER_FIELD_IFIDX: - case WINDIVERT_FILTER_FIELD_SUBIFIDX: case WINDIVERT_FILTER_FIELD_LOOPBACK: case WINDIVERT_FILTER_FIELD_IMPOSTOR: case WINDIVERT_FILTER_FIELD_IP: @@ -3683,7 +4360,18 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_ICMPV6: case WINDIVERT_FILTER_FIELD_TCP: case WINDIVERT_FILTER_FIELD_UDP: - filter0[i].protocol = WINDIVERT_FILTER_PROTOCOL_NONE; + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_NONE; + break; + case WINDIVERT_FILTER_FIELD_IFIDX: + case WINDIVERT_FILTER_FIELD_SUBIFIDX: + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_NETWORK; + break; + case WINDIVERT_FILTER_FIELD_LOCALADDR: + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + case WINDIVERT_FILTER_FIELD_LOCALPORT: + case WINDIVERT_FILTER_FIELD_REMOTEPORT: + case WINDIVERT_FILTER_FIELD_PROTOCOL: + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_FLOW; break; case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: case WINDIVERT_FILTER_FIELD_IP_TOS: @@ -3697,7 +4385,7 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_IP_CHECKSUM: case WINDIVERT_FILTER_FIELD_IP_SRCADDR: case WINDIVERT_FILTER_FIELD_IP_DSTADDR: - filter0[i].protocol = WINDIVERT_FILTER_PROTOCOL_IP; + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_IP; break; case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: case WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL: @@ -3706,19 +4394,19 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT: case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: - filter0[i].protocol = WINDIVERT_FILTER_PROTOCOL_IPV6; + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_IPV6; break; case WINDIVERT_FILTER_FIELD_ICMP_TYPE: case WINDIVERT_FILTER_FIELD_ICMP_CODE: case WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM: case WINDIVERT_FILTER_FIELD_ICMP_BODY: - filter0[i].protocol = WINDIVERT_FILTER_PROTOCOL_ICMP; + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_ICMP; break; case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: case WINDIVERT_FILTER_FIELD_ICMPV6_CODE: case WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: case WINDIVERT_FILTER_FIELD_ICMPV6_BODY: - filter0[i].protocol = WINDIVERT_FILTER_PROTOCOL_ICMPV6; + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_ICMPV6; break; case WINDIVERT_FILTER_FIELD_TCP_SRCPORT: case WINDIVERT_FILTER_FIELD_TCP_DSTPORT: @@ -3735,29 +4423,25 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, case WINDIVERT_FILTER_FIELD_TCP_CHECKSUM: case WINDIVERT_FILTER_FIELD_TCP_URGPTR: case WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: - filter0[i].protocol = WINDIVERT_FILTER_PROTOCOL_TCP; + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_TCP; break; case WINDIVERT_FILTER_FIELD_UDP_SRCPORT: case WINDIVERT_FILTER_FIELD_UDP_DSTPORT: case WINDIVERT_FILTER_FIELD_UDP_LENGTH: case WINDIVERT_FILTER_FIELD_UDP_CHECKSUM: case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: - filter0[i].protocol = WINDIVERT_FILTER_PROTOCOL_UDP; + filter[i].protocol = WINDIVERT_FILTER_PROTOCOL_UDP; break; default: - goto windivert_filter_compile_exit; + goto windivert_filter_compile_error; } } - result = (filter_t)windivert_malloc(i*sizeof(struct filter_s), FALSE); - if (result != NULL) - { - RtlMoveMemory(result, filter0, i*sizeof(struct filter_s)); - } + return filter; -windivert_filter_compile_exit: +windivert_filter_compile_error: - windivert_free(filter0); - return result; + windivert_free(filter); + return NULL; }