From 609ab6359424a665ea2a3c6df5589a120fb06778 Mon Sep 17 00:00:00 2001 From: basil00 Date: Sat, 25 Jul 2015 13:24:28 +0800 Subject: [PATCH] Add a NO_REPLACE for WinDivertHelperCalcChecksums The WINDIVERT_HELPER_NO_REPLACE flag tells WinDivertHelperCalcChecksums not to recalculate and replace non-zero checksum fields. This is useful for reconstructing the checksums for packets returned by WinDivertRecv(), where all non-zero checksum fields are already valid. This allows WinDivert1.2 to achieve similar efficiency as WinDivert1.1, where checksums were calculated by the driver. Note that for modified packets, the WINDIVERT_HELPER_NO_REPLACE flag should not be used, as all checksums need to be recalculated from scratch anyway. --- dll/windivert_helper.c | 27 +++++++++++++++++++++------ doc/windivert.html | 26 ++++++++++++++++++++++---- examples/netdump/netdump.c | 8 +++----- examples/passthru/passthru.c | 11 ++++------- examples/webfilter/webfilter.c | 3 ++- include/windivert.h | 1 + 6 files changed, 53 insertions(+), 23 deletions(-) diff --git a/dll/windivert_helper.c b/dll/windivert_helper.c index 833f79a..84f7d59 100644 --- a/dll/windivert_helper.c +++ b/dll/windivert_helper.c @@ -423,8 +423,14 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, PWINDIVERT_UDPHDR udp_header; UINT payload_len, checksum_len; UINT count = 0; + UINT64 flags_all = + (WINDIVERT_HELPER_NO_IP_CHECKSUM | + WINDIVERT_HELPER_NO_ICMP_CHECKSUM | + WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM | + WINDIVERT_HELPER_NO_TCP_CHECKSUM | + WINDIVERT_HELPER_NO_UDP_CHECKSUM); - if ((flags & 0x1F) == 0x1F) + if ((flags & flags_all) == flags_all) { return 0; } @@ -433,7 +439,8 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, &payload_len); - if (ip_header != NULL && !(flags & WINDIVERT_HELPER_NO_IP_CHECKSUM)) + if (ip_header != NULL && !(flags & WINDIVERT_HELPER_NO_IP_CHECKSUM) && + (!(flags & WINDIVERT_HELPER_NO_REPLACE) || ip_header->Checksum == 0)) { ip_header->Checksum = 0; ip_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0, @@ -443,7 +450,9 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, if (icmp_header != NULL) { - if (flags & WINDIVERT_HELPER_NO_ICMP_CHECKSUM) + if ((flags & WINDIVERT_HELPER_NO_ICMP_CHECKSUM) || + ((flags & WINDIVERT_HELPER_NO_REPLACE) && + icmp_header->Checksum != 0)) { return count; } @@ -456,7 +465,9 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, if (icmpv6_header != NULL) { - if (flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) + if ((flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) || + ((flags & WINDIVERT_HELPER_NO_REPLACE) && + icmpv6_header->Checksum != 0)) { return count; } @@ -472,7 +483,9 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, if (tcp_header != NULL) { - if (flags & WINDIVERT_HELPER_NO_TCP_CHECKSUM) + if ((flags & WINDIVERT_HELPER_NO_TCP_CHECKSUM) || + ((flags & WINDIVERT_HELPER_NO_REPLACE) && + tcp_header->Checksum != 0)) { return count; } @@ -499,7 +512,9 @@ extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, if (udp_header != NULL) { - if (flags & WINDIVERT_HELPER_NO_UDP_CHECKSUM) + if ((flags & WINDIVERT_HELPER_NO_UDP_CHECKSUM) || + ((flags & WINDIVERT_HELPER_NO_REPLACE) && + udp_header->Checksum != 0)) { return count; } diff --git a/doc/windivert.html b/doc/windivert.html index 640bef6..d7ff45a 100644 --- a/doc/windivert.html +++ b/doc/windivert.html @@ -614,7 +614,7 @@ WinDivert sometimes captures outbound or loopback packets before the IP/TCP/UDP checksum fields have been calculated. For outbound packets, this occurs when checksum offloading is enabled, which defers checksum calculation to a compatible NIC card. -If the checksum is absent the corresponding +If the checksum is absent then the corresponding checksum field will be set to zero. Correct checksums can be reconstructed using the WinDivertHelperCalcChecksums() @@ -730,6 +730,15 @@ fields are currently ignored and may be arbitrary values. Injecting an inbound packet on the outbound path may work (for some types of packets), however this should be considered "undocumented" behavior, and may be changed in the future. +

+Injected packets should have correct checksums. +Correct checksums can be calculated using the +WinDivertHelperCalcChecksums() +function. +Note that packets returned by +WinDivertRecv() are not +guaranteed to have correct checksums.

@@ -1246,6 +1255,8 @@ UINT WinDivertHelperCalcChecksums( checksum.
  • WINDIVERT_HELPER_NO_UDP_CHECKSUM: Do not calculate the UDP checksum.
  • +
  • WINDIVERT_HELPER_NO_REPLACE: Do not replace non-zero + checksums.
  • @@ -1259,12 +1270,19 @@ Individual checksum calculations may be disabled via the appropriate flag. Typically this function should be invoked on a modified packet before it is injected with WinDivertSend().

    -This function will calculate each checksum from scratch, even if the existing -checksum is correct. +By default this function will calculate each checksum from scratch, even if +the existing checksum is correct. This may be inefficient for some applications. For better performance, incremental checksum calculations should be used instead (not provided by this API). -

    +

    +If the WINDIVERT_HELPER_NO_REPLACE flag is used, this function will +assume that all non-zero checksum fields are already valid and will not +replace them. +This is useful for reconstructing checksums for packets returned by +WinDivertRecv(), where +non-zero checksum fields do not need to be recalculated. +

    6.11 WinDivertHelperCheckFilter

    diff --git a/examples/netdump/netdump.c b/examples/netdump/netdump.c index 39ac27d..93f5902 100644 --- a/examples/netdump/netdump.c +++ b/examples/netdump/netdump.c @@ -118,11 +118,9 @@ int __cdecl main(int argc, char **argv) continue; } - // Calculate checksums for outbound packets. - if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND) - { - WinDivertHelperCalcChecksums(packet, packet_len, 0); - } + // Calculate checksums. + WinDivertHelperCalcChecksums(packet, packet_len, + WINDIVERT_HELPER_NO_REPLACE); // Print info about the matching packet. WinDivertHelperParsePacket(packet, packet_len, &ip_header, diff --git a/examples/passthru/passthru.c b/examples/passthru/passthru.c index fe52562..7f862d5 100644 --- a/examples/passthru/passthru.c +++ b/examples/passthru/passthru.c @@ -107,14 +107,11 @@ static DWORD passthru(LPVOID arg) continue; } - // NOTE: Since WinDivert1.2 outbound packets are not guaranteed - // to have correct checksums. - if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND) - { - WinDivertHelperCalcChecksums(packet, packet_len, 0); - } - // Re-inject the matching packet. + // NOTE: Only use the WINDIVERT_HELPER_NO_REPLACE flag if the packet + // was not modified. + WinDivertHelperCalcChecksums(packet, packet_len, + WINDIVERT_HELPER_NO_REPLACE); if (!WinDivertSend(handle, packet, packet_len, &addr, NULL)) { fprintf(stderr, "warning: failed to reinject packet (%d)\n", diff --git a/examples/webfilter/webfilter.c b/examples/webfilter/webfilter.c index d162ef1..2895cb4 100644 --- a/examples/webfilter/webfilter.c +++ b/examples/webfilter/webfilter.c @@ -188,7 +188,8 @@ int __cdecl main(int argc, char **argv) !BlackListPayloadMatch(blacklist, payload, (UINT16)payload_len)) { // Packet does not match the blacklist; simply reinject it. - WinDivertHelperCalcChecksums(packet, packet_len, 0); + WinDivertHelperCalcChecksums(packet, packet_len, + WINDIVERT_HELPER_NO_REPLACE); if (!WinDivertSend(handle, packet, packet_len, &addr, NULL)) { fprintf(stderr, "warning: failed to reinject packet (%d)\n", diff --git a/include/windivert.h b/include/windivert.h index 23d327d..0655c82 100644 --- a/include/windivert.h +++ b/include/windivert.h @@ -313,6 +313,7 @@ typedef struct #define WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM 4 #define WINDIVERT_HELPER_NO_TCP_CHECKSUM 8 #define WINDIVERT_HELPER_NO_UDP_CHECKSUM 16 +#define WINDIVERT_HELPER_NO_REPLACE 2048 /* * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet.