diff --git a/.gitignore b/.gitignore index 36e04aa..1613ae7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode/ xdpfw xdpfw.s -xdpfw.conf \ No newline at end of file +xdpfw.conf +*.log \ No newline at end of file diff --git a/Makefile b/Makefile index e2701c2..6f6ac8a 100644 --- a/Makefile +++ b/Makefile @@ -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/ diff --git a/README.md b/README.md index cd278a2..5a451dc 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. | | --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. | -| --help | Displays help message. | +| --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! diff --git a/images/build_make.gif b/images/build_make.gif new file mode 100644 index 0000000..e51069f Binary files /dev/null and b/images/build_make.gif differ diff --git a/images/build_script.gif b/images/build_script.gif new file mode 100644 index 0000000..e091a4e Binary files /dev/null and b/images/build_script.gif differ diff --git a/images/demo.gif b/images/demo.gif deleted file mode 100644 index 5ac555d..0000000 Binary files a/images/demo.gif and /dev/null differ diff --git a/images/run.gif b/images/run.gif new file mode 100644 index 0000000..7d17f10 Binary files /dev/null and b/images/run.gif differ diff --git a/src/common/config.h b/src/common/config.h index 0140b41..e5bd1d3 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -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. // This allows for more precise rate limits (connection-specific instead of a single source IP). // I decided not to include the destination IP/port because the source IP, port, and protocol should be represent a unique connection. -#define USE_FLOW_RL \ No newline at end of file +#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 \ No newline at end of file diff --git a/src/common/types.h b/src/common/types.h index da727ee..722dfed 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -64,6 +64,8 @@ struct filter { u8 id; + unsigned int log : 1; + unsigned int enabled : 1; u8 action; @@ -131,4 +133,25 @@ struct flow6 u128 ip; u16 port; u8 protocol; -} typedef flow6_t; \ No newline at end of file +} 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; \ No newline at end of file diff --git a/src/loader/prog.c b/src/loader/prog.c index 8009134..c5628fc 100644 --- a/src/loader/prog.c +++ b/src/loader/prog.c @@ -14,10 +14,12 @@ #include #include #include +#include #include #include 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; diff --git a/src/loader/utils/config.c b/src/loader/utils/config.c index b454d03..8288bc1 100644 --- a/src/loader/utils/config.c +++ b/src/loader/utils/config.c @@ -1,7 +1,5 @@ #include -#include - 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; + + 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; - cfg->filters[i].bps = 0; + filter->do_bps = 0; + filter->bps = 0; - cfg->filters[i].blocktime = 1; + filter->blocktime = 1; - 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->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; - cfg->filters[i].udpopts.enabled = 0; - cfg->filters[i].udpopts.do_sport = 0; - cfg->filters[i].udpopts.do_dport = 0; + filter->udpopts.enabled = 0; + filter->udpopts.do_sport = 0; + filter->udpopts.do_dport = 0; - cfg->filters[i].icmpopts.enabled = 0; - cfg->filters[i].icmpopts.do_code = 0; - cfg->filters[i].icmpopts.do_type = 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); diff --git a/src/loader/utils/config.h b/src/loader/utils/config.h index 72c169d..6b4ba6c 100644 --- a/src/loader/utils/config.h +++ b/src/loader/utils/config.h @@ -9,10 +9,14 @@ #include +#include + #define CONFIG_DEFAULT_PATH "/etc/xdpfw/xdpfw.conf" struct config { + int verbose; + char *log_file; char *interface; u16 updatetime; unsigned int nostats : 1; @@ -25,4 +29,6 @@ void SetCfgDefaults(config__t *cfg); void PrintConfig(config__t* cfg); int OpenCfg(const char *filename); -int ReadCfg(config__t *cfg); \ No newline at end of file +int ReadCfg(config__t *cfg); + +#include \ No newline at end of file diff --git a/src/loader/utils/helpers.c b/src/loader/utils/helpers.c index 69d97e5..e2f1d80 100644 --- a/src/loader/utils/helpers.c +++ b/src/loader/utils/helpers.c @@ -55,4 +55,45 @@ 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" + ); } \ No newline at end of file diff --git a/src/loader/utils/helpers.h b/src/loader/utils/helpers.h index 8dbf165..68b67bd 100644 --- a/src/loader/utils/helpers.h +++ b/src/loader/utils/helpers.h @@ -17,4 +17,6 @@ extern int cont; void PrintHelpMenu(); void SignalHndl(int code); -ip_range_t ParseIpCidr(const char* ip); \ No newline at end of file +ip_range_t ParseIpCidr(const char* ip); +const char* GetProtocolStrById(int id); +void PrintToolInfo(); \ No newline at end of file diff --git a/src/loader/utils/logging.c b/src/loader/utils/logging.c new file mode 100644 index 0000000..f27402c --- /dev/null +++ b/src/loader/utils/logging.c @@ -0,0 +1,152 @@ +#include + +/** + * 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; +} \ No newline at end of file diff --git a/src/loader/utils/logging.h b/src/loader/utils/logging.h new file mode 100644 index 0000000..beabf54 --- /dev/null +++ b/src/loader/utils/logging.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +#include + +#include + +#include + +#include + +#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); \ No newline at end of file diff --git a/src/loader/utils/stats.c b/src/loader/utils/stats.c index e19dc8c..f317a7e 100644 --- a/src/loader/utils/stats.c +++ b/src/loader/utils/stats.c @@ -40,9 +40,12 @@ int CalculateStats(int stats_map, int cpus) dropped += stats[i].dropped; 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); + fflush(stdout); return EXIT_SUCCESS; } \ No newline at end of file diff --git a/src/loader/utils/xdp.c b/src/loader/utils/xdp.c index 4bc51cc..a2f3982 100644 --- a/src/loader/utils/xdp.c +++ b/src/loader/utils/xdp.c @@ -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; } diff --git a/src/loader/utils/xdp.h b/src/loader/utils/xdp.h index 9b7b127..038af45 100644 --- a/src/loader/utils/xdp.h +++ b/src/loader/utils/xdp.h @@ -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); \ No newline at end of file diff --git a/src/xdp/prog.c b/src/xdp/prog.c index 4542a82..4fc267f 100644 --- a/src/xdp/prog.c +++ b/src/xdp/prog.c @@ -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: @@ -513,6 +534,39 @@ int xdp_prog_main(struct xdp_md *ctx) 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. action = filter->action; diff --git a/src/xdp/utils/maps.h b/src/xdp/utils/maps.h index c5478ba..fdb5475 100644 --- a/src/xdp/utils/maps.h +++ b/src/xdp/utils/maps.h @@ -59,4 +59,12 @@ struct __uint(max_entries, MAX_TRACK_IPS); __type(key, u128); __type(value, u64); -} ip6_blacklist_map SEC(".maps"); \ No newline at end of file +} 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 \ No newline at end of file