Restructure project and organize code.

This commit is contained in:
Christian Deacon
2025-02-22 09:50:57 -05:00
parent e3d47fda6f
commit 8756892791
25 changed files with 403 additions and 334 deletions

1
.gitignore vendored
View File

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

154
Makefile
View File

@@ -3,92 +3,124 @@ LLC = llc
ARCH := $(shell uname -m | sed 's/x86_64/x86/') ARCH := $(shell uname -m | sed 's/x86_64/x86/')
# Main directories. # Top-level directories.
BUILDDIR = build BUILD_DIR = build
SRCDIR = src SRC_DIR = src
MODULEDIR = modules MODULES_DIR = modules
# XDP Tools directory. # Common directories.
XDPTOOLSDIR = $(MODULEDIR)/xdp-tools COMMON_DIR = $(SRC_DIR)/common
XDPTOOLSHEADERS = $(XDPTOOLSDIR)/headers 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. # 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 LIBBPF_SRC = $(LIBBPF_DIR)/src
LIBBPFSRC = $(LIBBPFDIR)/src
# LibBPF objects. # LibBPF objects.
LIBBPFOBJS = $(LIBBPFSRC)/staticobjs/bpf_prog_linfo.o $(LIBBPFSRC)/staticobjs/bpf.o $(LIBBPFSRC)/staticobjs/btf_dump.o LIBBPF_OBJS = $(LIBBPF_SRC)/staticobjs/bpf_prog_linfo.o $(LIBBPF_SRC)/staticobjs/bpf.o $(LIBBPF_SRC)/staticobjs/btf_dump.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/btf.o $(LIBBPFSRC)/staticobjs/gen_loader.o $(LIBBPFSRC)/staticobjs/hashmap.o LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/btf.o $(LIBBPF_SRC)/staticobjs/gen_loader.o $(LIBBPF_SRC)/staticobjs/hashmap.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/libbpf_errno.o $(LIBBPFSRC)/staticobjs/libbpf_probes.o $(LIBBPFSRC)/staticobjs/libbpf.o LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/libbpf_errno.o $(LIBBPF_SRC)/staticobjs/libbpf_probes.o $(LIBBPF_SRC)/staticobjs/libbpf.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/linker.o $(LIBBPFSRC)/staticobjs/netlink.o $(LIBBPFSRC)/staticobjs/nlattr.o LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/linker.o $(LIBBPF_SRC)/staticobjs/netlink.o $(LIBBPF_SRC)/staticobjs/nlattr.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/relo_core.o $(LIBBPFSRC)/staticobjs/ringbuf.o $(LIBBPFSRC)/staticobjs/str_error.o LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/relo_core.o $(LIBBPF_SRC)/staticobjs/ringbuf.o $(LIBBPF_SRC)/staticobjs/str_error.o
LIBBPFOBJS += $(LIBBPFSRC)/staticobjs/strset.o $(LIBBPFSRC)/staticobjs/usdt.o $(LIBBPFSRC)/staticobjs/zip.o LIBBPF_OBJS += $(LIBBPF_SRC)/staticobjs/strset.o $(LIBBPF_SRC)/staticobjs/usdt.o $(LIBBPF_SRC)/staticobjs/zip.o
# LibXDP objects. # LibXDP objects.
# To Do: Figure out why static objects produces errors relating to unreferenced functions with dispatcher. # 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. # Loader directories.
CONFIGSRC = config.c LOADER_SRC = loader.c
CONFIGOBJ = config.o LOADER_OUT = xdpfw
CMDLINESRC = cmdline.c
CMDLINEOBJ = cmdline.o
XDPFWSRC = xdpfw.c LOADER_UTILS_DIR = $(LOADER_DIR)/utils
XDPFWOUT = xdpfw
XDPPROGSRC = xdpfw_kern.c # Loader utils.
XDPPROGLL = xdpfw_kern.ll LOADER_UTILS_CONFIG_SRC = config.c
XDPPROGOBJ = xdpfw_kern.o 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. LOADER_UTILS_HELPERS_SRC = helpers.c
LDFLAGS += -lconfig -lelf -lz LOADER_UTILS_HELPERS_OBJ = helpers.o
INCS = -I $(SRCDIR) -I $(LIBBPFSRC)
INCS += -I /usr/include -I /usr/local/include
# All chain. # Loader objects.
all: xdpfw xdpfw_filter utils 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. # XDP directories.
xdpfw: utils libxdp $(OBJS) XDP_SRC = prog.c
mkdir -p $(BUILDDIR)/ XDP_OBJ = xdp_prog.o
$(CC) $(LDFLAGS) $(INCS) -o $(BUILDDIR)/$(XDPFWOUT) $(LIBBPFOBJS) $(LIBXDPOBJS) $(OBJS) $(SRCDIR)/$(XDPFWSRC)
# XDP program chain. XDP_UTILS_DIR = $(XDP_DIR)/utils
xdpfw_filter:
mkdir -p $(BUILDDIR)/ # XDP utils.
$(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) XDP_UTILS_HELPERS_SRC = helpers.c
$(LLC) -march=bpf -filetype=obj -o $(BUILDDIR)/$(XDPPROGOBJ) $(BUILDDIR)/$(XDPPROGLL) XDP_UTILS_HELPERS_OBJ = helpers.o
# Utils chain. XDP_UTILS_RL_SRC = rl.c
utils: XDP_UTILS_RL_OBJ = rl.o
mkdir -p $(BUILDDIR)/
$(CC) $(INCS) -O2 -c -o $(BUILDDIR)/$(CONFIGOBJ) $(SRCDIR)/$(CONFIGSRC) # Includes.
$(CC) $(INCS) -O2 -c -o $(BUILDDIR)/$(CMDLINEOBJ) $(SRCDIR)/$(CMDLINESRC) 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 chain. We need to install objects here since our program relies on installed object files and such.
libxdp: libxdp:
$(MAKE) -C $(XDPTOOLSDIR) libxdp $(MAKE) -C $(XDP_TOOLS_DIR) libxdp
sudo $(MAKE) -C $(LIBBPFSRC) install sudo $(MAKE) -C $(LIBBPF_SRC) install
sudo $(MAKE) -C $(LIBXDPDIR) install sudo $(MAKE) -C $(LIBXDP_DIR) install
# Clean chain.
clean: clean:
$(MAKE) -C $(LIBBPFSRC) clean $(MAKE) -C $(XDP_TOOLS_DIR) clean
$(MAKE) -C $(XDPTOOLSDIR) clean $(MAKE) -C $(LIBBPF_SRC) clean
rm -f $(BUILDDIR)/*.o $(BUILDDIR)/*.bc
rm -f $(BUILDDIR)/$(XDPFWOUT) 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: install:
mkdir -p /etc/xdpfw/ mkdir -p /etc/xdpfw/
cp -n xdpfw.conf.example /etc/xdpfw/xdpfw.conf 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/ cp -n other/xdpfw.service /etc/systemd/system/
.PHONY: libxdp all
.PHONY: all libxdp
.DEFAULT: all .DEFAULT: all

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 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

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 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;
};

View File

@@ -20,12 +20,14 @@
#include <libbpf.h> #include <libbpf.h>
#include <xdp/libxdp.h> #include <xdp/libxdp.h>
#include <xdpfw.h> #include <common/all.h>
#include <config.h>
#include <cmdline.h> #include <loader/utils/cmdline.h>
#include <loader/utils/config.h>
#include <loader/utils/helpers.h>
// Other variables. // Other variables.
static __u8 cont = 1; static u8 cont = 1;
static int filtersmap = -1; static int filtersmap = -1;
static int statsmap = -1; static int statsmap = -1;
@@ -44,15 +46,15 @@ void SignalHndl(int tmp)
void UpdateFilters(struct config *cfg) 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. // 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); bpf_map_delete_elem(filtersmap, &key);
} }
// Add a filter to the filter maps. // 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. // Check if we have a valid ID.
if (cfg->filters[i].id < 1) if (cfg->filters[i].id < 1)
@@ -96,7 +98,7 @@ int UpdateConfig(struct config *cfg, char *cfgfile)
SetCfgDefaults(cfg); SetCfgDefaults(cfg);
for (__u16 i = 0; i < MAX_FILTERS; i++) for (u16 i = 0; i < MAX_FILTERS; i++)
{ {
cfg->filters[i] = (struct filter) {0}; 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. * @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; int err;
__u32 mode = XDP_MODE_NATIVE; u32 mode = XDP_MODE_NATIVE;
char *smode; char *smode;
smode = "DRV/native"; smode = "DRV/native";
@@ -199,7 +201,7 @@ int AttachXdp(struct xdp_program *prog, int ifidx, __u8 detach, struct cmdline *
mode = XDP_MODE_SKB; mode = XDP_MODE_SKB;
} }
__u8 exit = 0; u8 exit = 0;
while (!exit) while (!exit)
{ {
@@ -427,7 +429,7 @@ int main(int argc, char *argv[])
} }
// XDP variables. // XDP variables.
const char *filename = "/etc/xdpfw/xdpfw_kern.o"; const char *filename = "/etc/xdpfw/xdp_prog.o";
// Load BPF object. // Load BPF object.
struct xdp_program *prog = LoadBpfObj(filename); struct xdp_program *prog = LoadBpfObj(filename);
@@ -518,7 +520,7 @@ int main(int argc, char *argv[])
// Update stats. // Update stats.
if (!cfg.nostats) if (!cfg.nostats)
{ {
__u32 key = 0; u32 key = 0;
struct stats stats[MAX_CPUS]; struct stats stats[MAX_CPUS];
//memset(stats, 0, sizeof(struct stats) * MAX_CPUS); //memset(stats, 0, sizeof(struct stats) * MAX_CPUS);

View File

@@ -1,14 +1,6 @@
#include <stdio.h> #include <loader/utils/config.h>
#include <stdlib.h>
#include <libconfig.h>
#include <string.h>
#include <linux/types.h>
#include <arpa/inet.h> #include <loader/utils/helpers.h>
#include <xdpfw.h>
#include <config.h>
#include <utils.h>
FILE *file; FILE *file;
@@ -26,7 +18,7 @@ void SetCfgDefaults(struct config *cfg)
cfg->nostats = 0; cfg->nostats = 0;
cfg->stdout_update_time = 1000; 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].id = 0;
cfg->filters[i].enabled = 0; cfg->filters[i].enabled = 0;
@@ -34,7 +26,7 @@ void SetCfgDefaults(struct config *cfg)
cfg->filters[i].src_ip = 0; cfg->filters[i].src_ip = 0;
cfg->filters[i].dst_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].src_ip6[j] = 0;
cfg->filters[i].dst_ip6[j] = 0; cfg->filters[i].dst_ip6[j] = 0;
@@ -197,7 +189,7 @@ int ReadCfg(struct config *cfg)
// Set filter count. // Set filter count.
int filters = 0; 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); 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); 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]; 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); 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]; 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)) 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; 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)) 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; cfg->filters[i].do_max_ttl = 1;
} }
@@ -321,7 +313,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int(filter, "tos", &tos)) 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; cfg->filters[i].do_tos = 1;
} }
@@ -369,7 +361,7 @@ int ReadCfg(struct config *cfg)
if (config_setting_lookup_int64(filter, "tcp_sport", &tcpsport)) 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; 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)) 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; 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)) 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; 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)) 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; 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)) 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; 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)) 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; cfg->filters[i].icmpopts.do_type = 1;
} }

View File

@@ -1,13 +1,19 @@
#pragma once #pragma once
#include <common/all.h>
#include <stdio.h>
#include <stdlib.h>
#include <libconfig.h>
#include <string.h>
#include <linux/types.h> #include <linux/types.h>
#include "xdpfw.h" #include <arpa/inet.h>
struct config struct config
{ {
char *interface; char *interface;
__u16 updatetime; u16 updatetime;
unsigned int nostats : 1; unsigned int nostats : 1;
int stdout_update_time; int stdout_update_time;
struct filter filters[MAX_FILTERS]; struct filter filters[MAX_FILTERS];

View File

@@ -1,16 +1,4 @@
#pragma once #include <loader/utils/helpers.h>
#include <linux/types.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
struct ip
{
__u32 ip;
__u32 cidr;
};
/** /**
* Parses an IP string with CIDR support. Stores IP in network byte order in ip.ip and CIDR in ip.cidr. * Parses an IP string with CIDR support. Stores IP in network byte order in ip.ip and CIDR in ip.cidr.

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
{
u32 ip;
u32 cidr;
};
struct ip ParseIp(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,12 @@
#include <linux/in.h> #include <linux/in.h>
#include <stdatomic.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/utils/maps.h>
#include <xdp/rl.h>
#include <xdp/utils.h>
SEC("xdp_prog") SEC("xdp_prog")
int xdp_prog_main(struct xdp_md *ctx) int xdp_prog_main(struct xdp_md *ctx)
@@ -38,13 +37,13 @@ int xdp_prog_main(struct xdp_md *ctx)
return XDP_PASS; return XDP_PASS;
} }
__u8 action = 0; u8 action = 0;
__u64 blocktime = 1; __u64 blocktime = 1;
// Initialize IP headers. // Initialize IP headers.
struct iphdr *iph = NULL; struct iphdr *iph = NULL;
struct ipv6hdr *iph6 = NULL; struct ipv6hdr *iph6 = NULL;
__u128 src_ip6 = 0; u128 src_ip6 = 0;
// Set IPv4 and IPv6 common variables. // Set IPv4 and IPv6 common variables.
if (eth->h_proto == htons(ETH_P_IPV6)) if (eth->h_proto == htons(ETH_P_IPV6))
@@ -75,7 +74,7 @@ int xdp_prog_main(struct xdp_md *ctx)
} }
// Get stats map. // Get stats map.
__u32 key = 0; u32 key = 0;
struct stats *stats = bpf_map_lookup_elem(&stats_map, &key); struct stats *stats = bpf_map_lookup_elem(&stats_map, &key);
__u64 now = bpf_ktime_get_ns(); __u64 now = bpf_ktime_get_ns();
@@ -122,7 +121,7 @@ int xdp_prog_main(struct xdp_md *ctx)
} }
// Retrieve total packet length. // 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. // Parse layer-4 headers and determine source port and protocol.
struct tcphdr *tcph = NULL; struct tcphdr *tcph = NULL;
@@ -130,8 +129,8 @@ int xdp_prog_main(struct xdp_md *ctx)
struct icmphdr *icmph = NULL; struct icmphdr *icmph = NULL;
struct icmp6hdr *icmp6h = NULL; struct icmp6hdr *icmp6h = NULL;
__u16 src_port = 0; u16 src_port = 0;
__u8 protocol = 0; u8 protocol = 0;
if (iph6) 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); 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); struct filter *filter = bpf_map_lookup_elem(&filters_map, &key);

View File

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

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

@@ -0,0 +1,33 @@
#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);
#include "helpers.c"

View File

@@ -1,8 +1,9 @@
#pragma once #pragma once
#include <xdpfw.h> #include <common/int_types.h>
#include <common/types.h>
#include <xdp/helpers.h> #include <xdp/utils/helpers.h>
struct struct
{ {
@@ -14,7 +15,7 @@ struct
{ {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, MAX_FILTERS); __uint(max_entries, MAX_FILTERS);
__type(key, __u32); __type(key, u32);
__type(value, struct filter); __type(value, struct filter);
} filters_map SEC(".maps"); } filters_map SEC(".maps");
@@ -22,7 +23,7 @@ struct
{ {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1); __uint(max_entries, 1);
__type(key, __u32); __type(key, u32);
__type(value, struct stats); __type(value, struct stats);
} stats_map SEC(".maps"); } stats_map SEC(".maps");
@@ -33,7 +34,7 @@ struct
#ifdef USE_FLOW_RL #ifdef USE_FLOW_RL
__type(key, struct flow); __type(key, struct flow);
#else #else
__type(key, __u32); __type(key, u32);
#endif #endif
__type(value, struct ip_stats); __type(value, struct ip_stats);
} ip_stats_map SEC(".maps"); } ip_stats_map SEC(".maps");
@@ -42,7 +43,7 @@ struct
{ {
__uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, MAX_TRACK_IPS); __uint(max_entries, MAX_TRACK_IPS);
__type(key, __u32); __type(key, u32);
__type(value, __u64); __type(value, __u64);
} ip_blacklist_map SEC(".maps"); } ip_blacklist_map SEC(".maps");
@@ -53,7 +54,7 @@ struct
#ifdef USE_FLOW_RL #ifdef USE_FLOW_RL
__type(key, struct flow6); __type(key, struct flow6);
#else #else
__type(key, __u128); __type(key, u128);
#endif #endif
__type(value, struct ip_stats); __type(value, struct ip_stats);
} ip6_stats_map SEC(".maps"); } ip6_stats_map SEC(".maps");
@@ -62,6 +63,6 @@ struct
{ {
__uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(type, BPF_MAP_TYPE_LRU_HASH);
__uint(max_entries, MAX_TRACK_IPS); __uint(max_entries, MAX_TRACK_IPS);
__type(key, __u128); __type(key, u128);
__type(value, __u64); __type(value, __u64);
} ip6_blacklist_map SEC(".maps"); } ip6_blacklist_map SEC(".maps");

View File

@@ -1,9 +1,4 @@
#pragma once #include <xdp/utils/rl.h>
#include <xdpfw.h>
#include <xdp/maps.h>
#include <xdp/helpers.h>
/** /**
* Updates IPv4 client stats. * Updates IPv4 client stats.
@@ -18,7 +13,7 @@
* *
* @return void * @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 #ifdef USE_FLOW_RL
struct flow key = {0}; struct flow key = {0};
@@ -83,7 +78,7 @@ static __always_inline void UpdateIpStats(__u64 *pps, __u64 *bps, __u32 ip, __u1
* *
* @return void * @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 #ifdef USE_FLOW_RL
struct flow6 key = {0}; struct flow6 key = {0};

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

@@ -0,0 +1,12 @@
#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);
#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;
};