Merge pull request #59 from gamemann/20250226-logging

Logging System Overhaul
This commit is contained in:
Christian Deacon
2025-02-26 13:09:37 -05:00
committed by GitHub
21 changed files with 700 additions and 209 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
xdpfw
xdpfw.s
xdpfw.conf
*.log

View File

@@ -13,6 +13,7 @@ LOADER_DIR = $(SRC_DIR)/loader
XDP_DIR = $(SRC_DIR)/xdp
ETC_DIR = /etc/xdpfw
LOG_DIR = /var/log/xdpfw
# Additional build directories.
BUILD_LOADER_DIR = $(BUILD_DIR)/loader
@@ -52,6 +53,9 @@ LOADER_UTILS_CMDLINE_OBJ = cmdline.o
LOADER_UTILS_XDP_SRC = xdp.c
LOADER_UTILS_XDP_OBJ = xdp.o
LOADER_UTILS_LOGGING_SRC = logging.c
LOADER_UTILS_LOGGING_OBJ = logging.o
LOADER_UTILS_STATS_SRC = stats.c
LOADER_UTILS_STATS_OBJ = stats.o
@@ -59,7 +63,7 @@ LOADER_UTILS_HELPERS_SRC = helpers.c
LOADER_UTILS_HELPERS_OBJ = helpers.o
# Loader objects.
LOADER_OBJS = $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CMDLINE_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_XDP_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_STATS_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_XDP_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_LOGGING_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_STATS_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ)
ifeq ($(LIBXDP_STATIC), 1)
LOADER_OBJS := $(LIBBPF_OBJS) $(LIBXDP_OBJS) $(LOADER_OBJS)
@@ -93,7 +97,7 @@ all: loader xdp
loader: 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_xdp loader_utils_stats
loader_utils: loader_utils_config loader_utils_cmdline loader_utils_helpers loader_utils_xdp loader_utils_logging loader_utils_stats
loader_utils_config:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_CONFIG_SRC)
@@ -104,6 +108,9 @@ loader_utils_cmdline:
loader_utils_xdp:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_XDP_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_XDP_SRC)
loader_utils_logging:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_LOGGING_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_LOGGING_SRC)
loader_utils_stats:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_STATS_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_STATS_SRC)
@@ -128,6 +135,8 @@ libxdp_clean:
install:
mkdir -p $(ETC_DIR)
mkdir -p $(LOG_DIR)
cp -n xdpfw.conf.example $(ETC_DIR)/xdpfw.conf
cp -n other/xdpfw.service /etc/systemd/system/

View File

@@ -12,7 +12,7 @@ With that said, reasons for a host's network configuration not supporting XDP's
I hope this project helps existing network engineers/programmers interested in utilizing XDP or anybody interested in getting into those fields! (D)DoS mitigation/prevention is such an important part of Cyber Security and understanding the concept of networking and packet flow on a low-medium level would certainly help those who are pursuing a career in the field 🙂
![Demo](./images/demo.gif)
![Demo Run](./images/run.gif)
## Building & Installing
Before building, ensure the following packages are installed. These packages can be installed with `apt` on Debian-based systems (e.g. Ubuntu, etc.), but there should be similar package names in other package managers.
@@ -61,6 +61,8 @@ Additionally, here is a list of flags you may pass to this script.
| --no-static | Do *not* statically link LibXDP and LibBPF object files when building the tool. This makes the build process faster, but you may need to alter your `LD_LIBRARY_PATH` env variable before running the tool and requires LibXDP to be installed on your system already. |
| --help | Displays help message. |
![Script Build Demo](./images/build_script.gif)
### Without Bash Script
If you do not want to use the Bash script above, you may use `make` to build and install this tool instead.
@@ -80,6 +82,8 @@ make
sudo make install
```
![Script Build Demo](./images/build_make.gif)
## Command Line Usage
The following command line arguments are supported.
@@ -118,6 +122,8 @@ The following table quickly explains the data types used within the configuratio
### Main
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| verbose | int | `2` | The verbose level for logging (0 - 5 supported so far). |
| log_file | string | `/var/log/xdpfw/xdpfw.log` | The log file location. If the string is empty (`""`), the log file is disabled. |
| interface | string | `NULL` | The network interface name to attach the XDP program to (usually retrieved with `ip a` or `ifconfig`). |
| update_time | uint | `0` | How often to update the config and filtering rules from the file system in seconds (0 disables). |
| no_stats | bool | `false` | Whether to enable or disable packet counters. Disabling packet counters will improve performance, but result in less visibility on what the XDP Firewall is doing. |
@@ -127,8 +133,9 @@ The following table quickly explains the data types used within the configuratio
### Filter Object
| Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- |
| enabled | bool | `false` | Whether the rule is enabled or not. |
| action | uint | `0` | The value of `0` drops or blocks the packet while `1` allows/passes the packet through. |
| enabled | bool | `true` | Whether the rule is enabled or not. |
| log | bool | `false` | Whether to log packets that are matched. |
| action | uint | `1` | The value of `0` drops or blocks the packet while `1` allows/passes the packet through. |
| block_time | uint | `1` | The amount of seconds to block the source IP for if matched. |
| src_ip | string | `NULL` | The source IPv4 address to match (e.g. `10.50.0.3`). CIDRs are also supported (e.g. `10.50.0.0/24`)! |
| dst_ip | string | `NULL` | The destination IPv4 address to match (e.g. `10.50.0.4`). CIDRs are also supported (e.g. `10.50.0.0/24`)! |
@@ -301,6 +308,22 @@ export LD_LIBRARY_PATH=/usr/local/lib
sudo xdpfw
```
### Filter Logging
This tool uses `bpf_ringbuf_reserve()` and `bpf_ringbuf_submit()` for filter match logging. At this time, there is no rate limit for the amount of log messages that may be sent. Therefore, if you're encountering a spoofed attack that is matching a filter rule with logging enabled, it will cause additional processing and disk load.
I recommend only enabling filter logging at this time for debugging. If you'd like to disable filter logging entirely (which will improve performance slightly), you may comment out the `ENABLE_FILTER_LOGGING` line [here](https://github.com/gamemann/XDP-Firewall/blob/master/src/common/config.h#L27).
```C
//#define ENABLE_FILTER_LOGGING
```
I will most likely implement functionality to rate limit log messages from XDP in the future.
### LibBPF Logging
When loading the BPF/XDP program through LibXDP/LibBPF, logging is disabled unless if the `verbose` log setting is set to `5` or higher.
If the tool fails to load or attach the XDP program, it is recommended you set `verbose` to 5 or above so LibXDP outputs specific warnings and errors.
## My Other XDP Projects
I just wanted to share other open source projects I've made which also utilize XDP (or AF_XDP sockets) for those interested. I hope code from these other projects help programmers trying to utilize XDP in their own projects!

BIN
images/build_make.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
images/build_script.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

BIN
images/run.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

View File

@@ -21,3 +21,7 @@
// 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
// Enables filter logging through XDP.
// If performance is a concerned, it is best to disable this feature by commenting out the below line with //.
#define ENABLE_FILTER_LOGGING

View File

@@ -64,6 +64,8 @@ struct filter
{
u8 id;
unsigned int log : 1;
unsigned int enabled : 1;
u8 action;
@@ -132,3 +134,24 @@ struct flow6
u16 port;
u8 protocol;
} typedef flow6_t;
struct filter_log_event
{
u64 ts;
int filter_id;
u32 src_ip;
u32 src_ip6[4];
u16 src_port;
u32 dst_ip;
u32 dst_ip6[4];
u16 dst_port;
u8 protocol;
u64 pps;
u64 bps;
} typedef filter_log_event_t;

View File

@@ -14,10 +14,12 @@
#include <loader/utils/cmdline.h>
#include <loader/utils/config.h>
#include <loader/utils/xdp.h>
#include <loader/utils/logging.h>
#include <loader/utils/stats.h>
#include <loader/utils/helpers.h>
int cont = 1;
int doing_stats = 0;
int main(int argc, char *argv[])
{
@@ -37,16 +39,6 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
// Raise RLimit.
struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
if (setrlimit(RLIMIT_MEMLOCK, &rl))
{
fprintf(stderr, "[ERROR] Failed to raise rlimit. Please make sure this program is ran as root!\n");
return EXIT_FAILURE;
}
// Initialize config.
config__t cfg = {0};
@@ -68,54 +60,119 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
// Print tool info.
if (cfg.verbose > 0)
{
PrintToolInfo();
}
LogMsg(&cfg, 2, 0, "Raising RLimit...");
// Raise RLimit.
struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY };
if (setrlimit(RLIMIT_MEMLOCK, &rl))
{
LogMsg(&cfg, 0, 1, "[ERROR] Failed to raise rlimit. Please make sure this program is ran as root!\n");
return EXIT_FAILURE;
}
LogMsg(&cfg, 2, 0, "Retrieving interface index for '%s'...", cfg.interface);
// Get interface index.
int ifidx = if_nametoindex(cfg.interface);
if (ifidx < 0)
{
fprintf(stderr, "[ERROR] Failed to retrieve index of network interface '%s'.\n", cfg.interface);
LogMsg(&cfg, 0, 1, "[ERROR] Failed to retrieve index of network interface '%s'.\n", cfg.interface);
return EXIT_FAILURE;
}
LogMsg(&cfg, 2, 0, "Loading XDP/BPF program at '%s'...", XDP_OBJ_PATH);
// Determine custom LibBPF log level.
int silent = 1;
if (cfg.verbose > 4)
{
silent = 0;
}
SetLibBPFLogMode(silent);
// Load BPF object.
struct xdp_program *prog = LoadBpfObj(XDP_OBJ_PATH);
if (prog == NULL)
{
fprintf(stderr, "[ERROR] Failed to load eBPF object file. Object path => %s.\n", XDP_OBJ_PATH);
LogMsg(&cfg, 0, 1, "[ERROR] Failed to load eBPF object file. Object path => %s.\n", XDP_OBJ_PATH);
return EXIT_FAILURE;
}
LogMsg(&cfg, 2, 0, "Attaching XDP program to interface '%s'...", cfg.interface);
// Attach XDP program.
if ((ret = AttachXdp(prog, ifidx, 0, &cmd)) != 0)
char *mode_used = NULL;
if ((ret = AttachXdp(prog, &mode_used, ifidx, 0, &cmd)) != 0)
{
fprintf(stderr, "[ERROR] Failed to attach XDP program to interface '%s' (%d).\n", cfg.interface, ret);
LogMsg(&cfg, 0, 1, "[ERROR] Failed to attach XDP program to interface '%s' using available modes (%d).\n", cfg.interface, ret);
return EXIT_FAILURE;
}
if (mode_used != NULL)
{
LogMsg(&cfg, 1, 0, "Attached XDP program using mode '%s'...", mode_used);
}
LogMsg(&cfg, 2, 0, "Retrieving BPF map FDs...");
// Retrieve BPF maps.
int filters_map = FindMapFd(prog, "filters_map");
// Check for valid maps.
if (filters_map < 0)
{
fprintf(stderr, "[ERROR] Failed to find 'filters_map' BPF map.\n");
LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'filters_map' BPF map.\n");
return EXIT_FAILURE;
}
LogMsg(&cfg, 3, 0, "filters_map FD => %d.", filters_map);
int stats_map = FindMapFd(prog, "stats_map");
if (stats_map < 0)
{
fprintf(stderr, "[ERROR] Failed to find 'stats_map' BPF map.\n");
LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'stats_map' BPF map.\n");
return EXIT_FAILURE;
}
#ifdef ENABLE_FILTER_LOGGING
int filter_log_map = FindMapFd(prog, "filter_log_map");
struct ring_buffer* rb = NULL;
if (filter_log_map < 0)
{
LogMsg(&cfg, 1, 0, "[WARNING] Failed to find 'filter_log_map' BPF map. Filter logging will be disabled...");
}
else
{
LogMsg(&cfg, 3, 0, "filter_log_map FD => %d.", filter_log_map);
rb = ring_buffer__new(filter_log_map, HandleRbEvent, &cfg, NULL);
}
#endif
LogMsg(&cfg, 3, 0, "stats_map FD => %d.", stats_map);
LogMsg(&cfg, 2, 0, "Updating filters...");
// Update BPF maps.
UpdateFilters(filters_map, &cfg);
@@ -126,6 +183,8 @@ int main(int argc, char *argv[])
// Receive CPU count for stats map parsing.
int cpus = get_nprocs_conf();
LogMsg(&cfg, 4, 0, "Retrieved %d CPUs on host.", cpus);
unsigned int end_time = (cmd.time > 0) ? time(NULL) + cmd.time : 0;
// Create last updated variables.
@@ -136,6 +195,12 @@ int main(int argc, char *argv[])
struct stat conf_stat;
// Check if we're doing stats.
if (!cfg.nostats)
{
doing_stats = 1;
}
while (cont)
{
// Get current time.
@@ -159,7 +224,7 @@ int main(int argc, char *argv[])
// Update config.
if ((ret = LoadConfig(&cfg, cmd.cfgfile)) != 0)
{
fprintf(stderr, "[WARNING] Failed to load config after update check (%d)...\n", ret);
LogMsg(&cfg, 1, 0, "[WARNING] Failed to load config after update check (%d)...\n", ret);
}
// Update BPF maps.
@@ -167,6 +232,12 @@ int main(int argc, char *argv[])
// Update timer
last_config_check = time(NULL);
// Make sure we set doing stats if needed.
if (!cfg.nostats && !doing_stats)
{
doing_stats = 1;
}
}
// Update last updated variable.
@@ -178,24 +249,38 @@ int main(int argc, char *argv[])
{
if (CalculateStats(stats_map, cpus))
{
fprintf(stderr, "[WARNING] Failed to calculate packet stats. Stats map FD => %d...\n", stats_map);
LogMsg(&cfg, 1, 0, "[WARNING] Failed to calculate packet stats. Stats map FD => %d...\n", stats_map);
}
}
#ifdef ENABLE_FILTER_LOGGING
if (rb)
{
ring_buffer__poll(rb, RB_TIMEOUT);
}
#endif
usleep(sleep_time);
}
fprintf(stdout, "\n");
// Detach XDP program.
if (AttachXdp(prog, ifidx, 1, &cmd))
#ifdef ENABLE_FILTER_LOGGING
if (rb)
{
fprintf(stderr, "[ERROR] Failed to detach XDP program from interface '%s'.\n", cfg.interface);
ring_buffer__free(rb);
}
#endif
// Detach XDP program.
if (AttachXdp(prog, &mode_used, ifidx, 1, &cmd))
{
LogMsg(&cfg, 0, 1, "[ERROR] Failed to detach XDP program from interface '%s'.\n", cfg.interface);
return EXIT_FAILURE;
}
fprintf(stdout, "Cleaned up and exiting...\n");
LogMsg(&cfg, 1, 0, "Cleaned up and exiting...\n");
// Exit program successfully.
return EXIT_SUCCESS;

View File

@@ -1,7 +1,5 @@
#include <loader/utils/config.h>
#include <loader/utils/helpers.h>
static FILE *file;
/**
@@ -46,6 +44,8 @@ int LoadConfig(config__t *cfg, char *cfg_file)
*/
void SetCfgDefaults(config__t *cfg)
{
cfg->verbose = 2;
cfg->log_file = strdup("/var/log/xdpfw/xdpfw.log");
cfg->updatetime = 0;
cfg->interface = NULL;
cfg->nostats = 0;
@@ -53,60 +53,62 @@ void SetCfgDefaults(config__t *cfg)
for (int i = 0; i < MAX_FILTERS; i++)
{
cfg->filters[i].id = 0;
cfg->filters[i].enabled = 0;
cfg->filters[i].action = 0;
cfg->filters[i].src_ip = 0;
cfg->filters[i].dst_ip = 0;
filter_t* filter = &cfg->filters[i];
for (int j = 0; j < 4; j++)
{
cfg->filters[i].src_ip6[j] = 0;
cfg->filters[i].dst_ip6[j] = 0;
}
filter->id = 0;
filter->enabled = 1;
cfg->filters[i].do_min_len = 0;
cfg->filters[i].min_len = 0;
filter->log = 0;
cfg->filters[i].do_max_len = 0;
cfg->filters[i].max_len = 65535;
filter->action = 1;
filter->src_ip = 0;
filter->dst_ip = 0;
cfg->filters[i].do_min_ttl = 0;
cfg->filters[i].min_ttl = 0;
memset(filter->src_ip6, 0, 4);
memset(filter->dst_ip6, 0, 4);
cfg->filters[i].do_max_ttl = 0;
cfg->filters[i].max_ttl = 255;
filter->do_min_len = 0;
filter->min_len = 0;
cfg->filters[i].do_tos = 0;
cfg->filters[i].tos = 0;
filter->do_max_len = 0;
filter->max_len = 65535;
cfg->filters[i].do_pps = 0;
cfg->filters[i].pps = 0;
filter->do_min_ttl = 0;
filter->min_ttl = 0;
cfg->filters[i].do_bps = 0;
cfg->filters[i].bps = 0;
filter->do_max_ttl = 0;
filter->max_ttl = 255;
cfg->filters[i].blocktime = 1;
filter->do_tos = 0;
filter->tos = 0;
cfg->filters[i].tcpopts.enabled = 0;
cfg->filters[i].tcpopts.do_dport = 0;
cfg->filters[i].tcpopts.do_dport = 0;
cfg->filters[i].tcpopts.do_urg = 0;
cfg->filters[i].tcpopts.do_ack = 0;
cfg->filters[i].tcpopts.do_rst = 0;
cfg->filters[i].tcpopts.do_psh = 0;
cfg->filters[i].tcpopts.do_syn = 0;
cfg->filters[i].tcpopts.do_fin = 0;
cfg->filters[i].tcpopts.do_ece = 0;
cfg->filters[i].tcpopts.do_cwr = 0;
filter->do_pps = 0;
filter->pps = 0;
cfg->filters[i].udpopts.enabled = 0;
cfg->filters[i].udpopts.do_sport = 0;
cfg->filters[i].udpopts.do_dport = 0;
filter->do_bps = 0;
filter->bps = 0;
cfg->filters[i].icmpopts.enabled = 0;
cfg->filters[i].icmpopts.do_code = 0;
cfg->filters[i].icmpopts.do_type = 0;
filter->blocktime = 1;
filter->tcpopts.enabled = 0;
filter->tcpopts.do_dport = 0;
filter->tcpopts.do_dport = 0;
filter->tcpopts.do_urg = 0;
filter->tcpopts.do_ack = 0;
filter->tcpopts.do_rst = 0;
filter->tcpopts.do_psh = 0;
filter->tcpopts.do_syn = 0;
filter->tcpopts.do_fin = 0;
filter->tcpopts.do_ece = 0;
filter->tcpopts.do_cwr = 0;
filter->udpopts.enabled = 0;
filter->udpopts.do_sport = 0;
filter->udpopts.do_dport = 0;
filter->icmpopts.enabled = 0;
filter->icmpopts.do_code = 0;
filter->icmpopts.do_type = 0;
}
}
@@ -161,19 +163,48 @@ int ReadCfg(config__t *cfg)
// Attempt to read the config.
if (config_read(&conf, file) == CONFIG_FALSE)
{
fprintf(stderr, "Error from LibConfig when reading file - %s (Line %d)\n\n", config_error_text(&conf), config_error_line(&conf));
LogMsg(cfg, 0, 1, "Error from LibConfig when reading file - %s (Line %d)", config_error_text(&conf), config_error_line(&conf));
config_destroy(&conf);
return EXIT_FAILURE;
}
int verbose;
if (config_lookup_int(&conf, "verbose", &verbose) == CONFIG_TRUE)
{
cfg->verbose = verbose;
}
const char* log_file;
if (config_lookup_string(&conf, "log_file", &log_file) == CONFIG_TRUE)
{
// We must free previous value to prevent memory leak.
if (cfg->log_file != NULL)
{
free(cfg->log_file);
cfg->log_file = NULL;
}
if (strlen(log_file) > 0)
{
cfg->log_file = strdup(log_file);
}
else
{
cfg->log_file = NULL;
}
}
// Get interface.
const char *interface;
if (!config_lookup_string(&conf, "interface", &interface))
{
fprintf(stderr, "Error from LibConfig when reading 'interface' setting - %s\n\n", config_error_text(&conf));
LogMsg(cfg, 0, 1, "Error from LibConfig when reading 'interface' setting - %s", config_error_text(&conf));
config_destroy(&conf);
@@ -212,7 +243,7 @@ int ReadCfg(config__t *cfg)
// Check if filters map is valid. If not, not a biggie since they aren't required.
if (setting == NULL)
{
fprintf(stderr, "Error from LibConfig when reading 'filters' array - %s\n\n", config_error_text(&conf));
LogMsg(cfg, 0, 1, "Error from LibConfig when reading 'filters' array - %s.", config_error_text(&conf));
config_destroy(&conf);
@@ -224,312 +255,318 @@ int ReadCfg(config__t *cfg)
for (int i = 0; i < config_setting_length(setting); i++)
{
config_setting_t* filter = config_setting_get_elem(setting, i);
filter_t* filter = &cfg->filters[i];
config_setting_t* filter_cfg = config_setting_get_elem(setting, i);
if (filter == NULL || filter_cfg == NULL)
{
LogMsg(cfg, 0, 1, "[WARNING] Failed to read filter rule at index #%d. 'filter' or 'filter_cfg' is NULL (make sure you didn't exceed the maximum filters allowed!)...");
continue;
}
// Enabled.
int enabled;
if (config_setting_lookup_bool(filter, "enabled", &enabled) == CONFIG_FALSE)
if (config_setting_lookup_bool(filter_cfg, "enabled", &enabled) == CONFIG_TRUE)
{
// Print error and stop from existing this rule any further.
fprintf(stderr, "Error from LibConfig when reading 'enabled' setting from filters array #%d. Error - %s\n\n", filters, config_error_text(&conf));
continue;
filter->enabled = enabled;
}
cfg->filters[i].enabled = enabled;
// Log.
int log;
if (config_setting_lookup_bool(filter_cfg, "log", &log) == CONFIG_TRUE)
{
filter->log = log;
}
// Action (required).
int action;
if (config_setting_lookup_int(filter, "action", &action) == CONFIG_FALSE)
if (config_setting_lookup_int(filter_cfg, "action", &action) == CONFIG_TRUE)
{
fprintf(stderr, "Error from LibConfig when reading 'action' setting from filters array #%d. Error - %s\n\n", filters, config_error_text(&conf));
cfg->filters[i].enabled = 0;
continue;
filter->action = action;
}
cfg->filters[i].action = action;
// Source IP (not required).
const char *sip;
if (config_setting_lookup_string(filter, "src_ip", &sip))
if (config_setting_lookup_string(filter_cfg, "src_ip", &sip) == CONFIG_TRUE)
{
ip_range_t ip = ParseIpCidr(sip);
cfg->filters[i].src_ip = ip.ip;
cfg->filters[i].src_cidr = ip.cidr;
filter->src_ip = ip.ip;
filter->src_cidr = ip.cidr;
}
// Destination IP (not required).
const char *dip;
if (config_setting_lookup_string(filter, "dst_ip", &dip))
if (config_setting_lookup_string(filter_cfg, "dst_ip", &dip) == CONFIG_TRUE)
{
ip_range_t ip = ParseIpCidr(dip);
cfg->filters[i].dst_ip = ip.ip;
cfg->filters[i].dst_cidr = ip.cidr;
filter->dst_ip = ip.ip;
filter->dst_cidr = ip.cidr;
}
// Source IP (IPv6) (not required).
const char *sip6;
if (config_setting_lookup_string(filter, "src_ip6", &sip6))
if (config_setting_lookup_string(filter_cfg, "src_ip6", &sip6) == CONFIG_TRUE)
{
struct in6_addr in;
inet_pton(AF_INET6, sip6, &in);
memcpy(cfg->filters[i].src_ip6, in.__in6_u.__u6_addr32, 4);
memcpy(filter->src_ip6, in.__in6_u.__u6_addr32, 4);
}
// Destination IP (IPv6) (not required).
const char *dip6;
if (config_setting_lookup_string(filter, "dst_ip6", &dip6))
if (config_setting_lookup_string(filter_cfg, "dst_ip6", &dip6) == CONFIG_TRUE)
{
struct in6_addr in;
inet_pton(AF_INET6, dip6, &in);
memcpy(cfg->filters[i].dst_ip6, in.__in6_u.__u6_addr32, 4);
memcpy(filter->dst_ip6, in.__in6_u.__u6_addr32, 4);
}
// Minimum TTL (not required).
int min_ttl;
if (config_setting_lookup_int(filter, "min_ttl", &min_ttl))
if (config_setting_lookup_int(filter_cfg, "min_ttl", &min_ttl) == CONFIG_TRUE)
{
cfg->filters[i].min_ttl = (u8)min_ttl;
cfg->filters[i].do_min_ttl = 1;
filter->min_ttl = (u8)min_ttl;
filter->do_min_ttl = 1;
}
// Maximum TTL (not required).
int max_ttl;
if (config_setting_lookup_int(filter, "max_ttl", &max_ttl))
if (config_setting_lookup_int(filter_cfg, "max_ttl", &max_ttl) == CONFIG_TRUE)
{
cfg->filters[i].max_ttl = (u8)max_ttl;
cfg->filters[i].do_max_ttl = 1;
filter->max_ttl = (u8)max_ttl;
filter->do_max_ttl = 1;
}
// Minimum length (not required).
int min_len;
if (config_setting_lookup_int(filter, "min_len", &min_len))
if (config_setting_lookup_int(filter_cfg, "min_len", &min_len) == CONFIG_TRUE)
{
cfg->filters[i].min_len = min_len;
cfg->filters[i].do_min_len = 1;
filter->min_len = min_len;
filter->do_min_len = 1;
}
// Maximum length (not required).
int max_len;
if (config_setting_lookup_int(filter, "max_len", &max_len))
if (config_setting_lookup_int(filter_cfg, "max_len", &max_len) == CONFIG_TRUE)
{
cfg->filters[i].max_len = max_len;
cfg->filters[i].do_max_len = 1;
filter->max_len = max_len;
filter->do_max_len = 1;
}
// TOS (not required).
int tos;
if (config_setting_lookup_int(filter, "tos", &tos))
if (config_setting_lookup_int(filter_cfg, "tos", &tos) == CONFIG_TRUE)
{
cfg->filters[i].tos = (u8)tos;
cfg->filters[i].do_tos = 1;
filter->tos = (u8)tos;
filter->do_tos = 1;
}
// PPS (not required).
long long pps;
if (config_setting_lookup_int64(filter, "pps", &pps))
if (config_setting_lookup_int64(filter_cfg, "pps", &pps) == CONFIG_TRUE)
{
cfg->filters[i].pps = pps;
cfg->filters[i].do_pps = 1;
filter->pps = pps;
filter->do_pps = 1;
}
// BPS (not required).
long long bps;
if (config_setting_lookup_int64(filter, "bps", &bps))
if (config_setting_lookup_int64(filter_cfg, "bps", &bps) == CONFIG_TRUE)
{
cfg->filters[i].bps = bps;
cfg->filters[i].do_bps = 1;
filter->bps = bps;
filter->do_bps = 1;
}
// Block time (default 1).
long long blocktime;
if (config_setting_lookup_int64(filter, "block_time", &blocktime))
if (config_setting_lookup_int64(filter_cfg, "block_time", &blocktime) == CONFIG_TRUE)
{
cfg->filters[i].blocktime = blocktime;
filter->blocktime = blocktime;
}
else
{
cfg->filters[i].blocktime = 1;
filter->blocktime = 1;
}
/* TCP options */
// Enabled.
int tcpenabled;
if (config_setting_lookup_bool(filter, "tcp_enabled", &tcpenabled))
if (config_setting_lookup_bool(filter_cfg, "tcp_enabled", &tcpenabled) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.enabled = tcpenabled;
filter->tcpopts.enabled = tcpenabled;
}
// Source port.
long long tcpsport;
if (config_setting_lookup_int64(filter, "tcp_sport", &tcpsport))
if (config_setting_lookup_int64(filter_cfg, "tcp_sport", &tcpsport) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.sport = (u16)tcpsport;
cfg->filters[i].tcpopts.do_sport = 1;
filter->tcpopts.sport = (u16)tcpsport;
filter->tcpopts.do_sport = 1;
}
// Destination port.
long long tcpdport;
if (config_setting_lookup_int64(filter, "tcp_dport", &tcpdport))
if (config_setting_lookup_int64(filter_cfg, "tcp_dport", &tcpdport) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.dport = (u16)tcpdport;
cfg->filters[i].tcpopts.do_dport = 1;
filter->tcpopts.dport = (u16)tcpdport;
filter->tcpopts.do_dport = 1;
}
// URG flag.
int tcpurg;
if (config_setting_lookup_bool(filter, "tcp_urg", &tcpurg))
if (config_setting_lookup_bool(filter_cfg, "tcp_urg", &tcpurg) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.urg = tcpurg;
cfg->filters[i].tcpopts.do_urg = 1;
filter->tcpopts.urg = tcpurg;
filter->tcpopts.do_urg = 1;
}
// ACK flag.
int tcpack;
if (config_setting_lookup_bool(filter, "tcp_ack", &tcpack))
if (config_setting_lookup_bool(filter_cfg, "tcp_ack", &tcpack) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.ack = tcpack;
cfg->filters[i].tcpopts.do_ack = 1;
filter->tcpopts.ack = tcpack;
filter->tcpopts.do_ack = 1;
}
// RST flag.
int tcprst;
if (config_setting_lookup_bool(filter, "tcp_rst", &tcprst))
if (config_setting_lookup_bool(filter_cfg, "tcp_rst", &tcprst) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.rst = tcprst;
cfg->filters[i].tcpopts.do_rst = 1;
filter->tcpopts.rst = tcprst;
filter->tcpopts.do_rst = 1;
}
// PSH flag.
int tcppsh;
if (config_setting_lookup_bool(filter, "tcp_psh", &tcppsh))
if (config_setting_lookup_bool(filter_cfg, "tcp_psh", &tcppsh) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.psh = tcppsh;
cfg->filters[i].tcpopts.do_psh = 1;
filter->tcpopts.psh = tcppsh;
filter->tcpopts.do_psh = 1;
}
// SYN flag.
int tcpsyn;
if (config_setting_lookup_bool(filter, "tcp_syn", &tcpsyn))
if (config_setting_lookup_bool(filter_cfg, "tcp_syn", &tcpsyn) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.syn = tcpsyn;
cfg->filters[i].tcpopts.do_syn = 1;
filter->tcpopts.syn = tcpsyn;
filter->tcpopts.do_syn = 1;
}
// FIN flag.
int tcpfin;
if (config_setting_lookup_bool(filter, "tcp_fin", &tcpfin))
if (config_setting_lookup_bool(filter_cfg, "tcp_fin", &tcpfin) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.fin = tcpfin;
cfg->filters[i].tcpopts.do_fin = 1;
filter->tcpopts.fin = tcpfin;
filter->tcpopts.do_fin = 1;
}
// ECE flag.
int tcpece;
if (config_setting_lookup_bool(filter, "tcp_ece", &tcpece))
if (config_setting_lookup_bool(filter_cfg, "tcp_ece", &tcpece) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.ece = tcpece;
cfg->filters[i].tcpopts.do_ece = 1;
filter->tcpopts.ece = tcpece;
filter->tcpopts.do_ece = 1;
}
// CWR flag.
int tcpcwr;
if (config_setting_lookup_bool(filter, "tcp_cwr", &tcpcwr))
if (config_setting_lookup_bool(filter_cfg, "tcp_cwr", &tcpcwr) == CONFIG_TRUE)
{
cfg->filters[i].tcpopts.cwr = tcpcwr;
cfg->filters[i].tcpopts.do_cwr = 1;
filter->tcpopts.cwr = tcpcwr;
filter->tcpopts.do_cwr = 1;
}
/* UDP options */
// Enabled.
int udpenabled;
if (config_setting_lookup_bool(filter, "udp_enabled", &udpenabled))
if (config_setting_lookup_bool(filter_cfg, "udp_enabled", &udpenabled) == CONFIG_TRUE)
{
cfg->filters[i].udpopts.enabled = udpenabled;
filter->udpopts.enabled = udpenabled;
}
// Source port.
long long udpsport;
if (config_setting_lookup_int64(filter, "udp_sport", &udpsport))
if (config_setting_lookup_int64(filter_cfg, "udp_sport", &udpsport) == CONFIG_TRUE)
{
cfg->filters[i].udpopts.sport = (u16)udpsport;
cfg->filters[i].udpopts.do_sport = 1;
filter->udpopts.sport = (u16)udpsport;
filter->udpopts.do_sport = 1;
}
// Destination port.
long long udpdport;
if (config_setting_lookup_int64(filter, "udp_dport", &udpdport))
if (config_setting_lookup_int64(filter_cfg, "udp_dport", &udpdport) == CONFIG_TRUE)
{
cfg->filters[i].udpopts.dport = (u16)udpdport;
cfg->filters[i].udpopts.do_dport = 1;
filter->udpopts.dport = (u16)udpdport;
filter->udpopts.do_dport = 1;
}
/* ICMP options */
// Enabled.
int icmpenabled;
if (config_setting_lookup_bool(filter, "icmp_enabled", &icmpenabled))
if (config_setting_lookup_bool(filter_cfg, "icmp_enabled", &icmpenabled) == CONFIG_TRUE)
{
cfg->filters[i].icmpopts.enabled = icmpenabled;
filter->icmpopts.enabled = icmpenabled;
}
// ICMP code.
int icmpcode;
if (config_setting_lookup_int(filter, "icmp_code", &icmpcode))
if (config_setting_lookup_int(filter_cfg, "icmp_code", &icmpcode) == CONFIG_TRUE)
{
cfg->filters[i].icmpopts.code = (u8)icmpcode;
cfg->filters[i].icmpopts.do_code = 1;
filter->icmpopts.code = (u8)icmpcode;
filter->icmpopts.do_code = 1;
}
// ICMP type.
int icmptype;
if (config_setting_lookup_int(filter, "icmp_type", &icmptype))
if (config_setting_lookup_int(filter_cfg, "icmp_type", &icmptype) == CONFIG_TRUE)
{
cfg->filters[i].icmpopts.type = (u8)icmptype;
cfg->filters[i].icmpopts.do_type = 1;
filter->icmpopts.type = (u8)icmptype;
filter->icmpopts.do_type = 1;
}
// Assign ID and increase filter count.
cfg->filters[i].id = ++filters;
filter->id = ++filters;
}
config_destroy(&conf);
@@ -568,6 +605,7 @@ void PrintConfig(config__t* cfg)
// Main.
fprintf(stdout, "\t\t\tID => %d\n", filter->id);
fprintf(stdout, "\t\t\tLog => %d\n", filter->log);
fprintf(stdout, "\t\t\tEnabled => %d\n", filter->enabled);
fprintf(stdout, "\t\t\tAction => %d (0 = Block, 1 = Allow).\n\n", filter->action);

View File

@@ -9,10 +9,14 @@
#include <arpa/inet.h>
#include <loader/utils/helpers.h>
#define CONFIG_DEFAULT_PATH "/etc/xdpfw/xdpfw.conf"
struct config
{
int verbose;
char *log_file;
char *interface;
u16 updatetime;
unsigned int nostats : 1;
@@ -26,3 +30,5 @@ void PrintConfig(config__t* cfg);
int OpenCfg(const char *filename);
int ReadCfg(config__t *cfg);
#include <loader/utils/logging.h>

View File

@@ -56,3 +56,44 @@ ip_range_t ParseIpCidr(const char *ip)
return ret;
}
/**
* Retrieves protocol name by ID.
*
* @param id The protocol ID
*
* @return The protocol string.
*/
const char* GetProtocolStrById(int id)
{
switch (id)
{
case IPPROTO_TCP:
return "TCP";
case IPPROTO_UDP:
return "UDP";
case IPPROTO_ICMP:
return "ICMP";
}
return "N/A";
}
/**
* Prints tool name and author.
*
* @return void
*/
void PrintToolInfo()
{
printf(
" __ ______ ____ _____ _ _ _ \n"
" \\ \\/ / _ \\| _ \\ | ___(_)_ __ _____ ____ _| | |\n"
" \\ /| | | | |_) | | |_ | | '__/ _ \\ \\ /\\ / / _` | | |\n"
" / \\| |_| | __/ | _| | | | | __/\\ V V / (_| | | |\n"
" /_/\\_\\____/|_| |_| |_|_| \\___| \\_/\\_/ \\__,_|_|_|\n"
"\n\n"
);
}

View File

@@ -18,3 +18,5 @@ extern int cont;
void PrintHelpMenu();
void SignalHndl(int code);
ip_range_t ParseIpCidr(const char* ip);
const char* GetProtocolStrById(int id);
void PrintToolInfo();

152
src/loader/utils/logging.c Normal file
View File

@@ -0,0 +1,152 @@
#include <loader/utils/logging.h>
/**
* Prints a log message to stdout/stderr along with a file if specified.
*
* @param req_lvl The required level for this message.
* @param cur_lvl The current verbose level.
* @param error If 1, sets pipe to stderr instead of stdout.
* @param msg The log message.
* @param args A VA list of arguments for the message.
*
* @return void
*/
static void LogMsgRaw(int req_lvl, int cur_lvl, int error, const char* log_path, const char* msg, va_list args)
{
if (cur_lvl < req_lvl)
{
return;
}
FILE* pipe = stdout;
if (error)
{
pipe = stderr;
}
// We need to format the message.
va_list args_copy;
va_copy(args_copy, args);
int len = vsnprintf(NULL, 0, msg, args_copy);
va_end(args_copy);
if (len < 0)
{
return;
}
char f_msg[len + 1];
vsnprintf(f_msg, sizeof(f_msg), msg, args);
char full_msg[len + 6 + 1];
snprintf(full_msg, sizeof(full_msg), "[%d] %s", req_lvl, f_msg);
// If we're calculating stats, we need to prepend a new line.
if (doing_stats)
{
fprintf(pipe, "\n%s\n", full_msg);
}
else
{
fprintf(pipe, "%s\n", full_msg);
}
if (log_path != NULL)
{
FILE* log_file = fopen(log_path, "a");
if (!log_file)
{
return;
}
time_t now = time(NULL);
struct tm* tm_val = localtime(&now);
if (!tm_val)
{
fclose(log_file);
return;
}
char log_file_msg[len + 22 + 1];
snprintf(log_file_msg, sizeof(log_file_msg), "[%02d-%02d-%02d %02d:%02d:%02d]%s", tm_val->tm_year % 100, tm_val->tm_mon + 1, tm_val->tm_mday,
tm_val->tm_hour, tm_val->tm_min, tm_val->tm_sec, full_msg);
fprintf(log_file, "%s\n", log_file_msg);
fclose(log_file);
}
}
/**
* Prints a log message using LogMsgRaw().
*
* @param cfg A pointer to the config structure.
* @param req_lvl The required level for this message.
* @param error Whether this is an error.
* @param msg The log message with format support.
*
* @return void
*/
void LogMsg(config__t* cfg, int req_lvl, int error, const char* msg, ...)
{
va_list args;
va_start(args, msg);
LogMsgRaw(req_lvl, cfg->verbose, error, (const char*)cfg->log_file, msg, args);
va_end(args);
}
/**
* Callback for BPF ringbuffer event (filter logging).
*
* @param ctx The context (should be config__t*).
* @param data The event data (should be filter_log_event_t*).
* @param sz The event data size.
*
* @return 0 on success or 1 on failure.
*/
int HandleRbEvent(void* ctx, void* data, size_t sz)
{
config__t* cfg = (config__t*)ctx;
filter_log_event_t* e = (filter_log_event_t*)data;
filter_t* filter = &cfg->filters[e->filter_id];
if (filter == NULL)
{
return 1;
}
char src_ip_str[INET6_ADDRSTRLEN];
char dst_ip_str[INET_ADDRSTRLEN];
if (memcmp(e->src_ip6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0)
{
inet_ntop(AF_INET6, e->src_ip6, src_ip_str, sizeof(src_ip_str));
inet_ntop(AF_INET6, e->dst_ip6, dst_ip_str, sizeof(dst_ip_str));
} else
{
inet_ntop(AF_INET, &e->src_ip, src_ip_str, sizeof(src_ip_str));
inet_ntop(AF_INET, &e->dst_ip, dst_ip_str, sizeof(dst_ip_str));
}
char* action = "Dropped";
if (filter->action == 1)
{
action = "Passed";
}
const char* protocol_str = GetProtocolStrById(e->protocol);
LogMsg(cfg, 0, 0, "[FILTER %d] %s %s packet '%s:%d' => '%s:%d' (PPS => %llu, BPS => %llu, Filter Block Time => %llu)...", e->filter_id, action, protocol_str, src_ip_str, htons(e->src_port), dst_ip_str, htons(e->dst_port), e->pps, e->bps, filter->blocktime);
return 0;
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <common/all.h>
#include <loader/utils/config.h>
#include <xdp/libxdp.h>
#define RB_TIMEOUT 100
extern int doing_stats;
void LogMsg(config__t* cfg, int req_lvl, int error, const char* msg, ...);
int HandleRbEvent(void* ctx, void* data, size_t sz);

View File

@@ -41,8 +41,11 @@ int CalculateStats(int stats_map, int cpus)
passed += stats[i].passed;
}
printf("\r\033[1;32mAllowed:\033[0m %llu | ", allowed);
printf("\033[1;31mDropped:\033[0m %llu | ", dropped);
printf("\033[1;34mPassed:\033[0m %llu", passed);
fflush(stdout);
fprintf(stdout, "\rAllowed: %llu | Dropped: %llu | Passed: %llu", allowed, dropped, passed);
return EXIT_SUCCESS;
}

View File

@@ -36,6 +36,35 @@ int FindMapFd(struct xdp_program *prog, const char *map_name)
return fd;
}
/**
* Custom print function for LibBPF that doesn't print anything (silent mode).
*
* @param level The current LibBPF log level.
* @param format The message format.
* @param args Format arguments for the message.
*
* @return void
*/
static int LibBPFSilent(enum libbpf_print_level level, const char *format, va_list args)
{
return 0;
}
/**
* Sets custom LibBPF log mode.
*
* @param silent If 1, disables LibBPF logging entirely.
*
* @return void
*/
void SetLibBPFLogMode(int silent)
{
if (silent)
{
libbpf_set_print(LibBPFSilent);
}
}
/**
* Loads a BPF object file.
*
@@ -60,31 +89,32 @@ struct xdp_program *LoadBpfObj(const char *file_name)
* Attempts to attach or detach (progfd = -1) a BPF/XDP program to an interface.
*
* @param prog A pointer to the XDP program structure.
* @param mode_used The mode being used.
* @param ifidx The index to the interface to attach to.
* @param detach If above 0, attempts to detach XDP program.
* @param cmd A pointer to a cmdline struct that includes command line arguments (mostly checking for offload/HW mode set).
*
* @return 0 on success and 1 on error.
*/
int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd)
int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, u8 detach, cmdline_t *cmd)
{
int err;
u32 mode = XDP_MODE_NATIVE;
char *smode;
u32 attach_mode = XDP_MODE_NATIVE;
smode = "DRV/native";
*mode = "DRV/native";
if (cmd->offload)
{
smode = "HW/offload";
*mode = "HW/offload";
mode = XDP_MODE_HW;
attach_mode = XDP_MODE_HW;
}
else if (cmd->skb)
{
smode = "SKB/generic";
mode = XDP_MODE_SKB;
*mode = "SKB/generic";
attach_mode = XDP_MODE_SKB;
}
int exit = 0;
@@ -96,39 +126,35 @@ int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd)
if (detach)
{
err = xdp_program__detach(prog, ifidx, mode, 0);
err = xdp_program__detach(prog, ifidx, attach_mode, 0);
}
else
{
err = xdp_program__attach(prog, ifidx, mode, 0);
err = xdp_program__attach(prog, ifidx, attach_mode, 0);
}
if (err)
{
if (err)
{
fprintf(stderr, "Could not attach with mode %s (%s) (%d).\n", smode, strerror(-err), -err);
}
// Decrease mode.
switch (mode)
switch (attach_mode)
{
case XDP_MODE_HW:
mode = XDP_MODE_NATIVE;
smode = "DRV/native";
attach_mode = XDP_MODE_NATIVE;
*mode = "DRV/native";
break;
case XDP_MODE_NATIVE:
mode = XDP_MODE_SKB;
smode = "SKB/generic";
attach_mode = XDP_MODE_SKB;
*mode = "SKB/generic";
break;
case XDP_MODE_SKB:
// Exit loop.
exit = 1;
smode = NULL;
*mode = NULL;
break;
}
@@ -142,16 +168,11 @@ int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd)
}
// If exit is set to 1 or smode is NULL, it indicates full failure.
if (exit || smode == NULL)
if (exit || *mode == NULL)
{
return EXIT_FAILURE;
}
if (detach < 1)
{
fprintf(stdout, "Loaded XDP program on mode %s...\n", smode);
}
return EXIT_SUCCESS;
}

View File

@@ -11,6 +11,7 @@
#define XDP_OBJ_PATH "/etc/xdpfw/xdp_prog.o"
int FindMapFd(struct xdp_program *prog, const char *map_name);
void SetLibBPFLogMode(int silent);
struct xdp_program *LoadBpfObj(const char *file_name);
int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd);
int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, u8 detach, cmdline_t *cmd);
void UpdateFilters(int filters_map, config__t *cfg);

View File

@@ -136,6 +136,11 @@ int xdp_prog_main(struct xdp_md *ctx)
struct icmp6hdr *icmp6h = NULL;
u16 src_port = 0;
#ifdef ENABLE_FILTER_LOGGING
u16 dst_port = 0;
#endif
u8 protocol = 0;
if (iph6)
@@ -156,6 +161,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = tcph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = tcph->dest;
#endif
break;
case IPPROTO_UDP:
@@ -170,6 +179,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = udph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = udph->dest;
#endif
break;
case IPPROTO_ICMPV6:
@@ -203,6 +216,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = tcph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = tcph->dest;
#endif
break;
case IPPROTO_UDP:
@@ -217,6 +234,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = udph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = udph->dest;
#endif
break;
case IPPROTO_ICMP:
@@ -514,6 +535,39 @@ int xdp_prog_main(struct xdp_md *ctx)
}
}
#ifdef ENABLE_FILTER_LOGGING
if (filter->log > 0)
{
filter_log_event_t* e = bpf_ringbuf_reserve(&filter_log_map, sizeof(*e), 0);
if (e)
{
e->ts = now;
e->filter_id = i;
if (iph)
{
e->src_ip = iph->saddr;
e->dst_ip = iph->daddr;
} else if (iph6)
{
memcpy(&e->src_ip6, iph6->saddr.in6_u.u6_addr32, 4);
memcpy(&e->dst_ip6, iph6->daddr.in6_u.u6_addr32, 4);
}
e->src_port = src_port;
e->dst_port = dst_port;
e->protocol = protocol;
e->pps = pps;
e->bps = bps;
bpf_ringbuf_submit(e, 0);
}
}
#endif
// Matched.
action = filter->action;
blocktime = filter->blocktime;

View File

@@ -60,3 +60,11 @@ struct
__type(key, u128);
__type(value, u64);
} ip6_blacklist_map SEC(".maps");
#ifdef ENABLE_FILTER_LOGGING
struct
{
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 16);
} filter_log_map SEC(".maps");
#endif