Add IPv6 support, rewrite program to improve performance, and update README.
This commit is contained in:
15
README.md
15
README.md
@@ -1,16 +1,9 @@
|
||||
# XDP Firewall
|
||||
## Description
|
||||
An XDP firewall designed to read filtering rules based off of a config file. This software only supports IPv4 and protocols TCP, UDP, and ICMP at the moment. With that said, the program comes with accepted and blocked packet statistics which can be disabled if need to be.
|
||||
A stateless firewall written using [XDP](https://www.iovisor.org/technology/xdp) designed to read filtering rules based off of a config file and filter incoming packets. Both IPv4 and **IPv6** are supported! Supported protocols include TCP, UDP, and ICMP at the moment. With that said, the program comes with accepted and blocked packet statistics which can be disabled if need to be.
|
||||
|
||||
Additionally, if the host's NIC doesn't support XDP-native, the program will attempt to attach via XDP generic. The program firstly tries XDP-native, though.
|
||||
|
||||
## Barricade Firewall (Faster)
|
||||
I'm working on a new firewall called [Barricade Firewall](https://github.com/Barricade-FW/Firewall) that is based off of this one. The new firewall will include more features and the XDP program itself is already faster than the XDP Firewall's program. This is due to using an eBPF map to indicate the current timestamp instead of using the kernel BPF function `bpf_ktime_get_ns()` which apparently impacts performance quite a bit according to [here](https://www.spinics.net/lists/xdp-newbies/msg01713.html).
|
||||
|
||||
The XDP program itself is fully functional in the new Barricade Firewall project and instead of using `libconfig`, it uses JSON to parse the config.
|
||||
|
||||
Feel free to check it out!
|
||||
|
||||
## Command Line Usage
|
||||
The following command line arguments are supported:
|
||||
|
||||
@@ -29,8 +22,10 @@ Config option `filters` is an array. Each filter includes the following options:
|
||||
|
||||
* `enabled` => If true, this rule is enabled.
|
||||
* `action` => What action to perform against the packet if matched. 0 = Block. 1 = Allow.
|
||||
* `srcip` => The source IP the packet must match (e.g. 10.50.0.3).
|
||||
* `dstip` => The destination IP the packet must match (e.g. 10.50.0.4).
|
||||
* `srcip` => The source IP address the packet must match (e.g. 10.50.0.3).
|
||||
* `dstip` => The destination IP address the packet must match (e.g. 10.50.0.4).
|
||||
* `srcip6` => The source IPv6 address the packet must match (e.g. fe80::18c4:dfff:fe70:d8a6).
|
||||
* `dstip6` => The destination IPv6 address the packet must match (e.g. fe80::ac21:14ff:fe4b:3a6d).
|
||||
* `min_ttl` => The minimum TTL (time to live) the packet must match.
|
||||
* `max_ttl` => The maximum TTL (time to live) the packet must match.
|
||||
* `max_len` => The maximum packet length the packet must match. This includes the entire frame (ethernet header, IP header, L4 header, and data).
|
||||
|
||||
36
src/config.c
36
src/config.c
@@ -24,6 +24,12 @@ void SetConfigDefaults(struct config_map *cfg)
|
||||
cfg->filters[i].srcIP = 0;
|
||||
cfg->filters[i].dstIP = 0;
|
||||
|
||||
for (uint8_t j = 0; j < 4; j++)
|
||||
{
|
||||
cfg->filters[i].srcIP6[j] = 0;
|
||||
cfg->filters[i].dstIP6[j] = 0;
|
||||
}
|
||||
|
||||
cfg->filters[i].do_min_len = 0;
|
||||
cfg->filters[i].min_len = 0;
|
||||
|
||||
@@ -210,6 +216,36 @@ int ReadConfig(struct config_map *cfg)
|
||||
cfg->filters[i].dstIP = inet_addr(dIP);
|
||||
}
|
||||
|
||||
// Source IP (IPv6) (not required).
|
||||
const char *sIP6;
|
||||
|
||||
if (config_setting_lookup_string(filter, "srcip6", &sIP6))
|
||||
{
|
||||
struct in6_addr in;
|
||||
|
||||
inet_pton(AF_INET6, sIP6, &in);
|
||||
|
||||
for (uint8_t j = 0; j < 4; j++)
|
||||
{
|
||||
cfg->filters[i].srcIP6[j] = in.__in6_u.__u6_addr32[j];
|
||||
}
|
||||
}
|
||||
|
||||
// Destination IP (IPv6) (not required).
|
||||
const char *dIP6;
|
||||
|
||||
if (config_setting_lookup_string(filter, "dstip6", &dIP6))
|
||||
{
|
||||
struct in6_addr in;
|
||||
|
||||
inet_pton(AF_INET6, dIP6, &in);
|
||||
|
||||
for (uint8_t j = 0; j < 4; j++)
|
||||
{
|
||||
cfg->filters[i].dstIP6[j] = in.__in6_u.__u6_addr32[j];
|
||||
}
|
||||
}
|
||||
|
||||
// Minimum TTL (not required).
|
||||
int min_ttl;
|
||||
|
||||
|
||||
@@ -70,6 +70,9 @@ struct filter
|
||||
uint32_t srcIP;
|
||||
uint32_t dstIP;
|
||||
|
||||
uint32_t srcIP6[4];
|
||||
uint32_t dstIP6[4];
|
||||
|
||||
unsigned int do_min_ttl : 1;
|
||||
uint8_t min_ttl;
|
||||
|
||||
|
||||
517
src/xdpfw_kern.c
517
src/xdpfw_kern.c
@@ -1,8 +1,10 @@
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/in.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
@@ -16,7 +18,9 @@
|
||||
#include "include/xdpfw.h"
|
||||
|
||||
//#define DEBUG
|
||||
#define DOSTATSONBLOCKMAP // Feel free to comment this out if you don't want the `blocked` entry on the stats map to be incremented every single time a packet is dropped from the source IP being on the blocked map. Commenting this line out should increase performance when blocking malicious traffic.
|
||||
//#define DOSTATSONBLOCKMAP // Feel free to comment this out if you don't want the `blocked` entry on the stats map to be incremented every single time a packet is dropped from the source IP being on the blocked map. Commenting this line out should increase performance when blocking malicious traffic.
|
||||
|
||||
#define ALLOWSINGLEIPV4V6 // When this is defined, a check will occur inside the IPv4 and IPv6 filters. For IPv6 packets, if no IPv6 source/destination IP addresses are set, but there is an IPv4 address, it will ignore the filter. The same goes for IPv4, if there is no IPv4 source/destination IP addresses set, if an IPv6 address is set, it will ignore the filter.
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
@@ -43,6 +47,8 @@
|
||||
#define ntohl(x) (x)
|
||||
#endif
|
||||
|
||||
#define uint128_t __uint128_t
|
||||
|
||||
struct bpf_map_def SEC("maps") filters_map =
|
||||
{
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
@@ -75,6 +81,22 @@ struct bpf_map_def SEC("maps") ip_blacklist_map =
|
||||
.max_entries = MAX_TRACK_IPS
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") ip6_stats_map =
|
||||
{
|
||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
||||
.key_size = sizeof(uint128_t),
|
||||
.value_size = sizeof(struct xdpfw_ip_stats),
|
||||
.max_entries = MAX_TRACK_IPS
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") ip6_blacklist_map =
|
||||
{
|
||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
||||
.key_size = sizeof(uint128_t),
|
||||
.value_size = sizeof(uint64_t),
|
||||
.max_entries = MAX_TRACK_IPS
|
||||
};
|
||||
|
||||
SEC("xdp_prog")
|
||||
int xdp_prog_main(struct xdp_md *ctx)
|
||||
{
|
||||
@@ -92,40 +114,67 @@ int xdp_prog_main(struct xdp_md *ctx)
|
||||
}
|
||||
|
||||
// Check Ethernet protocol.
|
||||
if (unlikely(ethhdr->h_proto != htons(ETH_P_IP)))
|
||||
if (unlikely(ethhdr->h_proto != htons(ETH_P_IP) && ethhdr->h_proto != htons(ETH_P_IPV6)))
|
||||
{
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
uint8_t matched = 0;
|
||||
uint8_t action = 0;
|
||||
uint64_t blocktime = 1;
|
||||
|
||||
// Scan IP header.
|
||||
struct iphdr *iph = data + sizeof(struct ethhdr);
|
||||
// Initialize IP headers.
|
||||
struct iphdr *iph;
|
||||
struct ipv6hdr *iph6;
|
||||
uint128_t srcip6 = 0;
|
||||
|
||||
// Check if the IP header is valid.
|
||||
if (unlikely(iph + 1 > (struct iphdr *)data_end))
|
||||
// Set IPv4 and IPv6 common variables.
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
return XDP_DROP;
|
||||
iph6 = (data + sizeof(struct ethhdr));
|
||||
|
||||
if (unlikely(iph6 + 1 > (struct ipv6hdr *)data_end))
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
srcip6 |= (uint128_t) iph6->saddr.in6_u.u6_addr32[0] << 0;
|
||||
srcip6 |= (uint128_t) iph6->saddr.in6_u.u6_addr32[1] << 16;
|
||||
srcip6 |= (uint128_t) iph6->saddr.in6_u.u6_addr32[2] << 32;
|
||||
srcip6 |= (uint128_t) iph6->saddr.in6_u.u6_addr32[3] << 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
iph = (data + sizeof(struct ethhdr));
|
||||
|
||||
if (unlikely(iph + 1 > (struct iphdr *)data_end))
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
}
|
||||
|
||||
// Check IP header protocols.
|
||||
if (unlikely(iph->protocol != IPPROTO_UDP && iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_ICMP))
|
||||
if ((ethhdr->h_proto == htons(ETH_P_IPV6) && iph6->nexthdr != IPPROTO_UDP && iph6->nexthdr != IPPROTO_TCP && iph6->nexthdr != IPPROTO_ICMP) && (ethhdr->h_proto == htons(ETH_P_IP) && iph->protocol != IPPROTO_UDP && iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_ICMP))
|
||||
{
|
||||
return XDP_DROP;
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
// Get stats map.
|
||||
uint32_t key = 0;
|
||||
struct xdpfw_stats *stats;
|
||||
|
||||
stats = bpf_map_lookup_elem(&stats_map, &key);
|
||||
struct xdpfw_stats *stats = bpf_map_lookup_elem(&stats_map, &key);
|
||||
|
||||
uint64_t now = bpf_ktime_get_ns();
|
||||
|
||||
// Check blacklist map.
|
||||
uint64_t *blocked = bpf_map_lookup_elem(&ip_blacklist_map, &iph->saddr);
|
||||
uint64_t *blocked = NULL;
|
||||
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
blocked = bpf_map_lookup_elem(&ip6_blacklist_map, &srcip6);
|
||||
}
|
||||
else
|
||||
{
|
||||
blocked = bpf_map_lookup_elem(&ip_blacklist_map, &iph->saddr);
|
||||
}
|
||||
|
||||
if (blocked != NULL && *blocked > 0)
|
||||
{
|
||||
@@ -136,7 +185,14 @@ int xdp_prog_main(struct xdp_md *ctx)
|
||||
if (now > *blocked)
|
||||
{
|
||||
// Remove element from map.
|
||||
bpf_map_delete_elem(&ip_blacklist_map, &iph->saddr);
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
bpf_map_delete_elem(&ip6_blacklist_map, &srcip6);
|
||||
}
|
||||
else
|
||||
{
|
||||
bpf_map_delete_elem(&ip_blacklist_map, &iph->saddr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -157,7 +213,16 @@ int xdp_prog_main(struct xdp_md *ctx)
|
||||
uint64_t pps = 0;
|
||||
uint64_t bps = 0;
|
||||
|
||||
struct xdpfw_ip_stats *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &iph->saddr);
|
||||
struct xdpfw_ip_stats *ip_stats = NULL;
|
||||
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
ip_stats = bpf_map_lookup_elem(&ip6_stats_map, &srcip6);
|
||||
}
|
||||
else
|
||||
{
|
||||
ip_stats = bpf_map_lookup_elem(&ip_stats_map, &iph->saddr);
|
||||
}
|
||||
|
||||
if (ip_stats)
|
||||
{
|
||||
@@ -188,212 +253,348 @@ int xdp_prog_main(struct xdp_md *ctx)
|
||||
pps = new.pps;
|
||||
bps = new.bps;
|
||||
|
||||
bpf_map_update_elem(&ip_stats_map, &iph->saddr, &new, BPF_ANY);
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
bpf_map_update_elem(&ip6_stats_map, &srcip6, &new, BPF_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
bpf_map_update_elem(&ip_stats_map, &iph->saddr, &new, BPF_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
// Let's get the filters we need.
|
||||
struct filter *filter[MAX_FILTERS];
|
||||
struct tcphdr *tcph = NULL;
|
||||
struct udphdr *udph = NULL;
|
||||
struct icmphdr *icmph = NULL;
|
||||
struct icmp6hdr *icmp6h = NULL;
|
||||
|
||||
// Check protocol.
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
switch (iph6->nexthdr)
|
||||
{
|
||||
case IPPROTO_TCP:
|
||||
// Scan TCP header.
|
||||
tcph = (data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr));
|
||||
|
||||
// Check TCP header.
|
||||
if (tcph + 1 > (struct tcphdr *)data_end)
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_UDP:
|
||||
// Scan UDP header.
|
||||
udph = (data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr));
|
||||
|
||||
// Check TCP header.
|
||||
if (udph + 1 > (struct udphdr *)data_end)
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_ICMPV6:
|
||||
// Scan ICMPv6 header.
|
||||
icmp6h = (data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr));
|
||||
|
||||
// Check ICMPv6 header.
|
||||
if (icmp6h + 1 > (struct icmp6hdr *)data_end)
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (iph->protocol)
|
||||
{
|
||||
case IPPROTO_TCP:
|
||||
// Scan TCP header.
|
||||
tcph = (data + sizeof(struct ethhdr) + (iph->ihl * 4));
|
||||
|
||||
// Check TCP header.
|
||||
if (tcph + 1 > (struct tcphdr *)data_end)
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_UDP:
|
||||
// Scan UDP header.
|
||||
udph = (data + sizeof(struct ethhdr) + (iph->ihl * 4));
|
||||
|
||||
// Check TCP header.
|
||||
if (udph + 1 > (struct udphdr *)data_end)
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPPROTO_ICMP:
|
||||
// Scan ICMP header.
|
||||
icmph = (data + sizeof(struct ethhdr) + (iph->ihl * 4));
|
||||
|
||||
// Check ICMP header.
|
||||
if (icmph + 1 > (struct icmphdr *)data_end)
|
||||
{
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < MAX_FILTERS; i++)
|
||||
{
|
||||
uint32_t key = i;
|
||||
|
||||
filter[i] = bpf_map_lookup_elem(&filters_map, &key);
|
||||
}
|
||||
struct filter *filter = bpf_map_lookup_elem(&filters_map, &key);
|
||||
|
||||
struct tcphdr *tcph;
|
||||
struct udphdr *udph;
|
||||
struct icmphdr *icmph;
|
||||
|
||||
uint16_t l4headerLen = 0;
|
||||
|
||||
// Check protocol.
|
||||
if (iph->protocol == IPPROTO_TCP)
|
||||
{
|
||||
// Scan TCP header.
|
||||
tcph = (data + sizeof(struct ethhdr) + (iph->ihl * 4));
|
||||
|
||||
// Check TCP header.
|
||||
if (tcph + 1 > (struct tcphdr *)data_end)
|
||||
{
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
// Set L4 Header length.
|
||||
l4headerLen = sizeof(struct tcphdr);
|
||||
}
|
||||
else if (iph->protocol == IPPROTO_UDP)
|
||||
{
|
||||
// Scan UDP header.
|
||||
udph = (data + sizeof(struct ethhdr) + (iph->ihl * 4));
|
||||
|
||||
// Check TCP header.
|
||||
if (udph + 1 > (struct udphdr *)data_end)
|
||||
{
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
// Set L4 Header length.
|
||||
l4headerLen = sizeof(struct udphdr);
|
||||
}
|
||||
else if (iph->protocol == IPPROTO_ICMP)
|
||||
{
|
||||
// Scan UDP header.
|
||||
icmph = (data + sizeof(struct ethhdr) + (iph->ihl * 4));
|
||||
|
||||
// Check TCP header.
|
||||
if (icmph + 1 > (struct icmphdr *)data_end)
|
||||
{
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
// Set L4 Header length.
|
||||
l4headerLen = sizeof(struct icmphdr);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < MAX_FILTERS; i++)
|
||||
{
|
||||
// Check if ID is above 0 (if 0, it's an invalid rule).
|
||||
if (!filter[i] || filter[i]->id < 1)
|
||||
if (!filter || filter->id < 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the rule is enabled.
|
||||
if (!filter[i]->enabled)
|
||||
if (!filter->enabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Source address.
|
||||
if (filter[i]->srcIP != 0 && iph->saddr != filter[i]->srcIP)
|
||||
// Do specific IPv6.
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (iph6 + 1 > (struct ipv6hdr *)data_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Destination address.
|
||||
if (filter[i]->dstIP != 0 && iph->daddr != filter[i]->dstIP)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Source address.
|
||||
if (filter->srcIP6[0] != 0 && (iph6->saddr.in6_u.u6_addr32[0] != filter->srcIP6[0] || iph6->saddr.in6_u.u6_addr32[1] != filter->srcIP6[1] || iph6->saddr.in6_u.u6_addr32[2] != filter->srcIP6[2] || iph6->saddr.in6_u.u6_addr32[3] != filter->srcIP6[3]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Max TTL length.
|
||||
if (filter[i]->do_max_ttl && filter[i]->max_ttl > iph->ttl)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Destination address.
|
||||
if (filter->dstIP6[0] != 0 && (iph6->daddr.in6_u.u6_addr32[0] != filter->dstIP6[0] || iph6->daddr.in6_u.u6_addr32[1] != filter->dstIP6[1] || iph6->daddr.in6_u.u6_addr32[2] != filter->dstIP6[2] || iph6->daddr.in6_u.u6_addr32[3] != filter->dstIP6[3]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Min TTL length.
|
||||
if (filter[i]->do_min_ttl && filter[i]->min_ttl < iph->ttl)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#ifdef ALLOWSINGLEIPV4V6
|
||||
if (filter->srcIP != 0 || filter->dstIP != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Max packet length.
|
||||
if (filter[i]->do_max_len && filter[i]->max_len > (ntohs(iph->tot_len) + sizeof(struct ethhdr)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Max TTL length.
|
||||
if (filter->do_max_ttl && filter->max_ttl > iph6->hop_limit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Min packet length.
|
||||
if (filter[i]->do_min_len && filter[i]->min_len < (ntohs(iph->tot_len) + sizeof(struct ethhdr)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Min TTL length.
|
||||
if (filter->do_min_ttl && filter->min_ttl < iph6->hop_limit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TOS.
|
||||
if (filter[i]->do_tos && filter[i]->tos != iph->tos)
|
||||
// Max packet length.
|
||||
if (filter->do_max_len && filter->max_len > (ntohs(iph6->payload_len) + sizeof(struct ethhdr)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Min packet length.
|
||||
if (filter->do_min_len && filter->min_len < (ntohs(iph6->payload_len) + sizeof(struct ethhdr)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
// Source address.
|
||||
if (filter->srcIP != 0 && iph->saddr != filter->srcIP)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Destination address.
|
||||
if (filter->dstIP != 0 && iph->daddr != filter->dstIP)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef ALLOWSINGLEIPV4V6
|
||||
if ((filter->srcIP6[0] != 0 || filter->srcIP6[1] != 0 || filter->srcIP6[2] != 0 || filter->srcIP6[3] != 0) || (filter->dstIP6[0] != 0 || filter->dstIP6[1] != 0 || filter->dstIP6[2] != 0 || filter->dstIP6[3] != 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TOS.
|
||||
if (filter->do_tos && filter->tos != iph->tos)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Max TTL length.
|
||||
if (filter->do_max_ttl && filter->max_ttl > iph->ttl)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Min TTL length.
|
||||
if (filter->do_min_ttl && filter->min_ttl < iph->ttl)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Max packet length.
|
||||
if (filter->do_max_len && filter->max_len > (ntohs(iph->tot_len) + sizeof(struct ethhdr)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Min packet length.
|
||||
if (filter->do_min_len && filter->min_len < (ntohs(iph->tot_len) + sizeof(struct ethhdr)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// PPS.
|
||||
if (filter[i]->do_pps && pps <= filter[i]->pps)
|
||||
if (filter->do_pps && pps <= filter->pps)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// BPS.
|
||||
if (filter[i]->do_bps && bps <= filter[i]->bps)
|
||||
if (filter->do_bps && bps <= filter->bps)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do TCP options.
|
||||
if (iph->protocol == IPPROTO_TCP && filter[i]->tcpopts.enabled)
|
||||
if (filter->tcpopts.enabled)
|
||||
{
|
||||
if (!tcph)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Source port.
|
||||
if (filter[i]->tcpopts.do_sport && htons(filter[i]->tcpopts.sport) != tcph->source)
|
||||
if (filter->tcpopts.do_sport && htons(filter->tcpopts.sport) != tcph->source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Destination port.
|
||||
if (filter[i]->tcpopts.do_dport && htons(filter[i]->tcpopts.dport) != tcph->dest)
|
||||
if (filter->tcpopts.do_dport && htons(filter->tcpopts.dport) != tcph->dest)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// URG flag.
|
||||
if (filter[i]->tcpopts.do_urg && filter[i]->tcpopts.urg != tcph->urg)
|
||||
if (filter->tcpopts.do_urg && filter->tcpopts.urg != tcph->urg)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// ACK flag.
|
||||
if (filter[i]->tcpopts.do_ack && filter[i]->tcpopts.ack != tcph->ack)
|
||||
if (filter->tcpopts.do_ack && filter->tcpopts.ack != tcph->ack)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// RST flag.
|
||||
if (filter[i]->tcpopts.do_rst && filter[i]->tcpopts.rst != tcph->rst)
|
||||
if (filter->tcpopts.do_rst && filter->tcpopts.rst != tcph->rst)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// PSH flag.
|
||||
if (filter[i]->tcpopts.do_psh && filter[i]->tcpopts.psh != tcph->psh)
|
||||
if (filter->tcpopts.do_psh && filter->tcpopts.psh != tcph->psh)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// SYN flag.
|
||||
if (filter[i]->tcpopts.do_syn && filter[i]->tcpopts.syn != tcph->syn)
|
||||
if (filter->tcpopts.do_syn && filter->tcpopts.syn != tcph->syn)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIN flag.
|
||||
if (filter[i]->tcpopts.do_fin && filter[i]->tcpopts.fin != tcph->fin)
|
||||
if (filter->tcpopts.do_fin && filter->tcpopts.fin != tcph->fin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (iph->protocol == IPPROTO_UDP && filter[i]->udpopts.enabled)
|
||||
else if (filter->udpopts.enabled)
|
||||
{
|
||||
if (!udph)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Source port.
|
||||
if (filter[i]->udpopts.do_sport && htons(filter[i]->udpopts.sport) != udph->source)
|
||||
if (filter->udpopts.do_sport && htons(filter->udpopts.sport) != udph->source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Destination port.
|
||||
if (filter[i]->udpopts.do_dport && htons(filter[i]->udpopts.dport) != udph->dest)
|
||||
if (filter->udpopts.do_dport && htons(filter->udpopts.dport) != udph->dest)
|
||||
{
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (iph->protocol == IPPROTO_ICMP && filter[i]->icmpopts.enabled)
|
||||
else if (filter->icmpopts.enabled)
|
||||
{
|
||||
if (!icmph)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Code.
|
||||
if (filter[i]->icmpopts.do_code && filter[i]->icmpopts.code != icmph->code)
|
||||
if (filter->icmpopts.do_code && filter->icmpopts.code != icmph->code)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Type.
|
||||
if (filter[i]->icmpopts.do_type && filter[i]->icmpopts.type != icmph->type)
|
||||
if (filter->icmpopts.do_type && filter->icmpopts.type != icmph->type)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (icmp6h && filter->icmpopts.enabled)
|
||||
{
|
||||
if (!icmp6h)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Code.
|
||||
if (filter->icmpopts.do_code && filter->icmpopts.code != icmp6h->icmp6_code)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Type.
|
||||
if (filter->icmpopts.do_type && filter->icmpopts.type != icmp6h->icmp6_type)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -401,51 +602,55 @@ int xdp_prog_main(struct xdp_md *ctx)
|
||||
|
||||
// Matched.
|
||||
#ifdef DEBUG
|
||||
bpf_printk("Matched rule ID #%" PRIu8 ".\n", filter[i]->id);
|
||||
bpf_printk("Matched rule ID #%" PRIu8 ".\n", filter->id);
|
||||
#endif
|
||||
|
||||
matched = 1;
|
||||
action = filter[i]->action;
|
||||
blocktime = filter[i]->blockTime;
|
||||
action = filter->action;
|
||||
blocktime = filter->blockTime;
|
||||
|
||||
break;
|
||||
goto matched;
|
||||
}
|
||||
|
||||
if (matched)
|
||||
{
|
||||
// Increase allowed or blocked entries on stats map.
|
||||
if (stats)
|
||||
return XDP_PASS;
|
||||
|
||||
matched:
|
||||
if (action == 0)
|
||||
{
|
||||
// Update stats map.
|
||||
if (action == 0)
|
||||
#ifdef DEBUG
|
||||
//bpf_printk("Matched with protocol %" PRIu8 " and sAddr %" PRIu32 ".\n", iph->protocol, iph->saddr);
|
||||
#endif
|
||||
|
||||
// Before dropping, update the blacklist map.
|
||||
if (blocktime > 0)
|
||||
{
|
||||
uint64_t newTime = now + (blocktime * 1000000000);
|
||||
|
||||
if (ethhdr->h_proto == htons(ETH_P_IPV6))
|
||||
{
|
||||
bpf_map_update_elem(&ip6_blacklist_map, &srcip6, &newTime, BPF_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
bpf_map_update_elem(&ip_blacklist_map, &iph->saddr, &newTime, BPF_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
if (stats)
|
||||
{
|
||||
__sync_fetch_and_add(&stats->blocked, 1);
|
||||
}
|
||||
else
|
||||
|
||||
return XDP_DROP;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stats)
|
||||
{
|
||||
__sync_fetch_and_add(&stats->allowed, 1);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
//bpf_printk("Matched with protocol %" PRIu8 " and sAddr %" PRIu32 ".\n", iph->protocol, iph->saddr);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((matched) && action == 0)
|
||||
{
|
||||
// Before dropping, update the blacklist map.
|
||||
if (blocktime > 0)
|
||||
{
|
||||
uint64_t newTime = now + (blocktime * 1000000000);
|
||||
|
||||
bpf_map_update_elem(&ip_blacklist_map, &iph->saddr, &newTime, BPF_ANY);
|
||||
}
|
||||
|
||||
return XDP_DROP;
|
||||
}
|
||||
|
||||
return XDP_PASS;
|
||||
return XDP_PASS;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
Reference in New Issue
Block a user