From 87568927918ae7bf246bfa9d9e64f8d1c2b977a3 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 22 Feb 2025 09:50:57 -0500 Subject: [PATCH 1/7] Restructure project and organize code. --- .gitignore | 1 - Makefile | 154 ++++++++++++--------- build/.gitignore | 3 + build/loader/.gitignore | 2 + build/xdp/.gitignore | 2 + src/common/all.h | 6 + src/common/config.h | 12 ++ src/common/constants.h | 7 + src/common/int_types.h | 17 +++ src/common/types.h | 134 ++++++++++++++++++ src/{xdpfw.c => loader/loader.c} | 28 ++-- src/{ => loader/utils}/cmdline.c | 0 src/{ => loader/utils}/cmdline.h | 0 src/{ => loader/utils}/config.c | 40 +++--- src/{ => loader/utils}/config.h | 10 +- src/{utils.h => loader/utils/helpers.c} | 14 +- src/loader/utils/helpers.h | 16 +++ src/xdp/helpers.h | 8 -- src/{xdpfw_kern.c => xdp/prog.c} | 25 ++-- src/xdp/{utils.h => utils/helpers.c} | 13 +- src/xdp/utils/helpers.h | 33 +++++ src/xdp/{ => utils}/maps.h | 17 +-- src/xdp/{rl.h => utils/rl.c} | 11 +- src/xdp/utils/rl.h | 12 ++ src/xdpfw.h | 172 ------------------------ 25 files changed, 403 insertions(+), 334 deletions(-) create mode 100644 build/.gitignore create mode 100644 build/loader/.gitignore create mode 100644 build/xdp/.gitignore create mode 100644 src/common/all.h create mode 100644 src/common/config.h create mode 100644 src/common/constants.h create mode 100644 src/common/int_types.h create mode 100644 src/common/types.h rename src/{xdpfw.c => loader/loader.c} (96%) rename src/{ => loader/utils}/cmdline.c (100%) rename src/{ => loader/utils}/cmdline.h (100%) rename src/{ => loader/utils}/config.c (92%) rename src/{ => loader/utils}/config.h (65%) rename src/{utils.h => loader/utils/helpers.c} (76%) create mode 100644 src/loader/utils/helpers.h delete mode 100644 src/xdp/helpers.h rename src/{xdpfw_kern.c => xdp/prog.c} (97%) rename src/xdp/{utils.h => utils/helpers.c} (53%) create mode 100644 src/xdp/utils/helpers.h rename src/xdp/{ => utils}/maps.h (83%) rename src/xdp/{rl.h => utils/rl.c} (93%) create mode 100644 src/xdp/utils/rl.h delete mode 100644 src/xdpfw.h diff --git a/.gitignore b/.gitignore index 93f6e7d..36e04aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .vscode/ -build/ xdpfw xdpfw.s xdpfw.conf \ No newline at end of file diff --git a/Makefile b/Makefile index 89ef96b..6d538c5 100644 --- a/Makefile +++ b/Makefile @@ -3,92 +3,124 @@ LLC = llc ARCH := $(shell uname -m | sed 's/x86_64/x86/') -# Main directories. -BUILDDIR = build -SRCDIR = src -MODULEDIR = modules +# Top-level directories. +BUILD_DIR = build +SRC_DIR = src +MODULES_DIR = modules -# XDP Tools directory. -XDPTOOLSDIR = $(MODULEDIR)/xdp-tools -XDPTOOLSHEADERS = $(XDPTOOLSDIR)/headers +# Common directories. +COMMON_DIR = $(SRC_DIR)/common +LOADER_DIR = $(SRC_DIR)/loader +XDP_DIR = $(SRC_DIR)/xdp + +# 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 +LIBBPF_OBJS = $(LIBBPF_SRC)/staticobjs/bpf_prog_linfo.o $(LIBBPF_SRC)/staticobjs/bpf.o $(LIBBPF_SRC)/staticobjs/btf_dump.o +LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/btf.o $(LIBBPF_SRC)/staticobjs/gen_loader.o $(LIBBPF_SRC)/staticobjs/hashmap.o +LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/libbpf_errno.o $(LIBBPF_SRC)/staticobjs/libbpf_probes.o $(LIBBPF_SRC)/staticobjs/libbpf.o +LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/linker.o $(LIBBPF_SRC)/staticobjs/netlink.o $(LIBBPF_SRC)/staticobjs/nlattr.o +LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/relo_core.o $(LIBBPF_SRC)/staticobjs/ringbuf.o $(LIBBPF_SRC)/staticobjs/str_error.o +LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/strset.o $(LIBBPF_SRC)/staticobjs/usdt.o $(LIBBPF_SRC)/staticobjs/zip.o # 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 +LIBXDP_OBJS = $(LIBXDP_DIR)/sharedobjs/xsk.o $(LIBXDP_DIR)/sharedobjs/libxdp.o -# Main program's objects. -CONFIGSRC = config.c -CONFIGOBJ = config.o -CMDLINESRC = cmdline.c -CMDLINEOBJ = cmdline.o +# Loader directories. +LOADER_SRC = loader.c +LOADER_OUT = xdpfw -XDPFWSRC = xdpfw.c -XDPFWOUT = xdpfw +LOADER_UTILS_DIR = $(LOADER_DIR)/utils -XDPPROGSRC = xdpfw_kern.c -XDPPROGLL = xdpfw_kern.ll -XDPPROGOBJ = xdpfw_kern.o +# Loader utils. +LOADER_UTILS_CONFIG_SRC = config.c +LOADER_UTILS_CONFIG_OBJ = config.o -OBJS = $(BUILDDIR)/$(CONFIGOBJ) $(BUILDDIR)/$(CMDLINEOBJ) +LOADER_UTILS_CMDLINE_SRC = cmdline.c +LOADER_UTILS_CMDLINE_OBJ = cmdline.o -# LD flags and includes. -LDFLAGS += -lconfig -lelf -lz -INCS = -I $(SRCDIR) -I $(LIBBPFSRC) -INCS += -I /usr/include -I /usr/local/include +LOADER_UTILS_HELPERS_SRC = helpers.c +LOADER_UTILS_HELPERS_OBJ = helpers.o -# All chain. -all: xdpfw xdpfw_filter utils +# Loader objects. +LOADER_OBJS = $(LIBXDP_OBJS) $(LIBBPF_OBJS) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ) -# User space application chain. -xdpfw: utils libxdp $(OBJS) - mkdir -p $(BUILDDIR)/ - $(CC) $(LDFLAGS) $(INCS) -o $(BUILDDIR)/$(XDPFWOUT) $(LIBBPFOBJS) $(LIBXDPOBJS) $(OBJS) $(SRCDIR)/$(XDPFWSRC) +# XDP directories. +XDP_SRC = prog.c +XDP_OBJ = xdp_prog.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) - -# Utils chain. -utils: - mkdir -p $(BUILDDIR)/ - $(CC) $(INCS) -O2 -c -o $(BUILDDIR)/$(CONFIGOBJ) $(SRCDIR)/$(CONFIGSRC) - $(CC) $(INCS) -O2 -c -o $(BUILDDIR)/$(CMDLINEOBJ) $(SRCDIR)/$(CMDLINESRC) +XDP_UTILS_DIR = $(XDP_DIR)/utils + +# XDP utils. +XDP_UTILS_HELPERS_SRC = helpers.c +XDP_UTILS_HELPERS_OBJ = helpers.o + +XDP_UTILS_RL_SRC = rl.c +XDP_UTILS_RL_OBJ = rl.o + +# Includes. +INCS = -I $(SRC_DIR) -I $(LIBBPF_SRC) -I /usr/include -I /usr/local/include +# Flags. +FLAGS = -O2 -g +FLAGS_LOADER = -lconfig -lelf -lz + +# 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 \ No newline at end of file diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..5d81bf7 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!*/ \ No newline at end of file diff --git a/build/loader/.gitignore b/build/loader/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/build/loader/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/build/xdp/.gitignore b/build/xdp/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/build/xdp/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/src/common/all.h b/src/common/all.h new file mode 100644 index 0000000..ec7a9c9 --- /dev/null +++ b/src/common/all.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include \ No newline at end of file diff --git a/src/common/config.h b/src/common/config.h new file mode 100644 index 0000000..5055bee --- /dev/null +++ b/src/common/config.h @@ -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 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 \ No newline at end of file diff --git a/src/common/constants.h b/src/common/constants.h new file mode 100644 index 0000000..8f77026 --- /dev/null +++ b/src/common/constants.h @@ -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 \ No newline at end of file diff --git a/src/common/int_types.h b/src/common/int_types.h new file mode 100644 index 0000000..6f0c4ee --- /dev/null +++ b/src/common/int_types.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +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; \ No newline at end of file diff --git a/src/common/types.h b/src/common/types.h new file mode 100644 index 0000000..208894e --- /dev/null +++ b/src/common/types.h @@ -0,0 +1,134 @@ +#pragma once + +#include + +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; + u8 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; +}; \ No newline at end of file diff --git a/src/xdpfw.c b/src/loader/loader.c similarity index 96% rename from src/xdpfw.c rename to src/loader/loader.c index f7401ee..4ba66a1 100644 --- a/src/xdpfw.c +++ b/src/loader/loader.c @@ -20,12 +20,14 @@ #include #include -#include -#include -#include +#include + +#include +#include +#include // Other variables. -static __u8 cont = 1; +static u8 cont = 1; static int filtersmap = -1; static int statsmap = -1; @@ -44,15 +46,15 @@ void SignalHndl(int tmp) void UpdateFilters(struct config *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) @@ -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, struct cmdline *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) { @@ -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,7 +520,7 @@ int main(int argc, char *argv[]) // Update stats. if (!cfg.nostats) { - __u32 key = 0; + u32 key = 0; struct stats stats[MAX_CPUS]; //memset(stats, 0, sizeof(struct stats) * MAX_CPUS); diff --git a/src/cmdline.c b/src/loader/utils/cmdline.c similarity index 100% rename from src/cmdline.c rename to src/loader/utils/cmdline.c diff --git a/src/cmdline.h b/src/loader/utils/cmdline.h similarity index 100% rename from src/cmdline.h rename to src/loader/utils/cmdline.h diff --git a/src/config.c b/src/loader/utils/config.c similarity index 92% rename from src/config.c rename to src/loader/utils/config.c index 2c28aa2..ca0013c 100644 --- a/src/config.c +++ b/src/loader/utils/config.c @@ -1,14 +1,6 @@ -#include -#include -#include -#include -#include +#include -#include - -#include -#include -#include +#include FILE *file; @@ -26,7 +18,7 @@ void SetCfgDefaults(struct config *cfg) 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; @@ -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); @@ -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; } diff --git a/src/config.h b/src/loader/utils/config.h similarity index 65% rename from src/config.h rename to src/loader/utils/config.h index fbac5a0..02ddeae 100644 --- a/src/config.h +++ b/src/loader/utils/config.h @@ -1,13 +1,19 @@ #pragma once +#include + +#include +#include +#include +#include #include -#include "xdpfw.h" +#include struct config { char *interface; - __u16 updatetime; + u16 updatetime; unsigned int nostats : 1; int stdout_update_time; struct filter filters[MAX_FILTERS]; diff --git a/src/utils.h b/src/loader/utils/helpers.c similarity index 76% rename from src/utils.h rename to src/loader/utils/helpers.c index 65a06af..a9cdcb7 100644 --- a/src/utils.h +++ b/src/loader/utils/helpers.c @@ -1,16 +1,4 @@ -#pragma once - -#include -#include -#include -#include -#include - -struct ip -{ - __u32 ip; - __u32 cidr; -}; +#include /** * Parses an IP string with CIDR support. Stores IP in network byte order in ip.ip and CIDR in ip.cidr. diff --git a/src/loader/utils/helpers.h b/src/loader/utils/helpers.h new file mode 100644 index 0000000..e8e37ff --- /dev/null +++ b/src/loader/utils/helpers.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include +#include +#include +#include + +struct ip +{ + u32 ip; + u32 cidr; +}; + +struct ip ParseIp(const char *ip); \ No newline at end of file diff --git a/src/xdp/helpers.h b/src/xdp/helpers.h deleted file mode 100644 index 2114f68..0000000 --- a/src/xdp/helpers.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include \ No newline at end of file diff --git a/src/xdpfw_kern.c b/src/xdp/prog.c similarity index 97% rename from src/xdpfw_kern.c rename to src/xdp/prog.c index 466d139..104fa14 100644 --- a/src/xdpfw_kern.c +++ b/src/xdp/prog.c @@ -8,13 +8,12 @@ #include #include -#include +#include -#include +#include +#include -#include -#include -#include +#include SEC("xdp_prog") int xdp_prog_main(struct xdp_md *ctx) @@ -38,13 +37,13 @@ int xdp_prog_main(struct xdp_md *ctx) return XDP_PASS; } - __u8 action = 0; + 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,7 +74,7 @@ int xdp_prog_main(struct xdp_md *ctx) } // Get stats map. - __u32 key = 0; + u32 key = 0; struct stats *stats = bpf_map_lookup_elem(&stats_map, &key); __u64 now = bpf_ktime_get_ns(); @@ -122,7 +121,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 +129,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) { @@ -241,9 +240,9 @@ 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); diff --git a/src/xdp/utils.h b/src/xdp/utils/helpers.c similarity index 53% rename from src/xdp/utils.h rename to src/xdp/utils/helpers.c index bf90987..5d5da17 100644 --- a/src/xdp/utils.h +++ b/src/xdp/utils/helpers.c @@ -1,13 +1,4 @@ -#pragma once - -#include - -#include -#include - -#ifndef memcpy -#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) -#endif +#include /** * 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))); } \ No newline at end of file diff --git a/src/xdp/utils/helpers.h b/src/xdp/utils/helpers.h new file mode 100644 index 0000000..dbd3992 --- /dev/null +++ b/src/xdp/utils/helpers.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +#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); + +#include "helpers.c" \ No newline at end of file diff --git a/src/xdp/maps.h b/src/xdp/utils/maps.h similarity index 83% rename from src/xdp/maps.h rename to src/xdp/utils/maps.h index 293fcb2..75aac07 100644 --- a/src/xdp/maps.h +++ b/src/xdp/utils/maps.h @@ -1,8 +1,9 @@ #pragma once -#include +#include +#include -#include +#include struct { @@ -14,7 +15,7 @@ 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 +23,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 +34,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,7 +43,7 @@ struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, MAX_TRACK_IPS); - __type(key, __u32); + __type(key, u32); __type(value, __u64); } ip_blacklist_map SEC(".maps"); @@ -53,7 +54,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 +63,6 @@ struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, MAX_TRACK_IPS); - __type(key, __u128); + __type(key, u128); __type(value, __u64); } ip6_blacklist_map SEC(".maps"); \ No newline at end of file diff --git a/src/xdp/rl.h b/src/xdp/utils/rl.c similarity index 93% rename from src/xdp/rl.h rename to src/xdp/utils/rl.c index 9687e07..8692036 100644 --- a/src/xdp/rl.h +++ b/src/xdp/utils/rl.c @@ -1,9 +1,4 @@ -#pragma once - -#include - -#include -#include +#include /** * Updates IPv4 client stats. @@ -18,7 +13,7 @@ * * @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}; @@ -83,7 +78,7 @@ 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}; diff --git a/src/xdp/utils/rl.h b/src/xdp/utils/rl.h new file mode 100644 index 0000000..f1b9c92 --- /dev/null +++ b/src/xdp/utils/rl.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include + +#include + +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); + +#include "rl.c" \ No newline at end of file diff --git a/src/xdpfw.h b/src/xdpfw.h deleted file mode 100644 index c2cfaa6..0000000 --- a/src/xdpfw.h +++ /dev/null @@ -1,172 +0,0 @@ -#pragma once - -#include - -#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; -}; \ No newline at end of file From 8a4ddf5184e11d75bef1d7d50374001f389b813e Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 22 Feb 2025 10:02:08 -0500 Subject: [PATCH 2/7] We don't need to specifically link LibBPF and LibXDP object files. --- Makefile | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 6d538c5..ac00dff 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,4 @@ CC = clang -LLC = llc - -ARCH := $(shell uname -m | sed 's/x86_64/x86/') # Top-level directories. BUILD_DIR = build @@ -27,18 +24,6 @@ LIBBPF_DIR = $(XDP_TOOLS_DIR)/lib/libbpf LIBBPF_SRC = $(LIBBPF_DIR)/src -# LibBPF objects. -LIBBPF_OBJS = $(LIBBPF_SRC)/staticobjs/bpf_prog_linfo.o $(LIBBPF_SRC)/staticobjs/bpf.o $(LIBBPF_SRC)/staticobjs/btf_dump.o -LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/btf.o $(LIBBPF_SRC)/staticobjs/gen_loader.o $(LIBBPF_SRC)/staticobjs/hashmap.o -LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/libbpf_errno.o $(LIBBPF_SRC)/staticobjs/libbpf_probes.o $(LIBBPF_SRC)/staticobjs/libbpf.o -LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/linker.o $(LIBBPF_SRC)/staticobjs/netlink.o $(LIBBPF_SRC)/staticobjs/nlattr.o -LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/relo_core.o $(LIBBPF_SRC)/staticobjs/ringbuf.o $(LIBBPF_SRC)/staticobjs/str_error.o -LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/strset.o $(LIBBPF_SRC)/staticobjs/usdt.o $(LIBBPF_SRC)/staticobjs/zip.o - -# LibXDP objects. -# To Do: Figure out why static objects produces errors relating to unreferenced functions with dispatcher. -LIBXDP_OBJS = $(LIBXDP_DIR)/sharedobjs/xsk.o $(LIBXDP_DIR)/sharedobjs/libxdp.o - # Loader directories. LOADER_SRC = loader.c LOADER_OUT = xdpfw @@ -56,7 +41,7 @@ LOADER_UTILS_HELPERS_SRC = helpers.c LOADER_UTILS_HELPERS_OBJ = helpers.o # Loader objects. -LOADER_OBJS = $(LIBXDP_OBJS) $(LIBBPF_OBJS) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ) +LOADER_OBJS = $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ) # XDP directories. XDP_SRC = prog.c @@ -73,21 +58,22 @@ XDP_UTILS_RL_OBJ = rl.o # Includes. INCS = -I $(SRC_DIR) -I $(LIBBPF_SRC) -I /usr/include -I /usr/local/include + # Flags. FLAGS = -O2 -g -FLAGS_LOADER = -lconfig -lelf -lz +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) + $(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) + $(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) From 50b0efa58ad07c534e8e4862bcf5f3f83b36113d Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 22 Feb 2025 10:02:35 -0500 Subject: [PATCH 3/7] Add note about linked objects not working with __always_inline functions. --- src/xdp/utils/helpers.h | 2 ++ src/xdp/utils/rl.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/xdp/utils/helpers.h b/src/xdp/utils/helpers.h index dbd3992..82eaab6 100644 --- a/src/xdp/utils/helpers.h +++ b/src/xdp/utils/helpers.h @@ -30,4 +30,6 @@ 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" \ No newline at end of file diff --git a/src/xdp/utils/rl.h b/src/xdp/utils/rl.h index f1b9c92..8b98c10 100644 --- a/src/xdp/utils/rl.h +++ b/src/xdp/utils/rl.h @@ -9,4 +9,6 @@ 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" \ No newline at end of file From c7a1822ce5888f4bae37e9f79d56dd58dfc88283 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 22 Feb 2025 10:05:49 -0500 Subject: [PATCH 4/7] Rename some config constants. --- src/common/config.h | 4 ++-- src/xdp/prog.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/config.h b/src/common/config.h index 5055bee..2342df4 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -1,10 +1,10 @@ #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 DOSTATSONBLOCKMAP +#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 ALLOWSINGLEIPV4V6 +#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). diff --git a/src/xdp/prog.c b/src/xdp/prog.c index 104fa14..6a8e842 100644 --- a/src/xdp/prog.c +++ b/src/xdp/prog.c @@ -107,7 +107,7 @@ int xdp_prog_main(struct xdp_md *ctx) } else { -#ifdef DOSTATSONBLOCKMAP +#ifdef DO_STATS_ON_BLOCK_MAP // Increase blocked stats entry. if (stats) { @@ -273,7 +273,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; @@ -334,7 +334,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; From 1b9e8052070139139e522abcab51d5ffcf607b1f Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 22 Feb 2025 10:06:32 -0500 Subject: [PATCH 5/7] Organize more code. --- src/xdp/prog.c | 6 ++++++ src/xdp/utils/maps.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/xdp/prog.c b/src/xdp/prog.c index 6a8e842..4ca1b4e 100644 --- a/src/xdp/prog.c +++ b/src/xdp/prog.c @@ -15,6 +15,12 @@ #include +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) { diff --git a/src/xdp/utils/maps.h b/src/xdp/utils/maps.h index 75aac07..1455ddf 100644 --- a/src/xdp/utils/maps.h +++ b/src/xdp/utils/maps.h @@ -5,12 +5,6 @@ #include -struct -{ - __uint(priority, 10); - __uint(XDP_PASS, 1); -} XDP_RUN_CONFIG(xdp_prog_main); - struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); From 09491e1462234d88fa2f7db9dddb23cb8872defd Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 22 Feb 2025 10:24:21 -0500 Subject: [PATCH 6/7] Add typedefs and organize code. --- src/common/types.h | 46 +++++++++++++++++++------------------- src/loader/loader.c | 22 +++++++++--------- src/loader/utils/cmdline.c | 2 +- src/loader/utils/cmdline.h | 4 ++-- src/loader/utils/config.c | 8 +++---- src/loader/utils/config.h | 8 +++---- src/loader/utils/helpers.c | 4 ++-- src/loader/utils/helpers.h | 6 ++--- src/xdp/prog.c | 16 ++++++------- src/xdp/utils/maps.h | 4 ++-- src/xdp/utils/rl.c | 20 ++++++++--------- src/xdp/utils/rl.h | 4 ++-- 12 files changed, 72 insertions(+), 72 deletions(-) diff --git a/src/common/types.h b/src/common/types.h index 208894e..da727ee 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -2,7 +2,7 @@ #include -struct tcpopts +struct tcp_opts { unsigned int enabled : 1; @@ -36,9 +36,9 @@ struct tcpopts unsigned int do_cwr : 1; unsigned int cwr : 1; -}; +} typedef tcp_opts_t; -struct udpopts +struct udp_opts { unsigned int enabled : 1; @@ -47,9 +47,9 @@ struct udpopts unsigned int do_dport : 1; u16 dport; -}; +} typedef udp_opts_t; -struct icmpopts +struct icmp_opts { unsigned int enabled : 1; @@ -58,7 +58,7 @@ struct icmpopts unsigned int do_type : 1; u8 type; -}; +} typedef icmp_opts_t; struct filter { @@ -93,42 +93,42 @@ struct filter u8 tos; unsigned int do_pps : 1; - __u64 pps; + u64 pps; unsigned int do_bps : 1; - __u64 bps; + u64 bps; - __u64 blocktime; + u64 blocktime; - struct tcpopts tcpopts; - struct udpopts udpopts; - struct icmpopts icmpopts; -} __attribute__((__aligned__(8))); + 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; -}; + u64 allowed; + u64 dropped; + u64 passed; +} typedef stats_t; struct ip_stats { - __u64 pps; - __u64 bps; - __u64 next_update; -}; + 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; -}; \ No newline at end of file +} typedef flow6_t; \ No newline at end of file diff --git a/src/loader/loader.c b/src/loader/loader.c index 4ba66a1..6306d93 100644 --- a/src/loader/loader.c +++ b/src/loader/loader.c @@ -43,7 +43,7 @@ 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++) @@ -63,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++) { @@ -86,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) @@ -180,7 +180,7 @@ 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; @@ -273,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, @@ -315,7 +315,7 @@ int main(int argc, char *argv[]) } // Initialize config. - struct config cfg = {0}; + config__t cfg = {0}; SetCfgDefaults(&cfg); @@ -332,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) { @@ -521,12 +521,12 @@ int main(int argc, char *argv[]) if (!cfg.nostats) { u32 key = 0; - struct stats stats[MAX_CPUS]; + 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) { diff --git a/src/loader/utils/cmdline.c b/src/loader/utils/cmdline.c index 2ddfd0d..e9bb330 100644 --- a/src/loader/utils/cmdline.c +++ b/src/loader/utils/cmdline.c @@ -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; diff --git a/src/loader/utils/cmdline.h b/src/loader/utils/cmdline.h index ff6bf95..905f1af 100644 --- a/src/loader/utils/cmdline.h +++ b/src/loader/utils/cmdline.h @@ -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[]); \ No newline at end of file +void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]); \ No newline at end of file diff --git a/src/loader/utils/config.c b/src/loader/utils/config.c index ca0013c..108490e 100644 --- a/src/loader/utils/config.c +++ b/src/loader/utils/config.c @@ -11,7 +11,7 @@ FILE *file; * * @return Void */ -void SetCfgDefaults(struct config *cfg) +void SetCfgDefaults(config__t *cfg) { cfg->updatetime = 0; cfg->interface = NULL; @@ -111,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) @@ -225,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; @@ -236,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; diff --git a/src/loader/utils/config.h b/src/loader/utils/config.h index 02ddeae..f539933 100644 --- a/src/loader/utils/config.h +++ b/src/loader/utils/config.h @@ -16,9 +16,9 @@ struct config u16 updatetime; unsigned int nostats : 1; int stdout_update_time; - struct filter filters[MAX_FILTERS]; -}; + filter_t filters[MAX_FILTERS]; +} typedef config__t; // config_t is taken by libconfig -.- -void SetCfgDefaults(struct config *cfg); +void SetCfgDefaults(config__t *cfg); int OpenCfg(const char *filename); -int ReadCfg(struct config *cfg); \ No newline at end of file +int ReadCfg(config__t *cfg); \ No newline at end of file diff --git a/src/loader/utils/helpers.c b/src/loader/utils/helpers.c index a9cdcb7..36d9bbc 100644 --- a/src/loader/utils/helpers.c +++ b/src/loader/utils/helpers.c @@ -7,9 +7,9 @@ * * @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, "/"); diff --git a/src/loader/utils/helpers.h b/src/loader/utils/helpers.h index e8e37ff..6377789 100644 --- a/src/loader/utils/helpers.h +++ b/src/loader/utils/helpers.h @@ -7,10 +7,10 @@ #include #include -struct ip +struct ip_range { u32 ip; u32 cidr; -}; +} typedef ip_range_t; -struct ip ParseIp(const char *ip); \ No newline at end of file +ip_range_t ParseIpCidr(const char *ip); \ No newline at end of file diff --git a/src/xdp/prog.c b/src/xdp/prog.c index 4ca1b4e..9f63816 100644 --- a/src/xdp/prog.c +++ b/src/xdp/prog.c @@ -44,7 +44,7 @@ int xdp_prog_main(struct xdp_md *ctx) } u8 action = 0; - __u64 blocktime = 1; + u64 blocktime = 1; // Initialize IP headers. struct iphdr *iph = NULL; @@ -81,12 +81,12 @@ int xdp_prog_main(struct xdp_md *ctx) // Get stats map. u32 key = 0; - struct stats *stats = bpf_map_lookup_elem(&stats_map, &key); + 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) { @@ -234,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) { @@ -250,7 +250,7 @@ int xdp_prog_main(struct xdp_md *ctx) { 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) @@ -534,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) { diff --git a/src/xdp/utils/maps.h b/src/xdp/utils/maps.h index 1455ddf..3dfe084 100644 --- a/src/xdp/utils/maps.h +++ b/src/xdp/utils/maps.h @@ -38,7 +38,7 @@ struct __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, MAX_TRACK_IPS); __type(key, u32); - __type(value, __u64); + __type(value, u64); } ip_blacklist_map SEC(".maps"); struct @@ -58,5 +58,5 @@ struct __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, MAX_TRACK_IPS); __type(key, u128); - __type(value, __u64); + __type(value, u64); } ip6_blacklist_map SEC(".maps"); \ No newline at end of file diff --git a/src/xdp/utils/rl.c b/src/xdp/utils/rl.c index 8692036..1df4587 100644 --- a/src/xdp/utils/rl.c +++ b/src/xdp/utils/rl.c @@ -13,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) @@ -48,7 +48,7 @@ static __always_inline void UpdateIpStats(__u64 *pps, __u64 *bps, u32 ip, u16 po else { // Create new entry. - struct ip_stats new = {0}; + ip_stats_t new = {0}; new.pps = 1; new.bps = pkt_len; @@ -78,17 +78,17 @@ static __always_inline void UpdateIpStats(__u64 *pps, __u64 *bps, u32 ip, u16 po * * @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) @@ -113,7 +113,7 @@ static __always_inline void UpdateIp6Stats(__u64 *pps, __u64 *bps, u128 *ip, u16 else { // Create new entry. - struct ip_stats new = {0}; + ip_stats_t new = {0}; new.pps = 1; new.bps = pkt_len; diff --git a/src/xdp/utils/rl.h b/src/xdp/utils/rl.h index 8b98c10..4088e14 100644 --- a/src/xdp/utils/rl.h +++ b/src/xdp/utils/rl.h @@ -6,8 +6,8 @@ #include -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); +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 From 7f59b678cf9b7b69b9c2d189004374173a08d03b Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 22 Feb 2025 10:29:19 -0500 Subject: [PATCH 7/7] Update links in README. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 05ca449..0a14352 100644 --- a/README.md +++ b/README.md @@ -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