#include /** * Loads the config from the file system. * * @param cfg A pointer to the config structure. * @param cfg_file The path to the config file. * @param load_defaults Whether to load defaults or not. * @param overrides Overrides to use instead of config values. * * @return 0 on success or 1 on error. */ int load_cfg(config__t *cfg, const char* cfg_file, int load_defaults, config_overrides_t* overrides) { int ret; if (load_defaults) { set_cfg_defaults(cfg); } FILE *file = NULL; // Open config file. if ((ret = open_cfg(&file, cfg_file)) != 0 || file == NULL) { fprintf(stderr, "Error opening config file.\n"); return ret; } char* buffer = NULL; // Read config. if ((ret = read_cfg(file, &buffer)) != 0) { fprintf(stderr, "Error reading config file.\n"); close_cfg(file); return ret; } // Parse config. if ((ret = parse_cfg(cfg, buffer, overrides)) != 0) { fprintf(stderr, "Error parsing config file.\n"); close_cfg(file); return ret; } free(buffer); if ((ret = close_cfg(file)) != 0) { fprintf(stderr, "Error closing config file.\n"); return ret; } return EXIT_SUCCESS; } /** * Opens the config file. * * @param file_name Path to config file. * * @return 0 on success or 1 on error. */ int open_cfg(FILE** file, const char *file_name) { // Close any existing files. if (*file != NULL) { fclose(*file); *file = NULL; } *file = fopen(file_name, "r"); if (*file == NULL) { return 1; } return 0; } /** * Close config file. * * @param file A pointer to the file to close. * * @param return 0 on success or error value of fclose(). */ int close_cfg(FILE* 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 read_cfg(FILE* file, char** buffer) { fseek(file, 0, SEEK_END); long file_size = ftell(file); rewind(file); if (file_size <= 0) { return 1; } *buffer = malloc(file_size + 1); if (*buffer == NULL) { return 1; } size_t read = fread(*buffer, 1, file_size, file); (*buffer)[read] = '\0'; return 0; } /** * 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 parse_cfg(config__t *cfg, const char* data, config_overrides_t* overrides) { // Initialize config. config_t conf; config_setting_t *setting; config_init(&conf); // Attempt to read the config. if (config_read_string(&conf, data) == CONFIG_FALSE) { log_msg(cfg, 0, 1, "Error from LibConfig when reading file - %s (Line %d)", config_error_text(&conf), config_error_line(&conf)); config_destroy(&conf); return EXIT_FAILURE; } int verbose; if (config_lookup_int(&conf, "verbose", &verbose) == CONFIG_TRUE || (overrides && overrides->verbose > -1)) { if (overrides && overrides->verbose > -1) { cfg->verbose = overrides->verbose; } else { cfg->verbose = verbose; } } const char* log_file; 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) { free(cfg->log_file); cfg->log_file = NULL; } if (overrides && overrides->log_file != NULL) { if (strlen(overrides->log_file) > 0) { cfg->log_file = strdup(overrides->log_file); } else { cfg->log_file = NULL; } } else { if (strlen(log_file) > 0) { cfg->log_file = strdup(log_file); } else { cfg->log_file = NULL; } } } // Get interface(s). config_setting_t* interfaces = config_lookup(&conf, "interface"); if (interfaces) { if (config_setting_is_list(interfaces)) { for (int i = 0; i < config_setting_length(interfaces); i++) { if (i >= MAX_INTERFACES) { break; } const char* interface = config_setting_get_string_elem(interfaces, i); if (!interface) { continue; } if (cfg->interfaces[i]) { free(cfg->interfaces[i]); cfg->interfaces[i] = NULL; } if (i == 0 && overrides && overrides->interface) { cfg->interfaces[i] = strdup(overrides->interface); } else { cfg->interfaces[i] = strdup(interface); } cfg->interfaces_cnt++; } } else { const char* interface; if (config_lookup_string(&conf, "interface", &interface) == CONFIG_TRUE) { if (cfg->interfaces[0]) { free(cfg->interfaces[0]); cfg->interfaces[0] = NULL; } if (overrides && overrides->interface) { cfg->interfaces[0] = strdup(overrides->interface); } else { cfg->interfaces[0] = strdup(interface); } cfg->interfaces_cnt = 1; } } } // Pin BPF maps. int pin_maps; if (config_lookup_bool(&conf, "pin_maps", &pin_maps) == CONFIG_TRUE || (overrides && overrides->pin_maps > -1)) { if (overrides && 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 && overrides->update_time > -1)) { if (overrides && overrides->update_time > -1) { cfg->update_time = overrides->update_time; } else { cfg->update_time = update_time; } } // Get no stats. int no_stats; if (config_lookup_bool(&conf, "no_stats", &no_stats) == CONFIG_TRUE || (overrides && overrides->no_stats > -1)) { if (overrides && overrides->no_stats > -1) { cfg->no_stats = overrides->no_stats; } else { cfg->no_stats = no_stats; } } // Stats per second. int stats_per_second; if (config_lookup_bool(&conf, "stats_per_second", &stats_per_second) == CONFIG_TRUE || (overrides && overrides->stats_per_second > -1)) { if (overrides && overrides->stats_per_second > -1) { cfg->stats_per_second = overrides->stats_per_second; } else { cfg->stats_per_second = stats_per_second; } } // Get stdout update time. int stdout_update_time; if (config_lookup_int(&conf, "stdout_update_time", &stdout_update_time) == CONFIG_TRUE || (overrides && overrides->stdout_update_time > -1)) { if (overrides && overrides->stdout_update_time > -1) { cfg->stdout_update_time = overrides->stdout_update_time; } else { cfg->stdout_update_time = stdout_update_time; } } // Read filters. setting = config_lookup(&conf, "filters"); if (setting && config_setting_is_list(setting)) { for (int i = 0; i < config_setting_length(setting); i++) { filter_rule_cfg_t* filter = &cfg->filters[i]; config_setting_t* filter_cfg = config_setting_get_elem(setting, i); if (filter == NULL || filter_cfg == NULL) { log_msg(cfg, 0, 1, "[WARNING] Failed to read filter rule at index #%d. 'filter' or 'filter_cfg' is NULL (make sure you didn't exceed the maximum filters allowed!)..."); continue; } cfg->filters_cnt++; // Make sure filter is set. filter->set = 1; // Enabled. int enabled; if (config_setting_lookup_bool(filter_cfg, "enabled", &enabled) == CONFIG_TRUE) { filter->enabled = enabled; } // Log. int log; if (config_setting_lookup_bool(filter_cfg, "log", &log) == CONFIG_TRUE) { filter->log = log; } // Action (required). int action; if (config_setting_lookup_int(filter_cfg, "action", &action) == CONFIG_TRUE) { filter->action = action; } // Block time (default 1). int block_time; if (config_setting_lookup_int(filter_cfg, "block_time", &block_time) == CONFIG_TRUE) { filter->block_time = block_time; } // IP PPS (not required). s64 ip_pps; if (config_setting_lookup_int64(filter_cfg, "ip_pps", &ip_pps) == CONFIG_TRUE) { filter->ip_pps = ip_pps; } // IP BPS (not required). s64 ip_bps; if (config_setting_lookup_int64(filter_cfg, "ip_bps", &ip_bps) == CONFIG_TRUE) { filter->ip_bps = ip_bps; } // Flow PPS (not required). s64 flow_pps; if (config_setting_lookup_int64(filter_cfg, "flow_pps", &flow_pps) == CONFIG_TRUE) { filter->flow_pps = flow_pps; } // Flow BPS (not required). s64 flow_bps; if (config_setting_lookup_int64(filter_cfg, "flow_bps", &flow_bps) == CONFIG_TRUE) { filter->flow_bps = flow_bps; } /* IP Options */ // Source IP (not required). const char *sip; if (config_setting_lookup_string(filter_cfg, "src_ip", &sip) == CONFIG_TRUE) { filter->ip.src_ip = strdup(sip); } // Destination IP (not required). const char *dip; if (config_setting_lookup_string(filter_cfg, "dst_ip", &dip) == CONFIG_TRUE) { filter->ip.dst_ip = strdup(dip); } // Source IP (IPv6) (not required). const char *sip6; if (config_setting_lookup_string(filter_cfg, "src_ip6", &sip6) == CONFIG_TRUE) { filter->ip.src_ip6 = strdup(sip6); } // Destination IP (IPv6) (not required). const char *dip6; if (config_setting_lookup_string(filter_cfg, "dst_ip6", &dip6) == CONFIG_TRUE) { filter->ip.dst_ip6 = strdup(dip6); } // Minimum TTL (not required). int min_ttl; if (config_setting_lookup_int(filter_cfg, "min_ttl", &min_ttl) == CONFIG_TRUE) { filter->ip.min_ttl = min_ttl; } // Maximum TTL (not required). int max_ttl; if (config_setting_lookup_int(filter_cfg, "max_ttl", &max_ttl) == CONFIG_TRUE) { filter->ip.max_ttl = max_ttl; } // Minimum length (not required). int min_len; if (config_setting_lookup_int(filter_cfg, "min_len", &min_len) == CONFIG_TRUE) { filter->ip.min_len = min_len; } // Maximum length (not required). int max_len; if (config_setting_lookup_int(filter_cfg, "max_len", &max_len) == CONFIG_TRUE) { filter->ip.max_len = max_len; } // TOS (not required). int tos; if (config_setting_lookup_int(filter_cfg, "tos", &tos) == CONFIG_TRUE) { filter->ip.tos = tos; } /* TCP options */ // Enabled. int tcp_enabled; if (config_setting_lookup_bool(filter_cfg, "tcp_enabled", &tcp_enabled) == CONFIG_TRUE) { filter->tcp.enabled = tcp_enabled; } // Source port. config_setting_t* tcp_sport = config_setting_lookup(filter_cfg, "tcp_sport"); if (tcp_sport) { int type = config_setting_type(tcp_sport); if (type == CONFIG_TYPE_STRING) { const char* val = config_setting_get_string(tcp_sport); if (val) { filter->tcp.sport = strdup(val); } } else if (type == CONFIG_TYPE_INT) { int val = config_setting_get_int(tcp_sport); char val_str[12]; snprintf(val_str, sizeof(val_str), "%d", val); filter->tcp.sport = strdup(val_str); } } // Destination port. config_setting_t* tcp_dport = config_setting_lookup(filter_cfg, "tcp_dport"); if (tcp_dport) { int type = config_setting_type(tcp_dport); if (type == CONFIG_TYPE_STRING) { const char* val = config_setting_get_string(tcp_dport); if (val) { filter->tcp.dport = strdup(val); } } else if (type == CONFIG_TYPE_INT) { int val = config_setting_get_int(tcp_dport); char val_str[12]; snprintf(val_str, sizeof(val_str), "%d", val); filter->tcp.dport = strdup(val_str); } } // URG flag. int tcp_urg; if (config_setting_lookup_bool(filter_cfg, "tcp_urg", &tcp_urg) == CONFIG_TRUE) { filter->tcp.urg = tcp_urg; } // ACK flag. int tcp_ack; if (config_setting_lookup_bool(filter_cfg, "tcp_ack", &tcp_ack) == CONFIG_TRUE) { filter->tcp.ack = tcp_ack; } // RST flag. int tcp_rst; if (config_setting_lookup_bool(filter_cfg, "tcp_rst", &tcp_rst) == CONFIG_TRUE) { filter->tcp.rst = tcp_rst; } // PSH flag. int tcp_psh; if (config_setting_lookup_bool(filter_cfg, "tcp_psh", &tcp_psh) == CONFIG_TRUE) { filter->tcp.psh = tcp_psh; } // SYN flag. int tcp_syn; if (config_setting_lookup_bool(filter_cfg, "tcp_syn", &tcp_syn) == CONFIG_TRUE) { filter->tcp.syn = tcp_syn; } // FIN flag. int tcp_fin; if (config_setting_lookup_bool(filter_cfg, "tcp_fin", &tcp_fin) == CONFIG_TRUE) { filter->tcp.fin = tcp_fin; } // ECE flag. int tcp_ece; if (config_setting_lookup_bool(filter_cfg, "tcp_ece", &tcp_ece) == CONFIG_TRUE) { filter->tcp.ece = tcp_ece; } // CWR flag. int tcp_cwr; if (config_setting_lookup_bool(filter_cfg, "tcp_cwr", &tcp_cwr) == CONFIG_TRUE) { filter->tcp.cwr = tcp_cwr; } /* UDP options */ // Enabled. int udp_enabled; if (config_setting_lookup_bool(filter_cfg, "udp_enabled", &udp_enabled) == CONFIG_TRUE) { filter->udp.enabled = udp_enabled; } // Source port. config_setting_t* udp_sport = config_setting_lookup(filter_cfg, "udp_sport"); if (udp_sport) { int type = config_setting_type(udp_sport); if (type == CONFIG_TYPE_STRING) { const char* val = config_setting_get_string(udp_sport); if (val) { filter->udp.sport = strdup(val); } } else if (type == CONFIG_TYPE_INT) { int val = config_setting_get_int(udp_sport); char val_str[12]; snprintf(val_str, sizeof(val_str), "%d", val); filter->udp.sport = strdup(val_str); } } // Destination port. config_setting_t* udp_dport = config_setting_lookup(filter_cfg, "udp_dport"); if (udp_dport) { int type = config_setting_type(udp_dport); if (type == CONFIG_TYPE_STRING) { const char* val = config_setting_get_string(udp_dport); if (val) { filter->udp.dport = strdup(val); } } else if (type == CONFIG_TYPE_INT) { int val = config_setting_get_int(udp_dport); char val_str[12]; snprintf(val_str, sizeof(val_str), "%d", val); filter->udp.dport = strdup(val_str); } } /* ICMP options */ // Enabled. int icmp_enabled; if (config_setting_lookup_bool(filter_cfg, "icmp_enabled", &icmp_enabled) == CONFIG_TRUE) { filter->icmp.enabled = icmp_enabled; } // ICMP code. int icmp_code; if (config_setting_lookup_int(filter_cfg, "icmp_code", &icmp_code) == CONFIG_TRUE) { filter->icmp.code = icmp_code; } // ICMP type. int icmp_type; if (config_setting_lookup_int(filter_cfg, "icmp_type", &icmp_type) == CONFIG_TRUE) { filter->icmp.type = icmp_type; } } } // Read IP range drops. setting = config_lookup(&conf, "ip_drop_ranges"); if (setting && config_setting_is_list(setting)) { for (int i = 0; i < config_setting_length(setting) && i < MAX_IP_RANGES; i++) { const char* range = cfg->drop_ranges[i]; if (cfg->drop_ranges[i]) { free(cfg->drop_ranges[i]); cfg->drop_ranges[i] = NULL; } const char* new_range = config_setting_get_string_elem(setting, i); if (!new_range) { continue; } cfg->drop_ranges[i] = strdup(new_range); cfg->drop_ranges_cnt++; } } config_destroy(&conf); 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 save_cfg(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(s). if (cfg->interfaces_cnt > 0) { if (cfg->interfaces_cnt > 1) { setting = config_setting_add(root, "interfaces", CONFIG_TYPE_LIST); for (int i = 0; i < cfg->interfaces_cnt; i++) { const char* interface = cfg->interfaces[i]; if (!interface) { continue; } config_setting_t* setting_interface = config_setting_add(setting, NULL, CONFIG_TYPE_STRING); config_setting_set_string(setting_interface, interface); } } else { const char* interface = cfg->interfaces[0]; if (interface) { setting = config_setting_add(root, "interfaces", CONFIG_TYPE_STRING); config_setting_set_string(setting, 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_rule_cfg_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. if (filter->enabled > -1) { config_setting_t* enabled = config_setting_add(filter_cfg, "enabled", CONFIG_TYPE_BOOL); config_setting_set_bool(enabled, filter->enabled); } // Add log setting. if (filter->log > -1) { config_setting_t* log = config_setting_add(filter_cfg, "log", CONFIG_TYPE_BOOL); config_setting_set_bool(log, filter->log); } // Add action setting. if (filter->action > -1) { config_setting_t* action = config_setting_add(filter_cfg, "action", CONFIG_TYPE_INT); config_setting_set_int(action, filter->action); } // Add block time. if (filter->block_time > -1) { config_setting_t* block_time = config_setting_add(filter_cfg, "block_time", CONFIG_TYPE_INT); config_setting_set_int(block_time, filter->block_time); } // Add IP PPS. if (filter->ip_pps > -1) { config_setting_t* pps = config_setting_add(filter_cfg, "ip_pps", CONFIG_TYPE_INT64); config_setting_set_int64(pps, filter->ip_pps); } // Add IP BPS. if (filter->ip_bps > -1) { config_setting_t* bps = config_setting_add(filter_cfg, "ip_bps", CONFIG_TYPE_INT64); config_setting_set_int64(bps, filter->ip_bps); } // Add flow PPS. if (filter->flow_pps > -1) { config_setting_t* pps = config_setting_add(filter_cfg, "flow_pps", CONFIG_TYPE_INT64); config_setting_set_int64(pps, filter->flow_pps); } // Add flow BPS. if (filter->flow_bps > -1) { config_setting_t* bps = config_setting_add(filter_cfg, "flow_bps", CONFIG_TYPE_INT64); config_setting_set_int64(bps, filter->flow_bps); } // Add source IPv4. if (filter->ip.src_ip) { config_setting_t* src_ip = config_setting_add(filter_cfg, "src_ip", CONFIG_TYPE_STRING); config_setting_set_string(src_ip, filter->ip.src_ip); } // Add destination IPv4. if (filter->ip.dst_ip) { config_setting_t* dst_ip = config_setting_add(filter_cfg, "dst_ip", CONFIG_TYPE_STRING); config_setting_set_string(dst_ip, filter->ip.dst_ip); } // Add source IPv6. if (filter->ip.src_ip6) { config_setting_t* src_ip6 = config_setting_add(filter_cfg, "src_ip6", CONFIG_TYPE_STRING); config_setting_set_string(src_ip6, filter->ip.src_ip6); } // Add source IPv6. if (filter->ip.dst_ip6) { config_setting_t* dst_ip6 = config_setting_add(filter_cfg, "dst_ip6", CONFIG_TYPE_STRING); config_setting_set_string(dst_ip6, filter->ip.dst_ip6); } // Add minimum TTL. if (filter->ip.min_ttl > -1) { config_setting_t* min_ttl = config_setting_add(filter_cfg, "min_ttl", CONFIG_TYPE_INT); config_setting_set_int(min_ttl, filter->ip.min_ttl); } // Add maximum TTL. if (filter->ip.max_ttl > -1) { config_setting_t* max_ttl = config_setting_add(filter_cfg, "max_ttl", CONFIG_TYPE_INT); config_setting_set_int(max_ttl, filter->ip.max_ttl); } // Add minimum length. if (filter->ip.min_len > -1) { config_setting_t* min_len = config_setting_add(filter_cfg, "min_len", CONFIG_TYPE_INT); config_setting_set_int(min_len, filter->ip.min_len); } // Add maximum length. if (filter->ip.max_len > -1) { config_setting_t* max_len = config_setting_add(filter_cfg, "max_len", CONFIG_TYPE_INT); config_setting_set_int(max_len, filter->ip.max_len); } // Add ToS. if (filter->ip.tos > -1) { config_setting_t* tos = config_setting_add(filter_cfg, "tos", CONFIG_TYPE_INT); config_setting_set_int(tos, filter->ip.tos); } // Add TCP enabled. if (filter->tcp.enabled > -1) { config_setting_t* tcp_enabled = config_setting_add(filter_cfg, "tcp_enabled", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_enabled, filter->tcp.enabled); } // Add TCP source port. if (filter->tcp.sport) { config_setting_t* tcp_sport = config_setting_add(filter_cfg, "tcp_sport", CONFIG_TYPE_STRING); config_setting_set_string(tcp_sport, filter->tcp.sport); } // Add TCP destination port. if (filter->tcp.dport) { config_setting_t* tcp_dport = config_setting_add(filter_cfg, "tcp_dport", CONFIG_TYPE_STRING); config_setting_set_string(tcp_dport, filter->tcp.dport); } // Add TCP URG flag. if (filter->tcp.urg > -1) { config_setting_t* tcp_urg = config_setting_add(filter_cfg, "tcp_urg", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_urg, filter->tcp.urg); } // Add TCP ACK flag. if (filter->tcp.ack > -1) { config_setting_t* tcp_ack = config_setting_add(filter_cfg, "tcp_ack", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_ack, filter->tcp.ack); } // Add TCP RST flag. if (filter->tcp.rst > -1) { config_setting_t* tcp_rst = config_setting_add(filter_cfg, "tcp_rst", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_rst, filter->tcp.rst); } // Add TCP PSH flag. if (filter->tcp.psh > -1) { config_setting_t* tcp_psh = config_setting_add(filter_cfg, "tcp_psh", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_psh, filter->tcp.psh); } // Add TCP SYN flag. if (filter->tcp.syn > -1) { config_setting_t* tcp_syn = config_setting_add(filter_cfg, "tcp_syn", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_syn, filter->tcp.syn); } // Add TCP FIN flag. if (filter->tcp.fin > -1) { config_setting_t* tcp_fin = config_setting_add(filter_cfg, "tcp_fin", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_fin, filter->tcp.fin); } // Add TCP ECE flag. if (filter->tcp.ece > -1) { config_setting_t* tcp_ece = config_setting_add(filter_cfg, "tcp_ece", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_ece, filter->tcp.ece); } // Add TCP CWR flag. if (filter->tcp.cwr > -1) { config_setting_t* tcp_cwr = config_setting_add(filter_cfg, "tcp_cwr", CONFIG_TYPE_BOOL); config_setting_set_bool(tcp_cwr, filter->tcp.cwr); } // Add UDP enabled. if (filter->udp.enabled > -1) { config_setting_t* udp_enabled = config_setting_add(filter_cfg, "udp_enabled", CONFIG_TYPE_BOOL); config_setting_set_bool(udp_enabled, filter->udp.enabled); } // Add UDP source port. if (filter->udp.sport) { config_setting_t* udp_sport = config_setting_add(filter_cfg, "udp_sport", CONFIG_TYPE_STRING); config_setting_set_string(udp_sport, filter->udp.sport); } // Add UDP destination port. if (filter->udp.dport) { config_setting_t* udp_dport = config_setting_add(filter_cfg, "udp_dport", CONFIG_TYPE_STRING); config_setting_set_string(udp_dport, filter->udp.dport); } // Add ICMP enabled. if (filter->icmp.enabled > -1) { config_setting_t* icmp_enabled = config_setting_add(filter_cfg, "icmp_enabled", CONFIG_TYPE_BOOL); config_setting_set_bool(icmp_enabled, filter->icmp.enabled); } // Add ICMP code. if (filter->icmp.code > -1) { config_setting_t* icmp_code = config_setting_add(filter_cfg, "icmp_code", CONFIG_TYPE_INT); config_setting_set_int(icmp_code, filter->icmp.code); } // Add ICMP type. if (filter->icmp.type > -1) { config_setting_t* icmp_type = config_setting_add(filter_cfg, "icmp_type", CONFIG_TYPE_INT); config_setting_set_int(icmp_type, filter->icmp.type); } } } } // 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"); if (!file) { config_destroy(&conf); return 1; } config_write(&conf, file); fclose(file); config_destroy(&conf); return 0; } /** * Sets the default values for a filter. * * @param filter A pointer to the filter. * * @return void */ void set_filter_defaults(filter_rule_cfg_t* filter) { filter->set = 0; filter->enabled = 1; filter->log = 0; filter->action = 1; filter->block_time = 1; filter->ip_pps = -1; filter->ip_bps = -1; filter->flow_pps = -1; filter->flow_bps = -1; if (filter->ip.src_ip) { free(filter->ip.src_ip); filter->ip.src_ip = NULL; } if (filter->ip.dst_ip) { free(filter->ip.dst_ip); filter->ip.dst_ip = NULL; } if (filter->ip.src_ip6) { free(filter->ip.src_ip6); filter->ip.src_ip6 = NULL; } if (filter->ip.dst_ip6) { free(filter->ip.dst_ip6); filter->ip.dst_ip6 = NULL; } filter->ip.min_ttl = -1; filter->ip.max_ttl = -1; filter->ip.min_len = -1; filter->ip.max_len = -1; filter->ip.tos = -1; filter->tcp.enabled = -1; if (filter->tcp.sport) { free(filter->tcp.sport); filter->tcp.sport = NULL; } if (filter->tcp.dport) { free(filter->tcp.dport); filter->tcp.dport = NULL; } filter->tcp.urg = -1; filter->tcp.ack = -1; filter->tcp.rst = -1; filter->tcp.psh = -1; filter->tcp.syn = -1; filter->tcp.fin = -1; filter->tcp.ece = -1; filter->tcp.cwr = -1; filter->udp.enabled = -1; if (filter->udp.sport) { free(filter->udp.sport); filter->udp.sport = NULL; } if (filter->udp.dport) { free(filter->udp.dport); filter->udp.dport = NULL; } filter->icmp.enabled = -1; filter->icmp.code = -1; filter->icmp.type = -1; } /** * Sets the config structure's default values. * * @param cfg A pointer to the config structure. * * @return void */ void set_cfg_defaults(config__t* cfg) { cfg->verbose = 2; cfg->update_time = 0; cfg->pin_maps = 1; cfg->no_stats = 0; cfg->stats_per_second = 0; cfg->stdout_update_time = 1000; if (cfg->log_file) { free(cfg->log_file); cfg->log_file = NULL; } cfg->log_file = strdup("/var/log/xdpfw.log"); cfg->interfaces_cnt = 0; for (int i = 0; i < MAX_INTERFACES; i++) { char* interface = cfg->interfaces[i]; if (!interface) { continue; } free(interface); cfg->interfaces[i] = NULL; } cfg->filters_cnt = 0; for (int i = 0; i < MAX_FILTERS; i++) { filter_rule_cfg_t* filter = &cfg->filters[i]; set_filter_defaults(filter); } cfg->drop_ranges_cnt = 0; for (int i = 0; i < MAX_IP_RANGES; i++) { char* drop_range = cfg->drop_ranges[i]; if (!drop_range) { continue; } free(drop_range); cfg->drop_ranges[i] = NULL; } } /** * Prints a filter rule. * * @param filter A pointer to the filter rule. * @param idx The current index. * * @return void */ void print_filter(filter_rule_cfg_t* filter, int idx) { printf("\tFilter #%d\n", idx); printf("\t\tEnabled => %d\n", filter->enabled); printf("\t\tLog => %d\n\n", filter->log); printf("\t\tAction => %d (0 = Block, 1 = Allow).\n", filter->action); printf("\t\t\tBlock Time => %d\n\n", filter->block_time); printf("\t\t\tIP PPS => %lld\n", filter->ip_pps); printf("\t\t\tIP BPS => %lld\n", filter->ip_bps); printf("\t\t\tFlow PPS => %lld\n", filter->flow_pps); printf("\t\t\tFlow BPS => %lld\n", filter->flow_bps); // IP Options. printf("\t\tIP Options\n"); // IP addresses require additional code for string printing. const char* src_ip = "N/A"; if (filter->ip.src_ip) { src_ip = filter->ip.src_ip; } printf("\t\t\tSource IPv4 => %s\n", src_ip); const char* dst_ip = "N/A"; if (filter->ip.dst_ip) { dst_ip = filter->ip.dst_ip; } printf("\t\t\tDestination IPv4 => %s\n", dst_ip); const char* src_ip6 = "N/A"; if (filter->ip.src_ip6) { src_ip6 = filter->ip.src_ip6; } printf("\t\t\tSource IPv6 => %s\n", src_ip6); const char* dst_ip6 = "N/A"; if (filter->ip.dst_ip6) { dst_ip6 = filter->ip.dst_ip6; } printf("\t\t\tDestination IPv6 => %s\n", dst_ip6); printf("\t\t\tMin TTL => %d\n", filter->ip.min_ttl); printf("\t\t\tMax TTL => %d\n", filter->ip.max_ttl); printf("\t\t\tMin Length => %d\n", filter->ip.min_len); printf("\t\t\tMax Length => %d\n", filter->ip.max_len); printf("\t\t\tTOS => %d\n\n", filter->ip.tos); // TCP Options. printf("\t\tTCP Options\n"); printf("\t\t\tTCP Enabled => %d\n", filter->tcp.enabled); printf("\t\t\tTCP Source Port => %s\n", filter->tcp.sport); printf("\t\t\tTCP Destination Port => %s\n", filter->tcp.dport); printf("\t\t\tTCP URG Flag => %d\n", filter->tcp.urg); printf("\t\t\tTCP ACK Flag => %d\n", filter->tcp.ack); printf("\t\t\tTCP RST Flag => %d\n", filter->tcp.rst); printf("\t\t\tTCP PSH Flag => %d\n", filter->tcp.psh); printf("\t\t\tTCP SYN Flag => %d\n", filter->tcp.syn); printf("\t\t\tTCP FIN Flag => %d\n", filter->tcp.fin); printf("\t\t\tTCP ECE Flag => %d\n", filter->tcp.ece); printf("\t\t\tTCP CWR Flag => %d\n\n", filter->tcp.cwr); // UDP Options. printf("\t\tUDP Options\n"); printf("\t\t\tUDP Enabled => %d\n", filter->udp.enabled); printf("\t\t\tUDP Source Port => %s\n", filter->udp.sport); printf("\t\t\tUDP Destination Port => %s\n\n", filter->udp.dport); // ICMP Options. printf("\t\tICMP Options\n"); printf("\t\t\tICMP Enabled => %d\n", filter->icmp.enabled); printf("\t\t\tICMP Code => %d\n", filter->icmp.code); printf("\t\t\tICMP Type => %d\n", filter->icmp.type); } /** * Prints config settings. * * @param cfg A pointer to the config structure. * * @return void */ void print_cfg(config__t* cfg) { const char* log_file = "N/A"; if (cfg->log_file != NULL) { log_file = cfg->log_file; } printf("Printing config...\n"); printf("General Settings\n"); printf("\tVerbose => %d\n", cfg->verbose); printf("\tLog File => %s\n", log_file); 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("Interfaces\n"); if (cfg->interfaces_cnt > 0) { for (int i = 0; i < cfg->interfaces_cnt; i++) { const char* interface = cfg->interfaces[i]; if (!interface) { continue; } printf("\t- %s\n", interface); } printf("\n"); } else { printf("\t- None\n\n"); } printf("Filters\n"); if (cfg->filters_cnt > 0) { for (int i = 0; i < cfg->filters_cnt; i++) { filter_rule_cfg_t *filter = &cfg->filters[i]; if (!filter->set) { break; } print_filter(filter, i + 1); printf("\n\n"); } printf("\n"); } else { printf("\t- None\n\n"); } printf("IP Drop Ranges\n"); if (cfg->drop_ranges_cnt > 0) { for (int i = 0; i < cfg->drop_ranges_cnt; i++) { const char* range = cfg->drop_ranges[i]; if (!range) { continue; } printf("\t- %s\n", range); } } else { printf("\t- None\n"); } } /** * 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 get_next_filter_idx(config__t* cfg) { for (int i = 0; i < MAX_FILTERS; i++) { filter_rule_cfg_t* filter = &cfg->filters[i]; if (filter->set) { continue; } 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 get_next_ip_drop_range_idx(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; }