diff --git a/dll/windivert_helper.c b/dll/windivert_helper.c
index fa50e86..c6e0862 100644
--- a/dll/windivert_helper.c
+++ b/dll/windivert_helper.c
@@ -128,6 +128,7 @@ typedef enum
TOKEN_FALSE,
TOKEN_INBOUND,
TOKEN_OUTBOUND,
+ TOKEN_FRAGMENT,
TOKEN_IF_IDX,
TOKEN_SUB_IF_IDX,
TOKEN_LOOPBACK,
@@ -679,6 +680,7 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer,
{"endpointId", TOKEN_ENDPOINT_ID, L__FS_},
{"event", TOKEN_EVENT, LNMFSR},
{"false", TOKEN_FALSE, LNMFSR},
+ {"fragment", TOKEN_FRAGMENT, LNM___},
{"icmp", TOKEN_ICMP, LNMFS_},
{"icmp.Body", TOKEN_ICMP_BODY, LNM___},
{"icmp.Checksum", TOKEN_ICMP_CHECKSUM, LNM___},
@@ -1058,6 +1060,7 @@ static PEXPR WinDivertMakeVar(KIND kind, PERROR error)
{{{0}}, TOKEN_FALSE},
{{{0}}, TOKEN_INBOUND},
{{{0}}, TOKEN_OUTBOUND},
+ {{{0}}, TOKEN_FRAGMENT},
{{{0}}, TOKEN_IF_IDX},
{{{0}}, TOKEN_SUB_IF_IDX},
{{{0}}, TOKEN_LOOPBACK},
@@ -1209,6 +1212,7 @@ static PEXPR WinDivertParseTest(HANDLE pool, TOKEN *toks, UINT *i, PERROR error)
case TOKEN_FALSE:
case TOKEN_OUTBOUND:
case TOKEN_INBOUND:
+ case TOKEN_FRAGMENT:
case TOKEN_IF_IDX:
case TOKEN_SUB_IF_IDX:
case TOKEN_LOOPBACK:
@@ -1543,6 +1547,7 @@ static BOOL WinDivertEvalTest(PEXPR test, BOOL *res)
break;
case TOKEN_INBOUND:
case TOKEN_OUTBOUND:
+ case TOKEN_FRAGMENT:
case TOKEN_IP:
case TOKEN_IPV6:
case TOKEN_ICMP:
@@ -1875,6 +1880,9 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset,
case TOKEN_INBOUND:
object->field = WINDIVERT_FILTER_FIELD_INBOUND;
break;
+ case TOKEN_FRAGMENT:
+ object->field = WINDIVERT_FILTER_FIELD_FRAGMENT;
+ break;
case TOKEN_IF_IDX:
object->field = WINDIVERT_FILTER_FIELD_IFIDX;
break;
@@ -2619,16 +2627,16 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, const VOID *packet,
PWINDIVERT_ICMPV6HDR icmpv6hdr = NULL;
PWINDIVERT_TCPHDR tcphdr = NULL;
PWINDIVERT_UDPHDR udphdr = NULL;
+ WINDIVERT_PACKET info;
UINT8 protocol = 0;
UINT header_len = 0, payload_len = 0;
UINT64 random64 = 0;
- BOOL neg;
UINT32 val[4];
ULARGE_INTEGER val64;
UINT8 data8;
UINT16 data16;
UINT32 data32;
- BOOL pass, big;
+ BOOL pass, big, neg, fragment = FALSE;
int cmp;
HANDLE pool;
WINDIVERT_FILTER *object;
@@ -2648,14 +2656,21 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, const VOID *packet,
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
- if (!WinDivertHelperParsePacket((PVOID)packet, packet_len,
- &iphdr, &ipv6hdr, &protocol, &icmphdr, &icmpv6hdr,
- &tcphdr, &udphdr, NULL, &payload_len, NULL, NULL))
+ if (!WinDivertHelperParsePacketEx((PVOID)packet, packet_len, &info))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
- header_len = packet_len - payload_len;
+ protocol = info.Protocol;
+ iphdr = info.IPHeader;
+ ipv6hdr = info.IPv6Header;
+ icmphdr = info.ICMPHeader;
+ icmpv6hdr = info.ICMPv6Header;
+ tcphdr = info.TCPHeader;
+ udphdr = info.UDPHeader;
+ payload_len = info.PayloadLength;
+ header_len = info.HeaderLength;
+ fragment = info.Fragment;
if ((addr->IPv6 && ipv6hdr == NULL) ||
(!addr->IPv6 && iphdr == NULL))
{
@@ -2759,6 +2774,7 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, const VOID *packet,
case WINDIVERT_FILTER_FIELD_PACKET16:
case WINDIVERT_FILTER_FIELD_PACKET32:
case WINDIVERT_FILTER_FIELD_LENGTH:
+ case WINDIVERT_FILTER_FIELD_FRAGMENT:
pass = (addr->Layer == WINDIVERT_LAYER_NETWORK ||
addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD);
break;
@@ -2954,6 +2970,9 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, const VOID *packet,
case WINDIVERT_FILTER_FIELD_OUTBOUND:
val[0] = addr->Outbound;
break;
+ case WINDIVERT_FILTER_FIELD_FRAGMENT:
+ val[0] = (UINT32)fragment;
+ break;
case WINDIVERT_FILTER_FIELD_IFIDX:
val[0] = addr->Network.IfIdx;
break;
@@ -3836,6 +3855,8 @@ static PEXPR WinDivertDecompileTest(HANDLE pool, PWINDIVERT_FILTER test)
kind = TOKEN_INBOUND; break;
case WINDIVERT_FILTER_FIELD_OUTBOUND:
kind = TOKEN_OUTBOUND; break;
+ case WINDIVERT_FILTER_FIELD_FRAGMENT:
+ kind = TOKEN_FRAGMENT; break;
case WINDIVERT_FILTER_FIELD_IFIDX:
kind = TOKEN_IF_IDX; break;
case WINDIVERT_FILTER_FIELD_SUBIFIDX:
@@ -4490,6 +4511,7 @@ static void WinDivertFormatTestExpr(PWINDIVERT_STREAM stream, PEXPR expr,
case TOKEN_ZERO:
case TOKEN_INBOUND:
case TOKEN_OUTBOUND:
+ case TOKEN_FRAGMENT:
case TOKEN_IP:
case TOKEN_IPV6:
case TOKEN_ICMP:
@@ -4787,6 +4809,8 @@ static void WinDivertFormatExpr(PWINDIVERT_STREAM stream, PEXPR expr,
WinDivertPutString(stream, "inbound"); return;
case TOKEN_OUTBOUND:
WinDivertPutString(stream, "outbound"); return;
+ case TOKEN_FRAGMENT:
+ WinDivertPutString(stream, "fragment"); return;
case TOKEN_IF_IDX:
WinDivertPutString(stream, "ifIdx"); return;
case TOKEN_SUB_IF_IDX:
diff --git a/doc/windivert.html b/doc/windivert.html
index 64e71e0..5b28903 100644
--- a/doc/windivert.html
+++ b/doc/windivert.html
@@ -2751,6 +2751,7 @@ The possible fields are:
subIfIdx | ✔ | ✔ | | | | Sub-interface index |
loopback | ✔ | | ✔ | ✔ | | Is loopback packet? |
impostor | ✔ | ✔ | | | | Is impostor packet? |
+fragment | ✔ | ✔ | | | | Is IP fragment packet? |
endpointId | | | ✔ | ✔ | | Endpoint ID |
parentEndpointId | | | ✔ | ✔ | | Parent endpoint ID |
processId | | | ✔ | ✔ | ✔ | Process ID |
diff --git a/include/windivert_device.h b/include/windivert_device.h
index 0022318..325944e 100644
--- a/include/windivert_device.h
+++ b/include/windivert_device.h
@@ -149,8 +149,9 @@
#define WINDIVERT_FILTER_FIELD_RANDOM8 82
#define WINDIVERT_FILTER_FIELD_RANDOM16 83
#define WINDIVERT_FILTER_FIELD_RANDOM32 84
+#define WINDIVERT_FILTER_FIELD_FRAGMENT 85
#define WINDIVERT_FILTER_FIELD_MAX \
- WINDIVERT_FILTER_FIELD_RANDOM32
+ WINDIVERT_FILTER_FIELD_FRAGMENT
#define WINDIVERT_FILTER_TEST_EQ 0
#define WINDIVERT_FILTER_TEST_NEQ 1
diff --git a/sys/windivert.c b/sys/windivert.c
index 4bc61f4..d3d54a2 100644
--- a/sys/windivert.c
+++ b/sys/windivert.c
@@ -472,7 +472,7 @@ static BOOL windivert_copy_data(PNET_BUFFER buffer, PVOID data, UINT size);
static BOOL windivert_lookup_data(PNET_BUFFER buffer, UINT offset, INT idx,
PVOID data, UINT size);
static BOOL windivert_parse_headers(PNET_BUFFER buffer, BOOL ipv4,
- BOOL frag_mode, PWINDIVERT_IPHDR *ip_header_ptr,
+ BOOL *fragment_ptr, 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, UINT8 *proto_ptr, UINT *header_len_ptr,
@@ -5286,7 +5286,7 @@ static BOOL windivert_lookup_data(PNET_BUFFER buffer, UINT offset, INT idx,
* Parse packet headers.
*/
static __forceinline BOOL windivert_parse_headers(PNET_BUFFER buffer,
- BOOL ipv4, BOOL frag_mode, PWINDIVERT_IPHDR *ip_header_ptr,
+ BOOL ipv4, BOOL *fragment_ptr, 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, UINT8 *proto_ptr, UINT *header_len_ptr,
@@ -5300,6 +5300,7 @@ static __forceinline BOOL windivert_parse_headers(PNET_BUFFER buffer,
PWINDIVERT_TCPHDR tcp_header = NULL;
PWINDIVERT_UDPHDR udp_header = NULL;
PWINDIVERT_IPV6FRAGHDR frag_header;
+ BOOL fragment = FALSE;
UINT8 protocol = 0;
UINT16 frag_off = 0;
UINT header_len = 0;
@@ -5344,12 +5345,7 @@ static __forceinline BOOL windivert_parse_headers(PNET_BUFFER buffer,
return FALSE;
}
frag_off = RtlUshortByteSwap(WINDIVERT_IPHDR_GET_FRAGOFF(ip_header));
- if (!frag_mode &&
- (WINDIVERT_IPHDR_GET_MF(ip_header) != 0 || frag_off != 0))
- {
- DEBUG("FILTER: REJECT (fragment)");
- return FALSE;
- }
+ fragment = (frag_off != 0 || WINDIVERT_IPHDR_GET_MF(ip_header) != 0);
protocol = ip_header->Protocol;
NdisAdvanceNetBufferDataStart(buffer, ip_header_len, FALSE, NULL);
}
@@ -5381,7 +5377,6 @@ static __forceinline BOOL windivert_parse_headers(PNET_BUFFER buffer,
NdisAdvanceNetBufferDataStart(buffer, ip_header_len, FALSE, NULL);
// Skip extension headers:
- frag_header = NULL;
while (frag_off == 0)
{
UINT8 *ext_header = NULL;
@@ -5390,26 +5385,15 @@ static __forceinline BOOL windivert_parse_headers(PNET_BUFFER buffer,
switch (protocol)
{
case IPPROTO_FRAGMENT:
- if (frag_header != NULL)
- {
- is_ext_header = FALSE;
- break;
- }
frag_header = (PWINDIVERT_IPV6FRAGHDR)
NdisGetDataBuffer(buffer, 8, NULL, 1, 0);
ext_header = (UINT8 *)frag_header;
- if (frag_header == NULL)
+ if (fragment || frag_header == NULL)
{
is_ext_header = FALSE;
break;
}
- if (!frag_mode)
- {
- DEBUG("FILTER: REJECT (fragment)");
- NdisRetreatNetBufferDataStart(buffer, ip_header_len,
- 0, NULL);
- return FALSE;
- }
+ fragment = TRUE;
frag_off = RtlUshortByteSwap(
WINDIVERT_IPV6FRAGHDR_GET_FRAGOFF(frag_header));
ext_header_len = 8;
@@ -5525,6 +5509,7 @@ static __forceinline BOOL windivert_parse_headers(PNET_BUFFER buffer,
return FALSE;
}
+ *fragment_ptr = fragment;
*ip_header_ptr = ip_header;
*ipv6_header_ptr = ipv6_header;
*icmp_header_ptr = icmp_header;
@@ -5556,6 +5541,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, WINDIVERT_LAYER layer,
UINT header_len = 0, payload_len = 0;
UINT64 random64 = 0;
UINT16 ip, ttl;
+ BOOL fragment = FALSE;
PWINDIVERT_DATA_NETWORK network_data = NULL;
PWINDIVERT_DATA_FLOW flow_data = NULL;
PWINDIVERT_DATA_SOCKET socket_data = NULL;
@@ -5568,12 +5554,16 @@ static BOOL windivert_filter(PNET_BUFFER buffer, WINDIVERT_LAYER layer,
{
case WINDIVERT_LAYER_NETWORK:
case WINDIVERT_LAYER_NETWORK_FORWARD:
- if (!windivert_parse_headers(buffer, ipv4, frag_mode, &ip_header,
+ if (!windivert_parse_headers(buffer, ipv4, &fragment, &ip_header,
&ipv6_header, &icmp_header, &icmpv6_header, &tcp_header,
&udp_header, &protocol, &header_len, &payload_len))
{
return FALSE;
}
+ if (fragment && !frag_mode)
+ {
+ return FALSE;
+ }
network_data = (PWINDIVERT_DATA_NETWORK)layer_data;
break;
case WINDIVERT_LAYER_FLOW:
@@ -5642,6 +5632,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, WINDIVERT_LAYER layer,
case WINDIVERT_FILTER_FIELD_PACKET16:
case WINDIVERT_FILTER_FIELD_PACKET32:
case WINDIVERT_FILTER_FIELD_LENGTH:
+ case WINDIVERT_FILTER_FIELD_FRAGMENT:
result = (layer == WINDIVERT_LAYER_NETWORK ||
layer == WINDIVERT_LAYER_NETWORK_FORWARD);
break;
@@ -5829,6 +5820,9 @@ static BOOL windivert_filter(PNET_BUFFER buffer, WINDIVERT_LAYER layer,
case WINDIVERT_FILTER_FIELD_OUTBOUND:
field[0] = (UINT32)outbound;
break;
+ case WINDIVERT_FILTER_FIELD_FRAGMENT:
+ field[0] = (UINT32)fragment;
+ break;
case WINDIVERT_FILTER_FIELD_IFIDX:
field[0] = network_data->IfIdx;
break;
@@ -6499,6 +6493,7 @@ static const WINDIVERT_FILTER *windivert_filter_compile(
case WINDIVERT_FILTER_FIELD_ZERO:
case WINDIVERT_FILTER_FIELD_INBOUND:
case WINDIVERT_FILTER_FIELD_OUTBOUND:
+ case WINDIVERT_FILTER_FIELD_FRAGMENT:
case WINDIVERT_FILTER_FIELD_IP:
case WINDIVERT_FILTER_FIELD_IPV6:
case WINDIVERT_FILTER_FIELD_ICMP:
diff --git a/test/test.c b/test/test.c
index 946b3c0..92f5aaa 100644
--- a/test/test.c
+++ b/test/test.c
@@ -192,6 +192,7 @@ static const struct test tests[] =
&pkt_echo_request, TRUE},
{"(tcp? tcp.DstPort == 80: true) and (udp? udp.DstPort == 80: true)",
&pkt_echo_request, TRUE},
+ {"fragment", &pkt_echo_request, FALSE},
{"ip and ip and ip and ip and ip and " // Max filter length:
"ip and ip and ip and ip and ip and "
"ip and ip and ip and ip and ip and "
@@ -752,6 +753,7 @@ static const struct test tests[] =
{"ip and !loopback and (outbound? tcp.DstPort == 80 or"
" tcp.DstPort == 443 or udp.DstPort == 53 :"
" icmp.Type == 11 and icmp.Code == 0)", &pkt_ipv6_echo_reply, FALSE},
+ {"fragment", &pkt_ipv6_echo_reply, FALSE},
{"random8 < 128", &pkt_ipv6_echo_reply, TRUE},
{"(random8 < 128? random16 < 0x8000: random32 < 0x80000000)",
&pkt_ipv6_echo_reply, TRUE},
@@ -884,6 +886,7 @@ static const struct test tests[] =
&pkt_ipv6_exthdrs_udp, FALSE},
{"localAddr == ::1 and remoteAddr == 1 and localPort == 4660 and "
"remotePort == 43690 and protocol == 17", &pkt_ipv6_exthdrs_udp, TRUE},
+ {"fragment", &pkt_ipv4_fragment_0, TRUE},
{"ip.MF or ip.FragOff != 0", &pkt_ipv4_fragment_0, TRUE},
{"icmp", &pkt_ipv4_fragment_0, TRUE},
{"icmp.Body != 123 || icmp.Body == 123", &pkt_ipv4_fragment_0, TRUE},
@@ -893,6 +896,7 @@ static const struct test tests[] =
"ip.TTL == 64 and ip.Protocol == 1 and ip.SrcAddr == 0xFFFF0A000001 and "
"ip.DstAddr == 0xFFFF08080808 and icmp.Type == 8 and icmp.Code == 0 and "
"icmp.Body == 0x0D560001", &pkt_ipv4_fragment_0, TRUE},
+ {"fragment", &pkt_ipv4_fragment_1, TRUE},
{"ip.MF or ip.FragOff != 0", &pkt_ipv4_fragment_1, TRUE},
{"icmp", &pkt_ipv4_fragment_1, FALSE},
{"icmp.Body != 123 || icmp.Body == 123", &pkt_ipv4_fragment_1, FALSE},
@@ -901,6 +905,7 @@ static const struct test tests[] =
"ip.Id == 0x1234 and ip.FragOff == 1 and ip.MF == 0 and ip.DF == 0 and "
"ip.TTL == 64 and ip.Protocol == 1 and ip.SrcAddr == 0xFFFF0A000001 and "
"ip.DstAddr == 0xFFFF08080808", &pkt_ipv4_fragment_1, TRUE},
+ {"fragment", &pkt_ipv6_fragment_0, TRUE},
{"icmpv6", &pkt_ipv6_fragment_0, TRUE},
{"length == 104 || ipv6.Length == 64", &pkt_ipv6_fragment_0, FALSE},
{"ipv6.TrafficClass == 0x00000000 and ipv6.FlowLabel == 0x0000 and "
@@ -908,6 +913,7 @@ static const struct test tests[] =
"ipv6.SrcAddr == 0:0:0:0:0:0:0:1 and ipv6.DstAddr == 0:0:0:0:0:0:0:1 and "
"icmpv6.Type == 129 and icmpv6.Code == 0 and icmpv6.Body == 0x10720003",
&pkt_ipv6_fragment_0, TRUE},
+ {"fragment", &pkt_ipv6_fragment_1, TRUE},
{"icmpv6", &pkt_ipv6_fragment_1, FALSE},
{"length == 104 || ipv6.Length == 64", &pkt_ipv6_fragment_1, FALSE},
{"ipv6.TrafficClass == 0x00000000 and ipv6.FlowLabel == 0x0000 and "