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

3
.gitignore vendored
View File

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

View File

@@ -13,6 +13,7 @@ LOADER_DIR = $(SRC_DIR)/loader
XDP_DIR = $(SRC_DIR)/xdp XDP_DIR = $(SRC_DIR)/xdp
ETC_DIR = /etc/xdpfw ETC_DIR = /etc/xdpfw
LOG_DIR = /var/log/xdpfw
# Additional build directories. # Additional build directories.
BUILD_LOADER_DIR = $(BUILD_DIR)/loader 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_SRC = xdp.c
LOADER_UTILS_XDP_OBJ = xdp.o 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_SRC = stats.c
LOADER_UTILS_STATS_OBJ = stats.o LOADER_UTILS_STATS_OBJ = stats.o
@@ -59,7 +63,7 @@ LOADER_UTILS_HELPERS_SRC = helpers.c
LOADER_UTILS_HELPERS_OBJ = helpers.o LOADER_UTILS_HELPERS_OBJ = helpers.o
# Loader objects. # 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) ifeq ($(LIBXDP_STATIC), 1)
LOADER_OBJS := $(LIBBPF_OBJS) $(LIBXDP_OBJS) $(LOADER_OBJS) LOADER_OBJS := $(LIBBPF_OBJS) $(LIBXDP_OBJS) $(LOADER_OBJS)
@@ -93,7 +97,7 @@ all: loader xdp
loader: loader_utils loader: loader_utils
$(CC) $(INCS) $(FLAGS) $(FLAGS_LOADER) -o $(BUILD_LOADER_DIR)/$(LOADER_OUT) $(LOADER_OBJS) $(LOADER_DIR)/$(LOADER_SRC) $(CC) $(INCS) $(FLAGS) $(FLAGS_LOADER) -o $(BUILD_LOADER_DIR)/$(LOADER_OUT) $(LOADER_OBJS) $(LOADER_DIR)/$(LOADER_SRC)
loader_utils: loader_utils_config loader_utils_cmdline loader_utils_helpers loader_utils_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: loader_utils_config:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_CONFIG_SRC) $(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_CONFIG_SRC)
@@ -104,6 +108,9 @@ loader_utils_cmdline:
loader_utils_xdp: loader_utils_xdp:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_XDP_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_XDP_SRC) $(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: loader_utils_stats:
$(CC) $(INCS) $(FLAGS) -c -o $(BUILD_LOADER_DIR)/$(LOADER_UTILS_STATS_OBJ) $(LOADER_UTILS_DIR)/$(LOADER_UTILS_STATS_SRC) $(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: install:
mkdir -p $(ETC_DIR) mkdir -p $(ETC_DIR)
mkdir -p $(LOG_DIR)
cp -n xdpfw.conf.example $(ETC_DIR)/xdpfw.conf cp -n xdpfw.conf.example $(ETC_DIR)/xdpfw.conf
cp -n other/xdpfw.service /etc/systemd/system/ 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 🙂 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 ## 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. 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.
@@ -59,7 +59,9 @@ Additionally, here is a list of flags you may pass to this script.
| --no-install | Build the tool and/or LibXDP without installing them. | | --no-install | Build the tool and/or LibXDP without installing them. |
| --clean | Remove build files for the tool and LibXDP. | | --clean | Remove build files for the tool and LibXDP. |
| --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. | | --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. | | --help | Displays help message. |
![Script Build Demo](./images/build_script.gif)
### Without Bash Script ### 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. 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 sudo make install
``` ```
![Script Build Demo](./images/build_make.gif)
## Command Line Usage ## Command Line Usage
The following command line arguments are supported. The following command line arguments are supported.
@@ -118,6 +122,8 @@ The following table quickly explains the data types used within the configuratio
### Main ### Main
| Name | Type | Default | Description | | 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`). | | 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). | | 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. | | 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 ### Filter Object
| Name | Type | Default | Description | | Name | Type | Default | Description |
| ---- | ---- | ------- | ----------- | | ---- | ---- | ------- | ----------- |
| enabled | bool | `false` | Whether the rule is enabled or not. | | enabled | bool | `true` | 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. | | 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. | | 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`)! | | 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`)! | | 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 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 ## 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! 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

@@ -20,4 +20,8 @@
// If uncommented, rate limits for clients are determined using the source IP, port, and protocol instead of just the source IP. // 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). // 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. // 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 #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; u8 id;
unsigned int log : 1;
unsigned int enabled : 1; unsigned int enabled : 1;
u8 action; u8 action;
@@ -131,4 +133,25 @@ struct flow6
u128 ip; u128 ip;
u16 port; u16 port;
u8 protocol; u8 protocol;
} typedef flow6_t; } 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/cmdline.h>
#include <loader/utils/config.h> #include <loader/utils/config.h>
#include <loader/utils/xdp.h> #include <loader/utils/xdp.h>
#include <loader/utils/logging.h>
#include <loader/utils/stats.h> #include <loader/utils/stats.h>
#include <loader/utils/helpers.h> #include <loader/utils/helpers.h>
int cont = 1; int cont = 1;
int doing_stats = 0;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@@ -37,16 +39,6 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS; 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. // Initialize config.
config__t cfg = {0}; config__t cfg = {0};
@@ -68,54 +60,119 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS; 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. // Get interface index.
int ifidx = if_nametoindex(cfg.interface); int ifidx = if_nametoindex(cfg.interface);
if (ifidx < 0) 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; 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. // Load BPF object.
struct xdp_program *prog = LoadBpfObj(XDP_OBJ_PATH); struct xdp_program *prog = LoadBpfObj(XDP_OBJ_PATH);
if (prog == NULL) 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; return EXIT_FAILURE;
} }
LogMsg(&cfg, 2, 0, "Attaching XDP program to interface '%s'...", cfg.interface);
// Attach XDP program. // 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; 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. // Retrieve BPF maps.
int filters_map = FindMapFd(prog, "filters_map"); int filters_map = FindMapFd(prog, "filters_map");
// Check for valid maps. // Check for valid maps.
if (filters_map < 0) 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; return EXIT_FAILURE;
} }
LogMsg(&cfg, 3, 0, "filters_map FD => %d.", filters_map);
int stats_map = FindMapFd(prog, "stats_map"); int stats_map = FindMapFd(prog, "stats_map");
if (stats_map < 0) 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; 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. // Update BPF maps.
UpdateFilters(filters_map, &cfg); UpdateFilters(filters_map, &cfg);
@@ -126,6 +183,8 @@ int main(int argc, char *argv[])
// Receive CPU count for stats map parsing. // Receive CPU count for stats map parsing.
int cpus = get_nprocs_conf(); 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; unsigned int end_time = (cmd.time > 0) ? time(NULL) + cmd.time : 0;
// Create last updated variables. // Create last updated variables.
@@ -136,6 +195,12 @@ int main(int argc, char *argv[])
struct stat conf_stat; struct stat conf_stat;
// Check if we're doing stats.
if (!cfg.nostats)
{
doing_stats = 1;
}
while (cont) while (cont)
{ {
// Get current time. // Get current time.
@@ -159,7 +224,7 @@ int main(int argc, char *argv[])
// Update config. // Update config.
if ((ret = LoadConfig(&cfg, cmd.cfgfile)) != 0) 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. // Update BPF maps.
@@ -167,6 +232,12 @@ int main(int argc, char *argv[])
// Update timer // Update timer
last_config_check = time(NULL); 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. // Update last updated variable.
@@ -178,24 +249,38 @@ int main(int argc, char *argv[])
{ {
if (CalculateStats(stats_map, cpus)) 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); usleep(sleep_time);
} }
fprintf(stdout, "\n"); fprintf(stdout, "\n");
// Detach XDP program. #ifdef ENABLE_FILTER_LOGGING
if (AttachXdp(prog, ifidx, 1, &cmd)) 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; return EXIT_FAILURE;
} }
fprintf(stdout, "Cleaned up and exiting...\n"); LogMsg(&cfg, 1, 0, "Cleaned up and exiting...\n");
// Exit program successfully. // Exit program successfully.
return EXIT_SUCCESS; return EXIT_SUCCESS;

View File

@@ -1,7 +1,5 @@
#include <loader/utils/config.h> #include <loader/utils/config.h>
#include <loader/utils/helpers.h>
static FILE *file; static FILE *file;
/** /**
@@ -46,6 +44,8 @@ int LoadConfig(config__t *cfg, char *cfg_file)
*/ */
void SetCfgDefaults(config__t *cfg) void SetCfgDefaults(config__t *cfg)
{ {
cfg->verbose = 2;
cfg->log_file = strdup("/var/log/xdpfw/xdpfw.log");
cfg->updatetime = 0; cfg->updatetime = 0;
cfg->interface = NULL; cfg->interface = NULL;
cfg->nostats = 0; cfg->nostats = 0;
@@ -53,60 +53,62 @@ void SetCfgDefaults(config__t *cfg)
for (int i = 0; i < MAX_FILTERS; i++) for (int i = 0; i < MAX_FILTERS; i++)
{ {
cfg->filters[i].id = 0; filter_t* filter = &cfg->filters[i];
cfg->filters[i].enabled = 0;
cfg->filters[i].action = 0;
cfg->filters[i].src_ip = 0;
cfg->filters[i].dst_ip = 0;
for (int j = 0; j < 4; j++) filter->id = 0;
{ filter->enabled = 1;
cfg->filters[i].src_ip6[j] = 0;
cfg->filters[i].dst_ip6[j] = 0;
}
cfg->filters[i].do_min_len = 0; filter->log = 0;
cfg->filters[i].min_len = 0;
cfg->filters[i].do_max_len = 0; filter->action = 1;
cfg->filters[i].max_len = 65535; filter->src_ip = 0;
filter->dst_ip = 0;
cfg->filters[i].do_min_ttl = 0; memset(filter->src_ip6, 0, 4);
cfg->filters[i].min_ttl = 0; memset(filter->dst_ip6, 0, 4);
cfg->filters[i].do_max_ttl = 0; filter->do_min_len = 0;
cfg->filters[i].max_ttl = 255; filter->min_len = 0;
cfg->filters[i].do_tos = 0; filter->do_max_len = 0;
cfg->filters[i].tos = 0; filter->max_len = 65535;
cfg->filters[i].do_pps = 0; filter->do_min_ttl = 0;
cfg->filters[i].pps = 0; filter->min_ttl = 0;
filter->do_max_ttl = 0;
filter->max_ttl = 255;
filter->do_tos = 0;
filter->tos = 0;
filter->do_pps = 0;
filter->pps = 0;
cfg->filters[i].do_bps = 0; filter->do_bps = 0;
cfg->filters[i].bps = 0; filter->bps = 0;
cfg->filters[i].blocktime = 1; filter->blocktime = 1;
cfg->filters[i].tcpopts.enabled = 0; filter->tcpopts.enabled = 0;
cfg->filters[i].tcpopts.do_dport = 0; filter->tcpopts.do_dport = 0;
cfg->filters[i].tcpopts.do_dport = 0; filter->tcpopts.do_dport = 0;
cfg->filters[i].tcpopts.do_urg = 0; filter->tcpopts.do_urg = 0;
cfg->filters[i].tcpopts.do_ack = 0; filter->tcpopts.do_ack = 0;
cfg->filters[i].tcpopts.do_rst = 0; filter->tcpopts.do_rst = 0;
cfg->filters[i].tcpopts.do_psh = 0; filter->tcpopts.do_psh = 0;
cfg->filters[i].tcpopts.do_syn = 0; filter->tcpopts.do_syn = 0;
cfg->filters[i].tcpopts.do_fin = 0; filter->tcpopts.do_fin = 0;
cfg->filters[i].tcpopts.do_ece = 0; filter->tcpopts.do_ece = 0;
cfg->filters[i].tcpopts.do_cwr = 0; filter->tcpopts.do_cwr = 0;
cfg->filters[i].udpopts.enabled = 0; filter->udpopts.enabled = 0;
cfg->filters[i].udpopts.do_sport = 0; filter->udpopts.do_sport = 0;
cfg->filters[i].udpopts.do_dport = 0; filter->udpopts.do_dport = 0;
cfg->filters[i].icmpopts.enabled = 0; filter->icmpopts.enabled = 0;
cfg->filters[i].icmpopts.do_code = 0; filter->icmpopts.do_code = 0;
cfg->filters[i].icmpopts.do_type = 0; filter->icmpopts.do_type = 0;
} }
} }
@@ -161,19 +163,48 @@ int ReadCfg(config__t *cfg)
// Attempt to read the config. // Attempt to read the config.
if (config_read(&conf, file) == CONFIG_FALSE) 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); config_destroy(&conf);
return EXIT_FAILURE; 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. // Get interface.
const char *interface; const char *interface;
if (!config_lookup_string(&conf, "interface", &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); 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. // Check if filters map is valid. If not, not a biggie since they aren't required.
if (setting == NULL) 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); config_destroy(&conf);
@@ -224,312 +255,318 @@ int ReadCfg(config__t *cfg)
for (int i = 0; i < config_setting_length(setting); i++) 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. // Enabled.
int 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. filter->enabled = enabled;
fprintf(stderr, "Error from LibConfig when reading 'enabled' setting from filters array #%d. Error - %s\n\n", filters, config_error_text(&conf));
continue;
} }
cfg->filters[i].enabled = enabled; // Log.
int log;
if (config_setting_lookup_bool(filter_cfg, "log", &log) == CONFIG_TRUE)
{
filter->log = log;
}
// Action (required). // Action (required).
int action; 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)); filter->action = action;
cfg->filters[i].enabled = 0;
continue;
} }
cfg->filters[i].action = action;
// Source IP (not required). // Source IP (not required).
const char *sip; 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); ip_range_t ip = ParseIpCidr(sip);
cfg->filters[i].src_ip = ip.ip; filter->src_ip = ip.ip;
cfg->filters[i].src_cidr = ip.cidr; filter->src_cidr = ip.cidr;
} }
// Destination IP (not required). // Destination IP (not required).
const char *dip; 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); ip_range_t ip = ParseIpCidr(dip);
cfg->filters[i].dst_ip = ip.ip; filter->dst_ip = ip.ip;
cfg->filters[i].dst_cidr = ip.cidr; filter->dst_cidr = ip.cidr;
} }
// Source IP (IPv6) (not required). // Source IP (IPv6) (not required).
const char *sip6; 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; struct in6_addr in;
inet_pton(AF_INET6, sip6, &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). // Destination IP (IPv6) (not required).
const char *dip6; 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; struct in6_addr in;
inet_pton(AF_INET6, dip6, &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). // Minimum TTL (not required).
int min_ttl; 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; filter->min_ttl = (u8)min_ttl;
cfg->filters[i].do_min_ttl = 1; filter->do_min_ttl = 1;
} }
// Maximum TTL (not required). // Maximum TTL (not required).
int max_ttl; 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; filter->max_ttl = (u8)max_ttl;
cfg->filters[i].do_max_ttl = 1; filter->do_max_ttl = 1;
} }
// Minimum length (not required). // Minimum length (not required).
int min_len; 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; filter->min_len = min_len;
cfg->filters[i].do_min_len = 1; filter->do_min_len = 1;
} }
// Maximum length (not required). // Maximum length (not required).
int max_len; 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; filter->max_len = max_len;
cfg->filters[i].do_max_len = 1; filter->do_max_len = 1;
} }
// TOS (not required). // TOS (not required).
int tos; 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; filter->tos = (u8)tos;
cfg->filters[i].do_tos = 1; filter->do_tos = 1;
} }
// PPS (not required). // PPS (not required).
long long pps; 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; filter->pps = pps;
cfg->filters[i].do_pps = 1; filter->do_pps = 1;
} }
// BPS (not required). // BPS (not required).
long long bps; 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; filter->bps = bps;
cfg->filters[i].do_bps = 1; filter->do_bps = 1;
} }
// Block time (default 1). // Block time (default 1).
long long blocktime; 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 else
{ {
cfg->filters[i].blocktime = 1; filter->blocktime = 1;
} }
/* TCP options */ /* TCP options */
// Enabled. // Enabled.
int tcpenabled; 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. // Source port.
long long tcpsport; 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; filter->tcpopts.sport = (u16)tcpsport;
cfg->filters[i].tcpopts.do_sport = 1; filter->tcpopts.do_sport = 1;
} }
// Destination port. // Destination port.
long long tcpdport; 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; filter->tcpopts.dport = (u16)tcpdport;
cfg->filters[i].tcpopts.do_dport = 1; filter->tcpopts.do_dport = 1;
} }
// URG flag. // URG flag.
int tcpurg; 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; filter->tcpopts.urg = tcpurg;
cfg->filters[i].tcpopts.do_urg = 1; filter->tcpopts.do_urg = 1;
} }
// ACK flag. // ACK flag.
int tcpack; 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; filter->tcpopts.ack = tcpack;
cfg->filters[i].tcpopts.do_ack = 1; filter->tcpopts.do_ack = 1;
} }
// RST flag. // RST flag.
int tcprst; 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; filter->tcpopts.rst = tcprst;
cfg->filters[i].tcpopts.do_rst = 1; filter->tcpopts.do_rst = 1;
} }
// PSH flag. // PSH flag.
int tcppsh; 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; filter->tcpopts.psh = tcppsh;
cfg->filters[i].tcpopts.do_psh = 1; filter->tcpopts.do_psh = 1;
} }
// SYN flag. // SYN flag.
int tcpsyn; 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; filter->tcpopts.syn = tcpsyn;
cfg->filters[i].tcpopts.do_syn = 1; filter->tcpopts.do_syn = 1;
} }
// FIN flag. // FIN flag.
int tcpfin; 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; filter->tcpopts.fin = tcpfin;
cfg->filters[i].tcpopts.do_fin = 1; filter->tcpopts.do_fin = 1;
} }
// ECE flag. // ECE flag.
int tcpece; 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; filter->tcpopts.ece = tcpece;
cfg->filters[i].tcpopts.do_ece = 1; filter->tcpopts.do_ece = 1;
} }
// CWR flag. // CWR flag.
int tcpcwr; 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; filter->tcpopts.cwr = tcpcwr;
cfg->filters[i].tcpopts.do_cwr = 1; filter->tcpopts.do_cwr = 1;
} }
/* UDP options */ /* UDP options */
// Enabled. // Enabled.
int udpenabled; 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. // Source port.
long long udpsport; 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; filter->udpopts.sport = (u16)udpsport;
cfg->filters[i].udpopts.do_sport = 1; filter->udpopts.do_sport = 1;
} }
// Destination port. // Destination port.
long long udpdport; 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; filter->udpopts.dport = (u16)udpdport;
cfg->filters[i].udpopts.do_dport = 1; filter->udpopts.do_dport = 1;
} }
/* ICMP options */ /* ICMP options */
// Enabled. // Enabled.
int icmpenabled; 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. // ICMP code.
int icmpcode; 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; filter->icmpopts.code = (u8)icmpcode;
cfg->filters[i].icmpopts.do_code = 1; filter->icmpopts.do_code = 1;
} }
// ICMP type. // ICMP type.
int icmptype; 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; filter->icmpopts.type = (u8)icmptype;
cfg->filters[i].icmpopts.do_type = 1; filter->icmpopts.do_type = 1;
} }
// Assign ID and increase filter count. // Assign ID and increase filter count.
cfg->filters[i].id = ++filters; filter->id = ++filters;
} }
config_destroy(&conf); config_destroy(&conf);
@@ -568,6 +605,7 @@ void PrintConfig(config__t* cfg)
// Main. // Main.
fprintf(stdout, "\t\t\tID => %d\n", filter->id); 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\tEnabled => %d\n", filter->enabled);
fprintf(stdout, "\t\t\tAction => %d (0 = Block, 1 = Allow).\n\n", filter->action); 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 <arpa/inet.h>
#include <loader/utils/helpers.h>
#define CONFIG_DEFAULT_PATH "/etc/xdpfw/xdpfw.conf" #define CONFIG_DEFAULT_PATH "/etc/xdpfw/xdpfw.conf"
struct config struct config
{ {
int verbose;
char *log_file;
char *interface; char *interface;
u16 updatetime; u16 updatetime;
unsigned int nostats : 1; unsigned int nostats : 1;
@@ -25,4 +29,6 @@ void SetCfgDefaults(config__t *cfg);
void PrintConfig(config__t* cfg); void PrintConfig(config__t* cfg);
int OpenCfg(const char *filename); int OpenCfg(const char *filename);
int ReadCfg(config__t *cfg); int ReadCfg(config__t *cfg);
#include <loader/utils/logging.h>

View File

@@ -55,4 +55,45 @@ ip_range_t ParseIpCidr(const char *ip)
} }
return ret; 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

@@ -17,4 +17,6 @@ extern int cont;
void PrintHelpMenu(); void PrintHelpMenu();
void SignalHndl(int code); void SignalHndl(int code);
ip_range_t ParseIpCidr(const char* ip); 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

@@ -40,9 +40,12 @@ int CalculateStats(int stats_map, int cpus)
dropped += stats[i].dropped; dropped += stats[i].dropped;
passed += stats[i].passed; 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); fflush(stdout);
fprintf(stdout, "\rAllowed: %llu | Dropped: %llu | Passed: %llu", allowed, dropped, passed);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@@ -36,6 +36,35 @@ int FindMapFd(struct xdp_program *prog, const char *map_name)
return fd; 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. * 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. * Attempts to attach or detach (progfd = -1) a BPF/XDP program to an interface.
* *
* @param prog A pointer to the XDP program structure. * @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 ifidx The index to the interface to attach to.
* @param detach If above 0, attempts to detach XDP program. * @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). * @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. * @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; int err;
u32 mode = XDP_MODE_NATIVE; u32 attach_mode = XDP_MODE_NATIVE;
char *smode;
smode = "DRV/native"; *mode = "DRV/native";
if (cmd->offload) if (cmd->offload)
{ {
smode = "HW/offload"; *mode = "HW/offload";
mode = XDP_MODE_HW; attach_mode = XDP_MODE_HW;
} }
else if (cmd->skb) else if (cmd->skb)
{ {
smode = "SKB/generic"; *mode = "SKB/generic";
mode = XDP_MODE_SKB;
attach_mode = XDP_MODE_SKB;
} }
int exit = 0; int exit = 0;
@@ -96,39 +126,35 @@ int AttachXdp(struct xdp_program *prog, int ifidx, u8 detach, cmdline_t *cmd)
if (detach) if (detach)
{ {
err = xdp_program__detach(prog, ifidx, mode, 0); err = xdp_program__detach(prog, ifidx, attach_mode, 0);
} }
else else
{ {
err = xdp_program__attach(prog, ifidx, mode, 0); err = xdp_program__attach(prog, ifidx, attach_mode, 0);
} }
if (err) if (err)
{ {
if (err)
{
fprintf(stderr, "Could not attach with mode %s (%s) (%d).\n", smode, strerror(-err), -err);
}
// Decrease mode. // Decrease mode.
switch (mode) switch (attach_mode)
{ {
case XDP_MODE_HW: case XDP_MODE_HW:
mode = XDP_MODE_NATIVE; attach_mode = XDP_MODE_NATIVE;
smode = "DRV/native"; *mode = "DRV/native";
break; break;
case XDP_MODE_NATIVE: case XDP_MODE_NATIVE:
mode = XDP_MODE_SKB; attach_mode = XDP_MODE_SKB;
smode = "SKB/generic"; *mode = "SKB/generic";
break; break;
case XDP_MODE_SKB: case XDP_MODE_SKB:
// Exit loop. // Exit loop.
exit = 1; exit = 1;
smode = NULL;
*mode = NULL;
break; 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 is set to 1 or smode is NULL, it indicates full failure.
if (exit || smode == NULL) if (exit || *mode == NULL)
{ {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (detach < 1)
{
fprintf(stdout, "Loaded XDP program on mode %s...\n", smode);
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@@ -11,6 +11,7 @@
#define XDP_OBJ_PATH "/etc/xdpfw/xdp_prog.o" #define XDP_OBJ_PATH "/etc/xdpfw/xdp_prog.o"
int FindMapFd(struct xdp_program *prog, const char *map_name); int FindMapFd(struct xdp_program *prog, const char *map_name);
void SetLibBPFLogMode(int silent);
struct xdp_program *LoadBpfObj(const char *file_name); 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); 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; struct icmp6hdr *icmp6h = NULL;
u16 src_port = 0; u16 src_port = 0;
#ifdef ENABLE_FILTER_LOGGING
u16 dst_port = 0;
#endif
u8 protocol = 0; u8 protocol = 0;
if (iph6) if (iph6)
@@ -156,6 +161,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = tcph->source; src_port = tcph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = tcph->dest;
#endif
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
@@ -170,6 +179,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = udph->source; src_port = udph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = udph->dest;
#endif
break; break;
case IPPROTO_ICMPV6: case IPPROTO_ICMPV6:
@@ -203,6 +216,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = tcph->source; src_port = tcph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = tcph->dest;
#endif
break; break;
case IPPROTO_UDP: case IPPROTO_UDP:
@@ -217,6 +234,10 @@ int xdp_prog_main(struct xdp_md *ctx)
src_port = udph->source; src_port = udph->source;
#ifdef ENABLE_FILTER_LOGGING
dst_port = udph->dest;
#endif
break; break;
case IPPROTO_ICMP: case IPPROTO_ICMP:
@@ -513,6 +534,39 @@ int xdp_prog_main(struct xdp_md *ctx)
continue; continue;
} }
} }
#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. // Matched.
action = filter->action; action = filter->action;

View File

@@ -59,4 +59,12 @@ struct
__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");
#ifdef ENABLE_FILTER_LOGGING
struct
{
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 16);
} filter_log_map SEC(".maps");
#endif