/*
* netfilter.c
* (C) 2019, 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:
* This is a simple traffic filter/firewall using WinDivert.
*
* usage: netfilter.exe windivert-filter [priority]
*
* Any traffic that matches the windivert-filter will be blocked using one of
* the following methods:
* - TCP: send a TCP RST to the packet's source.
* - UDP: send a ICMP(v6) "destination unreachable" to the packet's source.
* - ICMP/ICMPv6: Drop the packet.
*
* This program is similar to Linux's iptables with the "-j REJECT" target.
*/
#include
#include
#include
#include
#include "windivert.h"
#define ntohs(x) WinDivertHelperNtohs(x)
#define ntohl(x) WinDivertHelperNtohl(x)
#define htons(x) WinDivertHelperHtons(x)
#define htonl(x) WinDivertHelperHtonl(x)
#define MAXBUF WINDIVERT_MTU_MAX
#define INET6_ADDRSTRLEN 45
#define IPPROTO_ICMPV6 58
/*
* Pre-fabricated packets.
*/
typedef struct
{
WINDIVERT_IPHDR ip;
WINDIVERT_TCPHDR tcp;
} TCPPACKET, *PTCPPACKET;
typedef struct
{
WINDIVERT_IPV6HDR ipv6;
WINDIVERT_TCPHDR tcp;
} TCPV6PACKET, *PTCPV6PACKET;
typedef struct
{
WINDIVERT_IPHDR ip;
WINDIVERT_ICMPHDR icmp;
UINT8 data[];
} ICMPPACKET, *PICMPPACKET;
typedef struct
{
WINDIVERT_IPV6HDR ipv6;
WINDIVERT_ICMPV6HDR icmpv6;
UINT8 data[];
} ICMPV6PACKET, *PICMPV6PACKET;
/*
* Prototypes.
*/
static void PacketIpInit(PWINDIVERT_IPHDR packet);
static void PacketIpTcpInit(PTCPPACKET packet);
static void PacketIpIcmpInit(PICMPPACKET packet);
static void PacketIpv6Init(PWINDIVERT_IPV6HDR packet);
static void PacketIpv6TcpInit(PTCPV6PACKET packet);
static void PacketIpv6Icmpv6Init(PICMPV6PACKET packet);
/*
* Entry.
*/
int __cdecl main(int argc, char **argv)
{
HANDLE handle, console;
UINT i;
INT16 priority = 0;
unsigned char packet[MAXBUF];
UINT packet_len;
WINDIVERT_ADDRESS recv_addr, send_addr;
PWINDIVERT_IPHDR ip_header;
PWINDIVERT_IPV6HDR ipv6_header;
PWINDIVERT_ICMPHDR icmp_header;
PWINDIVERT_ICMPV6HDR icmpv6_header;
PWINDIVERT_TCPHDR tcp_header;
PWINDIVERT_UDPHDR udp_header;
UINT32 src_addr[4], dst_addr[4];
char src_str[INET6_ADDRSTRLEN+1], dst_str[INET6_ADDRSTRLEN+1];
UINT payload_len;
const char *err_str;
TCPPACKET reset0;
PTCPPACKET reset = &reset0;
UINT8 dnr0[sizeof(ICMPPACKET) + 0x0F*sizeof(UINT32) + 8 + 1];
PICMPPACKET dnr = (PICMPPACKET)dnr0;
TCPV6PACKET resetv6_0;
PTCPV6PACKET resetv6 = &resetv6_0;
UINT8 dnrv6_0[sizeof(ICMPV6PACKET) + sizeof(WINDIVERT_IPV6HDR) +
sizeof(WINDIVERT_TCPHDR)];
PICMPV6PACKET dnrv6 = (PICMPV6PACKET)dnrv6_0;
// Check arguments.
switch (argc)
{
case 2:
break;
case 3:
priority = (INT16)atoi(argv[2]);
break;
default:
fprintf(stderr, "usage: %s windivert-filter [priority]\n",
argv[0]);
fprintf(stderr, "examples:\n");
fprintf(stderr, "\t%s true\n", argv[0]);
fprintf(stderr, "\t%s \"outbound and tcp.DstPort == 80\" 1000\n",
argv[0]);
fprintf(stderr, "\t%s \"inbound and tcp.Syn\" -400\n", argv[0]);
exit(EXIT_FAILURE);
}
// Initialize all packets.
PacketIpTcpInit(reset);
reset->tcp.Rst = 1;
reset->tcp.Ack = 1;
PacketIpIcmpInit(dnr);
dnr->icmp.Type = 3; // Destination not reachable.
dnr->icmp.Code = 3; // Port not reachable.
PacketIpv6TcpInit(resetv6);
resetv6->tcp.Rst = 1;
resetv6->tcp.Ack = 1;
PacketIpv6Icmpv6Init(dnrv6);
dnrv6->ipv6.Length = htons(sizeof(WINDIVERT_ICMPV6HDR) + 4 +
sizeof(WINDIVERT_IPV6HDR) + sizeof(WINDIVERT_TCPHDR));
dnrv6->icmpv6.Type = 1; // Destination not reachable.
dnrv6->icmpv6.Code = 4; // Port not reachable.
// Get console for pretty colors.
console = GetStdHandle(STD_OUTPUT_HANDLE);
// Divert traffic matching the filter:
handle = WinDivertOpen(argv[1], WINDIVERT_LAYER_NETWORK, priority, 0);
if (handle == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_INVALID_PARAMETER &&
!WinDivertHelperCompileFilter(argv[1], WINDIVERT_LAYER_NETWORK,
NULL, 0, &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());
exit(EXIT_FAILURE);
}
// Main loop:
while (TRUE)
{
// Read a matching packet.
if (!WinDivertRecv(handle, packet, sizeof(packet), &packet_len,
&recv_addr))
{
fprintf(stderr, "warning: failed to read packet\n");
continue;
}
// Print info about the matching packet.
WinDivertHelperParsePacket(packet, packet_len, &ip_header, &ipv6_header,
NULL, &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL,
&payload_len, NULL, NULL);
if (ip_header == NULL && ipv6_header == NULL)
{
continue;
}
// Dump packet info:
SetConsoleTextAttribute(console, FOREGROUND_RED);
fputs("BLOCK ", stdout);
SetConsoleTextAttribute(console,
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
if (ip_header != NULL)
{
WinDivertHelperFormatIPv4Address(ntohl(ip_header->SrcAddr),
src_str, sizeof(src_str));
WinDivertHelperFormatIPv4Address(ntohl(ip_header->DstAddr),
dst_str, sizeof(dst_str));
}
if (ipv6_header != NULL)
{
WinDivertHelperNtohIPv6Address(ipv6_header->SrcAddr, src_addr);
WinDivertHelperNtohIPv6Address(ipv6_header->DstAddr, dst_addr);
WinDivertHelperFormatIPv6Address(src_addr, src_str,
sizeof(src_str));
WinDivertHelperFormatIPv6Address(dst_addr, dst_str,
sizeof(dst_str));
}
printf("ip.SrcAddr=%s ip.DstAddr=%s ", src_str, dst_str);
if (icmp_header != NULL)
{
printf("icmp.Type=%u icmp.Code=%u ",
icmp_header->Type, icmp_header->Code);
// Simply drop ICMP
}
if (icmpv6_header != NULL)
{
printf("icmpv6.Type=%u icmpv6.Code=%u ",
icmpv6_header->Type, icmpv6_header->Code);
// Simply drop ICMPv6
}
if (tcp_header != NULL)
{
printf("tcp.SrcPort=%u tcp.DstPort=%u tcp.Flags=",
ntohs(tcp_header->SrcPort), ntohs(tcp_header->DstPort));
if (tcp_header->Fin)
{
fputs("[FIN]", stdout);
}
if (tcp_header->Rst)
{
fputs("[RST]", stdout);
}
if (tcp_header->Urg)
{
fputs("[URG]", stdout);
}
if (tcp_header->Syn)
{
fputs("[SYN]", stdout);
}
if (tcp_header->Psh)
{
fputs("[PSH]", stdout);
}
if (tcp_header->Ack)
{
fputs("[ACK]", stdout);
}
putchar(' ');
if (ip_header != NULL && !tcp_header->Rst && !tcp_header->Fin)
{
reset->ip.SrcAddr = ip_header->DstAddr;
reset->ip.DstAddr = ip_header->SrcAddr;
reset->tcp.SrcPort = tcp_header->DstPort;
reset->tcp.DstPort = tcp_header->SrcPort;
reset->tcp.SeqNum =
(tcp_header->Ack? tcp_header->AckNum: 0);
reset->tcp.AckNum =
(tcp_header->Syn?
htonl(ntohl(tcp_header->SeqNum) + 1):
htonl(ntohl(tcp_header->SeqNum) + payload_len));
memcpy(&send_addr, &recv_addr, sizeof(send_addr));
send_addr.Outbound = !recv_addr.Outbound;
WinDivertHelperCalcChecksums((PVOID)reset, sizeof(TCPPACKET),
&send_addr, 0);
if (!WinDivertSend(handle, (PVOID)reset, sizeof(TCPPACKET),
NULL, &send_addr))
{
fprintf(stderr, "warning: failed to send TCP reset (%d)\n",
GetLastError());
}
}
if (ipv6_header != NULL && !tcp_header->Rst && !tcp_header->Fin)
{
memcpy(resetv6->ipv6.SrcAddr, ipv6_header->DstAddr,
sizeof(resetv6->ipv6.SrcAddr));
memcpy(resetv6->ipv6.DstAddr, ipv6_header->SrcAddr,
sizeof(resetv6->ipv6.DstAddr));
resetv6->tcp.SrcPort = tcp_header->DstPort;
resetv6->tcp.DstPort = tcp_header->SrcPort;
resetv6->tcp.SeqNum =
(tcp_header->Ack? tcp_header->AckNum: 0);
resetv6->tcp.AckNum =
(tcp_header->Syn?
htonl(ntohl(tcp_header->SeqNum) + 1):
htonl(ntohl(tcp_header->SeqNum) + payload_len));
memcpy(&send_addr, &recv_addr, sizeof(send_addr));
send_addr.Outbound = !recv_addr.Outbound;
WinDivertHelperCalcChecksums((PVOID)resetv6,
sizeof(TCPV6PACKET), &send_addr, 0);
if (!WinDivertSend(handle, (PVOID)resetv6, sizeof(TCPV6PACKET),
NULL, &send_addr))
{
fprintf(stderr, "warning: failed to send TCP (IPV6) "
"reset (%d)\n", GetLastError());
}
}
}
if (udp_header != NULL)
{
printf("udp.SrcPort=%u udp.DstPort=%u ",
ntohs(udp_header->SrcPort), ntohs(udp_header->DstPort));
if (ip_header != NULL)
{
UINT icmp_length = ip_header->HdrLength*sizeof(UINT32) + 8;
memcpy(dnr->data, ip_header, icmp_length);
icmp_length += sizeof(ICMPPACKET);
dnr->ip.Length = htons((UINT16)icmp_length);
dnr->ip.SrcAddr = ip_header->DstAddr;
dnr->ip.DstAddr = ip_header->SrcAddr;
memcpy(&send_addr, &recv_addr, sizeof(send_addr));
send_addr.Outbound = !recv_addr.Outbound;
WinDivertHelperCalcChecksums((PVOID)dnr, icmp_length,
&send_addr, 0);
if (!WinDivertSend(handle, (PVOID)dnr, icmp_length, NULL,
&send_addr))
{
fprintf(stderr, "warning: failed to send ICMP message "
"(%d)\n", GetLastError());
}
}
if (ipv6_header != NULL)
{
UINT icmpv6_length = sizeof(WINDIVERT_IPV6HDR) +
sizeof(WINDIVERT_TCPHDR);
memcpy(dnrv6->data, ipv6_header, icmpv6_length);
icmpv6_length += sizeof(ICMPV6PACKET);
memcpy(dnrv6->ipv6.SrcAddr, ipv6_header->DstAddr,
sizeof(dnrv6->ipv6.SrcAddr));
memcpy(dnrv6->ipv6.DstAddr, ipv6_header->SrcAddr,
sizeof(dnrv6->ipv6.DstAddr));
memcpy(&send_addr, &recv_addr, sizeof(send_addr));
send_addr.Outbound = !recv_addr.Outbound;
WinDivertHelperCalcChecksums((PVOID)dnrv6, icmpv6_length,
&send_addr, 0);
if (!WinDivertSend(handle, (PVOID)dnrv6, icmpv6_length,
NULL, &send_addr))
{
fprintf(stderr, "warning: failed to send ICMPv6 message "
"(%d)\n", GetLastError());
}
}
}
putchar('\n');
}
}
/*
* Initialize a PACKET.
*/
static void PacketIpInit(PWINDIVERT_IPHDR packet)
{
memset(packet, 0, sizeof(WINDIVERT_IPHDR));
packet->Version = 4;
packet->HdrLength = sizeof(WINDIVERT_IPHDR) / sizeof(UINT32);
packet->Id = ntohs(0xDEAD);
packet->TTL = 64;
}
/*
* Initialize a TCPPACKET.
*/
static void PacketIpTcpInit(PTCPPACKET packet)
{
memset(packet, 0, sizeof(TCPPACKET));
PacketIpInit(&packet->ip);
packet->ip.Length = htons(sizeof(TCPPACKET));
packet->ip.Protocol = IPPROTO_TCP;
packet->tcp.HdrLength = sizeof(WINDIVERT_TCPHDR) / sizeof(UINT32);
}
/*
* Initialize an ICMPPACKET.
*/
static void PacketIpIcmpInit(PICMPPACKET packet)
{
memset(packet, 0, sizeof(ICMPPACKET));
PacketIpInit(&packet->ip);
packet->ip.Protocol = IPPROTO_ICMP;
}
/*
* Initialize a PACKETV6.
*/
static void PacketIpv6Init(PWINDIVERT_IPV6HDR packet)
{
memset(packet, 0, sizeof(WINDIVERT_IPV6HDR));
packet->Version = 6;
packet->HopLimit = 64;
}
/*
* Initialize a TCPV6PACKET.
*/
static void PacketIpv6TcpInit(PTCPV6PACKET packet)
{
memset(packet, 0, sizeof(TCPV6PACKET));
PacketIpv6Init(&packet->ipv6);
packet->ipv6.Length = htons(sizeof(WINDIVERT_TCPHDR));
packet->ipv6.NextHdr = IPPROTO_TCP;
packet->tcp.HdrLength = sizeof(WINDIVERT_TCPHDR) / sizeof(UINT32);
}
/*
* Initialize an ICMP PACKET.
*/
static void PacketIpv6Icmpv6Init(PICMPV6PACKET packet)
{
memset(packet, 0, sizeof(ICMPV6PACKET));
PacketIpv6Init(&packet->ipv6);
packet->ipv6.NextHdr = IPPROTO_ICMPV6;
}