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/
build/
xdpfw
xdpfw.s
xdpfw.conf

152
Makefile
View File

@@ -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)
XDP_UTILS_DIR = $(XDP_DIR)/utils
# 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.
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

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 <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;
@@ -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);

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

View File

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

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.

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

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

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
#include <xdpfw.h>
#include <common/int_types.h>
#include <common/types.h>
#include <xdp/helpers.h>
#include <xdp/utils/helpers.h>
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");

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

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