From a0a812a7f0f18f19de4744d1ea9af761f3dd8b9c Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Sat, 1 Mar 2025 10:40:28 -0500 Subject: [PATCH] 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