From c5629a8603ee2e14270bb3027e69bf8165a11a34 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Thu, 27 Feb 2025 16:25:26 -0500 Subject: [PATCH 01/19] Start preparing loader for xdpfw-add (rule_add) and xdpfw-del (rule_del) programs. --- src/loader/prog.c | 81 +++- src/loader/utils/cmdline.h | 1 + src/loader/utils/config.c | 748 ++++++++++++++++++++++++++++--------- src/loader/utils/config.h | 16 +- src/loader/utils/logging.c | 3 +- src/loader/utils/stats.c | 3 +- src/loader/utils/stats.h | 1 - src/loader/utils/xdp.c | 158 +++++++- src/loader/utils/xdp.h | 18 +- 9 files changed, 811 insertions(+), 218 deletions(-) diff --git a/src/loader/prog.c b/src/loader/prog.c index 38e2e2e..4d86c06 100644 --- a/src/loader/prog.c +++ b/src/loader/prog.c @@ -21,6 +21,34 @@ int cont = 1; int doing_stats = 0; +/** + * Unpins filter-specific BPF maps from file system. + * + * @param cfg A pointer to the config structure. + * @param obj A pointer to the BPF object. + * @param ignore_errors Whether to ignore errors. + */ +static void UnpinFilterMaps(config__t* cfg, struct bpf_object* obj, int ignore_errors) +{ + int ret; + + if ((ret = UnpinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filters")) != 0) + { + if (!ignore_errors) + { + LogMsg(cfg, 1, 0, "[WARNING] Failed to un-pin BPF map 'map_filters' from file system (%d).", ret); + } + } + + if ((ret = UnpinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filter_log")) != 0) + { + if (!ignore_errors) + { + LogMsg(cfg, 1, 0, "[WARNING] Failed to un-pin BPF map 'map_filter_log' from file system (%d).", ret); + } + } +} + int main(int argc, char *argv[]) { int ret; @@ -29,6 +57,7 @@ int main(int argc, char *argv[]) cmdline_t cmd = {0}; cmd.cfgfile = CONFIG_DEFAULT_PATH; cmd.verbose = -1; + cmd.pin_maps = -1; cmd.update_time = -1; cmd.no_stats = -1; cmd.stats_per_second = -1; @@ -54,6 +83,7 @@ int main(int argc, char *argv[]) cfg_overrides.verbose = cmd.verbose; cfg_overrides.log_file = cmd.log_file; cfg_overrides.interface = cmd.interface; + cfg_overrides.pin_maps = cmd.pin_maps; cfg_overrides.update_time = cmd.update_time; cfg_overrides.no_stats = cmd.no_stats; cfg_overrides.stats_per_second = cmd.stats_per_second; @@ -140,7 +170,7 @@ int main(int argc, char *argv[]) // Attach XDP program. char *mode_used = NULL; - if ((ret = AttachXdp(prog, &mode_used, ifidx, 0, &cmd)) != 0) + if ((ret = AttachXdp(prog, &mode_used, ifidx, 0, cmd.skb, cmd.offload)) != 0) { LogMsg(&cfg, 0, 1, "[ERROR] Failed to attach XDP program to interface '%s' using available modes (%d).\n", cfg.interface, ret); @@ -194,6 +224,36 @@ int main(int argc, char *argv[]) LogMsg(&cfg, 3, 0, "map_stats FD => %d.", map_stats); + // Pin BPF maps to file system if we need to. + if (cfg.pin_maps) + { + LogMsg(&cfg, 2, 0, "Pinning BPF maps..."); + + struct bpf_object* obj = GetBpfObj(prog); + + // There are times where the BPF maps from the last run weren't cleaned up properly. + // So it's best to attempt to unpin the maps before pinning while ignoring errors. + UnpinFilterMaps(&cfg, obj, 1); + + if ((ret = PinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filters")) != 0) + { + LogMsg(&cfg, 1, 0, "[WARNING] Failed to pin 'map_filters' to file system (%d)...", ret); + } + else + { + LogMsg(&cfg, 3, 0, "BPF map 'map_filters' pinned to '%s/map_filters'.", XDP_MAP_PIN_DIR); + } + + if ((ret = PinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filter_log")) != 0) + { + LogMsg(&cfg, 1, 0, "[WARNING] Failed to pin 'map_filter_log' to file system (%d)...", ret); + } + else + { + LogMsg(&cfg, 3, 0, "BPF map 'map_filter_log' pinned to '%s/map_filter_log'.", XDP_MAP_PIN_DIR); + } + } + LogMsg(&cfg, 2, 0, "Updating filters..."); // Update BPF maps. @@ -281,6 +341,8 @@ int main(int argc, char *argv[]) fprintf(stdout, "\n"); + LogMsg(&cfg, 2, 0, "Cleaning up..."); + #ifdef ENABLE_FILTER_LOGGING if (rb) { @@ -289,14 +351,27 @@ int main(int argc, char *argv[]) #endif // Detach XDP program. - if (AttachXdp(prog, &mode_used, ifidx, 1, &cmd)) + if (AttachXdp(prog, &mode_used, ifidx, 1, cmd.skb, cmd.offload)) { LogMsg(&cfg, 0, 1, "[ERROR] Failed to detach XDP program from interface '%s'.\n", cfg.interface); return EXIT_FAILURE; } - LogMsg(&cfg, 1, 0, "Cleaned up and exiting...\n"); + // Unpin maps from file system. + if (cfg.pin_maps) + { + LogMsg(&cfg, 2, 0, "Un-pinning BPF maps from file system..."); + + struct bpf_object* obj = GetBpfObj(prog); + + UnpinFilterMaps(&cfg, obj, 0); + } + + // Lastly, close the XDP program. + xdp_program__close(prog); + + LogMsg(&cfg, 1, 0, "Exiting.\n"); // Exit program successfully. return EXIT_SUCCESS; diff --git a/src/loader/utils/cmdline.h b/src/loader/utils/cmdline.h index 9207efc..ac1a0b9 100644 --- a/src/loader/utils/cmdline.h +++ b/src/loader/utils/cmdline.h @@ -16,6 +16,7 @@ struct cmdline int verbose; char* log_file; char* interface; + int pin_maps; int update_time; int no_stats; int stats_per_second; diff --git a/src/loader/utils/config.c b/src/loader/utils/config.c index eb0ce74..8cabc11 100644 --- a/src/loader/utils/config.c +++ b/src/loader/utils/config.c @@ -1,7 +1,5 @@ #include -static FILE *file; - /** * Loads the config from the file system. * @@ -13,131 +11,122 @@ static FILE *file; */ int LoadConfig(config__t *cfg, char *cfg_file, config_overrides_t* overrides) { + int ret; + + FILE *file = NULL; + // Open config file. - if (OpenCfg(cfg_file) != 0) + if ((ret = OpenCfg(&file, cfg_file)) != 0 || file == NULL) { fprintf(stderr, "Error opening config file.\n"); - return EXIT_FAILURE; + return ret; } SetCfgDefaults(cfg); memset(cfg->filters, 0, sizeof(cfg->filters)); - // Read config and check for errors. - if (ReadCfg(cfg, overrides) != 0) + char* buffer = NULL; + + // Read config. + if ((ret = ReadCfg(file, &buffer)) != 0) { fprintf(stderr, "Error reading config file.\n"); - return EXIT_FAILURE; + CloseCfg(file); + + return ret; + } + + // Parse config. + if ((ret = ParseCfg(cfg, buffer, overrides)) != 0) + { + fprintf(stderr, "Error parsing config file.\n"); + + CloseCfg(file); + + return ret; + } + + free(buffer); + + if ((ret = CloseCfg(file)) != 0) + { + fprintf(stderr, "Error closing config file.\n"); + + return ret; } return EXIT_SUCCESS; } /** - * Sets the config structure's default values. + * Opens the config file. * - * @param cfg A pointer to the config structure. + * @param file_name Path to config file. * - * @return Void + * @return 0 on success or 1 on error. */ -void SetCfgDefaults(config__t *cfg) +int OpenCfg(FILE** file, const char *file_name) { - cfg->verbose = 2; - cfg->log_file = strdup("/var/log/xdpfw.log"); - cfg->update_time = 0; - cfg->interface = NULL; - cfg->no_stats = 0; - cfg->stats_per_second = 0; - cfg->stdout_update_time = 1000; - - for (int i = 0; i < MAX_FILTERS; i++) + // Close any existing files. + if (*file != NULL) { - filter_t* filter = &cfg->filters[i]; - - filter->set = 0; - filter->enabled = 1; - - filter->log = 0; + fclose(*file); - filter->action = 1; - filter->src_ip = 0; - filter->dst_ip = 0; - - memset(filter->src_ip6, 0, 4); - memset(filter->dst_ip6, 0, 4); - - filter->do_min_len = 0; - filter->min_len = 0; - - filter->do_max_len = 0; - filter->max_len = 65535; - - 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; - - filter->do_bps = 0; - filter->bps = 0; - - filter->block_time = 1; - - filter->tcpopts.enabled = 0; - filter->tcpopts.do_dport = 0; - filter->tcpopts.do_dport = 0; - filter->tcpopts.do_urg = 0; - filter->tcpopts.do_ack = 0; - filter->tcpopts.do_rst = 0; - filter->tcpopts.do_psh = 0; - filter->tcpopts.do_syn = 0; - filter->tcpopts.do_fin = 0; - filter->tcpopts.do_ece = 0; - filter->tcpopts.do_cwr = 0; + *file = NULL; + } - filter->udpopts.enabled = 0; - filter->udpopts.do_sport = 0; - filter->udpopts.do_dport = 0; + *file = fopen(file_name, "r"); - filter->icmpopts.enabled = 0; - filter->icmpopts.do_code = 0; - filter->icmpopts.do_type = 0; + if (*file == NULL) + { + return 1; } + + return 0; } /** - * Opens the config file. + * Close config file. * - * @param file_name Path to config file. + * @param file A pointer to the file to close. * - * @return 0 on success or 1 on error. + * @param return 0 on success or error value of fclose(). */ -int OpenCfg(const char *file_name) +int CloseCfg(FILE* file) { - // Close any existing files. - if (file != NULL) - { - fclose(file); + return fclose(file); +} + +/** + * Reads contents from the config file. + * + * @param file The file pointer. + * @param buffer The buffer to store the data in (manually allocated). + */ +int ReadCfg(FILE* file, char** buffer) +{ + fseek(file, 0, SEEK_END); + long file_size = ftell(file); + rewind(file); - file = NULL; + if (file_size <= 0) + { + return 1; } - file = fopen(file_name, "r"); + *buffer = malloc(file_size + 1); - if (file == NULL) + if (*buffer == NULL) { return 1; } + size_t read = fread(*buffer, 1, file_size, file); + (*buffer)[read] = '\0'; + return 0; } @@ -145,18 +134,13 @@ int OpenCfg(const char *file_name) * Read the config file and stores values in config structure. * * @param cfg A pointer to the config structure. + * @param data The config data. * @param overrides Overrides to use instead of config values. * * @return 0 on success or 1/-1 on error. */ -int ReadCfg(config__t *cfg, config_overrides_t* overrides) +int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) { - // Not sure why this would be set to NULL after checking for it in OpenConfig(), but just for safety. - if (file == NULL) - { - return -1; - } - // Initialize config. config_t conf; config_setting_t *setting; @@ -164,7 +148,7 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) config_init(&conf); // Attempt to read the config. - if (config_read(&conf, file) == CONFIG_FALSE) + if (config_read_string(&conf, data) == CONFIG_FALSE) { LogMsg(cfg, 0, 1, "Error from LibConfig when reading file - %s (Line %d)", config_error_text(&conf), config_error_line(&conf)); @@ -189,7 +173,7 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) const char* log_file; - if (config_lookup_string(&conf, "log_file", &log_file) == CONFIG_TRUE || overrides->log_file != NULL) + if (config_lookup_string(&conf, "log_file", &log_file) == CONFIG_TRUE || (overrides && overrides->log_file != NULL)) { // We must free previous value to prevent memory leak. if (cfg->log_file != NULL) @@ -226,7 +210,7 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) // Get interface. const char *interface; - if (config_lookup_string(&conf, "interface", &interface) == CONFIG_TRUE || overrides->interface != NULL) + if (config_lookup_string(&conf, "interface", &interface) == CONFIG_TRUE || (overrides && overrides->interface != NULL)) { // We must free previous value to prevent memory leak. if (cfg->interface != NULL) @@ -245,10 +229,25 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) } } + // Pin BPF maps. + int pin_maps; + + if (config_lookup_bool(&conf, "pin_maps", &pin_maps) == CONFIG_TRUE || (overrides && overrides->pin_maps > -1)) + { + if (overrides->pin_maps > -1) + { + cfg->pin_maps = overrides->pin_maps; + } + else + { + cfg->pin_maps = pin_maps; + } + } + // Get auto update time. int update_time; - if (config_lookup_int(&conf, "update_time", &update_time) == CONFIG_TRUE || overrides->update_time > -1) + if (config_lookup_int(&conf, "update_time", &update_time) == CONFIG_TRUE || (overrides && overrides->update_time > -1)) { if (overrides->update_time > -1) { @@ -263,7 +262,7 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) // Get no stats. int no_stats; - if (config_lookup_bool(&conf, "no_stats", &no_stats) == CONFIG_TRUE || overrides->no_stats > -1) + if (config_lookup_bool(&conf, "no_stats", &no_stats) == CONFIG_TRUE || (overrides && overrides->no_stats > -1)) { if (overrides->no_stats > -1) { @@ -278,7 +277,7 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) // Stats per second. int stats_per_second; - if (config_lookup_bool(&conf, "stats_per_second", &stats_per_second) == CONFIG_TRUE || overrides->stats_per_second > -1) + if (config_lookup_bool(&conf, "stats_per_second", &stats_per_second) == CONFIG_TRUE || (overrides && overrides->stats_per_second > -1)) { if (overrides->stats_per_second > -1) { @@ -293,7 +292,7 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) // Get stdout update time. int stdout_update_time; - if (config_lookup_int(&conf, "stdout_update_time", &stdout_update_time) == CONFIG_TRUE || overrides->stdout_update_time > -1) + if (config_lookup_int(&conf, "stdout_update_time", &stdout_update_time) == CONFIG_TRUE || (overrides && overrides->stdout_update_time > -1)) { if (overrides->stdout_update_time > -1) { @@ -486,18 +485,18 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) } // Source port. - long long tcpsport; + int tcpsport; - if (config_setting_lookup_int64(filter_cfg, "tcp_sport", &tcpsport) == CONFIG_TRUE) + if (config_setting_lookup_int(filter_cfg, "tcp_sport", &tcpsport) == CONFIG_TRUE) { filter->tcpopts.sport = (u16)tcpsport; filter->tcpopts.do_sport = 1; } // Destination port. - long long tcpdport; + int tcpdport; - if (config_setting_lookup_int64(filter_cfg, "tcp_dport", &tcpdport) == CONFIG_TRUE) + if (config_setting_lookup_int(filter_cfg, "tcp_dport", &tcpdport) == CONFIG_TRUE) { filter->tcpopts.dport = (u16)tcpdport; filter->tcpopts.do_dport = 1; @@ -521,7 +520,6 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) filter->tcpopts.do_ack = 1; } - // RST flag. int tcprst; @@ -586,18 +584,18 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) } // Source port. - long long udpsport; + int udpsport; - if (config_setting_lookup_int64(filter_cfg, "udp_sport", &udpsport) == CONFIG_TRUE) + if (config_setting_lookup_int(filter_cfg, "udp_sport", &udpsport) == CONFIG_TRUE) { filter->udpopts.sport = (u16)udpsport; filter->udpopts.do_sport = 1; } // Destination port. - long long udpdport; + int udpdport; - if (config_setting_lookup_int64(filter_cfg, "udp_dport", &udpdport) == CONFIG_TRUE) + if (config_setting_lookup_int(filter_cfg, "udp_dport", &udpdport) == CONFIG_TRUE) { filter->udpopts.dport = (u16)udpdport; filter->udpopts.do_dport = 1; @@ -639,6 +637,430 @@ int ReadCfg(config__t *cfg, config_overrides_t* overrides) return EXIT_SUCCESS; } +/** + * Saves config to file system. + * + * @param cfg A pointer to the config. + * @param file_path The file path to store the config into. + * + * @param return 0 on success or 1 on failure. + */ +int SaveCfg(config__t* cfg, const char* file_path) +{ + config_t conf; + config_setting_t *root, *setting; + + FILE* file; + + config_init(&conf); + root = config_root_setting(&conf); + + // Add verbose. + setting = config_setting_add(root, "verbose", CONFIG_TYPE_INT); + config_setting_set_int(setting, cfg->verbose); + + // Add log file. + if (cfg->log_file) + { + setting = config_setting_add(root, "log_file", CONFIG_TYPE_STRING); + config_setting_set_string(setting, cfg->log_file); + } + + // Add interface. + if (cfg->interface) + { + setting = config_setting_add(root, "interface", CONFIG_TYPE_STRING); + config_setting_set_string(setting, cfg->interface); + } + + // Add pin maps. + setting = config_setting_add(root, "pin_maps", CONFIG_TYPE_BOOL); + config_setting_set_bool(setting, cfg->pin_maps); + + // Add update time. + setting = config_setting_add(root, "update_time", CONFIG_TYPE_INT); + config_setting_set_int(setting, cfg->update_time); + + // Add no stats. + setting = config_setting_add(root, "no_stats", CONFIG_TYPE_BOOL); + config_setting_set_bool(setting, cfg->no_stats); + + // Add stats per second. + setting = config_setting_add(root, "stats_per_second", CONFIG_TYPE_BOOL); + config_setting_set_bool(setting, cfg->stats_per_second); + + // Add stdout update time. + setting = config_setting_add(root, "stdout_update_time", CONFIG_TYPE_INT); + config_setting_set_int(setting, cfg->stdout_update_time); + + // Add filters. + config_setting_t* filters = config_setting_add(root, "filters", CONFIG_TYPE_LIST); + + if (filters) + { + for (int i = 0; i < MAX_FILTERS; i++) + { + filter_t* filter = &cfg->filters[i]; + + if (!filter->set) + { + continue; + } + + config_setting_t* filter_cfg = config_setting_add(filters, NULL, CONFIG_TYPE_GROUP); + + if (filter_cfg) + { + // Add enabled setting. + config_setting_t* enabled = config_setting_add(filter_cfg, "enabled", CONFIG_TYPE_BOOL); + config_setting_set_bool(enabled, filter->enabled); + + // Add log setting. + config_setting_t* log = config_setting_add(filter_cfg, "log", CONFIG_TYPE_BOOL); + config_setting_set_bool(log, filter->log); + + // Add action setting. + config_setting_t* action = config_setting_add(filter_cfg, "action", CONFIG_TYPE_INT); + config_setting_set_int(action, filter->action); + + // Add source IPv4. + if (filter->src_ip > 0) + { + char ip_str[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &filter->src_ip, ip_str, sizeof(ip_str)); + + char full_ip[INET_ADDRSTRLEN + 6]; + snprintf(full_ip, sizeof(full_ip), "%s/%d", ip_str, filter->src_cidr); + + config_setting_t* src_ip = config_setting_add(filter_cfg, "src_ip", CONFIG_TYPE_STRING); + config_setting_set_string(src_ip, full_ip); + } + + // Add destination IPv4. + if (filter->dst_ip > 0) + { + char ip_str[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &filter->dst_ip, ip_str, sizeof(ip_str)); + + char full_ip[INET_ADDRSTRLEN + 6]; + snprintf(full_ip, sizeof(full_ip), "%s/%d", ip_str, filter->src_cidr); + + config_setting_t* dst_ip = config_setting_add(filter_cfg, "dst_ip", CONFIG_TYPE_STRING); + config_setting_set_string(dst_ip, full_ip); + } + + // Add source IPv6. + if (memcmp(filter->src_ip6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0) + { + char ip_str[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET, filter->src_ip6, ip_str, sizeof(ip_str)); + + //char full_ip[INET6_ADDRSTRLEN + 6]; + //snprintf(full_ip, sizeof(full_ip), "%s/%d", ip_str, filter->src_cidr6); + + config_setting_t* src_ip6 = config_setting_add(filter_cfg, "src_ip6", CONFIG_TYPE_STRING); + config_setting_set_string(src_ip6, ip_str); + } + + // Add source IPv6. + if (memcmp(filter->dst_ip6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0) + { + char ip_str[INET6_ADDRSTRLEN]; + + inet_ntop(AF_INET, filter->dst_ip6, ip_str, sizeof(ip_str)); + + //char full_ip[INET6_ADDRSTRLEN + 6]; + //snprintf(full_ip, sizeof(full_ip), "%s/%d", ip_str, filter->src_cidr6); + + config_setting_t* dst_ip6 = config_setting_add(filter_cfg, "dst_ip6", CONFIG_TYPE_STRING); + config_setting_set_string(dst_ip6, ip_str); + } + + // Add minimum TTL. + config_setting_t* min_ttl = config_setting_add(filter_cfg, "min_ttl", CONFIG_TYPE_INT); + config_setting_set_int(min_ttl, filter->min_ttl); + + // Add maximum TTL. + config_setting_t* max_ttl = config_setting_add(filter_cfg, "max_ttl", CONFIG_TYPE_INT); + config_setting_set_int(max_ttl, filter->max_ttl); + + // Add minimum length. + config_setting_t* min_len = config_setting_add(filter_cfg, "min_len", CONFIG_TYPE_INT); + config_setting_set_int(min_len, filter->min_len); + + // Add maximum length. + config_setting_t* max_len = config_setting_add(filter_cfg, "max_len", CONFIG_TYPE_INT); + config_setting_set_int(max_len, filter->max_len); + + // Add ToS. + config_setting_t* tos = config_setting_add(filter_cfg, "tos", CONFIG_TYPE_INT); + config_setting_set_int(tos, filter->tos); + + // Add PPS. + config_setting_t* pps = config_setting_add(filter_cfg, "pps", CONFIG_TYPE_INT64); + config_setting_set_int64(pps, filter->pps); + + // Add BPS. + config_setting_t* bps = config_setting_add(filter_cfg, "bps", CONFIG_TYPE_INT64); + config_setting_set_int64(bps, filter->bps); + + // Add block time. + config_setting_t* block_time = config_setting_add(filter_cfg, "block_time", CONFIG_TYPE_INT64); + config_setting_set_int64(block_time, filter->block_time); + + // Add TCP enabled. + config_setting_t* tcp_enabled = config_setting_add(filter_cfg, "tcp_enabled", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_enabled, filter->tcpopts.enabled); + + // Add TCP source port. + config_setting_t* tcp_sport = config_setting_add(filter_cfg, "tcp_sport", CONFIG_TYPE_INT); + config_setting_set_int(tcp_sport, filter->tcpopts.sport); + + // Add TCP destination port. + config_setting_t* tcp_dport = config_setting_add(filter_cfg, "tcp_dport", CONFIG_TYPE_INT); + config_setting_set_int(tcp_dport, filter->tcpopts.dport); + + // Add TCP URG flag. + config_setting_t* tcp_urg = config_setting_add(filter_cfg, "tcp_urg", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_urg, filter->tcpopts.urg); + + // Add TCP ACK flag. + config_setting_t* tcp_ack = config_setting_add(filter_cfg, "tcp_ack", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_ack, filter->tcpopts.ack); + + // Add TCP RST flag. + config_setting_t* tcp_rst = config_setting_add(filter_cfg, "tcp_rst", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_rst, filter->tcpopts.rst); + + // Add TCP PSH flag. + config_setting_t* tcp_psh = config_setting_add(filter_cfg, "tcp_psh", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_psh, filter->tcpopts.psh); + + // Add TCP SYN flag. + config_setting_t* tcp_syn = config_setting_add(filter_cfg, "tcp_syn", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_syn, filter->tcpopts.syn); + + // Add TCP FIN flag. + config_setting_t* tcp_fin = config_setting_add(filter_cfg, "tcp_fin", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_fin, filter->tcpopts.fin); + + // Add TCP ECE flag. + config_setting_t* tcp_ece = config_setting_add(filter_cfg, "tcp_ece", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_ece, filter->tcpopts.ece); + + // Add TCP CWR flag. + config_setting_t* tcp_cwr = config_setting_add(filter_cfg, "tcp_cwr", CONFIG_TYPE_BOOL); + config_setting_set_bool(tcp_cwr, filter->tcpopts.cwr); + + // Add UDP enabled. + config_setting_t* udp_enabled = config_setting_add(filter_cfg, "udp_enabled", CONFIG_TYPE_BOOL); + config_setting_set_bool(udp_enabled, filter->udpopts.enabled); + + // Add UDP source port. + config_setting_t* udp_sport = config_setting_add(filter_cfg, "udp_sport", CONFIG_TYPE_INT); + config_setting_set_int(udp_sport, filter->udpopts.sport); + + // Add UDP destination port. + config_setting_t* udp_dport = config_setting_add(filter_cfg, "udp_dport", CONFIG_TYPE_INT); + config_setting_set_int(udp_dport, filter->udpopts.dport); + + // Add ICMP enabled. + config_setting_t* icmp_enabled = config_setting_add(filter_cfg, "icmp_enabled", CONFIG_TYPE_BOOL); + config_setting_set_bool(icmp_enabled, filter->icmpopts.enabled); + + // Add ICMP code. + config_setting_t* icmp_code = config_setting_add(filter_cfg, "icmp_code", CONFIG_TYPE_INT); + config_setting_set_int(icmp_code, filter->icmpopts.code); + + // Add ICMP type. + config_setting_t* icmp_type = config_setting_add(filter_cfg, "icmp_type", CONFIG_TYPE_INT); + config_setting_set_int(icmp_type, filter->icmpopts.type); + } + } + } + + // Write config to file. + file = fopen(file_path, "w"); + + if (!file) + { + config_destroy(&conf); + + return 1; + } + + config_write(&conf, file); + + fclose(file); + config_destroy(&conf); + + return 0; +} + +/** + * Sets the config structure's default values. + * + * @param cfg A pointer to the config structure. + * + * @return Void + */ +void SetCfgDefaults(config__t *cfg) +{ + cfg->verbose = 2; + cfg->log_file = strdup("/var/log/xdpfw.log"); + cfg->update_time = 0; + cfg->interface = NULL; + cfg->pin_maps = 1; + cfg->no_stats = 0; + cfg->stats_per_second = 0; + cfg->stdout_update_time = 1000; + + for (int i = 0; i < MAX_FILTERS; i++) + { + filter_t* filter = &cfg->filters[i]; + + filter->set = 0; + filter->enabled = 1; + + filter->log = 0; + + filter->action = 1; + filter->src_ip = 0; + filter->dst_ip = 0; + + memset(filter->src_ip6, 0, 4); + memset(filter->dst_ip6, 0, 4); + + filter->do_min_len = 0; + filter->min_len = 0; + + filter->do_max_len = 0; + filter->max_len = 65535; + + 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; + + filter->do_bps = 0; + filter->bps = 0; + + filter->block_time = 1; + + filter->tcpopts.enabled = 0; + filter->tcpopts.do_dport = 0; + filter->tcpopts.do_dport = 0; + filter->tcpopts.do_urg = 0; + filter->tcpopts.do_ack = 0; + filter->tcpopts.do_rst = 0; + filter->tcpopts.do_psh = 0; + filter->tcpopts.do_syn = 0; + filter->tcpopts.do_fin = 0; + filter->tcpopts.do_ece = 0; + filter->tcpopts.do_cwr = 0; + + filter->udpopts.enabled = 0; + filter->udpopts.do_sport = 0; + filter->udpopts.do_dport = 0; + + filter->icmpopts.enabled = 0; + filter->icmpopts.do_code = 0; + filter->icmpopts.do_type = 0; + } +} + +/** + * Prints a filter rule. + * + * @param filter A pointer to the filter rule. + * @param idx The current index. + * + * @return void + */ +void PrintFilter(filter_t* filter, int idx) +{ + printf("\tFilter #%d\n", idx); + printf("\t\tEnabled => %d\n", filter->enabled); + printf("\t\tAction => %d (0 = Block, 1 = Allow).\n", filter->action); + printf("\t\tLog => %d\n\n", filter->log); + + // IP Options. + printf("\t\tIP Options\n"); + + // IP addresses require additional code for string printing. + struct sockaddr_in sin; + sin.sin_addr.s_addr = filter->src_ip; + printf("\t\t\tSource IPv4 => %s\n", inet_ntoa(sin.sin_addr)); + printf("\t\t\tSource CIDR => %d\n", filter->src_cidr); + + struct sockaddr_in din; + din.sin_addr.s_addr = filter->dst_ip; + printf("\t\t\tDestination IPv4 => %s\n", inet_ntoa(din.sin_addr)); + printf("\t\t\tDestination CIDR => %d\n", filter->dst_cidr); + + struct in6_addr sin6; + memcpy(&sin6, &filter->src_ip6, sizeof(sin6)); + + char srcipv6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &sin6, srcipv6, sizeof(srcipv6)); + + printf("\t\t\tSource IPv6 => %s\n", srcipv6); + + struct in6_addr din6; + memcpy(&din6, &filter->dst_ip6, sizeof(din6)); + + char dstipv6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &din6, dstipv6, sizeof(dstipv6)); + + printf("\t\t\tDestination IPv6 => %s\n", dstipv6); + + // Other IP header information. + printf("\t\t\tMax Length => %d\n", filter->max_len); + printf("\t\t\tMin Length => %d\n", filter->min_len); + printf("\t\t\tMax TTL => %d\n", filter->max_ttl); + printf("\t\t\tMin TTL => %d\n", filter->min_ttl); + printf("\t\t\tTOS => %d\n", filter->tos); + printf("\t\t\tPPS => %llu\n", filter->pps); + printf("\t\t\tBPS => %llu\n", filter->bps); + printf("\t\t\tBlock Time => %llu\n\n", filter->block_time); + + // TCP Options. + printf("\t\tTCP Options\n"); + printf("\t\t\tTCP Enabled => %d\n", filter->tcpopts.enabled); + printf("\t\t\tTCP Source Port => %d\n", filter->tcpopts.sport); + printf("\t\t\tTCP Destination Port => %d\n", filter->tcpopts.dport); + printf("\t\t\tTCP URG Flag => %d\n", filter->tcpopts.urg); + printf("\t\t\tTCP ACK Flag => %d\n", filter->tcpopts.ack); + printf("\t\t\tTCP RST Flag => %d\n", filter->tcpopts.rst); + printf("\t\t\tTCP PSH Flag => %d\n", filter->tcpopts.psh); + printf("\t\t\tTCP SYN Flag => %d\n", filter->tcpopts.syn); + printf("\t\t\tTCP FIN Flag => %d\n", filter->tcpopts.fin); + printf("\t\t\tTCP ECE Flag => %d\n", filter->tcpopts.ece); + printf("\t\t\tTCP CWR Flag => %d\n\n", filter->tcpopts.cwr); + + // UDP Options. + printf("\t\tUDP Options\n"); + printf("\t\t\tUDP Enabled => %d\n", filter->udpopts.enabled); + printf("\t\t\tUDP Source Port => %d\n", filter->udpopts.sport); + printf("\t\t\tUDP Destination Port => %d\n\n", filter->udpopts.dport); + + // ICMP Options. + printf("\t\tICMP Options\n"); + printf("\t\t\tICMP Enabled => %d\n", filter->icmpopts.enabled); + printf("\t\t\tICMP Code => %d\n", filter->icmpopts.code); + printf("\t\t\tICMP Type => %d\n", filter->icmpopts.type); +} + /** * Prints config settings. * @@ -663,17 +1085,18 @@ void PrintConfig(config__t* cfg) } printf("Printing config...\n"); - printf("\tGeneral Settings\n"); + printf("General Settings\n"); - printf("\t\tVerbose => %d\n", cfg->verbose); - printf("\t\tLog File => %s\n", log_file); - printf("\t\tInterface Name => %s\n", interface); - printf("\t\tUpdate Time => %d\n", cfg->update_time); - printf("\t\tNo Stats => %d\n", cfg->no_stats); - printf("\t\tStats Per Second => %d\n", cfg->stats_per_second); - printf("\t\tStdout Update Time => %d\n\n", cfg->stdout_update_time); + printf("\tVerbose => %d\n", cfg->verbose); + printf("\tLog File => %s\n", log_file); + printf("\tInterface Name => %s\n", interface); + printf("\tPin BPF Maps => %d\n", cfg->pin_maps); + printf("\tUpdate Time => %d\n", cfg->update_time); + printf("\tNo Stats => %d\n", cfg->no_stats); + printf("\tStats Per Second => %d\n", cfg->stats_per_second); + printf("\tStdout Update Time => %d\n\n", cfg->stdout_update_time); - printf("\tFilters\n"); + printf("Filters\n"); for (int i = 0; i < MAX_FILTERS; i++) { @@ -684,79 +1107,32 @@ void PrintConfig(config__t* cfg) break; } - printf("\t\tFilter #%d:\n", (i + 1)); - - // Main. - printf("\t\t\tEnabled => %d\n", filter->enabled); - printf("\t\t\tAction => %d (0 = Block, 1 = Allow).\n", filter->action); - printf("\t\t\tLog => %d\n\n", filter->log); + PrintFilter(filter, i + 1); - // IP Options. - printf("\t\t\tIP Options\n"); - - // IP addresses require additional code for string printing. - struct sockaddr_in sin; - sin.sin_addr.s_addr = filter->src_ip; - printf("\t\t\t\tSource IPv4 => %s\n", inet_ntoa(sin.sin_addr)); - printf("\t\t\t\tSource CIDR => %d\n", filter->src_cidr); + printf("\n\n"); + } +} - struct sockaddr_in din; - din.sin_addr.s_addr = filter->dst_ip; - printf("\t\t\t\tDestination IPv4 => %s\n", inet_ntoa(din.sin_addr)); - printf("\t\t\t\tDestination CIDR => %d\n", filter->dst_cidr); +/** + * Retrieves next available filter index. + * + * @param cfg A pointer to the config structure. + * + * @return The next available index or -1 if there are no available indexes. + */ +int GetNextAvailableFilterIndex(config__t* cfg) +{ + for (int i = 0; i < MAX_FILTERS; i++) + { + filter_t* filter = &cfg->filters[i]; - struct in6_addr sin6; - memcpy(&sin6, &filter->src_ip6, sizeof(sin6)); - - char srcipv6[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &sin6, srcipv6, sizeof(srcipv6)); - - printf("\t\t\t\tSource IPv6 => %s\n", srcipv6); - - struct in6_addr din6; - memcpy(&din6, &filter->dst_ip6, sizeof(din6)); - - char dstipv6[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &din6, dstipv6, sizeof(dstipv6)); - - printf("\t\t\t\tDestination IPv6 => %s\n", dstipv6); - - // Other IP header information. - printf("\t\t\t\tMax Length => %d\n", filter->max_len); - printf("\t\t\t\tMin Length => %d\n", filter->min_len); - printf("\t\t\t\tMax TTL => %d\n", filter->max_ttl); - printf("\t\t\t\tMin TTL => %d\n", filter->min_ttl); - printf("\t\t\t\tTOS => %d\n", filter->tos); - printf("\t\t\t\tPPS => %llu\n", filter->pps); - printf("\t\t\t\tBPS => %llu\n", filter->bps); - printf("\t\t\t\tBlock Time => %llu\n\n", filter->block_time); - - // TCP Options. - printf("\t\t\tTCP Options\n"); - printf("\t\t\t\tTCP Enabled => %d\n", filter->tcpopts.enabled); - printf("\t\t\t\tTCP Source Port => %d\n", filter->tcpopts.sport); - printf("\t\t\t\tTCP Destination Port => %d\n", filter->tcpopts.dport); - printf("\t\t\t\tTCP URG Flag => %d\n", filter->tcpopts.urg); - printf("\t\t\t\tTCP ACK Flag => %d\n", filter->tcpopts.ack); - printf("\t\t\t\tTCP RST Flag => %d\n", filter->tcpopts.rst); - printf("\t\t\t\tTCP PSH Flag => %d\n", filter->tcpopts.psh); - printf("\t\t\t\tTCP SYN Flag => %d\n", filter->tcpopts.syn); - printf("\t\t\t\tTCP FIN Flag => %d\n", filter->tcpopts.fin); - printf("\t\t\t\tTCP ECE Flag => %d\n", filter->tcpopts.ece); - printf("\t\t\t\tTCP CWR Flag => %d\n\n", filter->tcpopts.cwr); - - // UDP Options. - printf("\t\t\tUDP Options\n"); - printf("\t\t\t\tUDP Enabled => %d\n", filter->udpopts.enabled); - printf("\t\t\t\tUDP Source Port => %d\n", filter->udpopts.sport); - printf("\t\t\t\tUDP Destination Port => %d\n\n", filter->udpopts.dport); - - // ICMP Options. - printf("\t\t\tICMP Options\n"); - printf("\t\t\t\tICMP Enabled => %d\n", filter->icmpopts.enabled); - printf("\t\t\t\tICMP Code => %d\n", filter->icmpopts.code); - printf("\t\t\t\tICMP Type => %d\n", filter->icmpopts.type); + if (filter->set) + { + continue; + } - printf("\n\n"); + return i; } + + return -1; } \ No newline at end of file diff --git a/src/loader/utils/config.h b/src/loader/utils/config.h index a1ea302..c2ad130 100644 --- a/src/loader/utils/config.h +++ b/src/loader/utils/config.h @@ -18,6 +18,7 @@ struct config int verbose; char *log_file; char *interface; + unsigned int pin_maps : 1; int update_time; unsigned int no_stats : 1; unsigned int stats_per_second : 1; @@ -30,6 +31,7 @@ struct config_overrides int verbose; const char* log_file; const char* interface; + int pin_maps; int update_time; int no_stats; int stats_per_second; @@ -37,11 +39,19 @@ struct config_overrides } typedef config_overrides_t; -int LoadConfig(config__t *cfg, char *cfg_file, config_overrides_t* overrides); void SetCfgDefaults(config__t *cfg); + +void PrintFilter(filter_t* filter, int idx); void PrintConfig(config__t* cfg); -int OpenCfg(const char *filename); -int ReadCfg(config__t *cfg, config_overrides_t* overrides); +int LoadConfig(config__t *cfg, char *cfg_file, config_overrides_t* overrides); +int SaveCfg(config__t* cfg, const char* file_path); + +int OpenCfg(FILE** file, const char *file_name); +int CloseCfg(FILE* file); +int ReadCfg(FILE* file, char** buffer); +int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides); + +int GetNextAvailableFilterIndex(config__t* cfg); #include \ No newline at end of file diff --git a/src/loader/utils/logging.c b/src/loader/utils/logging.c index 8b06284..eb35304 100644 --- a/src/loader/utils/logging.c +++ b/src/loader/utils/logging.c @@ -74,7 +74,6 @@ static void LogMsgRaw(int req_lvl, int cur_lvl, int error, const char* log_path, 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, @@ -143,7 +142,7 @@ int HandleRbEvent(void* ctx, void* data, size_t sz) } char src_ip_str[INET6_ADDRSTRLEN]; - char dst_ip_str[INET_ADDRSTRLEN]; + char dst_ip_str[INET6_ADDRSTRLEN]; if (memcmp(e->src_ip6, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0) { diff --git a/src/loader/utils/stats.c b/src/loader/utils/stats.c index c62fdbb..a37cb47 100644 --- a/src/loader/utils/stats.c +++ b/src/loader/utils/stats.c @@ -53,7 +53,8 @@ int CalculateStats(int map_stats, int cpus, int per_second) if (per_second) { struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); // Get precise time + clock_gettime(CLOCK_MONOTONIC, &now); + double elapsed_time = (now.tv_sec - last_update_time.tv_sec) + (now.tv_nsec - last_update_time.tv_nsec) / 1e9; diff --git a/src/loader/utils/stats.h b/src/loader/utils/stats.h index 25d4112..9281db9 100644 --- a/src/loader/utils/stats.h +++ b/src/loader/utils/stats.h @@ -4,7 +4,6 @@ #include -#include #include #include diff --git a/src/loader/utils/xdp.c b/src/loader/utils/xdp.c index c9bc935..96da61b 100644 --- a/src/loader/utils/xdp.c +++ b/src/loader/utils/xdp.c @@ -85,6 +85,18 @@ struct xdp_program *LoadBpfObj(const char *file_name) return prog; } +/** + * Retrieves BPF object from XDP program. + * + * @param prog A pointer to the XDP program. + * + * @return The BPF object. + */ +struct bpf_object* GetBpfObj(struct xdp_program* prog) +{ + return xdp_program__bpf_obj(prog); +} + /** * Attempts to attach or detach (progfd = -1) a BPF/XDP program to an interface. * @@ -92,11 +104,12 @@ struct xdp_program *LoadBpfObj(const char *file_name) * @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). + * @param force_skb If set, forces the XDP program to run in SKB mode. + * @param force_offload If set, forces the XDP program to run in offload mode. * * @return 0 on success and 1 on error. */ -int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, u8 detach, cmdline_t *cmd) +int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, int detach, int force_skb, int force_offload) { int err; @@ -104,13 +117,13 @@ int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, u8 detach, cmdli *mode = "DRV/native"; - if (cmd->offload) + if (force_offload) { *mode = "HW/offload"; attach_mode = XDP_MODE_HW; } - else if (cmd->skb) + else if (force_skb) { *mode = "SKB/generic"; @@ -176,6 +189,58 @@ int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, u8 detach, cmdli return EXIT_SUCCESS; } +/** + * Deletes a filter. + * + * @param map_filters The filters BPF map FD. + * @param idx The filter index to delete. + * + * @return 0 on success or the error value of bpf_map_delete_elem(). + */ +int DeleteFilter(int map_filters, u32 idx) +{ + return bpf_map_delete_elem(map_filters, &idx); +} + +/** + * Deletes all filters. + * + * @param map_filters The filters BPF map FD. + * + * @return void + */ +void DeleteFilters(int map_filters) +{ + for (int i = 0; i < MAX_FILTERS; i++) + { + DeleteFilter(map_filters, i); + } +} + +/** + * Updates a filter rule. + * + * @param map_filters The filters BPF map FD. + * @param filter A pointer to the filter. + * @param idx The filter index to insert or update. + * + * @return 0 on success or error value of bpf_map_update_elem(). + */ +int UpdateFilter(int map_filters, filter_t* filter, int idx) +{ + int ret; + + filter_t filter_cpus[MAX_CPUS]; + memset(filter_cpus, 0, sizeof(filter_cpus)); + + for (int j = 0; j < MAX_CPUS; j++) + { + filter_cpus[j] = *filter; + } + + return bpf_map_update_elem(map_filters, &idx, &filter_cpus, BPF_ANY); +} + /** * Updates the filter's BPF map with current config settings. * @@ -192,13 +257,11 @@ void UpdateFilters(int map_filters, config__t *cfg) // Add a filter to the filter maps. for (int i = 0; i < MAX_FILTERS; i++) { - filter_t* filter = &cfg->filters[i]; - // Delete previous rule from BPF map. // We do this in the case rules were edited and were put out of order since the key doesn't uniquely map to a specific rule. - u32 key = i; + DeleteFilter(map_filters, i); - bpf_map_delete_elem(map_filters, &key); + filter_t* filter = &cfg->filters[i]; // Only insert set and enabled filters. if (!filter->set || !filter->enabled) @@ -206,21 +269,78 @@ void UpdateFilters(int map_filters, config__t *cfg) continue; } - // Create value array (max CPUs in size) since we're using a per CPU map. - filter_t filter_cpus[MAX_CPUS]; - memset(filter_cpus, 0, sizeof(filter_cpus)); - - for (int j = 0; j < MAX_CPUS; j++) + // Attempt to update filter. + if ((ret = UpdateFilter(map_filters, filter, cur_idx)) != 0) { - filter_cpus[j] = *filter; - } + fprintf(stderr, "[WARNING] Failed to update filter #%d due to BPF update error (%d)...\n", cur_idx, ret); - // Attempt to update BPF map. - if ((ret = bpf_map_update_elem(map_filters, &cur_idx, &filter_cpus, BPF_ANY)) != 0) - { - fprintf(stderr, "[WARNING] Failed to update filter #%d due to BPF update error (%d)...\n", i, ret); + continue; } cur_idx++; } +} + +/** + * Pins a BPF map to the file system. + * + * @param obj A pointer to the BPF object. + * @param pin_dir The pin directory. + * @param map_name The map name. + * + * @return 0 on success or value of bpf_map__pin() on error. + */ +int PinBpfMap(struct bpf_object* obj, const char* pin_dir, const char* map_name) +{ + struct bpf_map* map = bpf_object__find_map_by_name(obj, map_name); + + if (!map) + { + return -1; + } + + char full_path[255]; + snprintf(full_path, sizeof(full_path), "%s/%s", XDP_MAP_PIN_DIR, map_name); + + return bpf_map__pin(map, full_path); +} + +/** + * Unpins a BPF map from the file system. + * + * @param obj A pointer to the BPF object. + * @param pin_dir The pin directory. + * @param map_name The map name. + * + * @return + */ +int UnpinBpfMap(struct bpf_object* obj, const char* pin_dir, const char* map_name) +{ + struct bpf_map* map = bpf_object__find_map_by_name(obj, map_name); + + if (!map) + { + return 1; + } + + char full_path[255]; + snprintf(full_path, sizeof(full_path), "%s/%s", XDP_MAP_PIN_DIR, map_name); + + return bpf_map__unpin(map, full_path); +} + +/** + * Retrieves a map FD on the file system (pinned). + * + * @param pin_dir The pin directory. + * @param map_name The map name. + * + * @return The map FD or -1 on error. + */ +int GetMapPinFd(const char* pin_dir, const char* map_name) +{ + char full_path[255]; + snprintf(full_path, sizeof(full_path), "%s/%s", pin_dir, map_name); + + return bpf_obj_get(full_path); } \ No newline at end of file diff --git a/src/loader/utils/xdp.h b/src/loader/utils/xdp.h index 77d46ff..6561da3 100644 --- a/src/loader/utils/xdp.h +++ b/src/loader/utils/xdp.h @@ -4,14 +4,26 @@ #include -#include #include #include #define XDP_OBJ_PATH "/etc/xdpfw/xdp_prog.o" +#define XDP_MAP_PIN_DIR "/sys/fs/bpf/xdpfw" 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, char** mode, int ifidx, u8 detach, cmdline_t *cmd); -void UpdateFilters(int map_filters, config__t *cfg); \ No newline at end of file +struct bpf_object* GetBpfObj(struct xdp_program* prog); + +int AttachXdp(struct xdp_program *prog, char** mode, int ifidx, int detach, int force_skb, int force_offload); + +int DeleteFilter(int map_filters, u32 idx); +void DeleteFilters(int map_filters); + +int UpdateFilter(int map_filters, filter_t* filter, int idx); +void UpdateFilters(int map_filters, config__t *cfg); + +int PinBpfMap(struct bpf_object* obj, const char* pin_dir, const char* map_name); +int UnpinBpfMap(struct bpf_object* obj, const char* pin_dir, const char* map_name); +int GetMapPinFd(const char* pin_dir, const char* map_name); \ No newline at end of file From e392b7355b3e492d7668c8e336c75a555d0f581a Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:37:13 -0500 Subject: [PATCH 02/19] Rename cfgfile to cfg_file in Loader. --- src/loader/utils/cmdline.c | 2 +- src/loader/utils/cmdline.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/loader/utils/cmdline.c b/src/loader/utils/cmdline.c index 3e8561b..207effd 100644 --- a/src/loader/utils/cmdline.c +++ b/src/loader/utils/cmdline.c @@ -36,7 +36,7 @@ void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]) switch (c) { case 'c': - cmd->cfgfile = optarg; + cmd->cfg_file = optarg; break; diff --git a/src/loader/utils/cmdline.h b/src/loader/utils/cmdline.h index ac1a0b9..c065644 100644 --- a/src/loader/utils/cmdline.h +++ b/src/loader/utils/cmdline.h @@ -6,7 +6,7 @@ struct cmdline { - char *cfgfile; + char *cfg_file; unsigned int offload : 1; unsigned int skb : 1; unsigned int time; From f261cf71991a67b1182d80d22c53982ac048851c Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:38:10 -0500 Subject: [PATCH 03/19] Update and add helper functions. --- src/loader/utils/helpers.c | 19 ++++++++++++++++++- src/loader/utils/helpers.h | 5 ++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/loader/utils/helpers.c b/src/loader/utils/helpers.c index f4d4963..d8d95bd 100644 --- a/src/loader/utils/helpers.c +++ b/src/loader/utils/helpers.c @@ -48,7 +48,11 @@ ip_range_t ParseIpCidr(const char *ip) ip_range_t ret = {0}; ret.cidr = 32; - char *token = strtok((char *) ip, "/"); + char ip_copy[INET_ADDRSTRLEN + 3]; + strncpy(ip_copy, ip, sizeof(ip_copy) - 1); + ip_copy[sizeof(ip_copy) - 1] = '\0'; + + char *token = strtok((char *) ip_copy, "/"); if (token) { @@ -104,4 +108,17 @@ void PrintToolInfo() " /_/\\_\\____/|_| |_| |_|_| \\___| \\_/\\_/ \\__,_|_|_|\n" "\n\n" ); +} + +/** + * Retrieves nanoseconds since system boot. + * + * @return The current nanoseconds since the system started. + */ +u64 GetBootNanoTime() +{ + struct sysinfo sys; + sysinfo(&sys); + + return sys.uptime * 1e9; } \ No newline at end of file diff --git a/src/loader/utils/helpers.h b/src/loader/utils/helpers.h index 68b67bd..5ee0e4d 100644 --- a/src/loader/utils/helpers.h +++ b/src/loader/utils/helpers.h @@ -7,6 +7,8 @@ #include #include +#include + struct ip_range { u32 ip; @@ -19,4 +21,5 @@ void PrintHelpMenu(); void SignalHndl(int code); ip_range_t ParseIpCidr(const char* ip); const char* GetProtocolStrById(int id); -void PrintToolInfo(); \ No newline at end of file +void PrintToolInfo(); +u64 GetBootNanoTime(); \ No newline at end of file From ff1ac4e8175031d4c0933eb56e280e44657dfb0b Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:38:45 -0500 Subject: [PATCH 04/19] Add support for IP range drop and make features more modular. --- src/xdp/prog.c | 51 +++++++++++++++++++++++++++------------- src/xdp/utils/helpers.c | 38 +++++++++++++++++++++++++++++- src/xdp/utils/helpers.h | 4 ++++ src/xdp/utils/logging.c | 4 +++- src/xdp/utils/logging.h | 2 ++ src/xdp/utils/maps.h | 52 +++++++++++++++++++++++++---------------- src/xdp/utils/rl.c | 4 +++- src/xdp/utils/rl.h | 2 ++ 8 files changed, 118 insertions(+), 39 deletions(-) diff --git a/src/xdp/prog.c b/src/xdp/prog.c index 6425b91..d631312 100644 --- a/src/xdp/prog.c +++ b/src/xdp/prog.c @@ -44,9 +44,6 @@ int xdp_prog_main(struct xdp_md *ctx) return XDP_PASS; } - u8 action = 0; - u64 block_time = 1; - // Initialize IP headers. struct iphdr *iph = NULL; struct ipv6hdr *iph6 = NULL; @@ -62,7 +59,7 @@ int xdp_prog_main(struct xdp_md *ctx) return XDP_DROP; } - memcpy(&src_ip6, &iph6->saddr.in6_u.u6_addr32, sizeof(src_ip6)); + memcpy(&src_ip6, iph6->saddr.in6_u.u6_addr32, sizeof(src_ip6)); } else { @@ -74,28 +71,29 @@ int xdp_prog_main(struct xdp_md *ctx) } } - // Check IP header protocols. + // We only want to process TCP, UDP, and ICMP packets. if ((iph6 && iph6->nexthdr != IPPROTO_UDP && iph6->nexthdr != IPPROTO_TCP && iph6->nexthdr != IPPROTO_ICMP) && (iph && iph->protocol != IPPROTO_UDP && iph->protocol != IPPROTO_TCP && iph->protocol != IPPROTO_ICMP)) { return XDP_PASS; } - // Get stats map. + // Retrieve stats map value. u32 key = 0; stats_t*stats = bpf_map_lookup_elem(&map_stats, &key); + // Retrieve nanoseconds since system boot as timestamp. u64 now = bpf_ktime_get_ns(); - // Check blacklist map. + // Check block map. u64 *blocked = NULL; if (iph6) { - blocked = bpf_map_lookup_elem(&map_ip6_blacklist, &src_ip6); + blocked = bpf_map_lookup_elem(&map_block6, &src_ip6); } else if (iph) { - blocked = bpf_map_lookup_elem(&map_ip_blacklist, &iph->saddr); + blocked = bpf_map_lookup_elem(&map_block, &iph->saddr); } if (blocked != NULL && *blocked > 0) @@ -105,11 +103,11 @@ int xdp_prog_main(struct xdp_md *ctx) // Remove element from map. if (iph6) { - bpf_map_delete_elem(&map_ip6_blacklist, &src_ip6); + bpf_map_delete_elem(&map_block6, &src_ip6); } else if (iph) { - bpf_map_delete_elem(&map_ip_blacklist, &iph->saddr); + bpf_map_delete_elem(&map_block, &iph->saddr); } } else @@ -127,6 +125,21 @@ int xdp_prog_main(struct xdp_md *ctx) } } +#ifdef ENABLE_IP_RANGE_DROP + if (iph && CheckIpRangeDrop(iph->saddr)) + { +#ifdef DO_STATS_ON_IP_RANGE_DROP_MAP + if (stats) + { + stats->dropped++; + } +#endif + + return XDP_DROP; + } +#endif + +#ifdef ENABLE_FILTERS // Retrieve total packet length. u16 pkt_len = data_end - data; @@ -267,7 +280,10 @@ int xdp_prog_main(struct xdp_md *ctx) { UpdateIpStats(&pps, &bps, iph->saddr, src_port, protocol, pkt_len, now); } - + + int action = 0; + u64 block_time = 1; + for (int i = 0; i < MAX_FILTERS; i++) { u32 key = i; @@ -543,6 +559,7 @@ int xdp_prog_main(struct xdp_md *ctx) goto matched; } +#endif if (stats) { @@ -551,21 +568,22 @@ int xdp_prog_main(struct xdp_md *ctx) return XDP_PASS; - matched: +#ifdef ENABLE_FILTERS +matched: if (action == 0) { - // Before dropping, update the blacklist map. + // Before dropping, update the block map. if (block_time > 0) { u64 new_time = now + (block_time * NANO_TO_SEC); if (iph6) { - bpf_map_update_elem(&map_ip6_blacklist, &src_ip6, &new_time, BPF_ANY); + bpf_map_update_elem(&map_block6, &src_ip6, &new_time, BPF_ANY); } else if (iph) { - bpf_map_update_elem(&map_ip_blacklist, &iph->saddr, &new_time, BPF_ANY); + bpf_map_update_elem(&map_block, &iph->saddr, &new_time, BPF_ANY); } } @@ -585,6 +603,7 @@ int xdp_prog_main(struct xdp_md *ctx) } return XDP_PASS; +#endif } char _license[] SEC("license") = "GPL"; diff --git a/src/xdp/utils/helpers.c b/src/xdp/utils/helpers.c index 5b10d10..36d3785 100644 --- a/src/xdp/utils/helpers.c +++ b/src/xdp/utils/helpers.c @@ -1,5 +1,7 @@ #include +#include + /** * Checks if an IP is within a specific CIDR range. * @@ -12,4 +14,38 @@ static __always_inline int IsIpInRange(u32 src_ip, u32 net_ip, u8 cidr) { return !((src_ip ^ net_ip) & htonl(0xFFFFFFFFu << (32 - cidr))); -} \ No newline at end of file +} + +#ifdef ENABLE_IP_RANGE_DROP +/** + * Checks if the IP is in the IP range drop map. + * + * @param ip The IP address. + * + * @return 1 on yes or 0 on no. + */ +static __always_inline int CheckIpRangeDrop(u32 ip) +{ + LpmTrieKey key = + { + .prefix_len = 32, + .data = ip + }; + + u64 *lookup = bpf_map_lookup_elem(&map_range_drop, &key); + + if (lookup) + { + u32 bit_mask = *lookup >> 32; + u32 prefix = *lookup & 0xFFFFFFFF; + + // Check if matched. + if ((ip & bit_mask) == prefix) + { + return 1; + } + } + + return 0; +} +#endif \ No newline at end of file diff --git a/src/xdp/utils/helpers.h b/src/xdp/utils/helpers.h index 14b99e5..e268b73 100644 --- a/src/xdp/utils/helpers.h +++ b/src/xdp/utils/helpers.h @@ -34,6 +34,10 @@ static __always_inline int IsIpInRange(u32 src_ip, u32 net_ip, u8 cidr); +#ifdef ENABLE_IP_RANGE_DROP +static __always_inline int CheckIpRangeDrop(u32 ip); +#endif + // The source file is included directly below instead of compiled and linked as an object because when linking, there is no guarantee the compiler will inline the function (which is crucial for performance). // I'd prefer not to include the function logic inside of the header file. // More Info: https://stackoverflow.com/questions/24289599/always-inline-does-not-work-when-function-is-implemented-in-different-file diff --git a/src/xdp/utils/logging.c b/src/xdp/utils/logging.c index 0268ebf..9cda33b 100644 --- a/src/xdp/utils/logging.c +++ b/src/xdp/utils/logging.c @@ -4,6 +4,7 @@ #include #include +#if defined(ENABLE_FILTERS) && defined(ENABLE_FILTER_LOGGING) /** * Logs a message to the filter ringbuffer map. * @@ -50,4 +51,5 @@ static __always_inline int LogFilterMsg(struct iphdr* iph, struct ipv6hdr* iph6, } return 0; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/xdp/utils/logging.h b/src/xdp/utils/logging.h index 4b42072..d3c395d 100644 --- a/src/xdp/utils/logging.h +++ b/src/xdp/utils/logging.h @@ -5,7 +5,9 @@ #include #include +#if defined(ENABLE_FILTERS) && defined(ENABLE_FILTER_LOGGING) static __always_inline int LogFilterMsg(struct iphdr* iph, struct ipv6hdr* iph6, u16 src_port, u16 dst_port, u8 protocol, u64 now, u64 pps, u64 bps, int filter_id); +#endif // The source file is included directly below instead of compiled and linked as an object because when linking, there is no guarantee the compiler will inline the function (which is crucial for performance). // I'd prefer not to include the function logic inside of the header file. diff --git a/src/xdp/utils/maps.h b/src/xdp/utils/maps.h index f48b225..a1eebfe 100644 --- a/src/xdp/utils/maps.h +++ b/src/xdp/utils/maps.h @@ -5,14 +5,6 @@ #include -struct -{ - __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); - __uint(max_entries, MAX_FILTERS); - __type(key, u32); - __type(value, filter_t); -} map_filters SEC(".maps"); - struct { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); @@ -25,21 +17,40 @@ struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, MAX_TRACK_IPS); -#ifdef USE_FLOW_RL - __type(key, flow_t); -#else __type(key, u32); + __type(value, u64); +} map_block SEC(".maps"); + +struct +{ + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(max_entries, MAX_TRACK_IPS); + __type(key, u128); + __type(value, u64); +} map_block6 SEC(".maps"); + +#ifdef ENABLE_IP_RANGE_DROP +struct { + __uint(type, BPF_MAP_TYPE_LPM_TRIE); + __uint(max_entries, MAX_IP_RANGES); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, LpmTrieKey); + __type(value, u64); +} map_range_drop SEC(".maps"); #endif - __type(value, ip_stats_t); -} map_ip_stats SEC(".maps"); +#ifdef ENABLE_FILTERS struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, MAX_TRACK_IPS); +#ifdef USE_FLOW_RL + __type(key, flow_t); +#else __type(key, u32); - __type(value, u64); -} map_ip_blacklist SEC(".maps"); +#endif + __type(value, ip_stats_t); +} map_ip_stats SEC(".maps"); struct { @@ -55,11 +66,11 @@ struct struct { - __uint(type, BPF_MAP_TYPE_LRU_HASH); - __uint(max_entries, MAX_TRACK_IPS); - __type(key, u128); - __type(value, u64); -} map_ip6_blacklist SEC(".maps"); + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __uint(max_entries, MAX_FILTERS); + __type(key, u32); + __type(value, filter_t); +} map_filters SEC(".maps"); #ifdef ENABLE_FILTER_LOGGING struct @@ -67,4 +78,5 @@ struct __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1 << 16); } map_filter_log SEC(".maps"); +#endif #endif \ No newline at end of file diff --git a/src/xdp/utils/rl.c b/src/xdp/utils/rl.c index f02f0aa..31b124a 100644 --- a/src/xdp/utils/rl.c +++ b/src/xdp/utils/rl.c @@ -1,5 +1,6 @@ #include +#ifdef ENABLE_FILTERS /** * Updates IPv4 client stats. * @@ -128,4 +129,5 @@ static __always_inline void UpdateIp6Stats(u64 *pps, u64 *bps, u128 *ip, u16 por bpf_map_update_elem(&map_ip6_stats, ip, &new, BPF_ANY); #endif } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/xdp/utils/rl.h b/src/xdp/utils/rl.h index 85ec5eb..13a1127 100644 --- a/src/xdp/utils/rl.h +++ b/src/xdp/utils/rl.h @@ -6,8 +6,10 @@ #include +#ifdef ENABLE_FILTERS static __always_inline void UpdateIpStats(u64 *pps, u64 *bps, u32 ip, u16 port, u8 protocol, u16 pkt_len, u64 now); static __always_inline void UpdateIp6Stats(u64 *pps, u64 *bps, u128 *ip, u16 port, u8 protocol, u16 pkt_len, u64 now); +#endif // The source file is included directly below instead of compiled and linked as an object because when linking, there is no guarantee the compiler will inline the function (which is crucial for performance). // I'd prefer not to include the function logic inside of the header file. From 2019029a4d10d9679d9475b0a22c9d31a04e30b3 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:39:11 -0500 Subject: [PATCH 05/19] Continue preparing Loader for new features. --- src/loader/prog.c | 150 ++++++-- src/loader/utils/config.c | 714 +++++++++++++++++++++----------------- src/loader/utils/config.h | 9 +- src/loader/utils/xdp.c | 124 +++++++ src/loader/utils/xdp.h | 12 +- 5 files changed, 669 insertions(+), 340 deletions(-) diff --git a/src/loader/prog.c b/src/loader/prog.c index 4d86c06..f35b598 100644 --- a/src/loader/prog.c +++ b/src/loader/prog.c @@ -22,16 +22,47 @@ int cont = 1; int doing_stats = 0; /** - * Unpins filter-specific BPF maps from file system. + * Unpins required BPF maps from file system. * * @param cfg A pointer to the config structure. * @param obj A pointer to the BPF object. * @param ignore_errors Whether to ignore errors. */ -static void UnpinFilterMaps(config__t* cfg, struct bpf_object* obj, int ignore_errors) +static void UnpinNeededMaps(config__t* cfg, struct bpf_object* obj, int ignore_errors) { int ret; + // Unpin block map. + if ((ret = UnpinBpfMap(obj, XDP_MAP_PIN_DIR, "map_block")) != 0) + { + if (!ignore_errors) + { + LogMsg(cfg, 1, 0, "[WARNING] Failed to un-pin BPF map 'map_block' from file system (%d).", ret); + } + } + + // Unpin block (IPv6) map. + if ((ret = UnpinBpfMap(obj, XDP_MAP_PIN_DIR, "map_block6")) != 0) + { + if (!ignore_errors) + { + LogMsg(cfg, 1, 0, "[WARNING] Failed to un-pin BPF map 'map_block6' from file system (%d).", ret); + } + } + +#ifdef ENABLE_IP_RANGE_DROP + // Unpin IPv4 range drop map. + if ((ret = UnpinBpfMap(obj, XDP_MAP_PIN_DIR, "map_range_drop")) != 0) + { + if (!ignore_errors) + { + LogMsg(cfg, 1, 0, "[WARNING] Failed to un-pin BPF map 'map_range_drop' from file system (%d).", ret); + } + } +#endif + +#ifdef ENABLE_FILTERS + // Unpin filters map. if ((ret = UnpinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filters")) != 0) { if (!ignore_errors) @@ -40,6 +71,8 @@ static void UnpinFilterMaps(config__t* cfg, struct bpf_object* obj, int ignore_e } } +#ifdef ENABLE_FILTER_LOGGING + // Unpin filters log map. if ((ret = UnpinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filter_log")) != 0) { if (!ignore_errors) @@ -47,6 +80,8 @@ static void UnpinFilterMaps(config__t* cfg, struct bpf_object* obj, int ignore_e LogMsg(cfg, 1, 0, "[WARNING] Failed to un-pin BPF map 'map_filter_log' from file system (%d).", ret); } } +#endif +#endif } int main(int argc, char *argv[]) @@ -55,7 +90,7 @@ int main(int argc, char *argv[]) // Parse the command line. cmdline_t cmd = {0}; - cmd.cfgfile = CONFIG_DEFAULT_PATH; + cmd.cfg_file = CONFIG_DEFAULT_PATH; cmd.verbose = -1; cmd.pin_maps = -1; cmd.update_time = -1; @@ -90,9 +125,9 @@ int main(int argc, char *argv[]) cfg_overrides.stdout_update_time = cmd.stdout_update_time; // Load config. - if ((ret = LoadConfig(&cfg, cmd.cfgfile, &cfg_overrides)) != 0) + if ((ret = LoadConfig(&cfg, cmd.cfg_file, &cfg_overrides)) != 0) { - fprintf(stderr, "[ERROR] Failed to load config from file system (%s)(%d).\n", cmd.cfgfile, ret); + fprintf(stderr, "[ERROR] Failed to load config from file system (%s)(%d).\n", cmd.cfg_file, ret); return EXIT_FAILURE; } @@ -185,29 +220,31 @@ int main(int argc, char *argv[]) LogMsg(&cfg, 2, 0, "Retrieving BPF map FDs..."); // Retrieve BPF maps. - int map_filters = FindMapFd(prog, "map_filters"); + int map_stats = FindMapFd(prog, "map_stats"); - // Check for valid maps. - if (map_filters < 0) + if (map_stats < 0) { - LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'map_filters' BPF map.\n"); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'map_stats' BPF map.\n"); return EXIT_FAILURE; } - LogMsg(&cfg, 3, 0, "map_filters FD => %d.", map_filters); - - int map_stats = FindMapFd(prog, "map_stats"); +#ifdef ENABLE_FILTERS + int map_filters = FindMapFd(prog, "map_filters"); - if (map_stats < 0) + // Check for valid maps. + if (map_filters < 0) { - LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'map_stats' BPF map.\n"); + LogMsg(&cfg, 0, 1, "[ERROR] Failed to find 'map_filters' BPF map.\n"); return EXIT_FAILURE; } + LogMsg(&cfg, 3, 0, "map_filters FD => %d.", map_filters); + #ifdef ENABLE_FILTER_LOGGING int map_filter_log = FindMapFd(prog, "map_filter_log"); + struct ring_buffer* rb = NULL; if (map_filter_log < 0) @@ -221,6 +258,20 @@ int main(int argc, char *argv[]) rb = ring_buffer__new(map_filter_log, HandleRbEvent, &cfg, NULL); } #endif +#endif + +#ifdef ENABLE_IP_RANGE_DROP + int map_range_drop = FindMapFd(prog, "map_range_drop"); + + if (map_range_drop < 0) + { + LogMsg(&cfg, 1, 0, "[WARNING] Failed to find 'map_range_drop' BPF map. IP range drops will be disabled..."); + } + else + { + LogMsg(&cfg, 3, 0, "map_range_drop FD => %d.", map_range_drop); + } +#endif LogMsg(&cfg, 3, 0, "map_stats FD => %d.", map_stats); @@ -233,8 +284,41 @@ int main(int argc, char *argv[]) // There are times where the BPF maps from the last run weren't cleaned up properly. // So it's best to attempt to unpin the maps before pinning while ignoring errors. - UnpinFilterMaps(&cfg, obj, 1); + UnpinNeededMaps(&cfg, obj, 1); + + // Pin the block maps. + if ((ret = PinBpfMap(obj, XDP_MAP_PIN_DIR, "map_block")) != 0) + { + LogMsg(&cfg, 1, 0, "[WARNING] Failed to pin 'map_block' to file system (%d)...", ret); + } + else + { + LogMsg(&cfg, 3, 0, "BPF map 'map_block' pinned to '%s/map_block'.", XDP_MAP_PIN_DIR); + } + + if ((ret = PinBpfMap(obj, XDP_MAP_PIN_DIR, "map_block6")) != 0) + { + LogMsg(&cfg, 1, 0, "[WARNING] Failed to pin 'map_block6' to file system (%d)...", ret); + } + else + { + LogMsg(&cfg, 3, 0, "BPF map 'map_block6' pinned to '%s/map_block6'.", XDP_MAP_PIN_DIR); + } + +#ifdef ENABLE_IP_RANGE_DROP + // Pin the IPv4 range drop map. + if ((ret = PinBpfMap(obj, XDP_MAP_PIN_DIR, "map_range_drop")) != 0) + { + LogMsg(&cfg, 1, 0, "[WARNING] Failed to pin 'map_range_drop' to file system (%d)...", ret); + } + else + { + LogMsg(&cfg, 3, 0, "BPF map 'map_range_drop' pinned to '%s/map_range_drop'.", XDP_MAP_PIN_DIR); + } +#endif +#ifdef ENABLE_FILTERS + // Pin the filters map. if ((ret = PinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filters")) != 0) { LogMsg(&cfg, 1, 0, "[WARNING] Failed to pin 'map_filters' to file system (%d)...", ret); @@ -244,6 +328,8 @@ int main(int argc, char *argv[]) LogMsg(&cfg, 3, 0, "BPF map 'map_filters' pinned to '%s/map_filters'.", XDP_MAP_PIN_DIR); } +#ifdef ENABLE_FILTER_LOGGING + // Pin the filters log map. if ((ret = PinBpfMap(obj, XDP_MAP_PIN_DIR, "map_filter_log")) != 0) { LogMsg(&cfg, 1, 0, "[WARNING] Failed to pin 'map_filter_log' to file system (%d)...", ret); @@ -252,12 +338,26 @@ int main(int argc, char *argv[]) { LogMsg(&cfg, 3, 0, "BPF map 'map_filter_log' pinned to '%s/map_filter_log'.", XDP_MAP_PIN_DIR); } +#endif +#endif } +#ifdef ENABLE_FILTERS LogMsg(&cfg, 2, 0, "Updating filters..."); - // Update BPF maps. + // Update filters. UpdateFilters(map_filters, &cfg); +#endif + +#ifdef ENABLE_IP_RANGE_DROP + if (map_range_drop > -1) + { + LogMsg(&cfg, 2, 0, "Updating IP drop ranges..."); + + // Update IP range drops. + UpdateRangeDrops(map_range_drop, &cfg); + } +#endif // Signal. signal(SIGINT, SignalHndl); @@ -299,17 +399,19 @@ int main(int argc, char *argv[]) if (cfg.update_time > 0 && (cur_time - last_update_check) > cfg.update_time) { // Check if config file have been modified - if (stat(cmd.cfgfile, &conf_stat) == 0 && conf_stat.st_mtime > last_config_check) { - // Update config. - if ((ret = LoadConfig(&cfg, cmd.cfgfile, &cfg_overrides)) != 0) + if (stat(cmd.cfg_file, &conf_stat) == 0 && conf_stat.st_mtime > last_config_check) { + // Reload config. + if ((ret = LoadConfig(&cfg, cmd.cfg_file, &cfg_overrides)) != 0) { LogMsg(&cfg, 1, 0, "[WARNING] Failed to load config after update check (%d)...\n", ret); } - // Update BPF maps. +#ifdef ENABLE_FILTERS + // Update filters. UpdateFilters(map_filters, &cfg); +#endif - // Update timer + // Update last check timer last_config_check = time(NULL); // Make sure we set doing stats if needed. @@ -332,7 +434,7 @@ int main(int argc, char *argv[]) } } -#ifdef ENABLE_FILTER_LOGGING +#if defined(ENABLE_FILTERS) && defined(ENABLE_FILTER_LOGGING) PollFiltersRb(rb); #endif @@ -343,7 +445,7 @@ int main(int argc, char *argv[]) LogMsg(&cfg, 2, 0, "Cleaning up..."); -#ifdef ENABLE_FILTER_LOGGING +#if defined(ENABLE_FILTERS) && defined(ENABLE_FILTER_LOGGING) if (rb) { ring_buffer__free(rb); @@ -365,7 +467,7 @@ int main(int argc, char *argv[]) struct bpf_object* obj = GetBpfObj(prog); - UnpinFilterMaps(&cfg, obj, 0); + UnpinNeededMaps(&cfg, obj, 0); } // Lastly, close the XDP program. diff --git a/src/loader/utils/config.c b/src/loader/utils/config.c index 8cabc11..2c2cc03 100644 --- a/src/loader/utils/config.c +++ b/src/loader/utils/config.c @@ -9,7 +9,7 @@ * * @return 0 on success or 1 on error. */ -int LoadConfig(config__t *cfg, char *cfg_file, config_overrides_t* overrides) +int LoadConfig(config__t *cfg, const char* cfg_file, config_overrides_t* overrides) { int ret; @@ -159,9 +159,9 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) int verbose; - if (config_lookup_int(&conf, "verbose", &verbose) == CONFIG_TRUE || overrides->verbose > -1) + if (config_lookup_int(&conf, "verbose", &verbose) == CONFIG_TRUE || (overrides && overrides->verbose > -1)) { - if (overrides->verbose > -1) + if (overrides && overrides->verbose > -1) { cfg->verbose = overrides->verbose; } @@ -182,7 +182,7 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) cfg->log_file = NULL; } - if (overrides->log_file != NULL) + if (overrides && overrides->log_file != NULL) { if (strlen(overrides->log_file) > 0) { @@ -219,7 +219,7 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) cfg->interface = NULL; } - if (overrides->interface != NULL) + if (overrides && overrides->interface != NULL) { cfg->interface = strdup(overrides->interface); } @@ -234,7 +234,7 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) if (config_lookup_bool(&conf, "pin_maps", &pin_maps) == CONFIG_TRUE || (overrides && overrides->pin_maps > -1)) { - if (overrides->pin_maps > -1) + if (overrides && overrides->pin_maps > -1) { cfg->pin_maps = overrides->pin_maps; } @@ -249,7 +249,7 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) if (config_lookup_int(&conf, "update_time", &update_time) == CONFIG_TRUE || (overrides && overrides->update_time > -1)) { - if (overrides->update_time > -1) + if (overrides && overrides->update_time > -1) { cfg->update_time = overrides->update_time; } @@ -264,7 +264,7 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) if (config_lookup_bool(&conf, "no_stats", &no_stats) == CONFIG_TRUE || (overrides && overrides->no_stats > -1)) { - if (overrides->no_stats > -1) + if (overrides && overrides->no_stats > -1) { cfg->no_stats = overrides->no_stats; } @@ -279,7 +279,7 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) if (config_lookup_bool(&conf, "stats_per_second", &stats_per_second) == CONFIG_TRUE || (overrides && overrides->stats_per_second > -1)) { - if (overrides->stats_per_second > -1) + if (overrides && overrides->stats_per_second > -1) { cfg->stats_per_second = overrides->stats_per_second; } @@ -294,7 +294,7 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) if (config_lookup_int(&conf, "stdout_update_time", &stdout_update_time) == CONFIG_TRUE || (overrides && overrides->stdout_update_time > -1)) { - if (overrides->stdout_update_time > -1) + if (overrides && overrides->stdout_update_time > -1) { cfg->stdout_update_time = overrides->stdout_update_time; } @@ -304,332 +304,349 @@ int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides) } } - // Read filters in map_filters structure. + // Read filters. setting = config_lookup(&conf, "filters"); - // Check if filters map is valid. If not, not a biggie since they aren't required. - if (setting == NULL) + if (setting && config_setting_is_list(setting)) { - LogMsg(cfg, 0, 1, "Error from LibConfig when reading 'filters' array - %s.", config_error_text(&conf)); - - config_destroy(&conf); + for (int i = 0; i < config_setting_length(setting); i++) + { + filter_t* filter = &cfg->filters[i]; - return 1; - } + config_setting_t* filter_cfg = config_setting_get_elem(setting, i); - for (int i = 0; i < config_setting_length(setting); i++) - { - filter_t* filter = &cfg->filters[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!)..."); - config_setting_t* filter_cfg = config_setting_get_elem(setting, i); + continue; + } - 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!)..."); + // Enabled. + int enabled; - continue; - } + if (config_setting_lookup_bool(filter_cfg, "enabled", &enabled) == CONFIG_TRUE) + { + filter->enabled = enabled; + } - // Enabled. - int enabled; + // Log. + int log; - if (config_setting_lookup_bool(filter_cfg, "enabled", &enabled) == CONFIG_TRUE) - { - filter->enabled = enabled; - } + if (config_setting_lookup_bool(filter_cfg, "log", &log) == CONFIG_TRUE) + { + filter->log = log; + } - // Log. - int log; + // Action (required). + int action; - if (config_setting_lookup_bool(filter_cfg, "log", &log) == CONFIG_TRUE) - { - filter->log = log; - } + if (config_setting_lookup_int(filter_cfg, "action", &action) == CONFIG_TRUE) + { + filter->action = action; + } - // Action (required). - int action; + // Source IP (not required). + const char *sip; - if (config_setting_lookup_int(filter_cfg, "action", &action) == CONFIG_TRUE) - { - filter->action = action; - } + if (config_setting_lookup_string(filter_cfg, "src_ip", &sip) == CONFIG_TRUE) + { + ip_range_t ip = ParseIpCidr(sip); - // Source IP (not required). - const char *sip; + filter->src_ip = ip.ip; + filter->src_cidr = ip.cidr; + } - if (config_setting_lookup_string(filter_cfg, "src_ip", &sip) == CONFIG_TRUE) - { - ip_range_t ip = ParseIpCidr(sip); + // Destination IP (not required). + const char *dip; - filter->src_ip = ip.ip; - filter->src_cidr = ip.cidr; - } + if (config_setting_lookup_string(filter_cfg, "dst_ip", &dip) == CONFIG_TRUE) + { + ip_range_t ip = ParseIpCidr(dip); - // Destination IP (not required). - const char *dip; + filter->dst_ip = ip.ip; + filter->dst_cidr = ip.cidr; + } - if (config_setting_lookup_string(filter_cfg, "dst_ip", &dip) == CONFIG_TRUE) - { - ip_range_t ip = ParseIpCidr(dip); + // Source IP (IPv6) (not required). + const char *sip6; - filter->dst_ip = ip.ip; - filter->dst_cidr = ip.cidr; - } + if (config_setting_lookup_string(filter_cfg, "src_ip6", &sip6) == CONFIG_TRUE) + { + struct in6_addr in; - // Source IP (IPv6) (not required). - const char *sip6; + inet_pton(AF_INET6, sip6, &in); - if (config_setting_lookup_string(filter_cfg, "src_ip6", &sip6) == CONFIG_TRUE) - { - struct in6_addr in; + memcpy(filter->src_ip6, in.__in6_u.__u6_addr32, 4); + } - inet_pton(AF_INET6, sip6, &in); + // Destination IP (IPv6) (not required). + const char *dip6; - memcpy(filter->src_ip6, in.__in6_u.__u6_addr32, 4); - } + if (config_setting_lookup_string(filter_cfg, "dst_ip6", &dip6) == CONFIG_TRUE) + { + struct in6_addr in; - // Destination IP (IPv6) (not required). - const char *dip6; + inet_pton(AF_INET6, dip6, &in); - if (config_setting_lookup_string(filter_cfg, "dst_ip6", &dip6) == CONFIG_TRUE) - { - struct in6_addr in; + memcpy(filter->dst_ip6, in.__in6_u.__u6_addr32, 4); + } - inet_pton(AF_INET6, dip6, &in); + // Minimum TTL (not required). + int min_ttl; - memcpy(filter->dst_ip6, in.__in6_u.__u6_addr32, 4); - } + if (config_setting_lookup_int(filter_cfg, "min_ttl", &min_ttl) == CONFIG_TRUE) + { + filter->min_ttl = (u8)min_ttl; + filter->do_min_ttl = 1; + } - // Minimum TTL (not required). - int min_ttl; + // Maximum TTL (not required). + int max_ttl; - if (config_setting_lookup_int(filter_cfg, "min_ttl", &min_ttl) == CONFIG_TRUE) - { - filter->min_ttl = (u8)min_ttl; - filter->do_min_ttl = 1; - } + if (config_setting_lookup_int(filter_cfg, "max_ttl", &max_ttl) == CONFIG_TRUE) + { + filter->max_ttl = (u8)max_ttl; + filter->do_max_ttl = 1; + } - // Maximum TTL (not required). - int max_ttl; + // Minimum length (not required). + int min_len; - if (config_setting_lookup_int(filter_cfg, "max_ttl", &max_ttl) == CONFIG_TRUE) - { - filter->max_ttl = (u8)max_ttl; - filter->do_max_ttl = 1; - } + if (config_setting_lookup_int(filter_cfg, "min_len", &min_len) == CONFIG_TRUE) + { + filter->min_len = min_len; + filter->do_min_len = 1; + } - // Minimum length (not required). - int min_len; + // Maximum length (not required). + int max_len; - if (config_setting_lookup_int(filter_cfg, "min_len", &min_len) == CONFIG_TRUE) - { - filter->min_len = min_len; - filter->do_min_len = 1; - } + if (config_setting_lookup_int(filter_cfg, "max_len", &max_len) == CONFIG_TRUE) + { + filter->max_len = max_len; + filter->do_max_len = 1; + } - // Maximum length (not required). - int max_len; + // TOS (not required). + int tos; - if (config_setting_lookup_int(filter_cfg, "max_len", &max_len) == CONFIG_TRUE) - { - filter->max_len = max_len; - filter->do_max_len = 1; - } + if (config_setting_lookup_int(filter_cfg, "tos", &tos) == CONFIG_TRUE) + { + filter->tos = (u8)tos; + filter->do_tos = 1; + } - // TOS (not required). - int tos; + // PPS (not required). + long long pps; - if (config_setting_lookup_int(filter_cfg, "tos", &tos) == CONFIG_TRUE) - { - filter->tos = (u8)tos; - filter->do_tos = 1; - } + if (config_setting_lookup_int64(filter_cfg, "pps", &pps) == CONFIG_TRUE) + { + filter->pps = pps; + filter->do_pps = 1; + } - // PPS (not required). - long long pps; + // BPS (not required). + long long bps; - if (config_setting_lookup_int64(filter_cfg, "pps", &pps) == CONFIG_TRUE) - { - filter->pps = pps; - filter->do_pps = 1; - } + if (config_setting_lookup_int64(filter_cfg, "bps", &bps) == CONFIG_TRUE) + { + filter->bps = bps; + filter->do_bps = 1; + } - // BPS (not required). - long long bps; + // Block time (default 1). + long long block_time; - if (config_setting_lookup_int64(filter_cfg, "bps", &bps) == CONFIG_TRUE) - { - filter->bps = bps; - filter->do_bps = 1; - } + if (config_setting_lookup_int64(filter_cfg, "block_time", &block_time) == CONFIG_TRUE) + { + filter->block_time = block_time; + } + else + { + filter->block_time = 1; + } - // Block time (default 1). - long long block_time; + /* TCP options */ + // Enabled. + int tcpenabled; - if (config_setting_lookup_int64(filter_cfg, "block_time", &block_time) == CONFIG_TRUE) - { - filter->block_time = block_time; - } - else - { - filter->block_time = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_enabled", &tcpenabled) == CONFIG_TRUE) + { + filter->tcpopts.enabled = tcpenabled; + } - /* TCP options */ - // Enabled. - int tcpenabled; + // Source port. + int tcpsport; - if (config_setting_lookup_bool(filter_cfg, "tcp_enabled", &tcpenabled) == CONFIG_TRUE) - { - filter->tcpopts.enabled = tcpenabled; - } + if (config_setting_lookup_int(filter_cfg, "tcp_sport", &tcpsport) == CONFIG_TRUE) + { + filter->tcpopts.sport = (u16)tcpsport; + filter->tcpopts.do_sport = 1; + } - // Source port. - int tcpsport; + // Destination port. + int tcpdport; - if (config_setting_lookup_int(filter_cfg, "tcp_sport", &tcpsport) == CONFIG_TRUE) - { - filter->tcpopts.sport = (u16)tcpsport; - filter->tcpopts.do_sport = 1; - } + if (config_setting_lookup_int(filter_cfg, "tcp_dport", &tcpdport) == CONFIG_TRUE) + { + filter->tcpopts.dport = (u16)tcpdport; + filter->tcpopts.do_dport = 1; + } - // Destination port. - int tcpdport; + // URG flag. + int tcpurg; - if (config_setting_lookup_int(filter_cfg, "tcp_dport", &tcpdport) == CONFIG_TRUE) - { - filter->tcpopts.dport = (u16)tcpdport; - filter->tcpopts.do_dport = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_urg", &tcpurg) == CONFIG_TRUE) + { + filter->tcpopts.urg = tcpurg; + filter->tcpopts.do_urg = 1; + } - // URG flag. - int tcpurg; + // ACK flag. + int tcpack; - if (config_setting_lookup_bool(filter_cfg, "tcp_urg", &tcpurg) == CONFIG_TRUE) - { - filter->tcpopts.urg = tcpurg; - filter->tcpopts.do_urg = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_ack", &tcpack) == CONFIG_TRUE) + { + filter->tcpopts.ack = tcpack; + filter->tcpopts.do_ack = 1; + } + + // RST flag. + int tcprst; - // ACK flag. - int tcpack; + if (config_setting_lookup_bool(filter_cfg, "tcp_rst", &tcprst) == CONFIG_TRUE) + { + filter->tcpopts.rst = tcprst; + filter->tcpopts.do_rst = 1; + } - if (config_setting_lookup_bool(filter_cfg, "tcp_ack", &tcpack) == CONFIG_TRUE) - { - filter->tcpopts.ack = tcpack; - filter->tcpopts.do_ack = 1; - } - - // RST flag. - int tcprst; + // PSH flag. + int tcppsh; - if (config_setting_lookup_bool(filter_cfg, "tcp_rst", &tcprst) == CONFIG_TRUE) - { - filter->tcpopts.rst = tcprst; - filter->tcpopts.do_rst = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_psh", &tcppsh) == CONFIG_TRUE) + { + filter->tcpopts.psh = tcppsh; + filter->tcpopts.do_psh = 1; + } - // PSH flag. - int tcppsh; + // SYN flag. + int tcpsyn; - if (config_setting_lookup_bool(filter_cfg, "tcp_psh", &tcppsh) == CONFIG_TRUE) - { - filter->tcpopts.psh = tcppsh; - filter->tcpopts.do_psh = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_syn", &tcpsyn) == CONFIG_TRUE) + { + filter->tcpopts.syn = tcpsyn; + filter->tcpopts.do_syn = 1; + } - // SYN flag. - int tcpsyn; + // FIN flag. + int tcpfin; - if (config_setting_lookup_bool(filter_cfg, "tcp_syn", &tcpsyn) == CONFIG_TRUE) - { - filter->tcpopts.syn = tcpsyn; - filter->tcpopts.do_syn = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_fin", &tcpfin) == CONFIG_TRUE) + { + filter->tcpopts.fin = tcpfin; + filter->tcpopts.do_fin = 1; + } - // FIN flag. - int tcpfin; + // ECE flag. + int tcpece; - if (config_setting_lookup_bool(filter_cfg, "tcp_fin", &tcpfin) == CONFIG_TRUE) - { - filter->tcpopts.fin = tcpfin; - filter->tcpopts.do_fin = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_ece", &tcpece) == CONFIG_TRUE) + { + filter->tcpopts.ece = tcpece; + filter->tcpopts.do_ece = 1; + } - // ECE flag. - int tcpece; + // CWR flag. + int tcpcwr; - if (config_setting_lookup_bool(filter_cfg, "tcp_ece", &tcpece) == CONFIG_TRUE) - { - filter->tcpopts.ece = tcpece; - filter->tcpopts.do_ece = 1; - } + if (config_setting_lookup_bool(filter_cfg, "tcp_cwr", &tcpcwr) == CONFIG_TRUE) + { + filter->tcpopts.cwr = tcpcwr; + filter->tcpopts.do_cwr = 1; + } - // CWR flag. - int tcpcwr; + /* UDP options */ + // Enabled. + int udpenabled; - if (config_setting_lookup_bool(filter_cfg, "tcp_cwr", &tcpcwr) == CONFIG_TRUE) - { - filter->tcpopts.cwr = tcpcwr; - filter->tcpopts.do_cwr = 1; - } + if (config_setting_lookup_bool(filter_cfg, "udp_enabled", &udpenabled) == CONFIG_TRUE) + { + filter->udpopts.enabled = udpenabled; + } - /* UDP options */ - // Enabled. - int udpenabled; + // Source port. + int udpsport; - if (config_setting_lookup_bool(filter_cfg, "udp_enabled", &udpenabled) == CONFIG_TRUE) - { - filter->udpopts.enabled = udpenabled; - } + if (config_setting_lookup_int(filter_cfg, "udp_sport", &udpsport) == CONFIG_TRUE) + { + filter->udpopts.sport = (u16)udpsport; + filter->udpopts.do_sport = 1; + } - // Source port. - int udpsport; + // Destination port. + int udpdport; - if (config_setting_lookup_int(filter_cfg, "udp_sport", &udpsport) == CONFIG_TRUE) - { - filter->udpopts.sport = (u16)udpsport; - filter->udpopts.do_sport = 1; - } + if (config_setting_lookup_int(filter_cfg, "udp_dport", &udpdport) == CONFIG_TRUE) + { + filter->udpopts.dport = (u16)udpdport; + filter->udpopts.do_dport = 1; + } - // Destination port. - int udpdport; + /* ICMP options */ + // Enabled. + int icmpenabled; - if (config_setting_lookup_int(filter_cfg, "udp_dport", &udpdport) == CONFIG_TRUE) - { - filter->udpopts.dport = (u16)udpdport; - filter->udpopts.do_dport = 1; - } + if (config_setting_lookup_bool(filter_cfg, "icmp_enabled", &icmpenabled) == CONFIG_TRUE) + { + filter->icmpopts.enabled = icmpenabled; + } - /* ICMP options */ - // Enabled. - int icmpenabled; + // ICMP code. + int icmpcode; - if (config_setting_lookup_bool(filter_cfg, "icmp_enabled", &icmpenabled) == CONFIG_TRUE) - { - filter->icmpopts.enabled = icmpenabled; - } + if (config_setting_lookup_int(filter_cfg, "icmp_code", &icmpcode) == CONFIG_TRUE) + { + filter->icmpopts.code = (u8)icmpcode; + filter->icmpopts.do_code = 1; + } - // ICMP code. - int icmpcode; + // ICMP type. + int icmptype; - if (config_setting_lookup_int(filter_cfg, "icmp_code", &icmpcode) == CONFIG_TRUE) - { - filter->icmpopts.code = (u8)icmpcode; - filter->icmpopts.do_code = 1; + if (config_setting_lookup_int(filter_cfg, "icmp_type", &icmptype) == CONFIG_TRUE) + { + filter->icmpopts.type = (u8)icmptype; + filter->icmpopts.do_type = 1; + } + + // Make sure filter is set. + filter->set = 1; } + } - // ICMP type. - int icmptype; + // Read IP range drops. + setting = config_lookup(&conf, "ip_drop_ranges"); - if (config_setting_lookup_int(filter_cfg, "icmp_type", &icmptype) == CONFIG_TRUE) + if (setting && config_setting_is_list(setting)) + { + for (int i = 0; i < config_setting_length(setting) && i < MAX_IP_RANGES; i++) { - filter->icmpopts.type = (u8)icmptype; - filter->icmpopts.do_type = 1; - } + const char* range = cfg->drop_ranges[i]; - // Make sure filter is set. - filter->set = 1; + if (cfg->drop_ranges[i]) + { + free((void*)cfg->drop_ranges[i]); + cfg->drop_ranges[i] = NULL; + } + + const char* new_range = config_setting_get_string_elem(setting, i); + + if (new_range) + { + cfg->drop_ranges[i] = strdup(new_range); + } + } } config_destroy(&conf); @@ -882,6 +899,27 @@ int SaveCfg(config__t* cfg, const char* file_path) } } + // Add IP ranges. + config_setting_t* ip_drop_ranges = config_setting_add(root, "ip_drop_ranges", CONFIG_TYPE_LIST); + + if (ip_drop_ranges) + { + for (int i = 0; i < MAX_IP_RANGES; i++) + { + const char* range = cfg->drop_ranges[i]; + + if (range) + { + config_setting_t* elem = config_setting_add(ip_drop_ranges, NULL, CONFIG_TYPE_STRING); + + if (elem) + { + config_setting_set_string(elem, range); + } + } + } + } + // Write config to file. file = fopen(file_path, "w"); @@ -900,14 +938,79 @@ int SaveCfg(config__t* cfg, const char* file_path) return 0; } +/** + * Sets the default values for a filter. + * + * @param filter A pointer to the filter. + * + * @return void + */ +void SetFilterDefaults(filter_t* filter) +{ + filter->set = 0; + filter->enabled = 1; + + filter->log = 0; + + filter->action = 1; + filter->src_ip = 0; + filter->dst_ip = 0; + + memset(filter->src_ip6, 0, 4); + memset(filter->dst_ip6, 0, 4); + + filter->do_min_len = 0; + filter->min_len = 0; + + filter->do_max_len = 0; + filter->max_len = 65535; + + 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; + + filter->do_bps = 0; + filter->bps = 0; + + filter->block_time = 1; + + filter->tcpopts.enabled = 0; + filter->tcpopts.do_dport = 0; + filter->tcpopts.do_dport = 0; + filter->tcpopts.do_urg = 0; + filter->tcpopts.do_ack = 0; + filter->tcpopts.do_rst = 0; + filter->tcpopts.do_psh = 0; + filter->tcpopts.do_syn = 0; + filter->tcpopts.do_fin = 0; + filter->tcpopts.do_ece = 0; + filter->tcpopts.do_cwr = 0; + + filter->udpopts.enabled = 0; + filter->udpopts.do_sport = 0; + filter->udpopts.do_dport = 0; + + filter->icmpopts.enabled = 0; + filter->icmpopts.do_code = 0; + filter->icmpopts.do_type = 0; +} + /** * Sets the config structure's default values. * * @param cfg A pointer to the config structure. * - * @return Void + * @return void */ -void SetCfgDefaults(config__t *cfg) +void SetCfgDefaults(config__t* cfg) { cfg->verbose = 2; cfg->log_file = strdup("/var/log/xdpfw.log"); @@ -922,61 +1025,10 @@ void SetCfgDefaults(config__t *cfg) { filter_t* filter = &cfg->filters[i]; - filter->set = 0; - filter->enabled = 1; - - filter->log = 0; - - filter->action = 1; - filter->src_ip = 0; - filter->dst_ip = 0; - - memset(filter->src_ip6, 0, 4); - memset(filter->dst_ip6, 0, 4); - - filter->do_min_len = 0; - filter->min_len = 0; - - filter->do_max_len = 0; - filter->max_len = 65535; - - 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; - - filter->do_bps = 0; - filter->bps = 0; - - filter->block_time = 1; - - filter->tcpopts.enabled = 0; - filter->tcpopts.do_dport = 0; - filter->tcpopts.do_dport = 0; - filter->tcpopts.do_urg = 0; - filter->tcpopts.do_ack = 0; - filter->tcpopts.do_rst = 0; - filter->tcpopts.do_psh = 0; - filter->tcpopts.do_syn = 0; - filter->tcpopts.do_fin = 0; - filter->tcpopts.do_ece = 0; - filter->tcpopts.do_cwr = 0; - - filter->udpopts.enabled = 0; - filter->udpopts.do_sport = 0; - filter->udpopts.do_dport = 0; - - filter->icmpopts.enabled = 0; - filter->icmpopts.do_code = 0; - filter->icmpopts.do_type = 0; + SetFilterDefaults(filter); } + + memset(cfg->drop_ranges, 0, sizeof(cfg->drop_ranges)); } /** @@ -1111,6 +1163,20 @@ void PrintConfig(config__t* cfg) printf("\n\n"); } + + printf("\n"); + + printf("IP Drop Ranges\n"); + + for (int i = 0; i < MAX_IP_RANGES; i++) + { + const char* range = cfg->drop_ranges[i]; + + if (range) + { + printf("\t- %s\n", range); + } + } } /** @@ -1134,5 +1200,29 @@ int GetNextAvailableFilterIndex(config__t* cfg) return i; } + return -1; +} + +/** + * Retrieves the next available IP drop range index. + * + * @param cfg A pointer to the config structure. + * + * @return The next available index or -1 if there are no available indexes. + */ +int GetNextAvailableIpDropRangeIndex(config__t* cfg) +{ + for (int i = 0; i < MAX_IP_RANGES; i++) + { + const char* range = cfg->drop_ranges[i]; + + if (range) + { + continue; + } + + return i; + } + return -1; } \ No newline at end of file diff --git a/src/loader/utils/config.h b/src/loader/utils/config.h index c2ad130..4cc57c9 100644 --- a/src/loader/utils/config.h +++ b/src/loader/utils/config.h @@ -23,7 +23,9 @@ struct config unsigned int no_stats : 1; unsigned int stats_per_second : 1; int stdout_update_time; + filter_t filters[MAX_FILTERS]; + const char* drop_ranges[MAX_IP_RANGES]; } typedef config__t; // config_t is taken by libconfig -.- struct config_overrides @@ -36,15 +38,15 @@ struct config_overrides int no_stats; int stats_per_second; int stdout_update_time; - } typedef config_overrides_t; void SetCfgDefaults(config__t *cfg); +void SetFilterDefaults(filter_t* filter); -void PrintFilter(filter_t* filter, int idx); void PrintConfig(config__t* cfg); +void PrintFilter(filter_t* filter, int idx); -int LoadConfig(config__t *cfg, char *cfg_file, config_overrides_t* overrides); +int LoadConfig(config__t *cfg, const char* cfg_file, config_overrides_t* overrides); int SaveCfg(config__t* cfg, const char* file_path); int OpenCfg(FILE** file, const char *file_name); @@ -53,5 +55,6 @@ int ReadCfg(FILE* file, char** buffer); int ParseCfg(config__t *cfg, const char* data, config_overrides_t* overrides); int GetNextAvailableFilterIndex(config__t* cfg); +int GetNextAvailableIpDropRangeIndex(config__t* cfg); #include \ No newline at end of file diff --git a/src/loader/utils/xdp.c b/src/loader/utils/xdp.c index 96da61b..425971f 100644 --- a/src/loader/utils/xdp.c +++ b/src/loader/utils/xdp.c @@ -343,4 +343,128 @@ int GetMapPinFd(const char* pin_dir, const char* map_name) snprintf(full_path, sizeof(full_path), "%s/%s", pin_dir, map_name); return bpf_obj_get(full_path); +} + +/** + * Deletes IPv4 address from block map. + * + * @param map_block The block map's FD. + * @param ip The IP address to remove. + * + * @return 0 on success or error value of bpf_map_delete_elem(). + */ +int DeleteBlock(int map_block, u32 ip) +{ + return bpf_map_delete_elem(map_block, &ip); +} + +/** + * Adds an IPv4 address to the block map. + * + * @param map_block The block map's FD. + * @param ip The IP address to add. + * @param expires When the block expires (nanoseconds since system boot). + * + * @return 0 on success or error value of bpf_map_update_elem(). + */ +int AddBlock(int map_block, u32 ip, u64 expires) +{ + return bpf_map_update_elem(map_block, &ip, &expires, BPF_ANY); +} + +/** + * Deletes IPv6 address from block map. + * + * @param map_block6 The block map's FD. + * @param ip The IP address to remove. + * + * @return 0 on success or error value of bpf_map_delete_elem(). + */ +int DeleteBlock6(int map_block6, u128 ip) +{ + return bpf_map_delete_elem(map_block6, &ip); +} + +/** + * Adds an IPv6 address to the block map. + * + * @param map_block6 The block map's FD. + * @param ip The IP address to add. + * @param expires When the block expires (nanoseconds since system boot). + * + * @return 0 on success or error value of bpf_map_update_elem(). + */ +int AddBlock6(int map_block6, u128 ip, u64 expires) +{ + return bpf_map_update_elem(map_block6, &ip, &expires, BPF_ANY); +} + +/** + * Deletes an IPv4 range from the drop map. + * + * @param map_range_drop The IPv4 range drop map's FD. + * @param net The network IP. + * @param cidr The network's CIDR. + * + * @return 0 on success or error value of bpf_map_delete_elem(). + */ +int DeleteRangeDrop(int map_range_drop, u32 net, u8 cidr) +{ + u32 bit_mask = ( ~( (1 << (32 - cidr) ) - 1) ); + u32 start = net & bit_mask; + + LpmTrieKey key = {0}; + key.prefix_len = cidr; + key.data = start; + + return bpf_map_delete_elem(map_range_drop, &key); +} + +/** + * Adds an IPv4 range to the drop map. + * + * @param map_range_drop The IPv4 range drop map's FD. + * @param net The network IP. + * @param cidr The network's CIDR. + * + * @return 0 on success or error value of bpf_map_update_elem(). + */ +int AddRangeDrop(int map_range_drop, u32 net, u8 cidr) +{ + u32 bit_mask = ( ~( (1 << (32 - cidr) ) - 1) ); + u32 start = net & bit_mask; + + LpmTrieKey key = {0}; + key.prefix_len = cidr; + key.data = start; + + u64 val = ( (u64)bit_mask << 32 ) | start; + + return bpf_map_update_elem(map_range_drop, &key, &val, BPF_ANY); +} + +/** + * Updates IP ranges from config file. + * + * @param map_range_drop The IPv4 range drop map's FD. + * @param cfg A pointer to the config file + * + * @return void + */ +void UpdateRangeDrops(int map_range_drop, config__t* cfg) +{ + for (int i = 0; i < MAX_IP_RANGES; i++) + { + const char* range = cfg->drop_ranges[i]; + + if (!range) + { + continue; + } + + // Parse IP range string and return network IP and CIDR. + ip_range_t t = ParseIpCidr(range); + + AddRangeDrop(map_range_drop, t.ip, t.cidr); + } } \ No newline at end of file diff --git a/src/loader/utils/xdp.h b/src/loader/utils/xdp.h index 6561da3..08d1c49 100644 --- a/src/loader/utils/xdp.h +++ b/src/loader/utils/xdp.h @@ -26,4 +26,14 @@ void UpdateFilters(int map_filters, config__t *cfg); int PinBpfMap(struct bpf_object* obj, const char* pin_dir, const char* map_name); int UnpinBpfMap(struct bpf_object* obj, const char* pin_dir, const char* map_name); -int GetMapPinFd(const char* pin_dir, const char* map_name); \ No newline at end of file +int GetMapPinFd(const char* pin_dir, const char* map_name); + +int DeleteBlock(int map_block, u32 ip); +int AddBlock(int map_block, u32 ip, u64 expires); + +int DeleteBlock6(int map_block6, u128 ip); +int AddBlock6(int map_block6, u128 ip, u64 expires); + +int DeleteRangeDrop(int map_range_drop, u32 net, u8 cidr); +int AddRangeDrop(int map_range_drop, u32 net, u8 cidr); +void UpdateRangeDrops(int map_range_drop, config__t* cfg); \ No newline at end of file From bab579949e1fb0638692f4d99be31bb26f59c8f0 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:39:38 -0500 Subject: [PATCH 06/19] Update hard-code config and types. --- src/common/config.h | 14 ++++++++++++++ src/common/types.h | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/common/config.h b/src/common/config.h index f32e66f..3157ac9 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -1,5 +1,16 @@ #pragma once +// Enables dynamic filters. +// Disable this for better performance if you only plan on adding entries to the block and drop maps. +#define ENABLE_FILTERS + +// Enables IPv4 range drop map. +// Disable this if you don't plan on adding IPv4 ranges to the drop map as it will increase performance. +#define ENABLE_IP_RANGE_DROP + +// The maximum IP ranges supported in the IP range drop map. +#define MAX_IP_RANGES 4096 + // The maximum amount of filters allowed. // Decrease this value if you receive errors related to the BPF program being too large. #define MAX_FILTERS 60 @@ -12,6 +23,9 @@ // Commenting this line out should increase performance when blocking malicious traffic. #define DO_STATS_ON_BLOCK_MAP +// Similar to DO_STATS_ON_BLOCK_MAP, but for IPv4 range drop map. +#define DO_STATS_ON_IP_RANGE_DROP_MAP + // When this is defined, a check will occur inside the IPv4 and IPv6 filters. // For IPv6 packets, if no IPv6 source/destination IP addresses are set, but there is an IPv4 address, it will ignore the filter. // The same goes for IPv4, if there is no IPv4 source/destination IP addresses set, if an IPv6 address is set, it will ignore the filter. diff --git a/src/common/types.h b/src/common/types.h index d9add98..d9380aa 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -152,4 +152,9 @@ struct filter_log_event u64 pps; u64 bps; -} typedef filter_log_event_t; \ No newline at end of file +} typedef filter_log_event_t; + +struct lpm_trie_key { + u32 prefix_len; + u32 data; +} typedef LpmTrieKey; \ No newline at end of file From 730852592be30f719ddc29cb2f1cfd0097f70161 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:39:59 -0500 Subject: [PATCH 07/19] Update Makefile. --- Makefile | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9c3e776..4669406 100644 --- a/Makefile +++ b/Makefile @@ -9,14 +9,20 @@ MODULES_DIR = modules # Common directories. COMMON_DIR = $(SRC_DIR)/common +ETC_DIR = /etc/xdpfw + +# Project source directories. LOADER_DIR = $(SRC_DIR)/loader XDP_DIR = $(SRC_DIR)/xdp -ETC_DIR = /etc/xdpfw +RULE_ADD_DIR = $(SRC_DIR)/rule_add +RULE_DEL_DIR = $(SRC_DIR)/rule_del # Additional build directories. BUILD_LOADER_DIR = $(BUILD_DIR)/loader BUILD_XDP_DIR = $(BUILD_DIR)/xdp +BUILD_RULE_ADD_DIR = $(BUILD_DIR)/rule_add +BUILD_RULE_DEL_DIR = $(BUILD_DIR)/rule_del # XDP Tools directories. XDP_TOOLS_DIR = $(MODULES_DIR)/xdp-tools @@ -72,6 +78,37 @@ endif XDP_SRC = prog.c XDP_OBJ = xdp_prog.o +# Rule common. +RULE_OBJS = $(BUILD_LOADER_DIR)/$(LOADER_UTILS_CONFIG_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_XDP_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_LOGGING_OBJ) $(BUILD_LOADER_DIR)/$(LOADER_UTILS_HELPERS_OBJ) + +ifeq ($(LIBXDP_STATIC), 1) + RULE_OBJS := $(LIBBPF_OBJS) $(LIBXDP_OBJS) $(RULE_OBJS) +endif + +# Rule add. +RULE_ADD_SRC = prog.c +RULE_ADD_OUT = xdpfw-add + +RULE_ADD_UTILS_DIR = $(RULE_ADD_DIR)/utils + +# Rule add utils. +RULE_ADD_UTILS_CMDLINE_SRC = cmdline.c +RULE_ADD_UTILS_CMDLINE_OBJ = cmdline.o + +RULE_ADD_OBJS = $(BUILD_RULE_ADD_DIR)/$(RULE_ADD_UTILS_CMDLINE_OBJ) + +# Rule delete. +RULE_DEL_SRC = prog.c +RULE_DEL_OUT = xdpfw-del + +RULE_DEL_UTILS_DIR = $(RULE_DEL_DIR)/utils + +# Rule delete utils. +RULE_DEL_UTILS_CMDLINE_SRC = cmdline.c +RULE_DEL_UTILS_CMDLINE_OBJ = cmdline.o + +RULE_DEL_OBJS = $(BUILD_RULE_DEL_DIR)/$(RULE_DEL_UTILS_CMDLINE_OBJ) + # Includes. INCS = -I $(SRC_DIR) -I /usr/include -I /usr/local/include @@ -90,7 +127,7 @@ else endif # All chains. -all: loader xdp +all: loader xdp rule_add rule_del # Loader program. loader: loader_utils @@ -120,6 +157,24 @@ loader_utils_helpers: xdp: $(CC) $(INCS) $(FLAGS) -target bpf -c -o $(BUILD_XDP_DIR)/$(XDP_OBJ) $(XDP_DIR)/$(XDP_SRC) +# Rule add. +rule_add: loader_utils rule_add_utils + $(CC) $(INCS) $(FLAGS) $(FLAGS_LOADER) -o $(BUILD_RULE_ADD_DIR)/$(RULE_ADD_OUT) $(RULE_OBJS) $(RULE_ADD_OBJS) $(RULE_ADD_DIR)/$(RULE_ADD_SRC) + +rule_add_utils: rule_add_utils_cmdline + +rule_add_utils_cmdline: + $(CC) $(INCS) $(FLAGS) -c -o $(BUILD_RULE_ADD_DIR)/$(RULE_ADD_UTILS_CMDLINE_OBJ) $(RULE_ADD_UTILS_DIR)/$(RULE_ADD_UTILS_CMDLINE_SRC) + +# Rule delete. +rule_del: loader_utils rule_del_utils + $(CC) $(INCS) $(FLAGS) $(FLAGS_LOADER) -o $(BUILD_RULE_DEL_DIR)/$(RULE_DEL_OUT) $(RULE_OBJS) $(RULE_DEL_OBJS) $(RULE_DEL_DIR)/$(RULE_DEL_SRC) + +rule_del_utils: rule_del_utils_cmdline + +rule_del_utils_cmdline: + $(CC) $(INCS) $(FLAGS) -c -o $(BUILD_RULE_DEL_DIR)/$(RULE_DEL_UTILS_CMDLINE_OBJ) $(RULE_DEL_UTILS_DIR)/$(RULE_DEL_UTILS_CMDLINE_SRC) + # LibXDP chain. We need to install objects here since our program relies on installed object files and such. libxdp: $(MAKE) -C $(XDP_TOOLS_DIR) libxdp @@ -140,12 +195,17 @@ install: cp -n other/xdpfw.service /etc/systemd/system/ cp -f $(BUILD_LOADER_DIR)/$(LOADER_OUT) /usr/bin + cp -f $(BUILD_RULE_ADD_DIR)/$(RULE_ADD_OUT) /usr/bin + cp -f $(BUILD_RULE_DEL_DIR)/$(RULE_DEL_OUT) /usr/bin + cp -f $(BUILD_XDP_DIR)/$(XDP_OBJ) $(ETC_DIR) clean: find $(BUILD_DIR) -type f ! -name ".*" -exec rm -f {} + find $(BUILD_LOADER_DIR) -type f ! -name ".*" -exec rm -f {} + find $(BUILD_XDP_DIR) -type f ! -name ".*" -exec rm -f {} + + find $(BUILD_RULE_ADD_DIR) -type f ! -name ".*" -exec rm -f {} + + find $(BUILD_RULE_DEL_DIR) -type f ! -name ".*" -exec rm -f {} + .PHONY: all libxdp .DEFAULT: all \ No newline at end of file From 2dd90d8c56509d3b8c1f84e67446b16ffb6dfba5 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:40:06 -0500 Subject: [PATCH 08/19] Update .gitignore. --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 8d0f333..269552e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .vscode/ xdpfw -xdpfw.s -xdpfw.conf +*.s +*.conf *.log *.asm \ No newline at end of file From a0a812a7f0f18f19de4744d1ea9af761f3dd8b9c Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:40:28 -0500 Subject: [PATCH 09/19] Start working on xdpfw-add and xdpfw-del utilities. --- build/rule_add/.gitignore | 2 + build/rule_del/.gitignore | 2 + src/rule_add/prog.c | 506 +++++++++++++++++++++++++++++++++++ src/rule_add/utils/cmdline.c | 251 +++++++++++++++++ src/rule_add/utils/cmdline.h | 62 +++++ src/rule_del/prog.c | 287 ++++++++++++++++++++ src/rule_del/utils/cmdline.c | 71 +++++ src/rule_del/utils/cmdline.h | 23 ++ 8 files changed, 1204 insertions(+) create mode 100644 build/rule_add/.gitignore create mode 100644 build/rule_del/.gitignore create mode 100644 src/rule_add/prog.c create mode 100644 src/rule_add/utils/cmdline.c create mode 100644 src/rule_add/utils/cmdline.h create mode 100644 src/rule_del/prog.c create mode 100644 src/rule_del/utils/cmdline.c create mode 100644 src/rule_del/utils/cmdline.h diff --git a/build/rule_add/.gitignore b/build/rule_add/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/build/rule_add/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/build/rule_del/.gitignore b/build/rule_del/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/build/rule_del/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/src/rule_add/prog.c b/src/rule_add/prog.c new file mode 100644 index 0000000..50ac4ed --- /dev/null +++ b/src/rule_add/prog.c @@ -0,0 +1,506 @@ +#include + +#include +#include +#include + +#include +#include + +#include + +// These are required due to being extern with Loader. +// To Do: Figure out a way to not require the below without requiring separate object files. +int cont = 0; +int doing_stats = 0; + +int main(int argc, char *argv[]) +{ + int ret; + + // Parse command line. + cmdline_t cmd = {0}; + cmd.cfg_file = CONFIG_DEFAULT_PATH; + + ParseCommandLine(&cmd, argc, argv); + + if (!cmd.help) + { + printf("Parsed command line...\n"); + } + else + { + printf("Usage: xdpfw-add [OPTIONS]\n\n"); + + printf("OPTIONS:\n"); + printf(" -c, --cfg The path to the config file (default /etc/xdpfw/xdpfw.conf).\n"); + printf(" -s, --save Saves the new config to file system.\n"); + printf(" -m, --mode The mode to use (0 = filters, 1 = IPv4 range drop, 2 = IP block map).\n"); + printf(" -i, --idx The filters index to update when using filters mode (0) (index starts from 1; retrieve index using xdpfw -l).\n"); + printf(" -d, --ip The IP range or single IP to add (for modes 1 and 2).\n"); + printf(" -v, --v6 If set, parses IP address as IPv6 when adding to block map (for mode 2).\n"); + printf(" -e, --expires How long to block the IP for in seconds (for mode 2).\n\n"); + + printf("Filter Mode Options:\n"); + printf(" --sip The source IPv4 address (with CIDR support).\n"); + printf(" --dip The destination IPv4 address (with CIDR support).\n"); + printf(" --sip6 The source IPv6 address.\n"); + printf(" --dip6 The destination IPv6 address.\n"); + printf(" --min-ttl The minimum IP TTL to match.\n"); + printf(" --max-ttl The maximum IP TTL to match.\n"); + printf(" --min-len The minimum packet length to match.\n"); + printf(" --max-len The maximum packet length to match.\n"); + printf(" --tos The IP Type of Service to match.\n\n"); + + printf(" --pps The minimum packet rate (per second) to match.\n"); + printf(" --bps The minimum byte rate (per second) to match\n\n"); + + printf(" --tcp Enable or disables matching on the TCP protocol.\n"); + printf(" --tsport The TCP source port to match on.\n"); + printf(" --tdport The TCP destination port to match on.\n"); + printf(" --urg Enables or disables matching on TCP URG flag.\n"); + printf(" --ack Enables or disables matching on TCP ACK flag.\n"); + printf(" --rst Enables or disables matching on TCP RST flag.\n"); + printf(" --psh Enables or disables matching on TCP PSH flag.\n"); + printf(" --syn Enables or disables matching on TCP SYN flag.\n"); + printf(" --fin Enables or disables matching on TCP FIN flag.\n"); + printf(" --ece Enables or disables matching on TCP ECE flag.\n"); + printf(" --cwr Enables or disables matching on TCP CWR flag.\n\n"); + + printf(" --udp Enable or disables matching on the UDP protocol.\n"); + printf(" --usport The UDP source port to match on.\n"); + printf(" --udport The UDP destination port to match on.\n"); + + printf(" --icmp Enable or disables matching on the ICMP protocol.\n"); + printf(" --code The ICMP code to match on.\n"); + printf(" --type The ICMP type to match on.\n"); + + return EXIT_SUCCESS; + } + + // Check for config file path. + if ((cmd.save || cmd.mode == 0) && (!cmd.cfg_file || strlen(cmd.cfg_file) < 1)) + { + fprintf(stderr, "[ERROR] CFG file not specified or empty. This is required for filters mode or when saving config.\n"); + + return EXIT_FAILURE; + } + + // Load config. + config__t cfg = {0}; + + if (cmd.save || cmd.mode == 0) + { + if ((ret = LoadConfig(&cfg, cmd.cfg_file, NULL)) != 0) + { + fprintf(stderr, "[ERROR] Failed to load config at '%s' (%d)\n", cmd.cfg_file, ret); + + return EXIT_FAILURE; + } + + printf("Loaded config...\n"); + } + + // Handle filters mode. + if (cmd.mode == 0) + { + printf("Using filters mode (0)...\n"); + + // Check index. + if (cmd.idx < 1) + { + fprintf(stderr, "Invalid filter index. Index must start from 1.\n"); + + return EXIT_FAILURE; + } + + // Retrieve filters map FD. + int map_filters = GetMapPinFd(XDP_MAP_PIN_DIR, "map_filters"); + + if (map_filters < 0) + { + fprintf(stderr, "[ERROR] Failed to retrieve BPF map 'map_filters' from file system.\n"); + + return EXIT_FAILURE; + } + + printf("Using 'map_filters' FD => %d...\n", map_filters); + + // Create new base filter and set its defaults. + filter_t new_filter = {0}; + SetFilterDefaults(&new_filter); + + // Determine what index we'll be storing this filter at. + int idx = -1; + + if (cmd.idx > 0) + { + idx = cmd.idx - 1; + } + else + { + idx = GetNextAvailableFilterIndex(&cfg); + } + + if (idx < 0) + { + fprintf(stderr, "Failed to retrieve filter next. Make sure you haven't exceeded the maximum filters allowed (%d).\n", MAX_FILTERS); + + return EXIT_FAILURE; + } + + // Fill out new filter. + if (cmd.src_ip) + { + ip_range_t range = ParseIpCidr(cmd.src_ip); + + new_filter.src_ip = range.ip; + new_filter.src_cidr = range.cidr; + } + + if (cmd.dst_ip) + { + ip_range_t range = ParseIpCidr(cmd.dst_ip); + + new_filter.dst_ip = range.ip; + new_filter.dst_cidr = range.cidr; + } + + if (cmd.src_ip6) + { + struct in6_addr addr; + + if ((ret = inet_pton(AF_INET6, cmd.src_ip6, &addr)) != 1) + { + fprintf(stderr, "Failed to convert source IPv6 address to decimal (%d).\n", ret); + + return EXIT_FAILURE; + } + + memcpy(new_filter.src_ip6, addr.s6_addr, sizeof(new_filter.src_ip6)); + } + + if (cmd.dst_ip6) + { + struct in6_addr addr; + + if ((ret = inet_pton(AF_INET6, cmd.dst_ip6, &addr)) != 1) + { + fprintf(stderr, "Failed to convert destination IPv6 address to decimal (%d).\n", ret); + + return EXIT_FAILURE; + } + + memcpy(new_filter.dst_ip6, addr.s6_addr, sizeof(new_filter.dst_ip6)); + } + + // To Do: See if I can create a macro for below. + // As long as the naming convention lines up, it should be easily possible. + if (cmd.pps > -1) + { + new_filter.do_pps = 1; + new_filter.pps = cmd.pps; + } + + if (cmd.bps > -1) + { + new_filter.do_bps = 1; + new_filter.bps = cmd.bps; + } + + if (cmd.min_ttl > -1) + { + new_filter.do_min_ttl = 1; + new_filter.min_ttl = cmd.min_ttl; + } + + if (cmd.max_ttl > -1) + { + new_filter.do_max_ttl = 1; + new_filter.max_ttl = cmd.max_ttl; + } + + if (cmd.min_len > -1) + { + new_filter.do_min_len = 1; + new_filter.min_len = cmd.min_len; + } + + if (cmd.max_len > -1) + { + new_filter.do_max_len = 1; + new_filter.max_len = cmd.max_len; + } + + if (cmd.tos > -1) + { + new_filter.do_tos = 1; + new_filter.tos = cmd.tos; + } + + if (cmd.tcp_enabled > -1) + { + new_filter.tcpopts.enabled = cmd.tcp_enabled; + } + + if (cmd.tcp_sport > -1) + { + new_filter.tcpopts.do_sport = 1; + new_filter.tcpopts.sport = cmd.tcp_sport; + } + + if (cmd.tcp_dport > -1) + { + new_filter.tcpopts.do_dport = 1; + new_filter.tcpopts.dport = cmd.tcp_dport; + } + + if (cmd.tcp_urg > -1) + { + new_filter.tcpopts.do_urg = 1; + new_filter.tcpopts.urg = cmd.tcp_urg; + } + + if (cmd.tcp_ack > -1) + { + new_filter.tcpopts.do_ack = 1; + new_filter.tcpopts.ack = cmd.tcp_ack; + } + + if (cmd.tcp_rst > -1) + { + new_filter.tcpopts.do_rst = 1; + new_filter.tcpopts.rst = cmd.tcp_rst; + } + + if (cmd.tcp_psh > -1) + { + new_filter.tcpopts.do_psh = 1; + new_filter.tcpopts.psh = cmd.tcp_psh; + } + + if (cmd.tcp_syn > -1) + { + new_filter.tcpopts.do_syn = 1; + new_filter.tcpopts.syn = cmd.tcp_syn; + } + + if (cmd.tcp_fin > -1) + { + new_filter.tcpopts.do_fin = 1; + new_filter.tcpopts.fin = cmd.tcp_fin; + } + + if (cmd.tcp_ece > -1) + { + new_filter.tcpopts.do_ece = 1; + new_filter.tcpopts.ece = cmd.tcp_ece; + } + + if (cmd.tcp_cwr > -1) + { + new_filter.tcpopts.do_cwr = 1; + new_filter.tcpopts.cwr = cmd.tcp_cwr; + } + + if (cmd.udp_enabled > -1) + { + new_filter.udpopts.enabled = cmd.udp_enabled; + } + + if (cmd.udp_sport > -1) + { + new_filter.udpopts.do_sport = 1; + new_filter.udpopts.sport = cmd.udp_sport; + } + + if (cmd.udp_dport > -1) + { + new_filter.udpopts.do_dport = 1; + new_filter.udpopts.dport = cmd.udp_dport; + } + + if (cmd.icmp_enabled > -1) + { + new_filter.icmpopts.enabled = cmd.icmp_enabled; + } + + if (cmd.icmp_code > -1) + { + new_filter.icmpopts.do_code = 1; + new_filter.icmpopts.code = cmd.icmp_code; + } + + if (cmd.icmp_type > -1) + { + new_filter.icmpopts.do_type = 1; + new_filter.icmpopts.type = cmd.icmp_type; + } + + // Set filter at index. + cfg.filters[idx] = new_filter; + + // Update filters. + fprintf(stdout, "Updating filters...\n"); + + UpdateFilters(map_filters, &cfg); + } + // Handle IPv4 range drop mode. + else if (cmd.mode == 1) + { + printf("Using IPv4 range drop mode (1)...\n"); + + // Make sure IP range is specified. + if (!cmd.ip) + { + fprintf(stderr, "No IP address or range specified. Please set an IP range using -d, --ip arguments.\n"); + + return EXIT_FAILURE; + } + + // Get range map. + int map_range_drop = GetMapPinFd(XDP_MAP_PIN_DIR, "map_range_drop"); + + if (map_range_drop < 0) + { + fprintf(stderr, "Failed to retrieve 'map_range_drop' BPF map FD.\n"); + + return EXIT_FAILURE; + } + + printf("Using 'map_range_drop' FD => %d.\n", map_range_drop); + + // Parse IP range. + ip_range_t range = ParseIpCidr(cmd.ip); + + // Attempt to add range. + if ((ret = AddRangeDrop(map_range_drop, range.ip, range.cidr)) != 0) + { + fprintf(stderr, "Error adding range to BPF map (%d).\n", ret); + + return EXIT_FAILURE; + } + + printf("Added IP range '%s' to IP range drop map...\n", cmd.ip); + + if (cmd.save) + { + // Get next available index. + int idx = GetNextAvailableIpDropRangeIndex(&cfg); + + if (idx < 0) + { + fprintf(stderr, "No available IP drop range indexes. Perhaps the maximum IP ranges has been exceeded?\n"); + + return EXIT_FAILURE; + } + + cfg.drop_ranges[idx] = strdup(cmd.ip); + } + } + // Handle block map mode. + else + { + printf("Using source IP block mode (2)...\n"); + + if (!cmd.ip) + { + fprintf(stderr, "No source IP address specified. Please set an IP using -s, --ip arguments.\n"); + + return EXIT_FAILURE; + } + + int expires = 0; + + if (cmd.expires > -1) + { + expires = cmd.expires; + } + + u64 expires_rel = GetBootNanoTime() + ((u64)expires * 1e9); + + int map_block = GetMapPinFd(XDP_MAP_PIN_DIR, "map_block"); + int map_block6 = GetMapPinFd(XDP_MAP_PIN_DIR, "map_block6"); + + if (cmd.v6) + { + if (map_block6 < 0) + { + fprintf(stderr, "Failed to find the 'map_block6' BPF map.\n"); + + return EXIT_FAILURE; + } + + printf("Using 'map_block6' FD => %d.\n", map_block6); + + struct in6_addr addr; + + if ((ret = inet_pton(AF_INET6, cmd.ip, &addr)) != 1) + { + fprintf(stderr, "Failed to convert IPv6 address '%s' to decimal (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + + u128 ip = 0; + + for (int i = 0; i < 16; i++) + { + ip = (ip << 8) | addr.s6_addr[i]; + } + + if ((ret = AddBlock6(map_block6, ip, expires_rel)) != 0) + { + fprintf(stderr, "Failed to add IP '%s' to BPF map (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + } + else + { + if (map_block < 0) + { + fprintf(stderr, "Failed to find the 'map_block' BPF map.\n"); + + return EXIT_FAILURE; + } + + printf("Using 'map_block' FD => %d.\n", map_block); + + struct in_addr addr; + + if ((ret = inet_pton(AF_INET, cmd.ip, &addr)) != 1) + { + fprintf(stderr, "Failed to convert IP address '%s' to decimal (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + + if ((ret = AddBlock(map_block, addr.s_addr, expires_rel)) != 0) + { + fprintf(stderr, "Failed to add IP '%s' too BPF map (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + + printf("Added '%s' to block map...\n", cmd.ip); + } + } + + if (cmd.save) + { + // Save config. + printf("Saving config...\n"); + + if ((ret = SaveCfg(&cfg, cmd.cfg_file)) != 0) + { + fprintf(stderr, "[ERROR] Failed to save config.\n"); + + return EXIT_FAILURE; + } + } + + printf("Success! Exiting.\n"); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/rule_add/utils/cmdline.c b/src/rule_add/utils/cmdline.c new file mode 100644 index 0000000..a5794e2 --- /dev/null +++ b/src/rule_add/utils/cmdline.c @@ -0,0 +1,251 @@ +#include + +const struct option opts[] = +{ + { "cfg", required_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + + { "save", no_argument, NULL, 's' }, + + { "mode", required_argument, NULL, 'm' }, + + { "idx", required_argument, NULL, 'i' }, + + { "ip", required_argument, NULL, 'd' }, + { "v6", no_argument, NULL, 'v' }, + { "expires", required_argument, NULL, 'e' }, + + { "sip", required_argument, NULL, 0 }, + { "dip", required_argument, NULL, 1 }, + { "sip6", required_argument, NULL, 2 }, + { "dip6", required_argument, NULL, 3 }, + { "min-ttl", required_argument, NULL, 4 }, + { "max-ttl", required_argument, NULL, 5 }, + { "min-len", required_argument, NULL, 6 }, + { "max-len", required_argument, NULL, 7 }, + { "tos", required_argument, NULL, 8 }, + + { "pps", required_argument, NULL, 9 }, + { "bps", required_argument, NULL, 10 }, + + { "tcp", required_argument, NULL, 11 }, + { "tsport", required_argument, NULL, 12 }, + { "tdport", required_argument, NULL, 13 }, + { "urg", required_argument, NULL, 14 }, + { "ack", required_argument, NULL, 15 }, + { "rst", required_argument, NULL, 16 }, + { "psh", required_argument, NULL, 17 }, + { "syn", required_argument, NULL, 18 }, + { "fin", required_argument, NULL, 19 }, + { "ece", required_argument, NULL, 20 }, + { "cwr", required_argument, NULL, 21 }, + + { "udp", required_argument, NULL, 22 }, + { "usport", required_argument, NULL, 23 }, + { "udport", required_argument, NULL, 24 }, + + { "icmp", required_argument, NULL, 25 }, + { "code", required_argument, NULL, 26 }, + { "type", required_argument, NULL, 27 }, + + { NULL, 0, NULL, 0 } +}; + +void ParseCommandLine(cmdline_t* cmd, int argc, char* argv[]) +{ + int c; + + while ((c = getopt_long(argc, argv, "c:lhm:i:rsv", opts, NULL)) != -1) + { + switch (c) + { + case 'c': + cmd->cfg_file = optarg; + + break; + + case 'h': + cmd->help = 1; + + break; + + case 's': + cmd->save = 1; + + break; + + case 'm': + cmd->mode = atoi(optarg); + + break; + + case 'i': + cmd->idx = atoi(optarg); + + break; + + case 'd': + cmd->ip = optarg; + + break; + + case 'v': + cmd->v6 = atoi(optarg); + + break; + + case 'e': + cmd->expires = strtoll(optarg, NULL, 10); + + break; + + case 0: + cmd->src_ip = optarg; + + break; + + case 1: + cmd->dst_ip = optarg; + + break; + + case 2: + cmd->src_ip6 = optarg; + + break; + + case 3: + cmd->dst_ip6 = optarg; + + break; + + case 4: + cmd->min_ttl = atoi(optarg); + + break; + + case 5: + cmd->max_ttl = atoi(optarg); + + break; + + case 6: + cmd->min_len = atoi(optarg); + + break; + + case 7: + cmd->max_len = atoi(optarg); + + break; + + case 8: + cmd->tos = atoi(optarg); + + break; + + case 9: + cmd->pps = strtoll(optarg, NULL, 10); + + break; + + case 10: + cmd->bps = strtoll(optarg, NULL, 10); + + break; + + case 11: + cmd->tcp_enabled = atoi(optarg); + + break; + + case 12: + cmd->tcp_sport = atoi(optarg); + + break; + + case 13: + cmd->tcp_dport = atoi(optarg); + + break; + + case 14: + cmd->tcp_urg = atoi(optarg); + + break; + + case 15: + cmd->tcp_ack = atoi(optarg); + + break; + + case 16: + cmd->tcp_rst = atoi(optarg); + + break; + + case 17: + cmd->tcp_psh = atoi(optarg); + + break; + + case 18: + cmd->tcp_syn = atoi(optarg); + + break; + + case 19: + cmd->tcp_fin = atoi(optarg); + + break; + + case 20: + cmd->tcp_ece = atoi(optarg); + + break; + + case 21: + cmd->tcp_cwr = atoi(optarg); + + break; + + case 22: + cmd->udp_enabled = atoi(optarg); + + break; + + case 23: + cmd->udp_sport = atoi(optarg); + + break; + + case 24: + cmd->udp_dport = atoi(optarg); + + break; + + case 25: + cmd->icmp_enabled = atoi(optarg); + + break; + + case 26: + cmd->icmp_code = atoi(optarg); + + break; + + case 27: + cmd->icmp_type = atoi(optarg); + + break; + + case '?': + fprintf(stderr, "Missing argument option...\n"); + + break; + + default: + break; + } + } +} \ No newline at end of file diff --git a/src/rule_add/utils/cmdline.h b/src/rule_add/utils/cmdline.h new file mode 100644 index 0000000..4ade24e --- /dev/null +++ b/src/rule_add/utils/cmdline.h @@ -0,0 +1,62 @@ +#pragma once + +#include + +#include +#include +#include + +struct cmdline +{ + const char* cfg_file; + + int help; + + int save; + + int mode; + + int idx; + + const char* ip; + int v6; + + s64 expires; + + const char* src_ip; + const char* dst_ip; + + const char* src_ip6; + const char* dst_ip6; + + s64 pps; + s64 bps; + + int min_ttl; + int max_ttl; + int min_len; + int max_len; + int tos; + + int tcp_enabled; + int tcp_sport; + int tcp_dport; + int tcp_urg; + int tcp_ack; + int tcp_rst; + int tcp_psh; + int tcp_syn; + int tcp_fin; + int tcp_ece; + int tcp_cwr; + + int udp_enabled; + int udp_sport; + int udp_dport; + + int icmp_enabled; + int icmp_code; + int icmp_type; +} typedef cmdline_t; + +void ParseCommandLine(cmdline_t* cmd, int argc, char* argv[]); \ No newline at end of file diff --git a/src/rule_del/prog.c b/src/rule_del/prog.c new file mode 100644 index 0000000..a495fa2 --- /dev/null +++ b/src/rule_del/prog.c @@ -0,0 +1,287 @@ +#include + +#include +#include +#include + +#include +#include + +#include + +// These are required due to being extern with Loader. +// To Do: Figure out a way to not require the below without requiring separate object files. +int cont = 0; +int doing_stats = 0; + +int main(int argc, char *argv[]) +{ + int ret; + + // Parse command line. + cmdline_t cmd = {0}; + cmd.cfg_file = CONFIG_DEFAULT_PATH; + + ParseCommandLine(&cmd, argc, argv); + + if (!cmd.help) + { + printf("Parsed command line...\n"); + } else + { + printf("Usage: xdpfw-del [OPTIONS]\n\n"); + printf("OPTIONS:\n"); + printf(" -c, --cfg The path to the config file (default /etc/xdpfw/xdpfw.conf).\n"); + printf(" -s, --save Saves the new config to file system.\n"); + printf(" -m, --mode The mode to use (0 = filters, 1 = IPv4 range drop, 2 = IP block map).\n"); + printf(" -i, --idx The filters index to remove when using filters mode (0) (index starts from 1; retrieve index using xdpfw -l).\n"); + printf(" -d, --ip The IP range or single IP to use (for modes 1 and 2).\n"); + printf(" -v, --v6 If set, parses IP address as IPv6 when removing from block map (for mode 2).\n"); + + return EXIT_SUCCESS; + } + + // Check for config file path. + if ((cmd.save || cmd.mode == 0) && (!cmd.cfg_file || strlen(cmd.cfg_file) < 1)) + { + fprintf(stderr, "[ERROR] CFG file not specified or empty. This is required for current mode or options set.\n"); + + return EXIT_FAILURE; + } + + // Load config. + config__t cfg = {0}; + + if (cmd.save || cmd.mode == 0) + { + if ((ret = LoadConfig(&cfg, cmd.cfg_file, NULL)) != 0) + { + fprintf(stderr, "[ERROR] Failed to load config at '%s' (%d)\n", cmd.cfg_file, ret); + + return EXIT_FAILURE; + } + + printf("Loaded config...\n"); + } + + // Handle filters mode. + if (cmd.mode == 0) + { + printf("Using filters mode (0)...\n"); + + // Check index. + if (cmd.idx < 1) + { + fprintf(stderr, "Invalid filter index. Index must start from 1.\n"); + + return EXIT_FAILURE; + } + + // Retrieve filters map FD. + int map_filters = GetMapPinFd(XDP_MAP_PIN_DIR, "map_filters"); + + if (map_filters < 0) + { + fprintf(stderr, "[ERROR] Failed to retrieve BPF map 'map_filters' from file system.\n"); + + return EXIT_FAILURE; + } + + printf("Using 'map_filters' FD => %d...\n", map_filters); + + int index = -1; + int cfg_idx = cmd.idx - 1; + int cur_idx = 0; + + // This is where things are a bit tricky due to the layout of our filtering system in XDP. + // Since each filter rule doesn't have any unique identifier other than the index, we need to use that. + // However, rules that are not enabled are not inserted into the BPF map which can mismatch the indexes in the config and XDP program. + // So we need to loop through each and ignore disabled rules. + for (int i = 0; i < MAX_FILTERS; i++) + { + filter_t* filter = &cfg.filters[i]; + + if (!filter->set || !filter->enabled) + { + continue; + } + + if (i == cur_idx) + { + index = cur_idx; + + break; + } + + cur_idx++; + } + + if (index < 0) + { + fprintf(stderr, "[ERROR] Failed to find proper index in config file (%d).\n", index); + + return EXIT_FAILURE; + } + + // Unset affected filter in config. + if (cmd.save) + { + cfg.filters[cfg_idx].set = 0; + } + + // Update filters. + fprintf(stdout, "Updating filters...\n"); + + UpdateFilters(map_filters, &cfg); + } + // Handle IPv4 range drop mode. + else if (cmd.mode == 1) + { + printf("Using IPv4 range drop mode (1)...\n"); + + // Make sure IP range is specified. + if (!cmd.ip) + { + fprintf(stderr, "No IP address or range specified. Please set an IP range using -s, --ip arguments.\n"); + + return EXIT_FAILURE; + } + + // Get range map. + int map_range_drop = GetMapPinFd(XDP_MAP_PIN_DIR, "map_range_drop"); + + if (map_range_drop < 0) + { + fprintf(stderr, "Failed to retrieve 'map_range_drop' BPF map FD.\n"); + + return EXIT_FAILURE; + } + + // Parse IP range. + ip_range_t range = ParseIpCidr(cmd.ip); + + // Attempt to delete range. + if ((ret = DeleteRangeDrop(map_range_drop, range.ip, range.cidr)) != 0) + { + fprintf(stderr, "Error deleting range from BPF map (%d).\n", ret); + + return EXIT_FAILURE; + } + + printf("Removed IP range '%s' from BPF map.\n", cmd.ip); + + if (cmd.save) + { + // Loop through IP drop ranges and unset if found. + for (int i = 0; i < MAX_IP_RANGES; i++) + { + const char* cur_range = cfg.drop_ranges[i]; + + if (!cur_range) + { + continue; + } + + if (strcmp(cur_range, cmd.ip) != 0) + { + continue; + } + + free((void*)cfg.drop_ranges[i]); + cfg.drop_ranges[i] = NULL; + } + } + } + // Handle block map mode. + else + { + printf("Using source IP block mode (2)...\n"); + + if (!cmd.ip) + { + fprintf(stderr, "No source IP address specified. Please set an IP using -s, --ip arguments.\n"); + + return EXIT_FAILURE; + } + + int map_block = GetMapPinFd(XDP_MAP_PIN_DIR, "map_block"); + int map_block6 = GetMapPinFd(XDP_MAP_PIN_DIR, "map_block6"); + + if (cmd.v6) + { + if (map_block6 < 0) + { + fprintf(stderr, "Failed to find the 'map_block6' BPF map.\n"); + + return EXIT_FAILURE; + } + + struct in6_addr addr; + + if ((ret = inet_pton(AF_INET6, cmd.ip, &addr)) != 1) + { + fprintf(stderr, "Failed to convert IPv6 address '%s' to decimal (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + + u128 ip = 0; + + for (int i = 0; i < 16; i++) + { + ip = (ip << 8) | addr.s6_addr[i]; + } + + if ((ret = DeleteBlock6(map_block6, ip)) != 0) + { + fprintf(stderr, "Failed to delete IP '%s' from BPF map (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + } + else + { + if (map_block < 0) + { + fprintf(stderr, "Failed to find the 'map_block' BPF map.\n"); + + return EXIT_FAILURE; + } + + struct in_addr addr; + + if ((ret = inet_pton(AF_INET, cmd.ip, &addr)) != 1) + { + fprintf(stderr, "Failed to convert IP address '%s' to decimal (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + + if ((ret = DeleteBlock(map_block, addr.s_addr)) != 0) + { + fprintf(stderr, "Failed to delete IP '%s' from BPF map (%d).\n", cmd.ip, ret); + + return EXIT_FAILURE; + } + + printf("Deleted '%s' from block map...\n", cmd.ip); + } + } + + if (cmd.save) + { + // Save config. + printf("Saving config...\n"); + + if ((ret = SaveCfg(&cfg, cmd.cfg_file)) != 0) + { + fprintf(stderr, "[ERROR] Failed to save config.\n"); + + return EXIT_FAILURE; + } + } + + printf("Success! Exiting.\n"); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/src/rule_del/utils/cmdline.c b/src/rule_del/utils/cmdline.c new file mode 100644 index 0000000..14bb107 --- /dev/null +++ b/src/rule_del/utils/cmdline.c @@ -0,0 +1,71 @@ +#include + +const struct option opts[] = +{ + { "cfg", required_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + + { "save", no_argument, NULL, 's' }, + + { "mode", required_argument, NULL, 'm' }, + + { "idx", required_argument, NULL, 'i' }, + { "ip", required_argument, NULL, 'd' }, + { "v6", no_argument, NULL, 'v' }, + + { NULL, 0, NULL, 0 } +}; + +void ParseCommandLine(cmdline_t* cmd, int argc, char* argv[]) +{ + int c; + + while ((c = getopt_long(argc, argv, "c:lhm:i:rsv", opts, NULL)) != -1) + { + switch (c) + { + case 'c': + cmd->cfg_file = optarg; + + break; + + case 'h': + cmd->help = 1; + + break; + + case 's': + cmd->save = 1; + + break; + + case 'm': + cmd->mode = atoi(optarg); + + break; + + case 'i': + cmd->idx = atoi(optarg); + + break; + + case 'd': + cmd->ip = optarg; + + break; + + case 'v': + cmd->v6 = 1; + + break; + + case '?': + fprintf(stderr, "Missing argument option...\n"); + + break; + + default: + break; + } + } +} \ No newline at end of file diff --git a/src/rule_del/utils/cmdline.h b/src/rule_del/utils/cmdline.h new file mode 100644 index 0000000..f1b8807 --- /dev/null +++ b/src/rule_del/utils/cmdline.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +struct cmdline +{ + const char* cfg_file; + + int help; + + int save; + + int mode; + + int idx; + + const char* ip; + int v6; +} typedef cmdline_t; + +void ParseCommandLine(cmdline_t* cmd, int argc, char* argv[]); \ No newline at end of file From 628b18f32d8fd811ed133a5071f8ee58766aa35b Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 12:08:37 -0500 Subject: [PATCH 10/19] Update README. --- README.md | 149 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index b1cde40..bf44ef5 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,50 @@ [![XDP Firewall Build Workflow](https://github.com/gamemann/XDP-Firewall/actions/workflows/build.yml/badge.svg)](https://github.com/gamemann/XDP-Firewall/actions/workflows/build.yml) [![XDP Firewall Run Workflow](https://github.com/gamemann/XDP-Firewall/actions/workflows/run.yml/badge.svg)](https://github.com/gamemann/XDP-Firewall/actions/workflows/run.yml) -A *stateless* firewall that attaches to the Linux kernel's [XDP](https://www.iovisor.org/technology/xdp) hook through [(e)BPF](https://ebpf.io/) for fast packet processing. This firewall is designed to read filtering rules from a config file and filter incoming packets. Both IPv4 and **IPv6** are supported! The protocols currently supported are TCP, UDP, and ICMP. The firewall also has the option to display counters for allowed, dropped, and passed packets which are written to `stdout`. +A *stateless* firewall that attaches to the [XDP](https://www.iovisor.org/technology/xdp) hook in the Linux kernel using [(e)BPF](https://ebpf.io/) for fast packet processing. + +This firewall is designed for performance and flexibility, offering features such as dynamic filtering, source IP blocking, IP range dropping, and real-time packet counters. This makes it a powerful tool for network engineers, security professionals, and anyone interested in XDP or high-performance firewalling. ![Demo Run](./images/run.gif) -I hope this project helps existing network engineers and programmers interested in utilizing XDP or anybody interested in getting into those fields! (D)DoS protection and mitigation is 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 ultimately hope this tool helps existing network engineers and programmers interested in utilizing XDP or anybody interested in getting into those fields! (D)DoS protection and mitigation is an important part of Cyber Security and understanding networking concepts and packet flow at a low-to-medium level would certainly help those who are pursuing a career in the field 🙂 + +## 🚀 Features Overview +All features can be enabled or disabled through the configuration file on disk or by modifying [`config.h`](./src/common/config.h) before compilation. If you're planning to only use certain features such as the source IP block and drop functionality, it is recommended you disable other features like dynamic filtering to achieve best performance. + +### 🔥 High-Performance Packet Filtering +* **XDP-Powered** - Runs at the earliest point in the network stack for **minimal latency**. +* **eBPF-Based** - Uses BPF maps for efficient rule lookups and packet processing. + +### 🛑 Source IP Blocking & Dropping +* Instantly **drop packets** from known bad IP addresses. +* Uses a **BPF map** for **efficient** lookups and blocking. +* Can be managed dynamically via CLI tools (`xdpfw-add`, `xdpfw-del`). + +### ⚡ Dynamic Filtering (Rule-Based) +* Define **custom rules** to allow or drop packets based on protocol, port, or IP. +* Supports **temporary bans** by adding IPs to the block list for a configurable duration. +* Ideal for mitigating **non-spoofed (D)DoS attacks**. + +### 🌍 IP Range Dropping (CIDR) +* Block entire **IP subnets** efficiently at the XDP level. +* Supports **CIDR-based filtering** (e.g., `192.168.1.0/24`). +* Disabled by default but can be enabled in [`config.h`](./src/common/config.h). + +### 📊 Real-Time Packet Counters +* Track **allowed, dropped, and passed** packets in real time. +* Supports **per-second statistics** for better traffic analysis. + +### 📜 Logging System +* Built-in **logging** to terminal and/or a file. +* Configurable **verbosity levels** to control log output. -## 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. +### 📌 Pinned Maps & CLI Utilities +* **Pinned BPF maps** allow external programs to interact with firewall rules. +* CLI utilities (`xdpfw-add`, `xdpfw-del`) enable **dynamic rule** management without restarting the firewall. +* Supports integration with **user-space security systems** for enhanced protection. + +## 🛠️ 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 names in other package managers. ```bash # Install dependencies. @@ -20,20 +57,20 @@ sudo apt install -y libpcap-dev m4 gcc-multilib sudo apt install -y linux-tools-$(uname -r) ``` -You can use `git` to clone this project. Make sure to include the `--recursive` flag so it downloads the XDP Tools submodule! +You can use `git` to clone this project. Make sure to include the `--recursive` flag so it downloads the XDP Tools sub-module! ```bash -# Clone repository via Git. Use recursive flag to download XDP Tools submodule. +# Clone repository via Git. Use recursive flag to download XDP Tools sub-module. git clone --recursive https://github.com/gamemann/XDP-Firewall.git # Change directory to repository. cd XDP-Firewall ``` -From here, you have two options to build and install this tool which are explained below. +From here, you have two options to build and install the firewall. ### With Bash Script -The easiest way to build and install this tool is to use the provided [`install.sh`](./install.sh) Bash script. This script relies on `sudo` being installed on your system. If you do not have sudo, please refer to the below steps on building and installing this tool without the Bash script. +The easiest way to build and install the firewall is to use the provided [`install.sh`](./install.sh) Bash script. This script relies on `sudo` being installed on your system. If you do not have sudo, please refer to the below steps on building and installing this tool without the Bash script. If you don't have LibXDP installed on your system yet, I'd recommend using the following command. @@ -51,7 +88,7 @@ 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. | -| --objdump | Dumps the XDP/BPF object file using [`llvm-objdump`](https://llvm.org/docs/CommandGuide/llvm-objdump.html) to Assemby into `objdump.asm`. | +| --objdump | Dumps the XDP/BPF object file using [`llvm-objdump`](https://llvm.org/docs/CommandGuide/llvm-objdump.html) to Assemby into `objdump.asm`. This is used for debugging. | | --help | Displays help message. | ![Script Build Demo](./images/build_script.gif) @@ -77,8 +114,8 @@ sudo make install ![Script Build Demo](./images/build_make.gif) -## Command Line Usage -The following general command line arguments are supported. +## 💻 CLI Usage +The following command line arguments are supported when running the firewall. | Name | Default | Description | | ---- | ------- | ----------- | @@ -89,23 +126,30 @@ The following general command line arguments are supported. | -l, --list | N/A | If set, will print the current config values and exit. | | -h, --help | N/A | Prints a help message. | -Additionally, there are command line overrides for base config options. +Additionally, there are command line overrides for base config options you may include. | Name | Example | Description | | ---- | ------- | ----------- | | -v, --verbose | `-v 3` | Overrides the config's verbose value. | | --log-file | `--log-file ./test.log` | Overrides the config's log file value. | | -i, --interface | `-i enp1s0` | Overrides the config's interface value. | +| -p, --pin-maps | `-p 0` | Overrides the config's pin maps value. | | -u, --update-time | `-u 30` | Overrides the config's update time value. | | -n, --no-stats | `-n 1` | Overrides the config's no stats value. | | --stats-ps | `--stats-ps 1` | Overrides the config's stats per second value. | | --stdout-ut | `--stdout-ut 500` | Overrides the config's stdout update time value. | -## Configuration -By default, the configuration file path is `/etc/xdpfw/xdpfw.conf`. This path may be altered with the `-c --config` CLI arguments detailed above. +## ⚙️ Configuration +There are two configuration methods for this firewall: + +1️⃣ **Build-Time Configuration** - Modify hard-coded constants in [`config.h`](./src/common/config.h) by commenting (`//`) or uncommenting options along with setting values. Since these settings are required at build time, the firewall must be rebuilt for changes to take effect. + +2️⃣ **Runtime Configuration** - Settings can also be adjusted via a configuration file stored on disk. By default, this file is located at `/etc/xdpfw/xdpfw.conf`, but you can specify a different path using the `-c` or `--config` CLI options. The [`libconfig`](https://hyperrealm.github.io/libconfig/libconfig_manual.html) library and syntax is used when parsing the config file. +Here are more details on the layout of the runtime configuration. + ### Data Types The following table quickly explains the data types used within the configuration documentation below (known data types which are not used within the configuration below will **not** be listed). @@ -125,11 +169,13 @@ The following table quickly explains the data types used within the configuratio | verbose | int | `2` | The verbose level for logging (0 - 5 supported so far). | | log_file | string | `/var/log/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`). | +| pin_maps | bool | `true` | Pins main BPF maps to `/sys/fs/bpf/xdpfw/[map_name]` on the file system. | | 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. | | stats_per_second | bool | `false` | If true, packet counters and stats are calculated per second. `stdout_update_time` must be 1000 or less for this to work properly. | | stdout_update_time | uint | `1000` | How often to update `stdout` when displaying packet counters in milliseconds. | | filters | Array of Filter Object(s) | `NULL` | An array of filters to use with the XDP Firewall. | +| ip_drop_ranges | Array Of IP Ranges | `NULL` | An array of IP ranges (strings) to drop if the IP range drop feature is enabled. | ### Filter Object | Name | Type | Default | Description | @@ -188,10 +234,10 @@ You may additionally specified UDP header options for a filter rule which start #### Notes * All settings within a filter rule other than `enabled` and `action` are **not** required. This means you do not have to define them within your config. * When a filter rule's setting is set (not `NULL`), but doesn't match the packet, the program moves onto the next filter rule. Therefore, all of the filter rule's settings that are set must match the packet in order to perform the action specified. Think of it as something like `if src_ip == "10.50.0.3" and udp_dport == 27015: action`. -* As of right now, you can specify up to 60 total filter rules. You may increase this limit by raising the `MAX_FILTERS` constant in the `src/common/config.h` [file](https://github.com/gamemann/XDP-Firewall/blob/master/src/common/config.h#L5) and then recompile the firewall. If you receive a BPF program too large error, this is due to BPF's limitations with complexity and jumps. You may try increasing BPF limitations manually or with a patch. If you want to do this, please read [this](https://github.com/gamemann/XDP-Forwarding/tree/master/patches) README from my XDP Forwarding project. +* As of right now, you can specify up to **60 total** dynamic filter rules. You may increase this limit by raising the `MAX_FILTERS` constant in the `src/common/config.h` [file](https://github.com/gamemann/XDP-Firewall/blob/master/src/common/config.h#L5) and then recompile the firewall. If you receive a BPF program too large error, this is due to BPF's limitations with complexity and jumps. You may try increasing BPF limitations manually or with a patch. If you want to do this, please read [this](https://github.com/gamemann/XDP-Forwarding/tree/master/patches) README from my XDP Forwarding project. -### Example -Here's a config example. +### Runtime Example +Here's a runtime config example. ```squidconf verbose = 5; @@ -232,7 +278,7 @@ filters = ( ); ``` -## Notes +## 📝 Notes ### XDP Attach Modes By default, the firewall attaches to the Linux kernel's XDP hook using **DRV** mode (AKA native; occurs before [SKB creation](http://vger.kernel.org/~davem/skb.html)). If the host's network configuration or network interface card (NIC) doesn't support DRV mode, the program will attempt to attach to the XDP hook using **SKB** mode (AKA generic; occurs after SKB creation which is where IPTables and NFTables are processed via the `netfilter` kernel module). You may use overrides through the command-line to force SKB or offload modes. @@ -247,16 +293,8 @@ Offloading your XDP/BPF program to your system's NIC allows for the fastest pack As of this time, I am not aware of any NIC manufacturers that will be able to offload this firewall completely to the NIC due to its BPF complexity. To be honest, in the current networking age, I believe it's best to leave offloaded programs to BPF map lookups and minimum packet inspection. For example, a BPF blacklist map lookup for malicious source IPs or ports. However, XDP is still very new and I would imagine we're going to see these limitations loosened or lifted in the next upcoming years. This is why I added support for offload mode on this firewall. -### Moved To LibXDP -On **June 6th, 2023**, we've started using [LibXDP](https://github.com/xdp-project/xdp-tools/tree/master/lib/libxdp) from [XDP Tools](https://github.com/xdp-project/xdp-tools) to load and attach the XDP/(e)BPF program. This requires additional packages and tools to install and use with this XDP firewall as noted above. - -### Issues On Ubuntu 20.04 -If you have issues on Ubuntu 20.04 or earlier, please refer to the reply on [this](https://github.com/gamemann/XDP-Firewall/issues/41#issuecomment-1758701008) issue. - -Basically, Clang/LLVM 12 or above is required and I'd recommend running Linux kernel 5.3 or above. - -### BPF For/While Loop Support + Performance Notes -This project requires loop support with BPF. Older kernels will not support this feature and output an error such as the following. +### BPF Loop Support + Performance Notes +The dynamic filters feature requires loop support with BPF. Older kernels will not support this feature and output an error such as the following. ```vim libbpf: load bpf program failed: Invalid argument @@ -271,35 +309,38 @@ libbpf: failed to load object '/etc/xdpfw/xdpfw_kern.o' It looks like BPF loop [support](https://lwn.net/Articles/794934/) was added in kernel 5.3. Therefore, you'll need kernel 5.3 or above for this program to run properly. -#### Performance With `For` Loops +#### Performance With Loops & Dynamic Filters Due to the usage of a [`for` loop](https://github.com/gamemann/XDP-Firewall/blob/master/src/xdp/prog.c#L249) inside the XDP program that handles looping through all filtering rules inside of a BPF array map, performance will be impacted depending on how many filtering rules you have configured (ultimately, the firewall **doesn't scale** that well). This firewall was designed to be as flexible as possible regarding configuration and is most effective when configured to add malicious source IPs to the block map for a certain amount of time which are then dropped at the beginning of the XDP program for the best performance. Unfortunately, we can't really eliminate the `for` loop with the current amount of flexibility we allow (especially minimum/maximum TTL, packet lengths, IDs, etc.), unless if we were to create more BPF maps and insert many more entries which would result in a lot more memory consumed and isn't ideal at all. If we were to remove flexibility, the best approach would be to store filtering rules inside a hashed BPF map using the packet's destination IP/port as the entry's key in my opinion (this would then eliminate flexibility related to being able to specify a filtering rule to match against a single destination IP without a port, unless if we implemented multiple BPF map lookups inside the XDP program which would then impact performance). However, there are currently no plans to switch to this format due to the amount of flexibility lost and also not having the time on my side (if somebody else creates a PR to implement this, I'd be willing to have a separate branch with the new functionality for others to use if the current branch isn't working out for their needs). The firewall is still decent at filtering non-spoofed attacks, especially when a block time is specified so that malicious IPs are filtered at the beginning of the program for some time. -### Error Related To Toolchain Hardening -As stated in issue [#38](https://github.com/gamemann/XDP-Firewall/issues/38) by [g00g1](https://github.com/g00g1), if you have toolchain hardening enabled, you may receive the following error when compiling. +### Rate Limits +By default, client stats including packets and bytes per second are calculated per *partial* flow (source IP/port and protocol). This is useful if you want to specify connection-specific rate limits inside of your filtering rules using the `pps` and `bps` settings. However, if you want to calculate client stats using only the source IP, you may comment out [this](https://github.com/gamemann/XDP-Firewall/blob/master/src/common/config.h#L12) line. +```C +//#define USE_FLOW_RL ``` -error: :0:0: in function xdp_prog_main i32 (ptr): A call to built-in function '__stack_chk_fail' is not supported. -``` - -In order to fix this, you'll need to pass the `-fno-stack-protector` flag to Clang when building LibBPF and the firewall itself. You'll want to modify the `Makefile` for each project to add this flag. Patches for this may be found [here](https://github.com/gamemann/XDP-Firewall/issues/38#issuecomment-1547965524)! -### Will You Make This Firewall Stateful? -There is a possibility I may make this firewall stateful in the future *when* I have time, but this will require a complete overhaul along with implementing application-specific filters. With that said, I am still on contract from my previous employers for certain filters of game servers. If others are willing to contribute to the project and implement these features, feel free to make pull requests! - -You may also be interested in this awesome project called [FastNetMon](https://github.com/pavel-odintsov/fastnetmon)! +### 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. -### Rate Limits -By default, client stats including packets and bytes per second are calculated per *partial* flow (source IP/port and protocol). This is useful if you want to specify connection-specific rate limits inside of your filtering rules using the `pps` and `bps` settings. However, if you want to calculate client stats using only the source IP, you may comment out [this](https://github.com/gamemann/XDP-Firewall/blob/master/src/common/config.h#L12) line. +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 USE_FLOW_RL +//#define ENABLE_FILTER_LOGGING ``` -### Error While Loading Shared Libraries +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. + +## ❓ F.A.Q. +### I receive an error related to failing to load shared libraries. How do I fix this? If you receive an error similar to the one below when running the program and have built the program using the no static option, make sure you have LibXDP globally installed onto your system via [XDP Tools](https://github.com/xdp-project/xdp-tools). You can execute `make libxdp && sudo make libxdp_install` to build and install both LibXDP and LibBPF onto your system separately. ```bash @@ -314,23 +355,21 @@ 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). +### I receive an error related to toolchain hardening. How do I fix this? +As stated in issue [#38](https://github.com/gamemann/XDP-Firewall/issues/38) by [g00g1](https://github.com/g00g1), if you have toolchain hardening enabled, you may receive the following error when compiling. -```C -//#define ENABLE_FILTER_LOGGING +``` +error: :0:0: in function xdp_prog_main i32 (ptr): A call to built-in function '__stack_chk_fail' is not supported. ``` -I will most likely implement functionality to rate limit log messages from XDP in the future. +In order to fix this, you'll need to pass the `-fno-stack-protector` flag to Clang when building LibBPF and the firewall itself. You'll want to modify the `Makefile` for each project to add this flag. Patches for this may be found [here](https://github.com/gamemann/XDP-Firewall/issues/38#issuecomment-1547965524)! -### 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. +### I have issues running the firewall on Ubuntu 20.04. What could be the cause? +If you have issues on Ubuntu 20.04 or earlier, please refer to the reply on [this](https://github.com/gamemann/XDP-Firewall/issues/41#issuecomment-1758701008) issue. -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. +Basically, Clang/LLVM 12 or above is required and I'd recommend running Linux kernel 5.3 or above. -## 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! ### [XDP Forwarding](https://github.com/gamemann/XDP-Forwarding) @@ -344,6 +383,6 @@ An application that utilizes fast [AF_XDP](https://docs.kernel.org/networking/af While this application doesn't utilize native XDP or (e)BPF, I do feel it should be mentioned here. AF_XDP sockets are very fast and often used with raw XDP programs via the `bpf_redirect_map()` function call (also see my [XDP Stats](https://github.com/gamemann/XDP-Stats) project which calculates stats in raw XDP and AF_XDP programs). -## Credits +## 🙌 Credits * [Christian Deacon](https://github.com/gamemann) - Creator. * [Phil](https://github.com/Nasty07) - Contributor. From a410e928f273511fca23e988db8e8fe144341373 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 12:59:10 -0500 Subject: [PATCH 11/19] Update README. --- README.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bf44ef5..4e61f91 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This firewall is designed for performance and flexibility, offering features suc I ultimately hope this tool helps existing network engineers and programmers interested in utilizing XDP or anybody interested in getting into those fields! (D)DoS protection and mitigation is an important part of Cyber Security and understanding networking concepts and packet flow at a low-to-medium level would certainly help those who are pursuing a career in the field 🙂 ## 🚀 Features Overview -All features can be enabled or disabled through the configuration file on disk or by modifying [`config.h`](./src/common/config.h) before compilation. If you're planning to only use certain features such as the source IP block and drop functionality, it is recommended you disable other features like dynamic filtering to achieve best performance. +All features can be enabled or disabled through the build-time configuration ([`config.h`](./src/common/config.h) before compilation) or runtime configuration on disk. If you're planning to only use certain features such as the source IP block and drop functionality, it is recommended you disable other features like dynamic filtering to achieve best performance. ### 🔥 High-Performance Packet Filtering * **XDP-Powered** - Runs at the earliest point in the network stack for **minimal latency**. @@ -21,8 +21,9 @@ All features can be enabled or disabled through the configuration file on disk o * Can be managed dynamically via CLI tools (`xdpfw-add`, `xdpfw-del`). ### ⚡ Dynamic Filtering (Rule-Based) -* Define **custom rules** to allow or drop packets based on protocol, port, or IP. +* Define **custom rules** to allow or drop packets based on protocols, ports, IP addresses, and more! * Supports **temporary bans** by adding IPs to the block list for a configurable duration. +* Supports **TCP, UDP, and ICMP** network protocols and **IPv6**! * Ideal for mitigating **non-spoofed (D)DoS attacks**. ### 🌍 IP Range Dropping (CIDR) @@ -53,17 +54,17 @@ sudo apt install -y libconfig-dev llvm clang libelf-dev build-essential # Install dependencies for building LibXDP and LibBPF. sudo apt install -y libpcap-dev m4 gcc-multilib -# You need tools for your kernel since we require BPFTool. If this doesn't work, I'd suggest building BPFTool from source (https://github.com/libbpf/bpftool). +# You may need tools for your Linux kernel since BPFTool is required. If this doesn't work and you still run into issues, I'd suggest building BPFTool from source (https://github.com/libbpf/bpftool). sudo apt install -y linux-tools-$(uname -r) ``` -You can use `git` to clone this project. Make sure to include the `--recursive` flag so it downloads the XDP Tools sub-module! +You can use `git` to clone this project. Make sure to include the `--recursive` flag so it downloads the XDP Tools sub-module! Otherwise, you will need to execute `git submodule update --init` while in the cloned repository's directory. ```bash # Clone repository via Git. Use recursive flag to download XDP Tools sub-module. git clone --recursive https://github.com/gamemann/XDP-Firewall.git -# Change directory to repository. +# Change directory to cloned repository. cd XDP-Firewall ``` @@ -278,6 +279,64 @@ filters = ( ); ``` +## 🔧 The `xdpfw-add` & `xdpfw-del` Utilities +When the main BPF maps are pinned to the file system (depending on the `pin_maps` runtime option detailed above), this allows you to add or delete rules while the firewall is running using `xdpfw-add` and `xdpfw-del`. + +### General CLI Usage +The following general CLI arguments are supported with these utilities. + +| Name | Example | Description | +| ---- | ------- | ----------- | +| -c, --cfg | `-c ./local/conf` | The path to the configuration file (required if the save argument is specified or if you're using dynamic filters mode). | +| -s, --save | `-s` | Updates the runtime config file. | +| -m, --mode | `-m 1` | The mode to use (0 = dynamic filters, 1 = IP range drop list, 2 = source IP block list). | +| -i, --idx | `-i 3` | The index to update or delete when running in filters mode. | +| -d, --ip | `-d 192.168.1.0/24` | The IP range or source IP when running in IP range drop list or source IP block list modes. | +| -v, --v6 | `-v` | Parses and adds the IP address as IPv6 when running in source IP block list mode. | + +### The `xdpfw-add` Tool +This CLI tool allows you to add dynamic rules, IP ranges to the drop list, and source IPs to the block list. I'd recommend using `xdpfw-add -h` for more information. + +#### Additional CLI Usage +The following CLI arguments are supported. + +| Name | Example | Description | +| ---- | ------- | ----------- | +| -e, --expires | `-e 60` | When the source IP block expires in seconds when running in IP block list mode. | +| --sip | `--sip 192.168.1.0/24` | The source IPv4 address/range to match with the dynamic filter. | +| --dip | `--sip 10.90.0.0/24` | The destination IPv4 address/range to match with the dynamic filter. | +| --sip6 | `--sip 192.168.1.0/24` | The source IPv6 address to match with the dynamic filter. | +| --dip6 | `--sip 192.168.1.0/24` | The destination IPv6 address to match with the dynamic filter. | +| --min-ttl | `--min-ttl 0` | The IP's minimum TTL to match with the dynamic filter. | +| --max-ttl | `--max-ttl 6` | The IP's maximum TTL to match with the dynamic filter. | +| --min-len | `--min-len 42` | The packet's mimimum length to match with the dynamic filter. | +| --max-len | `--max-len 96` | The packet's maximum length to match with the dynamic filter. | +| --tos | `--tos 1` | The IP's Type of Service to match with the dynamic filter. | +| --pps | `--pps 10000` | The minimum PPS rate to match with the dynamic filter. | +| --bps | `--bps 126000` | The minimum BPS rate to match with the dynamic filter. | +| --tcp | `--tcp 1` | Enables or disables TCP matching with the dynamic filter. | +| --tsport | `--tsport 22` | The TCP source port to match with the dynamic filter. | +| --tdport | `--tdport 443` | The TCP destination port to match with the dynamic filter. | +| --urg | `--urg 1` | Enables or disables TCP URG flag matching with the dynamic filter. | +| --ack | `--ack 1` | Enables or disables TCP ACK flag matching with the dynamic filter. | +| --rst | `--rst 1` | Enables or disables TCP RST flag matching with the dynamic filter. | +| --psh | `--psh 1` | Enables or disables TCP PSH flag matching with the dynamic filter. | +| --syn | `--syn 1` | Enables or disables TCP SYN flag matching with the dynamic filter. | +| --fin | `--fin 1` | Enables or disables TCP FIN flag matching with the dynamic filter. | +| --ece | `--ece 1` | Enables or disables TCP ECE flag matching with the dynamic filter. | +| --cwr | `--cwr 1` | Enables or disables TCP CWR flag matching with the dynamic filter. | +| --udp | `--udp 1` | Enables or disables UDP matching with the dynamic filter. | +| --usport | `--usport 53` | The UDP source port to match with the dynamic filter. | +| --udport | `--udport 27015` | The UDP destination port to match with the dynamic filter. | +| --icmp | `--icmp 1` | Enables or disables ICMP matching with the dynamic filter. | +| --code | `--code 1` | The ICMP code to match with the dynamic filter. | +| --type | `--type 8` | The ICMP type to match with the dynamic filter. | + +### The `xdpfw-del` Tool +This CLI tool allows you to delete dynamic rules, IP ranges from the drop list, and source IPs from the block list. + +There is no additional CLI usage for this tool. Please refer to the general CLI usage above. + ## 📝 Notes ### XDP Attach Modes By default, the firewall attaches to the Linux kernel's XDP hook using **DRV** mode (AKA native; occurs before [SKB creation](http://vger.kernel.org/~davem/skb.html)). If the host's network configuration or network interface card (NIC) doesn't support DRV mode, the program will attempt to attach to the XDP hook using **SKB** mode (AKA generic; occurs after SKB creation which is where IPTables and NFTables are processed via the `netfilter` kernel module). You may use overrides through the command-line to force SKB or offload modes. @@ -369,6 +428,9 @@ If you have issues on Ubuntu 20.04 or earlier, please refer to the reply on [thi Basically, Clang/LLVM 12 or above is required and I'd recommend running Linux kernel 5.3 or above. +### Will you make this firewall stateful? +At this time, there are no plans to make this firewall stateful. There is a chance I may make a separate firewall with basic connection tracking, but I have no ETA on that and haven't started its development. With that said, I cannot share code for things like layer-7 filters or a full TCP proxy with SYN cookies support. + ## 🌟 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! From bfbc1a7554fd426601e39aae82c067e170057549 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 12:59:38 -0500 Subject: [PATCH 12/19] Organize code. --- src/loader/utils/cmdline.c | 8 +++++++- src/loader/utils/helpers.c | 2 +- src/rule_del/prog.c | 10 ++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/loader/utils/cmdline.c b/src/loader/utils/cmdline.c index 207effd..600eb89 100644 --- a/src/loader/utils/cmdline.c +++ b/src/loader/utils/cmdline.c @@ -12,6 +12,7 @@ const struct option opts[] = { "verbose", required_argument, NULL, 'v' }, { "log-file", required_argument, NULL, 0 }, { "interface", required_argument, NULL, 'i' }, + { "pin-maps", required_argument, NULL, 'p' }, { "update-time", required_argument, NULL, 'u' }, { "no-stats", required_argument, NULL, 'n' }, { "stats-ps", required_argument, NULL, 1 }, @@ -31,7 +32,7 @@ void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]) { int c; - while ((c = getopt_long(argc, argv, "c:ost:lhv:i:u:n:", opts, NULL)) != -1) + while ((c = getopt_long(argc, argv, "c:ost:lhv:i:p:u:n:", opts, NULL)) != -1) { switch (c) { @@ -80,6 +81,11 @@ void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]) break; + case 'p': + cmd->pin_maps = atoi(optarg); + + break; + case 'u': cmd->update_time = atoi(optarg); diff --git a/src/loader/utils/helpers.c b/src/loader/utils/helpers.c index d8d95bd..30018b1 100644 --- a/src/loader/utils/helpers.c +++ b/src/loader/utils/helpers.c @@ -113,7 +113,7 @@ void PrintToolInfo() /** * Retrieves nanoseconds since system boot. * - * @return The current nanoseconds since the system started. + * @return The current nanoseconds since the system last booted. */ u64 GetBootNanoTime() { diff --git a/src/rule_del/prog.c b/src/rule_del/prog.c index a495fa2..a2ddb94 100644 --- a/src/rule_del/prog.c +++ b/src/rule_del/prog.c @@ -157,6 +157,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + printf("Using 'map_range_drop' FD => %d.\n", map_range_drop); + // Parse IP range. ip_range_t range = ParseIpCidr(cmd.ip); @@ -168,7 +170,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - printf("Removed IP range '%s' from BPF map.\n", cmd.ip); + printf("Removed IP range '%s'...\n", cmd.ip); if (cmd.save) { @@ -216,6 +218,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + printf("Using 'map_block6' FD => %d.\n", map_block6); + struct in6_addr addr; if ((ret = inet_pton(AF_INET6, cmd.ip, &addr)) != 1) @@ -248,6 +252,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + printf("Using 'map_block' FD => %d.\n", map_block); + struct in_addr addr; if ((ret = inet_pton(AF_INET, cmd.ip, &addr)) != 1) @@ -264,7 +270,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - printf("Deleted '%s' from block map...\n", cmd.ip); + printf("Deleted IP '%s'...\n", cmd.ip); } } From e4464e19617aff763f763b904187d86734b9732d Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 13:00:06 -0500 Subject: [PATCH 13/19] Set CLI integer values to -1 by default in xdpfw-add. --- src/rule_add/prog.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/rule_add/prog.c b/src/rule_add/prog.c index 50ac4ed..7b3b1d4 100644 --- a/src/rule_add/prog.c +++ b/src/rule_add/prog.c @@ -22,6 +22,36 @@ int main(int argc, char *argv[]) cmdline_t cmd = {0}; cmd.cfg_file = CONFIG_DEFAULT_PATH; + // We need to set integers for dynamic filters to -1 since we consider -1 as 'unset'. + cmd.min_ttl = -1; + cmd.max_ttl = -1; + cmd.min_len = -1; + cmd.max_len = -1; + cmd.tos = -1; + + cmd.pps = -1; + cmd.bps = -1; + + cmd.tcp_enabled = -1; + cmd.tcp_sport = -1; + cmd.tcp_dport = -1; + cmd.tcp_urg = -1; + cmd.tcp_ack = -1; + cmd.tcp_rst = -1; + cmd.tcp_psh = -1; + cmd.tcp_syn = -1; + cmd.tcp_fin = -1; + cmd.tcp_ece = -1; + cmd.tcp_cwr = -1; + + cmd.udp_enabled = -1; + cmd.udp_sport = -1; + cmd.udp_dport = -1; + + cmd.icmp_enabled = -1; + cmd.icmp_code = -1; + cmd.icmp_type = -1; + ParseCommandLine(&cmd, argc, argv); if (!cmd.help) From b0385f975d25d8b9ad521334eeb2c746ebd53dcd Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 13:03:05 -0500 Subject: [PATCH 14/19] Update README. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e61f91..fbb3465 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,7 @@ Here's a runtime config example. verbose = 5; log_file = ""; interface = "ens18"; +pin_maps = true; update_time = 15; no_stats = false; stats_per_second = true; @@ -277,10 +278,12 @@ filters = ( src_ip = "10.50.0.4" } ); + +ip_drop_ranges = ( "192.168.1.0/24", "10.3.0.0/24" ); ``` ## 🔧 The `xdpfw-add` & `xdpfw-del` Utilities -When the main BPF maps are pinned to the file system (depending on the `pin_maps` runtime option detailed above), this allows you to add or delete rules while the firewall is running using `xdpfw-add` and `xdpfw-del`. +When the main BPF maps are pinned to the file system (depending on the `pin_maps` runtime option detailed above), this allows you to add or delete rules while the firewall is running using the `xdpfw-add` and `xdpfw-del` utilities. ### General CLI Usage The following general CLI arguments are supported with these utilities. From 786b47228752b1010f5c2d3727fb52cdb9a18451 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 13:35:21 -0500 Subject: [PATCH 15/19] Improve xdpfw-add util. --- README.md | 7 +++++- src/rule_add/prog.c | 45 +++++++++++++++++++++++++++--------- src/rule_add/utils/cmdline.c | 25 ++++++++++++++++++++ src/rule_add/utils/cmdline.h | 7 ++++++ 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fbb3465..cee4c03 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,8 @@ sudo apt install -y libconfig-dev llvm clang libelf-dev build-essential # Install dependencies for building LibXDP and LibBPF. sudo apt install -y libpcap-dev m4 gcc-multilib -# You may need tools for your Linux kernel since BPFTool is required. If this doesn't work and you still run into issues, I'd suggest building BPFTool from source (https://github.com/libbpf/bpftool). +# You may need tools for your Linux kernel since BPFTool is required. +# If this doesn't work and you still run into issues, I'd suggest building BPFTool from source (https://github.com/libbpf/bpftool). sudo apt install -y linux-tools-$(uname -r) ``` @@ -306,6 +307,10 @@ The following CLI arguments are supported. | Name | Example | Description | | ---- | ------- | ----------- | | -e, --expires | `-e 60` | When the source IP block expires in seconds when running in IP block list mode. | +| --enabled | `--enabled 0` | Enables or disables dynamic filter. | +| --action | `--action 1` | The action to perform on packets that match the filter (0 = drop, 1 = allow). | +| --log | `--log 1` | Enables or disables logging for the dynamic filter. | +| --block-time | `--block-time 60` | How long to block the source IP for if the packet is matched and the action is drop in the dynamic filter (0 = no time). | | --sip | `--sip 192.168.1.0/24` | The source IPv4 address/range to match with the dynamic filter. | | --dip | `--sip 10.90.0.0/24` | The destination IPv4 address/range to match with the dynamic filter. | | --sip6 | `--sip 192.168.1.0/24` | The source IPv6 address to match with the dynamic filter. | diff --git a/src/rule_add/prog.c b/src/rule_add/prog.c index 7b3b1d4..105dcde 100644 --- a/src/rule_add/prog.c +++ b/src/rule_add/prog.c @@ -23,6 +23,10 @@ int main(int argc, char *argv[]) cmd.cfg_file = CONFIG_DEFAULT_PATH; // We need to set integers for dynamic filters to -1 since we consider -1 as 'unset'. + cmd.enabled = -1; + cmd.action = -1; + cmd.log = -1; + cmd.min_ttl = -1; cmd.max_ttl = -1; cmd.min_len = -1; @@ -47,7 +51,7 @@ int main(int argc, char *argv[]) cmd.udp_enabled = -1; cmd.udp_sport = -1; cmd.udp_dport = -1; - + cmd.icmp_enabled = -1; cmd.icmp_code = -1; cmd.icmp_type = -1; @@ -72,6 +76,11 @@ int main(int argc, char *argv[]) printf(" -e, --expires How long to block the IP for in seconds (for mode 2).\n\n"); printf("Filter Mode Options:\n"); + printf(" --enabled Enables or disables the dynamic filter.\n"); + printf(" --action The action when a packet matches (0 = drop, 1 = allow).\n"); + printf(" --log Enables or disables logging for this filter.\n"); + printf(" --block-time How long to add the source IP to the block list for if matched and the action is drop (0 = no time).\n\n"); + printf(" --sip The source IPv4 address (with CIDR support).\n"); printf(" --dip The destination IPv4 address (with CIDR support).\n"); printf(" --sip6 The source IPv6 address.\n"); @@ -99,7 +108,7 @@ int main(int argc, char *argv[]) printf(" --udp Enable or disables matching on the UDP protocol.\n"); printf(" --usport The UDP source port to match on.\n"); - printf(" --udport The UDP destination port to match on.\n"); + printf(" --udport The UDP destination port to match on.\n\n"); printf(" --icmp Enable or disables matching on the ICMP protocol.\n"); printf(" --code The ICMP code to match on.\n"); @@ -136,14 +145,6 @@ int main(int argc, char *argv[]) { printf("Using filters mode (0)...\n"); - // Check index. - if (cmd.idx < 1) - { - fprintf(stderr, "Invalid filter index. Index must start from 1.\n"); - - return EXIT_FAILURE; - } - // Retrieve filters map FD. int map_filters = GetMapPinFd(XDP_MAP_PIN_DIR, "map_filters"); @@ -160,6 +161,8 @@ int main(int argc, char *argv[]) filter_t new_filter = {0}; SetFilterDefaults(&new_filter); + new_filter.set = 1; + // Determine what index we'll be storing this filter at. int idx = -1; @@ -180,6 +183,26 @@ int main(int argc, char *argv[]) } // Fill out new filter. + if (cmd.enabled > -1) + { + new_filter.enabled = cmd.enabled; + } + + if (cmd.action > -1) + { + new_filter.action = cmd.action; + } + + if (cmd.log > -1) + { + new_filter.log = cmd.log; + } + + if (cmd.block_time > -1) + { + new_filter.block_time = cmd.block_time; + } + if (cmd.src_ip) { ip_range_t range = ParseIpCidr(cmd.src_ip); @@ -371,7 +394,7 @@ int main(int argc, char *argv[]) cfg.filters[idx] = new_filter; // Update filters. - fprintf(stdout, "Updating filters...\n"); + fprintf(stdout, "Updating filters (index %d)...\n", idx); UpdateFilters(map_filters, &cfg); } diff --git a/src/rule_add/utils/cmdline.c b/src/rule_add/utils/cmdline.c index a5794e2..fae6347 100644 --- a/src/rule_add/utils/cmdline.c +++ b/src/rule_add/utils/cmdline.c @@ -15,6 +15,11 @@ const struct option opts[] = { "v6", no_argument, NULL, 'v' }, { "expires", required_argument, NULL, 'e' }, + { "enabled", required_argument, NULL, 28 }, + { "action", required_argument, NULL, 29 }, + { "log", required_argument, NULL, 30 }, + { "block-time", required_argument, NULL, 31 }, + { "sip", required_argument, NULL, 0 }, { "dip", required_argument, NULL, 1 }, { "sip6", required_argument, NULL, 2 }, @@ -99,6 +104,26 @@ void ParseCommandLine(cmdline_t* cmd, int argc, char* argv[]) break; + case 28: + cmd->enabled = atoi(optarg); + + break; + + case 29: + cmd->action = atoi(optarg); + + break; + + case 30: + cmd->log = atoi(optarg); + + break; + + case 31: + cmd->block_time = strtoll(optarg, NULL, 10); + + break; + case 0: cmd->src_ip = optarg; diff --git a/src/rule_add/utils/cmdline.h b/src/rule_add/utils/cmdline.h index 4ade24e..89e598d 100644 --- a/src/rule_add/utils/cmdline.h +++ b/src/rule_add/utils/cmdline.h @@ -6,6 +6,8 @@ #include #include +#include + struct cmdline { const char* cfg_file; @@ -23,6 +25,11 @@ struct cmdline s64 expires; + int enabled; + int log; + int action; + s64 block_time; + const char* src_ip; const char* dst_ip; From de3739d02758cf7e40db8b993aa37718b867ea51 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 13:47:55 -0500 Subject: [PATCH 16/19] Treat block time of 0 as indefinitely. --- src/xdp/prog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xdp/prog.c b/src/xdp/prog.c index d631312..98b9161 100644 --- a/src/xdp/prog.c +++ b/src/xdp/prog.c @@ -96,9 +96,9 @@ int xdp_prog_main(struct xdp_md *ctx) blocked = bpf_map_lookup_elem(&map_block, &iph->saddr); } - if (blocked != NULL && *blocked > 0) + if (blocked != NULL) { - if (now > *blocked) + if (*blocked > 0 && now > *blocked) { // Remove element from map. if (iph6) From 8756bbcdb8fca5387603a89776c40cb2ce87d771 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 13:48:06 -0500 Subject: [PATCH 17/19] Update xdpfw-add and xdpfw-del utils. --- src/rule_add/prog.c | 17 +++++++++++------ src/rule_del/prog.c | 8 ++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/rule_add/prog.c b/src/rule_add/prog.c index 105dcde..e73c37e 100644 --- a/src/rule_add/prog.c +++ b/src/rule_add/prog.c @@ -463,15 +463,13 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - int expires = 0; + u64 expires_rel = 0; - if (cmd.expires > -1) + if (cmd.expires > 0) { - expires = cmd.expires; + expires_rel = GetBootNanoTime() + ((u64)cmd.expires * 1e9); } - u64 expires_rel = GetBootNanoTime() + ((u64)expires * 1e9); - int map_block = GetMapPinFd(XDP_MAP_PIN_DIR, "map_block"); int map_block6 = GetMapPinFd(XDP_MAP_PIN_DIR, "map_block6"); @@ -536,7 +534,14 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - printf("Added '%s' to block map...\n", cmd.ip); + if (cmd.expires > 0) + { + printf("Added '%s' to block map for %lld seconds...\n", cmd.ip, cmd.expires); + } + else + { + printf("Added '%s' to block map indefinitely...\n", cmd.ip); + } } } diff --git a/src/rule_del/prog.c b/src/rule_del/prog.c index a2ddb94..ca56cad 100644 --- a/src/rule_del/prog.c +++ b/src/rule_del/prog.c @@ -89,7 +89,7 @@ int main(int argc, char *argv[]) printf("Using 'map_filters' FD => %d...\n", map_filters); - int index = -1; + int idx = -1; int cfg_idx = cmd.idx - 1; int cur_idx = 0; @@ -108,7 +108,7 @@ int main(int argc, char *argv[]) if (i == cur_idx) { - index = cur_idx; + idx = cur_idx; break; } @@ -116,9 +116,9 @@ int main(int argc, char *argv[]) cur_idx++; } - if (index < 0) + if (idx < 0) { - fprintf(stderr, "[ERROR] Failed to find proper index in config file (%d).\n", index); + fprintf(stderr, "[ERROR] Failed to find proper index in config file (%d).\n", idx); return EXIT_FAILURE; } From b18124b071a1d465bedede281921121f4adf4c6b Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 13:51:18 -0500 Subject: [PATCH 18/19] Disable IP ranges drop map by default. --- src/common/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/config.h b/src/common/config.h index 3157ac9..99f61f9 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -6,7 +6,7 @@ // Enables IPv4 range drop map. // Disable this if you don't plan on adding IPv4 ranges to the drop map as it will increase performance. -#define ENABLE_IP_RANGE_DROP +//#define ENABLE_IP_RANGE_DROP // The maximum IP ranges supported in the IP range drop map. #define MAX_IP_RANGES 4096 From d9af7e2115de4853e327727e32f0f1d0d7dd06fe Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 13:57:02 -0500 Subject: [PATCH 19/19] Update README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cee4c03..4b222eb 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ All features can be enabled or disabled through the build-time configuration ([` ### ⚡ Dynamic Filtering (Rule-Based) * Define **custom rules** to allow or drop packets based on protocols, ports, IP addresses, and more! * Supports **temporary bans** by adding IPs to the block list for a configurable duration. -* Supports **TCP, UDP, and ICMP** network protocols and **IPv6**! +* Supports **TCP, UDP, and ICMP** layer-4 protocols and **IPv6**! * Ideal for mitigating **non-spoofed (D)DoS attacks**. ### 🌍 IP Range Dropping (CIDR)