From f0be301f1d3200b8117c8f9b1214857bec65c46d Mon Sep 17 00:00:00 2001 From: basil00 Date: Sat, 25 Jul 2015 23:16:48 +0800 Subject: [PATCH] Expand tests & bug fixes. Extend the number of tests to cover some of the new features. Fix IPv6 address comparison which did not work correctly in some cases. --- dll/windivert_helper.c | 92 +++++++++++++++----------- sys/windivert.c | 85 ++++++++++++++---------- test/test.c | 147 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 245 insertions(+), 79 deletions(-) diff --git a/dll/windivert_helper.c b/dll/windivert_helper.c index 84f7d59..481c98a 100644 --- a/dll/windivert_helper.c +++ b/dll/windivert_helper.c @@ -2059,6 +2059,46 @@ extern BOOL WinDivertHelperCheckFilter(const char *filter_str, return !IS_ERROR(err); } +/* + * Big number comparison. + */ +static int WinDivertBigNumCompare(const UINT32 *a, const UINT32 *b) +{ + if (a[3] < b[3]) + { + return -1; + } + if (a[3] > b[3]) + { + return 1; + } + if (a[2] < b[2]) + { + return -1; + } + if (a[2] > b[2]) + { + return 1; + } + if (a[1] < b[1]) + { + return -1; + } + if (a[1] > b[1]) + { + return 1; + } + if (a[0] < b[0]) + { + return -1; + } + if (a[0] > b[0]) + { + return 1; + } + return 0; +} + /* * Evaluate the given filter with the given packet as input. */ @@ -2077,6 +2117,7 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, UINT payload_len; UINT32 val[4]; BOOL pass; + int cmp; struct windivert_ioctl_filter_s object[WINDIVERT_FILTER_MAXLEN]; UINT obj_len; @@ -2176,8 +2217,8 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, pass = (udphdr != NULL); break; default: - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; + pass = TRUE; + break; } if (!pass) { @@ -2293,7 +2334,7 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = ntohs(icmphdr->Checksum); break; case WINDIVERT_FILTER_FIELD_ICMP_BODY: - val[0] = ntohs(icmphdr->Body); + val[0] = ntohl(icmphdr->Body); break; case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: val[0] = icmpv6hdr->Type; @@ -2305,7 +2346,7 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = ntohs(icmpv6hdr->Checksum); break; case WINDIVERT_FILTER_FIELD_ICMPV6_BODY: - val[0] = ntohs(icmpv6hdr->Body); + val[0] = ntohl(icmpv6hdr->Body); break; case WINDIVERT_FILTER_FIELD_TCP_SRCPORT: val[0] = ntohs(tcphdr->SrcPort); @@ -2371,55 +2412,26 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } + cmp = WinDivertBigNumCompare(val, object[pc].arg); switch (object[pc].test) { case WINDIVERT_FILTER_TEST_EQ: - pass = (val[0] == object[pc].arg[0] && - val[1] == object[pc].arg[1] && - val[2] == object[pc].arg[2] && - val[3] == object[pc].arg[3]); + pass = (cmp == 0); break; case WINDIVERT_FILTER_TEST_NEQ: - pass = (val[0] != object[pc].arg[0] || - val[1] != object[pc].arg[1] || - val[2] != object[pc].arg[2] || - val[3] != object[pc].arg[3]); + pass = (cmp != 0); break; case WINDIVERT_FILTER_TEST_LT: - pass = (val[3] < object[pc].arg[3] || - (val[3] == object[pc].arg[3] && - (val[2] < object[pc].arg[2] || - (val[2] == object[pc].arg[2] && - (val[1] < object[pc].arg[1] || - (val[1] == object[pc].arg[1] && - val[0] < object[pc].arg[0])))))); + pass = (cmp < 0); break; case WINDIVERT_FILTER_TEST_LEQ: - pass = (val[3] < object[pc].arg[3] || - (val[3] == object[pc].arg[3] && - (val[2] < object[pc].arg[2] || - (val[2] == object[pc].arg[2] && - (val[1] < object[pc].arg[1] || - (val[1] == object[pc].arg[1] && - val[0] <= object[pc].arg[0])))))); + pass = (cmp <= 0); break; case WINDIVERT_FILTER_TEST_GT: - pass = (val[3] > object[pc].arg[3] || - (val[3] == object[pc].arg[3] && - (val[2] > object[pc].arg[2] || - (val[2] == object[pc].arg[2] && - (val[1] > object[pc].arg[1] || - (val[1] == object[pc].arg[1] && - val[0] > object[pc].arg[0])))))); + pass = (cmp > 0); break; case WINDIVERT_FILTER_TEST_GEQ: - pass = (val[3] > object[pc].arg[3] || - (val[3] == object[pc].arg[3] && - (val[2] > object[pc].arg[2] || - (val[2] == object[pc].arg[2] && - (val[1] > object[pc].arg[1] || - (val[1] == object[pc].arg[1] && - val[0] >= object[pc].arg[0])))))); + pass = (cmp >= 0); break; default: SetLastError(ERROR_INVALID_PARAMETER); diff --git a/sys/windivert.c b/sys/windivert.c index 4f5fca6..721cdf9 100644 --- a/sys/windivert.c +++ b/sys/windivert.c @@ -404,6 +404,7 @@ static void NTAPI windivert_reinject_complete(VOID *context, NET_BUFFER_LIST *buffers, BOOLEAN dispatch_level); static void windivert_free_packet(packet_t packet); static UINT8 windivert_skip_headers(UINT8 proto, UINT8 **header, size_t *len); +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 isipv4, UINT8 checksums, filter_t filter); @@ -2654,6 +2655,46 @@ static void windivert_zero_checksums(void *header, size_t len, } } +/* + * Big number comparison. + */ +static int windivert_big_num_compare(const UINT32 *a, const UINT32 *b) +{ + if (a[3] < b[3]) + { + return -1; + } + if (a[3] > b[3]) + { + return 1; + } + if (a[2] < b[2]) + { + return -1; + } + if (a[2] > b[2]) + { + return 1; + } + if (a[1] < b[1]) + { + return -1; + } + if (a[1] > b[1]) + { + return 1; + } + if (a[0] < b[0]) + { + return -1; + } + if (a[0] > b[0]) + { + return 1; + } + return 0; +} + /* * Checks if the given packet is of interest. */ @@ -2817,6 +2858,7 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, while (ttl-- != 0) { BOOL result; + int cmp; UINT32 field[4]; field[1] = 0; field[2] = 0; @@ -3075,55 +3117,26 @@ static BOOL windivert_filter(PNET_BUFFER buffer, UINT32 if_idx, field[0] = 0; break; } + cmp = windivert_big_num_compare(field, filter[ip].arg); switch (filter[ip].test) { case WINDIVERT_FILTER_TEST_EQ: - result = (field[0] == filter[ip].arg[0] && - field[1] == filter[ip].arg[1] && - field[2] == filter[ip].arg[2] && - field[3] == filter[ip].arg[3]); + result = (cmp == 0); break; case WINDIVERT_FILTER_TEST_NEQ: - result = (field[0] != filter[ip].arg[0] || - field[1] != filter[ip].arg[1] || - field[2] != filter[ip].arg[2] || - field[3] != filter[ip].arg[3]); + result = (cmp != 0); break; case WINDIVERT_FILTER_TEST_LT: - result = (field[3] < filter[ip].arg[3] || - (field[3] == filter[ip].arg[3] && - field[2] < filter[ip].arg[2] || - (field[2] == filter[ip].arg[2] && - field[1] < filter[ip].arg[1] || - (field[1] == filter[ip].arg[1] && - field[0] < filter[ip].arg[0])))); + result = (cmp < 0); break; case WINDIVERT_FILTER_TEST_LEQ: - result = (field[3] < filter[ip].arg[3] || - (field[3] == filter[ip].arg[3] && - field[2] < filter[ip].arg[2] || - (field[2] == filter[ip].arg[2] && - field[1] < filter[ip].arg[1] || - (field[1] == filter[ip].arg[1] && - field[0] <= filter[ip].arg[0])))); + result = (cmp <= 0); break; case WINDIVERT_FILTER_TEST_GT: - result = (field[3] > filter[ip].arg[3] || - (field[3] == filter[ip].arg[3] && - field[2] > filter[ip].arg[2] || - (field[2] == filter[ip].arg[2] && - field[1] > filter[ip].arg[1] || - (field[1] == filter[ip].arg[1] && - field[0] > filter[ip].arg[0])))); + result = (cmp > 0); break; case WINDIVERT_FILTER_TEST_GEQ: - result = (field[3] > filter[ip].arg[3] || - (field[3] == filter[ip].arg[3] && - field[2] > filter[ip].arg[2] || - (field[2] == filter[ip].arg[2] && - field[1] > filter[ip].arg[1] || - (field[1] == filter[ip].arg[1] && - field[0] >= filter[ip].arg[0])))); + result = (cmp >= 0); break; default: result = FALSE; diff --git a/test/test.c b/test/test.c index 500b1aa..18398fa 100644 --- a/test/test.c +++ b/test/test.c @@ -99,12 +99,115 @@ static struct test tests[] = { {"outbound and icmp", &pkt_echo_request, TRUE}, {"outbound", &pkt_echo_request, TRUE}, + {"outbound and inbound", &pkt_echo_request, FALSE}, {"icmp", &pkt_echo_request, TRUE}, {"not icmp", &pkt_echo_request, FALSE}, + {"ip or ipv6", &pkt_echo_request, TRUE}, {"inbound", &pkt_echo_request, FALSE}, {"tcp", &pkt_echo_request, FALSE}, {"icmp.Type == 8", &pkt_echo_request, TRUE}, {"icmp.Type == 9", &pkt_echo_request, FALSE}, + {"(tcp? ip.Checksum == 0: icmp)", &pkt_echo_request, TRUE}, + {"(udp? icmp: icmp.Body == 5555)", &pkt_echo_request, FALSE}, + {"(false? false: false)", &pkt_echo_request, FALSE}, + {"(true? true: true)", &pkt_echo_request, TRUE}, + {"(tcp or udp or icmpv6 or ipv6? true: false)", + &pkt_echo_request, FALSE}, + {"(ip and ipv6 and tcp and udp? false: icmp > 0)", + &pkt_echo_request, TRUE}, + {"(tcp? tcp.DstPort == 80: true) and (udp? udp.DstPort == 80: true)", + &pkt_echo_request, TRUE}, + {"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 " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip and ip and ip and " + "ip and ip and ip", &pkt_echo_request, TRUE}, + {"not true or false or not icmp or " // All fields: + "icmp.Body == 33 or icmp.Checksum==2 or " + "icmp.Code == 0x777 or " + "icmp.Type == 0x333 or icmpv6 or " + "icmpv6.Body or icmpv6.Checksum or " + "icmpv6.Code or icmpv6.Type or " + "ifIdx == 93923 or inbound or " + "not ip or ip.Checksum == 8 or " + "not ip.DF or ip.DstAddr == 1.2.3.4 or " + "ip.FragOff == 4212 or " + "ip.HdrLength == 2 or ip.Id = 0x0987 or " + "ip.Length == 788 or ip.MF == 1 or " + "ip.Protocol == 999 or " + "ip.SrcAddr == 9.8.7.255 or " + "ip.TOS == 3 or ip.TTL = 221 or ipv6 or " + "ipv6.DstAddr or ipv6.FlowLabel or " + "ipv6.HopLimit or ipv6.Length or " + "ipv6.NextHdr or ipv6.SrcAddr or " + "ipv6.TrafficClass or not outbound or " + "subIfIdx == 888 or tcp or tcp.Ack or " + "tcp.AckNum or tcp.Checksum or " + "tcp.DstPort or tcp.Fin or " + "tcp.HdrLength or tcp.PayloadLength or " + "tcp.Psh or tcp.Rst or tcp.SeqNum or " + "tcp.SrcPort or tcp.Syn or tcp.Urg or " + "tcp.UrgPtr or tcp.Window or udp or " + "udp.Checksum or udp.DstPort or " + "udp.Length or udp.PayloadLength or " + "udp.SrcPort", &pkt_echo_request, FALSE}, + {"(true and (true and (true and (true and "// Deep nesting: + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(true and (true and (true and (true and " + "(((((((((((((((icmp)))))))))))))))))))" + "))))))))))))))))))))))))))))))))))))))" + "))))))))))))))))))))))))))))))))))))))", &pkt_echo_request, TRUE}, + {"not not not not not not not not icmp", &pkt_echo_request, TRUE}, + {"not not not not not not not icmp", &pkt_echo_request, FALSE}, + {"!!!!!!!icmp", &pkt_echo_request, FALSE}, + {"false and true or true", &pkt_echo_request, TRUE}, + {"true and false or false", &pkt_echo_request, FALSE}, + {"true or true and false", &pkt_echo_request, TRUE}, + {"false or false and true", &pkt_echo_request, FALSE}, + {"tcp && icmp || ip", &pkt_echo_request, TRUE}, + {"icmp && udp || tcp", &pkt_echo_request, FALSE}, + {"ip || icmp && icmpv6", &pkt_echo_request, TRUE}, + {"!ip || !icmp && !udp", &pkt_echo_request, FALSE}, + {"(((icmp)? (true): (false)) and " + "(((tcp)? (false): (true)) and " + "((ipv6)? (false): (true))))", &pkt_echo_request, TRUE}, {"tcp", &pkt_http_request, TRUE}, {"outbound and tcp and tcp.DstPort == 80", &pkt_http_request, TRUE}, {"outbound and tcp and tcp.DstPort == 81", &pkt_http_request, FALSE}, @@ -116,12 +219,18 @@ static struct test tests[] = {"tcp.PayloadLength <= 469", &pkt_http_request, TRUE}, {"tcp.PayloadLength > 469", &pkt_http_request, FALSE}, {"tcp.PayloadLength < 469", &pkt_http_request, FALSE}, + {"(outbound? (ip? (tcp.DstPort == 80? (tcp.PayloadLength > 0? true: " + "false): false): false): false)", &pkt_http_request, TRUE}, + {"(outbound? (ip? (tcp.DstPort == 80? (tcp.PayloadLength == 0? true: " + "false): false): false): false)", &pkt_http_request, FALSE}, {"udp", &pkt_dns_request, TRUE}, {"udp && udp.SrcPort > 1 && ipv6", &pkt_dns_request, FALSE}, {"udp.DstPort == 53", &pkt_dns_request, TRUE}, {"udp.DstPort > 100", &pkt_dns_request, FALSE}, {"ip.DstAddr = 8.8.4.4", &pkt_dns_request, TRUE}, {"ip.DstAddr = 8.8.8.8", &pkt_dns_request, FALSE}, + {"ip.DstAddr >= 8.8.0.0 &&" + "ip.DstAddr <= 8.8.255.255", &pkt_dns_request, TRUE}, {"ip.SrcAddr >= 10.0.0.0 && ip.SrcAddr <= 10.255.255.255", &pkt_dns_request, TRUE}, {"ip.SrcAddr < 10.0.0.0 or ip.SrcAddr > 10.255.255.255", @@ -131,6 +240,10 @@ static struct test tests[] = {"ip", &pkt_ipv6_tcp_syn, FALSE}, {"tcp.Syn", &pkt_ipv6_tcp_syn, TRUE}, {"tcp.Syn == 1 && tcp.Ack == 0", &pkt_ipv6_tcp_syn, TRUE}, + {"tcp.Rst or tcp.Fin", &pkt_ipv6_tcp_syn, FALSE}, + {"(tcp.Syn? !tcp.Rst && !tcp.Fin: true)", &pkt_ipv6_tcp_syn, TRUE}, + {"(tcp.Rst? !tcp.Syn: (tcp.Fin? !tcp.Syn: tcp.Syn))", + &pkt_ipv6_tcp_syn, TRUE}, {"tcp.PayloadLength == 0", &pkt_ipv6_tcp_syn, TRUE}, {"ipv6.SrcAddr == 1234:5678:1::aabb:ccdd", &pkt_ipv6_tcp_syn, TRUE}, {"ipv6.SrcAddr == aabb:5678:1::1234:ccdd", &pkt_ipv6_tcp_syn, FALSE}, @@ -150,10 +263,20 @@ static struct test tests[] = {"tcp", &pkt_ipv6_exthdrs_udp, FALSE}, {"ipv6.SrcAddr == ::1", &pkt_ipv6_exthdrs_udp, TRUE}, {"ipv6.SrcAddr == ::2", &pkt_ipv6_exthdrs_udp, FALSE}, + {"ipv6.SrcAddr < abcd::1", &pkt_ipv6_exthdrs_udp, TRUE}, + {"ipv6.SrcAddr <= abcd::1", &pkt_ipv6_exthdrs_udp, TRUE}, + {"ipv6.SrcAddr != abcd::1", &pkt_ipv6_exthdrs_udp, TRUE}, + {"ipv6.SrcAddr >= abcd::1", &pkt_ipv6_exthdrs_udp, FALSE}, + {"ipv6.SrcAddr > abcd::1", &pkt_ipv6_exthdrs_udp, FALSE}, {"udp.SrcPort == 4660 and udp.DstPort == 43690", &pkt_ipv6_exthdrs_udp, TRUE}, {"udp.SrcPort == 4660 and udp.DstPort == 12345", &pkt_ipv6_exthdrs_udp, FALSE}, + {"(outbound and tcp? tcp.DstPort == 0xABAB: false) or " + "(outbound and udp? udp.DstPort == 0xAAAA: false) or " + "(inbound and tcp? tcp.SrcPort == 0xABAB: false) or " + "(inbound and udp? udp.SrcPort == 0xAAAA: false)", + &pkt_ipv6_exthdrs_udp, TRUE}, }; /* @@ -244,9 +367,29 @@ static BOOL run_test(HANDLE inject_handle, const char *filter, DWORD iolen; WINDIVERT_ADDRESS addr; OVERLAPPED overlapped; + const char *err_str; + UINT err_pos; HANDLE handle = INVALID_HANDLE_VALUE, handle0 = INVALID_HANDLE_VALUE, event = NULL; + // (0) Verify the test data: + if (!WinDivertHelperCheckFilter(filter, WINDIVERT_LAYER_NETWORK, &err_str, + &err_pos)) + { + fprintf(stderr, "error: filter string \"%s\" is invalid with error " + "\"%s\" (position=%u)\n", filter, err_str, err_pos); + goto failed; + } + memset(&addr, 0, sizeof(addr)); + addr.Direction = WINDIVERT_DIRECTION_OUTBOUND; + if (WinDivertHelperEvalFilter(filter, WINDIVERT_LAYER_NETWORK, + (PVOID)packet, packet_len, &addr) != match) + { + fprintf(stderr, "error: filter \"%s\" does not match the given " + "packet\n", filter); + goto failed; + } + // (1) Open a WinDivert handle to the given filter: handle = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, 0); if (handle == INVALID_HANDLE_VALUE) @@ -270,9 +413,7 @@ static BOOL run_test(HANDLE inject_handle, const char *filter, } // (2) Inject the packet: - memset(&addr, 0, sizeof(addr)); - addr.Direction = WINDIVERT_DIRECTION_OUTBOUND; - if (!WinDivertSend(inject_handle, packet, packet_len, &addr, NULL)) + if (!WinDivertSend(inject_handle, (PVOID)packet, packet_len, &addr, NULL)) { fprintf(stderr, "error: failed to inject test packet (err = %d)\n", GetLastError());