- WinDivert driver now skips IPv6 extension headers when filtering packets.
- windivert_filter() now uses Ndis(Advance/Retreat)NetBufferDataStart() to find packet headers. - NOTE: This new code is not yet very well tested.
This commit is contained in:
+121
-73
@@ -407,7 +407,7 @@ static UINT16 windivert_checksum(const void *pseudo_header,
|
||||
static void windivert_update_checksums(void *header, size_t len,
|
||||
BOOL update_ip, BOOL update_tcp, BOOL update_udp);
|
||||
static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx,
|
||||
UINT32 sub_if_idx, BOOL outbound, filter_t filter);
|
||||
UINT32 sub_if_idx, BOOL outbound, BOOL isipv4, 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,
|
||||
@@ -2198,7 +2198,7 @@ static void windivert_classify_callout(IN UINT8 direction, IN UINT32 if_idx,
|
||||
do
|
||||
{
|
||||
if (windivert_filter(buffer_fst, if_idx, sub_if_idx, outbound,
|
||||
context->filter))
|
||||
isipv4, context->filter))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -2247,7 +2247,7 @@ static void windivert_classify_callout(IN UINT8 direction, IN UINT32 if_idx,
|
||||
while (buffer_itr != NULL)
|
||||
{
|
||||
if (windivert_filter(buffer_itr, if_idx, sub_if_idx, outbound,
|
||||
context->filter))
|
||||
isipv4, context->filter))
|
||||
{
|
||||
if ((context->flags & WINDIVERT_FLAG_DROP) == 0)
|
||||
{
|
||||
@@ -2768,13 +2768,9 @@ static void windivert_update_checksums(void *header, size_t len,
|
||||
* Checks if the given packet is of interest.
|
||||
*/
|
||||
static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx,
|
||||
UINT32 sub_if_idx, BOOL outbound, filter_t filter)
|
||||
UINT32 sub_if_idx, BOOL outbound, BOOL isipv4, filter_t filter)
|
||||
{
|
||||
// Buffer contains enough space for a full size iphdr and tcphdr/udphdr
|
||||
// (without options)
|
||||
UINT8 storage[0xF*sizeof(UINT32) + sizeof(struct tcphdr)];
|
||||
UINT8 *headers;
|
||||
size_t tot_len, cpy_len, ip_header_len;
|
||||
size_t tot_len, ip_header_len;
|
||||
struct iphdr *ip_header = NULL;
|
||||
struct ipv6hdr *ipv6_header = NULL;
|
||||
struct icmphdr *icmp_header = NULL;
|
||||
@@ -2782,7 +2778,8 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx,
|
||||
struct tcphdr *tcp_header = NULL;
|
||||
struct udphdr *udp_header = NULL;
|
||||
UINT16 ip, ttl;
|
||||
UINT8 protocol;
|
||||
UINT8 proto;
|
||||
NTSTATUS status;
|
||||
|
||||
// Parse the headers:
|
||||
tot_len = NET_BUFFER_DATA_LENGTH(buffer);
|
||||
@@ -2791,87 +2788,138 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx,
|
||||
DEBUG("FILTER: REJECT (packet length too small)");
|
||||
return FALSE;
|
||||
}
|
||||
cpy_len = (tot_len < sizeof(storage)? tot_len: sizeof(storage));
|
||||
headers = (UINT8 *)NdisGetDataBuffer(buffer, (ULONG)cpy_len, storage, 1,
|
||||
0);
|
||||
if (headers == NULL)
|
||||
{
|
||||
headers = storage;
|
||||
}
|
||||
|
||||
ip_header = (struct iphdr *)headers;
|
||||
switch (ip_header->Version)
|
||||
// Get the IP header.
|
||||
if (isipv4)
|
||||
{
|
||||
case 4:
|
||||
ip_header_len = ip_header->HdrLength*sizeof(UINT32);
|
||||
if (RtlUshortByteSwap(ip_header->Length) != tot_len ||
|
||||
ip_header->HdrLength < 5 ||
|
||||
ip_header_len > tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad IPv4 packet)");
|
||||
return FALSE;
|
||||
}
|
||||
protocol = ip_header->Protocol;
|
||||
break;
|
||||
case 6:
|
||||
ip_header = NULL;
|
||||
ipv6_header = (struct ipv6hdr *)headers;
|
||||
ip_header_len = sizeof(struct ipv6hdr);
|
||||
if (ip_header_len > tot_len ||
|
||||
RtlUshortByteSwap(ipv6_header->Length) +
|
||||
sizeof(struct ipv6hdr) != tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad IPv6 packet)");
|
||||
return FALSE;
|
||||
}
|
||||
protocol = ipv6_header->NextHdr;
|
||||
break;
|
||||
default:
|
||||
DEBUG("FILTER: REJECT (packet is neither IPv4 nor IPv6)");
|
||||
// IPv4:
|
||||
if (tot_len < sizeof(struct iphdr))
|
||||
{
|
||||
DEBUG("FILTER: REJECT (packet length too small)");
|
||||
return FALSE;
|
||||
}
|
||||
ip_header = (struct iphdr *)NdisGetDataBuffer(buffer,
|
||||
sizeof(struct iphdr), NULL, 1, 0);
|
||||
if (ip_header == NULL)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (failed to get IPv4 header)");
|
||||
return FALSE;
|
||||
}
|
||||
ip_header_len = ip_header->HdrLength*sizeof(UINT32);
|
||||
if (ip_header->Version != 4 ||
|
||||
RtlUshortByteSwap(ip_header->Length) != tot_len ||
|
||||
ip_header->HdrLength < 5 ||
|
||||
ip_header_len > tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad IPv4 packet)");
|
||||
return FALSE;
|
||||
}
|
||||
proto = ip_header->Protocol;
|
||||
NdisAdvanceNetBufferDataStart(buffer, ip_header_len, FALSE, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// IPv6:
|
||||
if (tot_len < sizeof(struct ipv6hdr))
|
||||
{
|
||||
DEBUG("FILTER: REJECT (packet length too small)");
|
||||
return FALSE;
|
||||
}
|
||||
ipv6_header = (struct ipv6hdr *)NdisGetDataBuffer(buffer,
|
||||
sizeof(struct ipv6hdr), NULL, 1, 0);
|
||||
if (ipv6_header == NULL)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (failed to get IPv6 header)");
|
||||
return FALSE;
|
||||
}
|
||||
ip_header_len = sizeof(struct ipv6hdr);
|
||||
if (ipv6_header->Version != 6 ||
|
||||
ip_header_len > tot_len ||
|
||||
RtlUshortByteSwap(ipv6_header->Length) +
|
||||
sizeof(struct ipv6hdr) != tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad IPv6 packet)");
|
||||
return FALSE;
|
||||
}
|
||||
proto = ipv6_header->NextHdr;
|
||||
NdisAdvanceNetBufferDataStart(buffer, ip_header_len, FALSE, NULL);
|
||||
|
||||
// Skip extension headers:
|
||||
while (TRUE)
|
||||
{
|
||||
UINT8 *ext_header;
|
||||
size_t ext_header_len;
|
||||
BOOL isexthdr = TRUE;
|
||||
|
||||
ext_header = (UINT8 *)NdisGetDataBuffer(buffer, 2, NULL, 1, 0);
|
||||
if (ext_header == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ext_header_len = (size_t)ext_header[1];
|
||||
switch (proto)
|
||||
{
|
||||
case IPPROTO_FRAGMENT:
|
||||
ext_header_len = 8;
|
||||
break;
|
||||
case IPPROTO_AH:
|
||||
ext_header_len += 2;
|
||||
ext_header_len *= 4;
|
||||
break;
|
||||
case IPPROTO_HOPOPTS:
|
||||
case IPPROTO_DSTOPTS:
|
||||
case IPPROTO_ROUTING:
|
||||
ext_header_len++;
|
||||
ext_header_len *= 8;
|
||||
break;
|
||||
default:
|
||||
isexthdr = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isexthdr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
proto = ext_header[0];
|
||||
ip_header_len += ext_header_len;
|
||||
NdisAdvanceNetBufferDataStart(buffer, ext_header_len, FALSE,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
switch (protocol)
|
||||
switch (proto)
|
||||
{
|
||||
case IPPROTO_ICMP:
|
||||
icmp_header = (struct icmphdr *)(headers + ip_header_len);
|
||||
if (ip_header == NULL ||
|
||||
sizeof(struct icmphdr) + ip_header_len > tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad ICMP packet)");
|
||||
return FALSE;
|
||||
}
|
||||
icmp_header = (struct icmphdr *)NdisGetDataBuffer(buffer,
|
||||
sizeof(struct icmphdr), NULL, 1, 0);
|
||||
break;
|
||||
case IPPROTO_ICMPV6:
|
||||
icmpv6_header = (struct icmpv6hdr *)(headers + ip_header_len);
|
||||
if (ipv6_header == NULL ||
|
||||
sizeof(struct icmpv6hdr) + ip_header_len > tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad ICMPV6 packet)");
|
||||
return FALSE;
|
||||
}
|
||||
icmpv6_header = (struct icmpv6hdr *)NdisGetDataBuffer(buffer,
|
||||
sizeof(struct icmpv6hdr), NULL, 1, 0);
|
||||
break;
|
||||
case IPPROTO_TCP:
|
||||
tcp_header = (struct tcphdr *)(headers + ip_header_len);
|
||||
if (tcp_header->HdrLength < 5 ||
|
||||
tcp_header->HdrLength*sizeof(UINT32) + ip_header_len > tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad TCP packet)");
|
||||
return FALSE;
|
||||
}
|
||||
tcp_header = (struct tcphdr *)NdisGetDataBuffer(buffer,
|
||||
sizeof(struct tcphdr), NULL, 1, 0);
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
udp_header = (struct udphdr *)(headers + ip_header_len);
|
||||
if (sizeof(struct udphdr) + ip_header_len > tot_len)
|
||||
{
|
||||
DEBUG("FILTER: REJECT (bad UDP packet)");
|
||||
return FALSE;
|
||||
}
|
||||
udp_header = (struct udphdr *)NdisGetDataBuffer(buffer,
|
||||
sizeof(struct udphdr), NULL, 1, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
status = NdisRetreatNetBufferDataStart(buffer, ip_header_len, 0, NULL);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
// Should never occur.
|
||||
DEBUG("FILTER: REJECT (failed to retreat buffer)");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Execute the filter:
|
||||
ip = 0;
|
||||
ttl = WINDIVERT_FILTER_MAXLEN+1; // Additional safety
|
||||
|
||||
Reference in New Issue
Block a user