Merge pull request #56 from gamemann/20250222-restructure

Project Restructure (Source Code & Build Process)
This commit is contained in:
Christian Deacon
2025-02-22 10:40:26 -05:00
committed by GitHub
27 changed files with 463 additions and 404 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.vscode/
build/
xdpfw
xdpfw.s
xdpfw.conf

148
Makefile
View File

@@ -1,94 +1,112 @@
CC = clang
LLC = llc
ARCH := $(shell uname -m | sed 's/x86_64/x86/')
# Top-level directories.
BUILD_DIR = build
SRC_DIR = src
MODULES_DIR = modules
# Main directories.
BUILDDIR = build
SRCDIR = src
MODULEDIR = modules
# Common directories.
COMMON_DIR = $(SRC_DIR)/common
LOADER_DIR = $(SRC_DIR)/loader
XDP_DIR = $(SRC_DIR)/xdp
# XDP Tools directory.
XDPTOOLSDIR = $(MODULEDIR)/xdp-tools
XDPTOOLSHEADERS = $(XDPTOOLSDIR)/headers
# Additional build directories.
BUILD_LOADER_DIR = $(BUILD_DIR)/loader
BUILD_XDP_DIR = $(BUILD_DIR)/xdp
# XDP Tools directories.
XDP_TOOLS_DIR = $(MODULES_DIR)/xdp-tools
XDP_TOOLS_HEADERS = $(XDP_TOOLS_DIR)/headers
# LibXDP and LibBPF directories.
LIBXDPDIR = $(XDPTOOLSDIR)/lib/libxdp
LIBXDP_DIR = $(XDP_TOOLS_DIR)/lib/libxdp
LIBBPF_DIR = $(XDP_TOOLS_DIR)/lib/libbpf
LIBBPFDIR = $(XDPTOOLSDIR)/lib/libbpf
LIBBPFSRC = $(LIBBPFDIR)/src
LIBBPF_SRC = $(LIBBPF_DIR)/src
# LibBPF objects.
LIBBPFOBJS = $(LIBBPFSRC)/staticobjs/bpf_prog_linfo.o $(LIBBPFSRC)/staticobjs/bpf.o $(LIBBPFSRC)/staticobjs/btf_dump.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/btf.o $(LIBBPFSRC)/staticobjs/gen_loader.o $(LIBBPFSRC)/staticobjs/hashmap.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/libbpf_errno.o $(LIBBPFSRC)/staticobjs/libbpf_probes.o $(LIBBPFSRC)/staticobjs/libbpf.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/linker.o $(LIBBPFSRC)/staticobjs/netlink.o $(LIBBPFSRC)/staticobjs/nlattr.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/relo_core.o $(LIBBPFSRC)/staticobjs/ringbuf.o $(LIBBPFSRC)/staticobjs/str_error.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/strset.o $(LIBBPFSRC)/staticobjs/usdt.o $(LIBBPFSRC)/staticobjs/zip.o
# Loader directories.
LOADER_SRC = loader.c
LOADER_OUT = xdpfw
# LibXDP objects.
# To Do: Figure out why static objects produces errors relating to unreferenced functions with dispatcher.
LIBXDPOBJS = $(LIBXDPDIR)/sharedobjs/xsk.o $(LIBXDPDIR)/sharedobjs/libxdp.o
LOADER_UTILS_DIR = $(LOADER_DIR)/utils
# Main program's objects.
CONFIGSRC = config.c
CONFIGOBJ = config.o
CMDLINESRC = cmdline.c
CMDLINEOBJ = cmdline.o
# Loader utils.
LOADER_UTILS_CONFIG_SRC = config.c
LOADER_UTILS_CONFIG_OBJ = config.o
XDPFWSRC = xdpfw.c
XDPFWOUT = xdpfw
LOADER_UTILS_CMDLINE_SRC = cmdline.c
LOADER_UTILS_CMDLINE_OBJ = cmdline.o
XDPPROGSRC = xdpfw_kern.c
XDPPROGLL = xdpfw_kern.ll
XDPPROGOBJ = xdpfw_kern.o
LOADER_UTILS_HELPERS_SRC = helpers.c
LOADER_UTILS_HELPERS_OBJ = helpers.o
OBJS = $(BUILDDIR)/$(CONFIGOBJ) $(BUILDDIR)/$(CMDLINEOBJ)
# Loader objects.
LOADER_OBJS = $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ)
# LD flags and includes.
LDFLAGS += -lconfig -lelf -lz
INCS = -I $(SRCDIR) -I $(LIBBPFSRC)
INCS += -I /usr/include -I /usr/local/include
# XDP directories.
XDP_SRC = prog.c
XDP_OBJ = xdp_prog.o
# All chain.
all: xdpfw xdpfw_filter utils
XDP_UTILS_DIR = $(XDP_DIR)/utils
# User space application chain.
xdpfw: utils libxdp $(OBJS)
mkdir -p $(BUILDDIR)/
$(CC) $(LDFLAGS) $(INCS) -o $(BUILDDIR)/$(XDPFWOUT) $(LIBBPFOBJS) $(LIBXDPOBJS) $(OBJS) $(SRCDIR)/$(XDPFWSRC)
# XDP utils.
XDP_UTILS_HELPERS_SRC = helpers.c
XDP_UTILS_HELPERS_OBJ = helpers.o
# XDP program chain.
xdpfw_filter:
mkdir -p $(BUILDDIR)/
$(CC) $(INCS) -D__BPF__ -D __BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign -Wno-compare-distinct-pointer-types -O2 -emit-llvm -c -g -o $(BUILDDIR)/$(XDPPROGLL) $(SRCDIR)/$(XDPPROGSRC)
$(LLC) -march=bpf -filetype=obj -o $(BUILDDIR)/$(XDPPROGOBJ) $(BUILDDIR)/$(XDPPROGLL)
XDP_UTILS_RL_SRC = rl.c
XDP_UTILS_RL_OBJ = rl.o
# Utils chain.
utils:
mkdir -p $(BUILDDIR)/
$(CC) $(INCS) -O2 -c -o $(BUILDDIR)/$(CONFIGOBJ) $(SRCDIR)/$(CONFIGSRC)
$(CC) $(INCS) -O2 -c -o $(BUILDDIR)/$(CMDLINEOBJ) $(SRCDIR)/$(CMDLINESRC)
# Includes.
INCS = -I $(SRC_DIR) -I $(LIBBPF_SRC) -I /usr/include -I /usr/local/include
# Flags.
FLAGS = -O2 -g
FLAGS_LOADER = -lconfig -lelf -lz -lbpf -lxdp
# All chains.
all: loader xdp
# Loader program.
loader: libxdp loader_utils
$(CC) $(INCS) $(FLAGS) $(FLAGS_LOADER) -o $(BUILD_LOADER_DIR)/$(LOADER_OUT) $(LOADER_OBJS) $(LOADER_DIR)/$(LOADER_SRC)
loader_utils: loader_utils_config loader_utils_cmdline loader_utils_helpers
loader_utils_config:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_CONFIG_SRC)
loader_utils_cmdline:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_CMDLINE_SRC)
loader_utils_helpers:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_HELPERS_SRC)
# XDP program.
xdp:
$(CC) $(INCS) $(FLAGS) -target bpf -c -o $(BUILD_XDP_DIR)/$(XDP_OBJ) $(XDP_DIR)/$(XDP_SRC)
# LibXDP chain. We need to install objects here since our program relies on installed object files and such.
libxdp:
$(MAKE) -C $(XDPTOOLSDIR) libxdp
sudo $(MAKE) -C $(LIBBPFSRC) install
sudo $(MAKE) -C $(LIBXDPDIR) install
$(MAKE) -C $(XDP_TOOLS_DIR) libxdp
sudo $(MAKE) -C $(LIBBPF_SRC) install
sudo $(MAKE) -C $(LIBXDP_DIR) install
# Clean chain.
clean:
$(MAKE) -C $(LIBBPFSRC) clean
$(MAKE) -C $(XDPTOOLSDIR) clean
rm -f $(BUILDDIR)/*.o $(BUILDDIR)/*.bc
rm -f $(BUILDDIR)/$(XDPFWOUT)
$(MAKE) -C $(XDP_TOOLS_DIR) clean
$(MAKE) -C $(LIBBPF_SRC) clean
find $(BUILD_DIR) -type f ! -name ".*" -exec rm -f {} +
find $(BUILD_LOADER_DIR) -type f ! -name ".*" -exec rm -f {} +
find $(BUILD_XDP_DIR) -type f ! -name ".*" -exec rm -f {} +
# Install chain.
install:
mkdir -p /etc/xdpfw/
cp -n xdpfw.conf.example /etc/xdpfw/xdpfw.conf
cp $(BUILDDIR)/$(XDPPROGOBJ) /etc/xdpfw/$(XDPPROGOBJ)
cp $(BUILDDIR)/$(XDPFWOUT) /usr/bin/$(XDPFWOUT)
cp -f $(BUILD_LOADER_DIR)/$(LOADER_OUT) /usr/bin
cp -f $(BUILD_XDP_DIR)/$(XDP_OBJ) /etc/xdpfw
cp -n other/xdpfw.service /etc/systemd/system/
.PHONY: libxdp all
.PHONY: all libxdp
.DEFAULT: all

View File

@@ -108,7 +108,7 @@ You may additionally specified UDP header options for a filter rule which start
#### Notes
* All settings within a filter rule other than `enabled` and `action` are **not** required. This means you do not have to define them within your config.
* When a filter rule's setting is set (not `NULL`), but doesn't match the packet, the program moves onto the next filter rule. Therefore, all of the filter rule's settings that are set must match the packet in order to perform the action specified. Think of it as something like `if src_ip == "10.50.0.3" and udp_dport == 27015: action`.
* As of right now, you can specify up to 60 total filter rules. You may increase this limit by raising the `MAX_FILTERS` constant in the `src/xdpfw.h` [file](https://github.com/gamemann/XDP-Firewall/blob/master/src/xdpfw.h#L6) and then recompile the firewall. If you receive a BPF program too large error, this is due to BPF's limitations with complexity and jumps. You may try increasing BPF limitations manually or with a patch. If you want to do this, please read [this](https://github.com/gamemann/XDP-Forwarding/tree/master/patches) README from my XDP Forwarding project.
* As of right now, you can specify up to 60 total filter rules. You may increase this limit by raising the `MAX_FILTERS` constant in the `src/common/constants.h` [file](https://github.com/gamemann/XDP-Firewall/blob/master/src/common/constants.h#L4) and then recompile the firewall. If you receive a BPF program too large error, this is due to BPF's limitations with complexity and jumps. You may try increasing BPF limitations manually or with a patch. If you want to do this, please read [this](https://github.com/gamemann/XDP-Forwarding/tree/master/patches) README from my XDP Forwarding project.
## Configuration Example
Here's an example of a config:
@@ -219,7 +219,7 @@ libbpf: failed to load object '/etc/xdpfw/xdpfw_kern.o'
It looks like BPF while/for loop [support](https://lwn.net/Articles/794934/) was added in kernel 5.3. Therefore, you'll need kernel 5.3 or above for this program to run properly.
#### Performance With `For` Loops
Due to the usage of a [`for` loop](https://github.com/gamemann/XDP-Firewall/blob/master/src/xdpfw_kern.c#L330) inside the XDP program that handles looping through all filtering rules inside of a BPF array map, performance will be impacted depending on how many filtering rules you have configured (ultimately, the firewall **doesn't scale** that well). This firewall was designed to be as flexible as possible regarding configuration and is most effective when configured to add malicious source IPs to the block map for a certain amount of time which are then dropped at the beginning of the XDP program for the best performance.
Due to the usage of a [`for` loop](https://github.com/gamemann/XDP-Firewall/blob/master/src/xdp/prog.c#L249) inside the XDP program that handles looping through all filtering rules inside of a BPF array map, performance will be impacted depending on how many filtering rules you have configured (ultimately, the firewall **doesn't scale** that well). This firewall was designed to be as flexible as possible regarding configuration and is most effective when configured to add malicious source IPs to the block map for a certain amount of time which are then dropped at the beginning of the XDP program for the best performance.
Unfortunately, we can't really eliminate the `for` loop with the current amount of flexibility we allow (especially minimum/maximum TTL, packet lengths, IDs, etc.), unless if we were to create more BPF maps and insert many more entries which would result in a lot more memory consumed and isn't ideal at all. If we were to remove flexibility, the best approach would be to store filtering rules inside a hashed BPF map using the packet's destination IP/port as the entry's key in my opinion (this would then eliminate flexibility related to being able to specify a filtering rule to match against a single destination IP without a port, unless if we implemented multiple BPF map lookups inside the XDP program which would then impact performance). However, there are currently no plans to switch to this format due to the amount of flexibility lost and also not having the time on my side (if somebody else creates a PR to implement this, I'd be willing to have a separate branch with the new functionality for others to use if the current branch isn't working out for their needs).
@@ -240,7 +240,7 @@ There is a possibility I may make this firewall stateful in the future *when* I
You may also be interested in this awesome project called [FastNetMon](https://github.com/pavel-odintsov/fastnetmon)!
### Rate Limits
By default, client stats including packets and bytes per second are calculated per *partial* flow (source IP/port and protocol). This is useful if you want to specify connection-specific rate limits inside of your filtering rules using the `pps` and `bps` settings. However, if you want to calculate client stats using only the source IP, you may comment out [this](https://github.com/gamemann/XDP-Firewall/blob/master/src/xdpfw.h#L25) line.
By default, client stats including packets and bytes per second are calculated per *partial* flow (source IP/port and protocol). This is useful if you want to specify connection-specific rate limits inside of your filtering rules using the `pps` and `bps` settings. However, if you want to calculate client stats using only the source IP, you may comment out [this](https://github.com/gamemann/XDP-Firewall/blob/master/src/common/config.h#L12) line.
```C
//#define USE_FLOW_RL

3
build/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*
!.gitignore
!*/

2
build/loader/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

2
build/xdp/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

6
src/common/all.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <common/config.h>
#include <common/constants.h>
#include <common/int_types.h>
#include <common/types.h>

12
src/common/config.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
// 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 DO_STATS_ON_BLOCK_MAP
// 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.
#define ALLOW_SINGLE_IP_V4_V6
// If uncommented, rate limits for clients are determined using the source IP, port, and protocol instead of just the source IP.
// This allows for more precise rate limits (connection-specific instead of a single source IP).
// I decided not to include the destination IP/port because the source IP, port, and protocol should be represent a unique connection.
#define USE_FLOW_RL

7
src/common/constants.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
#define MAX_PCKT_LENGTH 65535
#define MAX_FILTERS 60
#define MAX_TRACK_IPS 100000
#define MAX_CPUS 256
#define NANO_TO_SEC 1000000000

17
src/common/int_types.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <linux/types.h>
typedef __uint128_t u128;
typedef __u64 u64;
typedef __u32 u32;
typedef __u16 u16;
typedef __u8 u8;
typedef __s64 s64;
typedef __s32 s32;
typedef __s16 s16;
typedef __be64 be64;
typedef __be32 be32;
typedef __be16 be16;

134
src/common/types.h Normal file
View File

@@ -0,0 +1,134 @@
#pragma once
#include <common/int_types.h>
struct tcp_opts
{
unsigned int enabled : 1;
unsigned int do_sport : 1;
u16 sport;
unsigned int do_dport : 1;
u16 dport;
// TCP flags.
unsigned int do_urg : 1;
unsigned int urg : 1;
unsigned int do_ack : 1;
unsigned int ack : 1;
unsigned int do_rst : 1;
unsigned int rst : 1;
unsigned int do_psh : 1;
unsigned int psh : 1;
unsigned int do_syn : 1;
unsigned int syn : 1;
unsigned int do_fin : 1;
unsigned int fin : 1;
unsigned int do_ece : 1;
unsigned int ece : 1;
unsigned int do_cwr : 1;
unsigned int cwr : 1;
} typedef tcp_opts_t;
struct udp_opts
{
unsigned int enabled : 1;
unsigned int do_sport : 1;
u16 sport;
unsigned int do_dport : 1;
u16 dport;
} typedef udp_opts_t;
struct icmp_opts
{
unsigned int enabled : 1;
unsigned int do_code : 1;
u8 code;
unsigned int do_type : 1;
u8 type;
} typedef icmp_opts_t;
struct filter
{
u8 id;
unsigned int enabled : 1;
u8 action;
u32 src_ip;
u8 src_cidr;
u32 dst_ip;
u8 dst_cidr;
u32 src_ip6[4];
u32 dst_ip6[4];
unsigned int do_min_ttl : 1;
u8 min_ttl;
unsigned int do_max_ttl : 1;
u8 max_ttl;
unsigned int do_min_len : 1;
u16 min_len;
unsigned int do_max_len : 1;
u16 max_len;
unsigned int do_tos : 1;
u8 tos;
unsigned int do_pps : 1;
u64 pps;
unsigned int do_bps : 1;
u64 bps;
u64 blocktime;
tcp_opts_t tcpopts;
udp_opts_t udpopts;
icmp_opts_t icmpopts;
} __attribute__((__aligned__(8))) typedef filter_t;
struct stats
{
u64 allowed;
u64 dropped;
u64 passed;
} typedef stats_t;
struct ip_stats
{
u64 pps;
u64 bps;
u64 next_update;
} typedef ip_stats_t ;
struct flow
{
u32 ip;
u16 port;
u8 protocol;
} typedef flow_t;
struct flow6
{
u128 ip;
u16 port;
u8 protocol;
} typedef flow6_t;

View File

@@ -1,18 +0,0 @@
#pragma once
#include <linux/types.h>
#include "xdpfw.h"
struct config
{
char *interface;
__u16 updatetime;
unsigned int nostats : 1;
int stdout_update_time;
struct filter filters[MAX_FILTERS];
};
void SetCfgDefaults(struct config *cfg);
int OpenCfg(const char *filename);
int ReadCfg(struct config *cfg);

View File

@@ -20,12 +20,14 @@
#include <libbpf.h>
#include <xdp/libxdp.h>
#include <xdpfw.h>
#include <config.h>
#include <cmdline.h>
#include <common/all.h>
#include <loader/utils/cmdline.h>
#include <loader/utils/config.h>
#include <loader/utils/helpers.h>
// Other variables.
static __u8 cont = 1;
static u8 cont = 1;
static int filtersmap = -1;
static int statsmap = -1;
@@ -41,18 +43,18 @@ void SignalHndl(int tmp)
*
* @return Void
*/
void UpdateFilters(struct config *cfg)
void UpdateFilters(config__t *cfg)
{
// Loop through all filters and delete the map. We do this in the case rules were edited and were put out of order since the key doesn't uniquely map to a specific rule.
for (__u8 i = 0; i < MAX_FILTERS; i++)
for (u8 i = 0; i < MAX_FILTERS; i++)
{
__u32 key = i;
u32 key = i;
bpf_map_delete_elem(filtersmap, &key);
}
// Add a filter to the filter maps.
for (__u32 i = 0; i < MAX_FILTERS; i++)
for (u32 i = 0; i < MAX_FILTERS; i++)
{
// Check if we have a valid ID.
if (cfg->filters[i].id < 1)
@@ -61,7 +63,7 @@ void UpdateFilters(struct config *cfg)
}
// Create value array (max CPUs in size) since we're using a per CPU map.
struct filter filter[MAX_CPUS];
filter_t filter[MAX_CPUS];
for (int j = 0; j < MAX_CPUS; j++)
{
@@ -84,7 +86,7 @@ void UpdateFilters(struct config *cfg)
*
* @return 0 on success or -1 on error.
*/
int UpdateConfig(struct config *cfg, char *cfgfile)
int UpdateConfig(config__t *cfg, char *cfgfile)
{
// Open config file.
if (OpenCfg(cfgfile) != 0)
@@ -96,7 +98,7 @@ int UpdateConfig(struct config *cfg, char *cfgfile)
SetCfgDefaults(cfg);
for (__u16 i = 0; i < MAX_FILTERS; i++)
for (u16 i = 0; i < MAX_FILTERS; i++)
{
cfg->filters[i] = (struct filter) {0};
}
@@ -178,11 +180,11 @@ struct xdp_program *LoadBpfObj(const char *filename)
*
* @return 0 on success and 1 on error.
*/
int AttachXdp(struct xdp_program *prog, int ifidx, __u8 detach, struct cmdline *cmd)
int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd)
{
int err;
__u32 mode = XDP_MODE_NATIVE;
u32 mode = XDP_MODE_NATIVE;
char *smode;
smode = "DRV/native";
@@ -199,7 +201,7 @@ int AttachXdp(struct xdp_program *prog, int ifidx, __u8 detach, struct cmdline *
mode = XDP_MODE_SKB;
}
__u8 exit = 0;
u8 exit = 0;
while (!exit)
{
@@ -271,7 +273,7 @@ struct stat conf_stat;
int main(int argc, char *argv[])
{
// Parse the command line.
struct cmdline cmd =
cmdline_t cmd =
{
.cfgfile = "/etc/xdpfw/xdpfw.conf",
.help = 0,
@@ -313,7 +315,7 @@ int main(int argc, char *argv[])
}
// Initialize config.
struct config cfg = {0};
config__t cfg = {0};
SetCfgDefaults(&cfg);
@@ -330,7 +332,7 @@ int main(int argc, char *argv[])
for (uint16_t i = 0; i < MAX_FILTERS; i++)
{
struct filter *filter = &cfg.filters[i];
filter_t *filter = &cfg.filters[i];
if (filter->id < 1)
{
@@ -427,7 +429,7 @@ int main(int argc, char *argv[])
}
// XDP variables.
const char *filename = "/etc/xdpfw/xdpfw_kern.o";
const char *filename = "/etc/xdpfw/xdp_prog.o";
// Load BPF object.
struct xdp_program *prog = LoadBpfObj(filename);
@@ -518,13 +520,13 @@ int main(int argc, char *argv[])
// Update stats.
if (!cfg.nostats)
{
__u32 key = 0;
struct stats stats[MAX_CPUS];
u32 key = 0;
stats_t stats[MAX_CPUS];
//memset(stats, 0, sizeof(struct stats) * MAX_CPUS);
__u64 allowed = 0;
__u64 dropped = 0;
__u64 passed = 0;
u64 allowed = 0;
u64 dropped = 0;
u64 passed = 0;
if (bpf_map_lookup_elem(statsmap, &key, stats) != 0)
{

View File

@@ -22,7 +22,7 @@ const struct option opts[] =
*
* @return Void
*/
void ParseCommandLine(struct cmdline *cmd, int argc, char *argv[])
void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[])
{
int c;

View File

@@ -8,6 +8,6 @@ struct cmdline
unsigned int time;
unsigned int list : 1;
unsigned int help : 1;
};
} typedef cmdline_t;
void ParseCommandLine(struct cmdline *cmd, int argc, char *argv[]);
void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]);

View File

@@ -1,14 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <libconfig.h>
#include <string.h>
#include <linux/types.h>
#include <loader/utils/config.h>
#include <arpa/inet.h>
#include <xdpfw.h>
#include <config.h>
#include <utils.h>
#include <loader/utils/helpers.h>
FILE *file;
@@ -19,14 +11,14 @@ FILE *file;
*
* @return Void
*/
void SetCfgDefaults(struct config *cfg)
void SetCfgDefaults(config__t *cfg)
{
cfg->updatetime = 0;
cfg->interface = NULL;
cfg->nostats = 0;
cfg->stdout_update_time = 1000;
for (__u16 i = 0; i < MAX_FILTERS; i++)
for (u16 i = 0; i < MAX_FILTERS; i++)
{
cfg->filters[i].id = 0;
cfg->filters[i].enabled = 0;
@@ -34,7 +26,7 @@ void SetCfgDefaults(struct config *cfg)
cfg->filters[i].src_ip = 0;
cfg->filters[i].dst_ip = 0;
for (__u8 j = 0; j < 4; j++)
for (u8 j = 0; j < 4; j++)
{
cfg->filters[i].src_ip6[j] = 0;
cfg->filters[i].dst_ip6[j] = 0;
@@ -119,7 +111,7 @@ int OpenCfg(const char *filename)
*
* @return 0 on success or 1/-1 on error.
*/
int ReadCfg(struct config *cfg)
int ReadCfg(config__t *cfg)
{
// Not sure why this would be set to NULL after checking for it in OpenConfig(), but just for safety.
if (file == NULL)
@@ -197,7 +189,7 @@ int ReadCfg(struct config *cfg)
// Set filter count.
int filters = 0;
for (__u8 i = 0; i < config_setting_length(setting); i++)
for (u8 i = 0; i < config_setting_length(setting); i++)
{
config_setting_t* filter = config_setting_get_elem(setting, i);
@@ -233,7 +225,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_string(filter, "src_ip", &sip))
{
struct ip ip = ParseIp(sip);
ip_range_t ip = ParseIpCidr(sip);
cfg->filters[i].src_ip = ip.ip;
cfg->filters[i].src_cidr = ip.cidr;
@@ -244,7 +236,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_string(filter, "dst_ip", &dip))
{
struct ip ip = ParseIp(dip);
ip_range_t ip = ParseIpCidr(dip);
cfg->filters[i].dst_ip = ip.ip;
cfg->filters[i].dst_cidr = ip.cidr;
@@ -259,7 +251,7 @@ int ReadCfg(struct config *cfg)
inet_pton(AF_INET6, sip6, &in);
for (__u8 j = 0; j < 4; j++)
for (u8 j = 0; j < 4; j++)
{
cfg->filters[i].src_ip6[j] = in.__in6_u.__u6_addr32[j];
}
@@ -274,7 +266,7 @@ int ReadCfg(struct config *cfg)
inet_pton(AF_INET6, dip6, &in);
for (__u8 j = 0; j < 4; j++)
for (u8 j = 0; j < 4; j++)
{
cfg->filters[i].dst_ip6[j] = in.__in6_u.__u6_addr32[j];
}
@@ -285,7 +277,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int(filter, "min_ttl", &min_ttl))
{
cfg->filters[i].min_ttl = (__u8)min_ttl;
cfg->filters[i].min_ttl = (u8)min_ttl;
cfg->filters[i].do_min_ttl = 1;
}
@@ -294,7 +286,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int(filter, "max_ttl", &max_ttl))
{
cfg->filters[i].max_ttl = (__u8)max_ttl;
cfg->filters[i].max_ttl = (u8)max_ttl;
cfg->filters[i].do_max_ttl = 1;
}
@@ -321,7 +313,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int(filter, "tos", &tos))
{
cfg->filters[i].tos = (__u8)tos;
cfg->filters[i].tos = (u8)tos;
cfg->filters[i].do_tos = 1;
}
@@ -369,7 +361,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int64(filter, "tcp_sport", &tcpsport))
{
cfg->filters[i].tcpopts.sport = (__u16)tcpsport;
cfg->filters[i].tcpopts.sport = (u16)tcpsport;
cfg->filters[i].tcpopts.do_sport = 1;
}
@@ -378,7 +370,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int64(filter, "tcp_dport", &tcpdport))
{
cfg->filters[i].tcpopts.dport = (__u16)tcpdport;
cfg->filters[i].tcpopts.dport = (u16)tcpdport;
cfg->filters[i].tcpopts.do_dport = 1;
}
@@ -469,7 +461,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int64(filter, "udp_sport", &udpsport))
{
cfg->filters[i].udpopts.sport = (__u16)udpsport;
cfg->filters[i].udpopts.sport = (u16)udpsport;
cfg->filters[i].udpopts.do_sport = 1;
}
@@ -478,7 +470,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int64(filter, "udp_dport", &udpdport))
{
cfg->filters[i].udpopts.dport = (__u16)udpdport;
cfg->filters[i].udpopts.dport = (u16)udpdport;
cfg->filters[i].udpopts.do_dport = 1;
}
@@ -496,7 +488,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int(filter, "icmp_code", &icmpcode))
{
cfg->filters[i].icmpopts.code = (__u8)icmpcode;
cfg->filters[i].icmpopts.code = (u8)icmpcode;
cfg->filters[i].icmpopts.do_code = 1;
}
@@ -505,7 +497,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int(filter, "icmp_type", &icmptype))
{
cfg->filters[i].icmpopts.type = (__u8)icmptype;
cfg->filters[i].icmpopts.type = (u8)icmptype;
cfg->filters[i].icmpopts.do_type = 1;
}

24
src/loader/utils/config.h Normal file
View File

@@ -0,0 +1,24 @@
#pragma once
#include <common/all.h>
#include <stdio.h>
#include <stdlib.h>
#include <libconfig.h>
#include <string.h>
#include <linux/types.h>
#include <arpa/inet.h>
struct config
{
char *interface;
u16 updatetime;
unsigned int nostats : 1;
int stdout_update_time;
filter_t filters[MAX_FILTERS];
} typedef config__t; // config_t is taken by libconfig -.-
void SetCfgDefaults(config__t *cfg);
int OpenCfg(const char *filename);
int ReadCfg(config__t *cfg);

View File

@@ -1,16 +1,4 @@
#pragma once
#include <linux/types.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
struct ip
{
__u32 ip;
__u32 cidr;
};
#include <loader/utils/helpers.h>
/**
* Parses an IP string with CIDR support. Stores IP in network byte order in ip.ip and CIDR in ip.cidr.
@@ -19,9 +7,9 @@ struct ip
*
* @return Returns an IP structure with IP and CIDR.
*/
struct ip ParseIp(const char *ip)
ip_range_t ParseIpCidr(const char *ip)
{
struct ip ret = {0};
ip_range_t ret = {0};
ret.cidr = 32;
char *token = strtok((char *) ip, "/");

View File

@@ -0,0 +1,16 @@
#pragma once
#include <common/all.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
struct ip_range
{
u32 ip;
u32 cidr;
} typedef ip_range_t;
ip_range_t ParseIpCidr(const char *ip);

View File

@@ -1,8 +0,0 @@
#pragma once
#include <linux/bpf.h>
#include <linux/bpf_common.h>
#include <bpf_helpers.h>
#include <xdp/xdp_helpers.h>
#include <xdp/prog_dispatcher.h>

View File

@@ -8,13 +8,18 @@
#include <linux/in.h>
#include <stdatomic.h>
#include <xdp/helpers.h>
#include <common/all.h>
#include <xdpfw.h>
#include <xdp/utils/rl.h>
#include <xdp/utils/helpers.h>
#include <xdp/maps.h>
#include <xdp/rl.h>
#include <xdp/utils.h>
#include <xdp/utils/maps.h>
struct
{
__uint(priority, 10);
__uint(XDP_PASS, 1);
} XDP_RUN_CONFIG(xdp_prog_main);
SEC("xdp_prog")
int xdp_prog_main(struct xdp_md *ctx)
@@ -38,13 +43,13 @@ int xdp_prog_main(struct xdp_md *ctx)
return XDP_PASS;
}
__u8 action = 0;
__u64 blocktime = 1;
u8 action = 0;
u64 blocktime = 1;
// Initialize IP headers.
struct iphdr *iph = NULL;
struct ipv6hdr *iph6 = NULL;
__u128 src_ip6 = 0;
u128 src_ip6 = 0;
// Set IPv4 and IPv6 common variables.
if (eth->h_proto == htons(ETH_P_IPV6))
@@ -75,13 +80,13 @@ int xdp_prog_main(struct xdp_md *ctx)
}
// Get stats map.
__u32 key = 0;
struct stats *stats = bpf_map_lookup_elem(&stats_map, &key);
u32 key = 0;
stats_t*stats = bpf_map_lookup_elem(&stats_map, &key);
__u64 now = bpf_ktime_get_ns();
u64 now = bpf_ktime_get_ns();
// Check blacklist map.
__u64 *blocked = NULL;
u64 *blocked = NULL;
if (iph6)
{
@@ -108,7 +113,7 @@ int xdp_prog_main(struct xdp_md *ctx)
}
else
{
#ifdef DOSTATSONBLOCKMAP
#ifdef DO_STATS_ON_BLOCK_MAP
// Increase blocked stats entry.
if (stats)
{
@@ -122,7 +127,7 @@ int xdp_prog_main(struct xdp_md *ctx)
}
// Retrieve total packet length.
__u16 pkt_len = data_end - data;
u16 pkt_len = data_end - data;
// Parse layer-4 headers and determine source port and protocol.
struct tcphdr *tcph = NULL;
@@ -130,8 +135,8 @@ int xdp_prog_main(struct xdp_md *ctx)
struct icmphdr *icmph = NULL;
struct icmp6hdr *icmp6h = NULL;
__u16 src_port = 0;
__u8 protocol = 0;
u16 src_port = 0;
u8 protocol = 0;
if (iph6)
{
@@ -229,8 +234,8 @@ int xdp_prog_main(struct xdp_md *ctx)
}
// Update client stats (PPS/BPS).
__u64 pps = 0;
__u64 bps = 0;
u64 pps = 0;
u64 bps = 0;
if (iph6)
{
@@ -241,11 +246,11 @@ int xdp_prog_main(struct xdp_md *ctx)
UpdateIpStats(&pps, &bps, iph->saddr, src_port, protocol, pkt_len, now);
}
for (__u8 i = 0; i < MAX_FILTERS; i++)
for (u8 i = 0; i < MAX_FILTERS; i++)
{
__u32 key = i;
u32 key = i;
struct filter *filter = bpf_map_lookup_elem(&filters_map, &key);
filter_t *filter = bpf_map_lookup_elem(&filters_map, &key);
// Check if ID is above 0 (if 0, it's an invalid rule).
if (!filter || filter->id < 1)
@@ -274,7 +279,7 @@ int xdp_prog_main(struct xdp_md *ctx)
continue;
}
#ifdef ALLOWSINGLEIPV4V6
#ifdef ALLOW_SINGLE_IP_V4_V6
if (filter->src_ip != 0 || filter->dst_ip != 0)
{
continue;
@@ -335,7 +340,7 @@ int xdp_prog_main(struct xdp_md *ctx)
}
}
#ifdef ALLOWSINGLEIPV4V6
#ifdef ALLOW_SINGLE_IP_V4_V6
if ((filter->src_ip6[0] != 0 || filter->src_ip6[1] != 0 || filter->src_ip6[2] != 0 || filter->src_ip6[3] != 0) || (filter->dst_ip6[0] != 0 || filter->dst_ip6[1] != 0 || filter->dst_ip6[2] != 0 || filter->dst_ip6[3] != 0))
{
continue;
@@ -529,7 +534,7 @@ int xdp_prog_main(struct xdp_md *ctx)
// Before dropping, update the blacklist map.
if (blocktime > 0)
{
__u64 newTime = now + (blocktime * NANO_TO_SEC);
u64 newTime = now + (blocktime * NANO_TO_SEC);
if (iph6)
{

View File

@@ -1,13 +1,4 @@
#pragma once
#include <xdpfw.h>
#include <xdp/maps.h>
#include <xdp/helpers.h>
#ifndef memcpy
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
#endif
#include <xdp/utils/helpers.h>
/**
* Checks if an IP is within a specific CIDR range.
@@ -18,7 +9,7 @@
*
* @return 1 on yes, 0 on no.
*/
static __always_inline __u8 IsIpInRange(__u32 src_ip, __u32 net_ip, __u8 cidr)
static __always_inline u8 IsIpInRange(u32 src_ip, u32 net_ip, u8 cidr)
{
return !((src_ip ^ net_ip) & htonl(0xFFFFFFFFu << (32 - cidr)));
}

35
src/xdp/utils/helpers.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
#include <common/all.h>
#include <linux/bpf.h>
#include <linux/bpf_common.h>
#include <bpf_helpers.h>
#include <xdp/xdp_helpers.h>
#include <xdp/prog_dispatcher.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define htons(x) ((__be16)___constant_swab16((x)))
#define ntohs(x) ((__be16)___constant_swab16((x)))
#define htonl(x) ((__be32)___constant_swab32((x)))
#define ntohl(x) ((__be32)___constant_swab32((x)))
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define htons(x) (x)
#define ntohs(X) (x)
#define htonl(x) (x)
#define ntohl(x) (x)
#endif
#ifndef memcpy
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
#endif
static __always_inline u8 IsIpInRange(u32 src_ip, u32 net_ip, u8 cidr);
// NOTE: We include the C source file below because we can't link object files which includes the function logic into the main XDP program because we need to ensure the function is always inlined for performance which doesn't work with linked objects.
// More Info: https://stackoverflow.com/questions/24289599/always-inline-does-not-work-when-function-is-implemented-in-different-file
#include "helpers.c"

View File

@@ -1,20 +1,15 @@
#pragma once
#include <xdpfw.h>
#include <common/int_types.h>
#include <common/types.h>
#include <xdp/helpers.h>
struct
{
__uint(priority, 10);
__uint(XDP_PASS, 1);
} XDP_RUN_CONFIG(xdp_prog_main);
#include <xdp/utils/helpers.h>
struct
{
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, MAX_FILTERS);
__type(key, __u32);
__type(key, u32);
__type(value, struct filter);
} filters_map SEC(".maps");
@@ -22,7 +17,7 @@ struct
{
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(key, u32);
__type(value, struct stats);
} stats_map SEC(".maps");
@@ -33,7 +28,7 @@ struct
#ifdef USE_FLOW_RL
__type(key, struct flow);
#else
__type(key, __u32);
__type(key, u32);
#endif
__type(value, struct ip_stats);
} ip_stats_map SEC(".maps");
@@ -42,8 +37,8 @@ struct
{
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, MAX_TRACK_IPS);
__type(key, __u32);
__type(value, __u64);
__type(key, u32);
__type(value, u64);
} ip_blacklist_map SEC(".maps");
struct
@@ -53,7 +48,7 @@ struct
#ifdef USE_FLOW_RL
__type(key, struct flow6);
#else
__type(key, __u128);
__type(key, u128);
#endif
__type(value, struct ip_stats);
} ip6_stats_map SEC(".maps");
@@ -62,6 +57,6 @@ struct
{
__uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, MAX_TRACK_IPS);
__type(key, __u128);
__type(value, __u64);
__type(key, u128);
__type(value, u64);
} ip6_blacklist_map SEC(".maps");

View File

@@ -1,9 +1,4 @@
#pragma once
#include <xdpfw.h>
#include <xdp/maps.h>
#include <xdp/helpers.h>
#include <xdp/utils/rl.h>
/**
* Updates IPv4 client stats.
@@ -18,17 +13,17 @@
*
* @return void
*/
static __always_inline void UpdateIpStats(__u64 *pps, __u64 *bps, __u32 ip, __u16 port, __u8 protocol, __u16 pkt_len, __u64 now)
static __always_inline void UpdateIpStats(u64 *pps, u64 *bps, u32 ip, u16 port, u8 protocol, u16 pkt_len, u64 now)
{
#ifdef USE_FLOW_RL
struct flow key = {0};
flow_t key = {0};
key.ip = ip;
key.port = port;
key.protocol = protocol;
struct ip_stats *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &key);
ip_stats_t *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &key);
#else
struct ip_stats *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &ip);
ip_stats_t *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &ip);
#endif
if (ip_stats)
@@ -53,7 +48,7 @@ static __always_inline void UpdateIpStats(__u64 *pps, __u64 *bps, __u32 ip, __u1
else
{
// Create new entry.
struct ip_stats new = {0};
ip_stats_t new = {0};
new.pps = 1;
new.bps = pkt_len;
@@ -83,17 +78,17 @@ static __always_inline void UpdateIpStats(__u64 *pps, __u64 *bps, __u32 ip, __u1
*
* @return void
*/
static __always_inline void UpdateIp6Stats(__u64 *pps, __u64 *bps, __u128 *ip, __u16 port, __u8 protocol, __u16 pkt_len, __u64 now)
static __always_inline void UpdateIp6Stats(u64 *pps, u64 *bps, u128 *ip, u16 port, u8 protocol, u16 pkt_len, u64 now)
{
#ifdef USE_FLOW_RL
struct flow6 key = {0};
flow6_t key = {0};
key.ip = *ip;
key.port = port;
key.protocol = protocol;
struct ip_stats *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &key);
ip_stats_t *ip_stats = bpf_map_lookup_elem(&ip_stats_map, &key);
#else
struct ip_stats *ip_stats = bpf_map_lookup_elem(&ip_stats_map, ip);
ip_stats_t *ip_stats = bpf_map_lookup_elem(&ip_stats_map, ip);
#endif
if (ip_stats)
@@ -118,7 +113,7 @@ static __always_inline void UpdateIp6Stats(__u64 *pps, __u64 *bps, __u128 *ip, _
else
{
// Create new entry.
struct ip_stats new = {0};
ip_stats_t new = {0};
new.pps = 1;
new.bps = pkt_len;

14
src/xdp/utils/rl.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include <common/all.h>
#include <xdp/utils/helpers.h>
#include <xdp/utils/maps.h>
static __always_inline void UpdateIpStats(u64 *pps, u64 *bps, u32 ip, u16 port, u8 protocol, u16 pkt_len, u64 now);
static __always_inline void UpdateIp6Stats(u64 *pps, u64 *bps, u128 *ip, u16 port, u8 protocol, u16 pkt_len, u64 now);
// NOTE: We include the C source file below because we can't link object files which includes the function logic into the main XDP program because we need to ensure the function is always inlined for performance which doesn't work with linked objects.
// More Info: https://stackoverflow.com/questions/24289599/always-inline-does-not-work-when-function-is-implemented-in-different-file
#include "rl.c"

View File

@@ -1,172 +0,0 @@
#pragma once
#include <linux/types.h>
#define MAX_PCKT_LENGTH 65535
#define MAX_FILTERS 60
#define MAX_TRACK_IPS 100000
#define MAX_CPUS 256
#define NANO_TO_SEC 1000000000
#define __u128 __uint128_t
// Additional options for XDP program.
//#define DEBUG
// 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
// 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.
#define ALLOWSINGLEIPV4V6
// If uncommented, rate limits for clients are determined using the source IP, port, and protocol instead of just the source IP.
// This allows for more precise rate limits (connection-specific instead of a single source IP).
// I decided not to include the destination IP/port because the source IP, port, and protocol should be represent a unique connection.
#define USE_FLOW_RL
#ifdef __BPF__
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define htons(x) ((__be16)___constant_swab16((x)))
#define ntohs(x) ((__be16)___constant_swab16((x)))
#define htonl(x) ((__be32)___constant_swab32((x)))
#define ntohl(x) ((__be32)___constant_swab32((x)))
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define htons(x) (x)
#define ntohs(X) (x)
#define htonl(x) (x)
#define ntohl(x) (x)
#endif
#endif
struct tcpopts
{
unsigned int enabled : 1;
unsigned int do_sport : 1;
__u16 sport;
unsigned int do_dport : 1;
__u16 dport;
// TCP flags.
unsigned int do_urg : 1;
unsigned int urg : 1;
unsigned int do_ack : 1;
unsigned int ack : 1;
unsigned int do_rst : 1;
unsigned int rst : 1;
unsigned int do_psh : 1;
unsigned int psh : 1;
unsigned int do_syn : 1;
unsigned int syn : 1;
unsigned int do_fin : 1;
unsigned int fin : 1;
unsigned int do_ece : 1;
unsigned int ece : 1;
unsigned int do_cwr : 1;
unsigned int cwr : 1;
};
struct udpopts
{
unsigned int enabled : 1;
unsigned int do_sport : 1;
__u16 sport;
unsigned int do_dport : 1;
__u16 dport;
};
struct icmpopts
{
unsigned int enabled : 1;
unsigned int do_code : 1;
__u8 code;
unsigned int do_type : 1;
__u8 type;
};
struct filter
{
__u8 id;
unsigned int enabled : 1;
__u8 action;
__u32 src_ip;
__u8 src_cidr;
__u32 dst_ip;
__u8 dst_cidr;
__u32 src_ip6[4];
__u32 dst_ip6[4];
unsigned int do_min_ttl : 1;
__u8 min_ttl;
unsigned int do_max_ttl : 1;
__u8 max_ttl;
unsigned int do_min_len : 1;
__u16 min_len;
unsigned int do_max_len : 1;
__u16 max_len;
unsigned int do_tos : 1;
int8_t tos;
unsigned int do_pps : 1;
__u64 pps;
unsigned int do_bps : 1;
__u64 bps;
__u64 blocktime;
struct tcpopts tcpopts;
struct udpopts udpopts;
struct icmpopts icmpopts;
} __attribute__((__aligned__(8)));
struct stats
{
__u64 allowed;
__u64 dropped;
__u64 passed;
};
struct ip_stats
{
__u64 pps;
__u64 bps;
__u64 next_update;
};
struct flow
{
__u32 ip;
__u16 port;
__u8 protocol;
};
struct flow6
{
__u128 ip;
__u16 port;
__u8 protocol;
};