diff --git a/README.md b/README.md index 44edc6c..c7a453e 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ sudo make install ![Script Build Demo](./images/build_make.gif) ## Command Line Usage -The following command line arguments are supported. +The following general command line arguments are supported. | Name | Default | Description | | ---- | ------- | ----------- | @@ -97,6 +97,18 @@ The following 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. + +| 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. | +| -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-update-time | `--stdout-update-time 500` | Overrides the config's stdout update time value. | + ### Offload Information Offloading your XDP/BPF program to your system's NIC allows for the fastest packet processing you can achieve due to the NIC dropping the packets with its hardware. However, for one, there are **not** many NIC manufacturers that do support this feature **and** you're limited to the NIC's memory/processing (e.g. your BPF map sizes will be extremely limited). Additionally, there are usually stricter BPF verifier limitations for offloaded BPF programs, but you may try reaching out to the NIC's manufacturer to see if they will give you a special version of their NIC driver raising these limitations (this is what I did with one manufacturer I used). @@ -195,8 +207,12 @@ You may additionally specified UDP header options for a filter rule which start Here's a config example. ```squidconf +verbose = 5; +log_file = ""; interface = "ens18"; update_time = 15; +no_stats = false; +stats_per_second = true; filters = ( { diff --git a/src/loader/prog.c b/src/loader/prog.c index 8449f0a..2913fea 100644 --- a/src/loader/prog.c +++ b/src/loader/prog.c @@ -28,6 +28,11 @@ int main(int argc, char *argv[]) // Parse the command line. cmdline_t cmd = {0}; cmd.cfgfile = CONFIG_DEFAULT_PATH; + cmd.verbose = -1; + cmd.update_time = -1; + cmd.no_stats = -1; + cmd.stats_per_second = -1; + cmd.stdout_update_time = -1; ParseCommandLine(&cmd, argc, argv); @@ -44,8 +49,18 @@ int main(int argc, char *argv[]) SetCfgDefaults(&cfg); + // Create overrides for config and set arguments from CLI. + config_overrides_t cfg_overrides = {0}; + cfg_overrides.verbose = cmd.verbose; + cfg_overrides.log_file = cmd.log_file; + cfg_overrides.interface = cmd.interface; + cfg_overrides.update_time = cmd.update_time; + cfg_overrides.no_stats = cmd.no_stats; + cfg_overrides.stats_per_second = cmd.stats_per_second; + cfg_overrides.stdout_update_time = cmd.stdout_update_time; + // Load config. - if ((ret = LoadConfig(&cfg, cmd.cfgfile)) != 0) + if ((ret = LoadConfig(&cfg, cmd.cfgfile, &cfg_overrides)) != 0) { fprintf(stderr, "[ERROR] Failed to load config from file system (%s)(%d).\n", cmd.cfgfile, ret); @@ -66,6 +81,14 @@ int main(int argc, char *argv[]) PrintToolInfo(); } + // Check interface. + if (cfg.interface == NULL) + { + LogMsg(&cfg, 0, 1, "[ERROR] No interface specified in config or CLI override."); + + return EXIT_FAILURE; + } + LogMsg(&cfg, 2, 0, "Raising RLimit..."); // Raise RLimit. @@ -217,12 +240,8 @@ int main(int argc, char *argv[]) { // Check if config file have been modified if (stat(cmd.cfgfile, &conf_stat) == 0 && conf_stat.st_mtime > last_config_check) { - // Memleak fix for strdup() in LoadConfig() - // Before updating it again, we need to free the old return value - free(cfg.interface); - // Update config. - if ((ret = LoadConfig(&cfg, cmd.cfgfile)) != 0) + if ((ret = LoadConfig(&cfg, cmd.cfgfile, &cfg_overrides)) != 0) { LogMsg(&cfg, 1, 0, "[WARNING] Failed to load config after update check (%d)...\n", ret); } diff --git a/src/loader/utils/cmdline.c b/src/loader/utils/cmdline.c index 36df207..d5ee674 100644 --- a/src/loader/utils/cmdline.c +++ b/src/loader/utils/cmdline.c @@ -8,6 +8,15 @@ const struct option opts[] = { "time", required_argument, NULL, 't' }, { "list", no_argument, NULL, 'l' }, { "help", no_argument, NULL, 'h' }, + + { "verbose", required_argument, NULL, 'v' }, + { "log-file", required_argument, NULL, 0 }, + { "interface", required_argument, NULL, 'i' }, + { "update-time", required_argument, NULL, 'u' }, + { "no-stats", required_argument, NULL, 'n' }, + { "stats-ps", required_argument, NULL, 1 }, + { "stdout-update-time", required_argument, NULL, 2 }, + { NULL, 0, NULL, 0 } }; @@ -22,7 +31,7 @@ void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]) { int c; - while ((c = getopt_long(argc, argv, "c:ost:lh", opts, NULL)) != -1) + while ((c = getopt_long(argc, argv, "c:ost:lhv:i:u:n:", opts, NULL)) != -1) { switch (c) { @@ -56,6 +65,41 @@ void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]) break; + case 'v': + cmd->verbose = atoi(optarg); + + break; + + case 0: + cmd->log_file = optarg; + + break; + + case 'i': + cmd->interface = optarg; + + break; + + case 'u': + cmd->update_time = atoi(optarg); + + break; + + case 'n': + cmd->no_stats = atoi(optarg); + + break; + + case 1: + cmd->stats_per_second = atoi(optarg); + + break; + + case 2: + cmd->stdout_update_time = atoi(optarg); + + break; + case '?': fprintf(stderr, "Missing argument option...\n"); diff --git a/src/loader/utils/cmdline.h b/src/loader/utils/cmdline.h index ba09b00..9207efc 100644 --- a/src/loader/utils/cmdline.h +++ b/src/loader/utils/cmdline.h @@ -12,6 +12,14 @@ struct cmdline unsigned int time; unsigned int list : 1; unsigned int help : 1; + + int verbose; + char* log_file; + char* interface; + int update_time; + int no_stats; + int stats_per_second; + int stdout_update_time; } typedef cmdline_t; void ParseCommandLine(cmdline_t *cmd, int argc, char *argv[]); \ No newline at end of file diff --git a/src/loader/utils/config.c b/src/loader/utils/config.c index 3591daf..12e5f5b 100644 --- a/src/loader/utils/config.c +++ b/src/loader/utils/config.c @@ -7,10 +7,11 @@ static FILE *file; * * @param cfg A pointer to the config structure. * @param cfg_file The path to the config file. + * @param overrides Overrides to use instead of config values. * * @return 0 on success or 1 on error. */ -int LoadConfig(config__t *cfg, char *cfg_file) +int LoadConfig(config__t *cfg, char *cfg_file, config_overrides_t* overrides) { // Open config file. if (OpenCfg(cfg_file) != 0) @@ -25,7 +26,7 @@ int LoadConfig(config__t *cfg, char *cfg_file) memset(cfg->filters, 0, sizeof(cfg->filters)); // Read config and check for errors. - if (ReadCfg(cfg) != 0) + if (ReadCfg(cfg, overrides) != 0) { fprintf(stderr, "Error reading config file.\n"); @@ -144,10 +145,11 @@ 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 overrides Overrides to use instead of config values. * * @return 0 on success or 1/-1 on error. */ -int ReadCfg(config__t *cfg) +int ReadCfg(config__t *cfg, 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) @@ -173,14 +175,21 @@ int ReadCfg(config__t *cfg) int verbose; - if (config_lookup_int(&conf, "verbose", &verbose) == CONFIG_TRUE) + if (config_lookup_int(&conf, "verbose", &verbose) == CONFIG_TRUE || overrides->verbose > -1) { - cfg->verbose = verbose; + if (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) + if (config_lookup_string(&conf, "log_file", &log_file) == CONFIG_TRUE || overrides->log_file != NULL) { // We must free previous value to prevent memory leak. if (cfg->log_file != NULL) @@ -189,61 +198,111 @@ int ReadCfg(config__t *cfg) cfg->log_file = NULL; } - if (strlen(log_file) > 0) + if (overrides->log_file != NULL) { - cfg->log_file = strdup(log_file); - + if (strlen(overrides->log_file) > 0) + { + cfg->log_file = strdup(overrides->log_file); + + } + else + { + cfg->log_file = NULL; + } } else { - cfg->log_file = NULL; + if (strlen(log_file) > 0) + { + cfg->log_file = strdup(log_file); + } + else + { + cfg->log_file = NULL; + } } } // Get interface. const char *interface; - if (!config_lookup_string(&conf, "interface", &interface)) + if (config_lookup_string(&conf, "interface", &interface) == CONFIG_TRUE || overrides->interface != NULL) { - LogMsg(cfg, 0, 1, "Error from LibConfig when reading 'interface' setting - %s", config_error_text(&conf)); - - config_destroy(&conf); + // We must free previous value to prevent memory leak. + if (cfg->interface != NULL) + { + free(cfg->interface); + cfg->interface = NULL; + } - return EXIT_FAILURE; + if (overrides->interface != NULL) + { + cfg->interface = strdup(overrides->interface); + } + else + { + cfg->interface = strdup(interface); + } } - cfg->interface = strdup(interface); - // Get auto update time. int update_time; - if (config_lookup_int(&conf, "update_time", &update_time) == CONFIG_TRUE) + if (config_lookup_int(&conf, "update_time", &update_time) == CONFIG_TRUE || overrides->update_time > -1) { - cfg->update_time = update_time; - } - - // Get stdout update time. - int stdout_update_time; - - if (config_lookup_int(&conf, "stdout_update_time", &stdout_update_time) == CONFIG_TRUE) - { - cfg->stdout_update_time = stdout_update_time; + if (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) + if (config_lookup_bool(&conf, "no_stats", &no_stats) == CONFIG_TRUE || overrides->no_stats > -1) { - cfg->no_stats = no_stats; + if (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) + if (config_lookup_bool(&conf, "stats_per_second", &stats_per_second) == CONFIG_TRUE || overrides->stats_per_second > -1) { - cfg->stats_per_second = stats_per_second; + if (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->stdout_update_time > -1) + { + if (overrides->stdout_update_time > -1) + { + cfg->stdout_update_time = overrides->stdout_update_time; + } + else + { + cfg->stdout_update_time = stdout_update_time; + } } // Read filters in filters_map structure. @@ -592,14 +651,32 @@ int ReadCfg(config__t *cfg) */ void PrintConfig(config__t* cfg) { - fprintf(stdout, "Printing config...\n"); - fprintf(stdout, "\tGeneral Settings\n"); - fprintf(stdout, "\t\tInterface Name => %s\n", cfg->interface); - fprintf(stdout, "\t\tUpdate Time => %d\n", cfg->update_time); - fprintf(stdout, "\t\tStdout Update Time => %d\n", cfg->stdout_update_time); - fprintf(stdout, "\t\tNo Stats => %d\n\n", cfg->no_stats); + char* interface = "N/A"; - fprintf(stdout, "\tFilters\n"); + if (cfg->interface != NULL) + { + interface = cfg->interface; + } + + char* log_file = "N/A"; + + if (cfg->log_file != NULL) + { + log_file = cfg->log_file; + } + + printf("Printing config...\n"); + printf("\tGeneral 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("\tFilters\n"); for (int i = 0; i < MAX_FILTERS; i++) { @@ -610,27 +687,27 @@ void PrintConfig(config__t* cfg) break; } - fprintf(stdout, "\t\tFilter #%d:\n", (i + 1)); + printf("\t\tFilter #%d:\n", (i + 1)); // Main. - fprintf(stdout, "\t\t\tID => %d\n", filter->id); - fprintf(stdout, "\t\t\tLog => %d\n", filter->log); - fprintf(stdout, "\t\t\tEnabled => %d\n", filter->enabled); - fprintf(stdout, "\t\t\tAction => %d (0 = Block, 1 = Allow).\n\n", filter->action); + printf("\t\t\tID => %d\n", filter->id); + printf("\t\t\tLog => %d\n", filter->log); + printf("\t\t\tEnabled => %d\n", filter->enabled); + printf("\t\t\tAction => %d (0 = Block, 1 = Allow).\n\n", filter->action); // IP Options. - fprintf(stdout, "\t\t\tIP Options\n"); + 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; - fprintf(stdout, "\t\t\t\tSource IPv4 => %s\n", inet_ntoa(sin.sin_addr)); - fprintf(stdout, "\t\t\t\tSource CIDR => %d\n", filter->src_cidr); + printf("\t\t\t\tSource IPv4 => %s\n", inet_ntoa(sin.sin_addr)); + printf("\t\t\t\tSource CIDR => %d\n", filter->src_cidr); struct sockaddr_in din; din.sin_addr.s_addr = filter->dst_ip; - fprintf(stdout, "\t\t\t\tDestination IPv4 => %s\n", inet_ntoa(din.sin_addr)); - fprintf(stdout, "\t\t\t\tDestination CIDR => %d\n", filter->dst_cidr); + printf("\t\t\t\tDestination IPv4 => %s\n", inet_ntoa(din.sin_addr)); + printf("\t\t\t\tDestination CIDR => %d\n", filter->dst_cidr); struct in6_addr sin6; memcpy(&sin6, &filter->src_ip6, sizeof(sin6)); @@ -638,7 +715,7 @@ void PrintConfig(config__t* cfg) char srcipv6[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &sin6, srcipv6, sizeof(srcipv6)); - fprintf(stdout, "\t\t\t\tSource IPv6 => %s\n", srcipv6); + printf("\t\t\t\tSource IPv6 => %s\n", srcipv6); struct in6_addr din6; memcpy(&din6, &filter->dst_ip6, sizeof(din6)); @@ -646,44 +723,44 @@ void PrintConfig(config__t* cfg) char dstipv6[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &din6, dstipv6, sizeof(dstipv6)); - fprintf(stdout, "\t\t\t\tDestination IPv6 => %s\n", dstipv6); + printf("\t\t\t\tDestination IPv6 => %s\n", dstipv6); // Other IP header information. - fprintf(stdout, "\t\t\t\tMax Length => %d\n", filter->max_len); - fprintf(stdout, "\t\t\t\tMin Length => %d\n", filter->min_len); - fprintf(stdout, "\t\t\t\tMax TTL => %d\n", filter->max_ttl); - fprintf(stdout, "\t\t\t\tMin TTL => %d\n", filter->min_ttl); - fprintf(stdout, "\t\t\t\tTOS => %d\n", filter->tos); - fprintf(stdout, "\t\t\t\tPPS => %llu\n", filter->pps); - fprintf(stdout, "\t\t\t\tBPS => %llu\n", filter->bps); - fprintf(stdout, "\t\t\t\tBlock Time => %llu\n\n", filter->blocktime); + 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->blocktime); // TCP Options. - fprintf(stdout, "\t\t\tTCP Options\n"); - fprintf(stdout, "\t\t\t\tTCP Enabled => %d\n", filter->tcpopts.enabled); - fprintf(stdout, "\t\t\t\tTCP Source Port => %d\n", filter->tcpopts.sport); - fprintf(stdout, "\t\t\t\tTCP Destination Port => %d\n", filter->tcpopts.dport); - fprintf(stdout, "\t\t\t\tTCP URG Flag => %d\n", filter->tcpopts.urg); - fprintf(stdout, "\t\t\t\tTCP ACK Flag => %d\n", filter->tcpopts.ack); - fprintf(stdout, "\t\t\t\tTCP RST Flag => %d\n", filter->tcpopts.rst); - fprintf(stdout, "\t\t\t\tTCP PSH Flag => %d\n", filter->tcpopts.psh); - fprintf(stdout, "\t\t\t\tTCP SYN Flag => %d\n", filter->tcpopts.syn); - fprintf(stdout, "\t\t\t\tTCP FIN Flag => %d\n", filter->tcpopts.fin); - fprintf(stdout, "\t\t\t\tTCP ECE Flag => %d\n", filter->tcpopts.ece); - fprintf(stdout, "\t\t\t\tTCP CWR Flag => %d\n\n", filter->tcpopts.cwr); + 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. - fprintf(stdout, "\t\t\tUDP Options\n"); - fprintf(stdout, "\t\t\t\tUDP Enabled => %d\n", filter->udpopts.enabled); - fprintf(stdout, "\t\t\t\tUDP Source Port => %d\n", filter->udpopts.sport); - fprintf(stdout, "\t\t\t\tUDP Destination Port => %d\n\n", filter->udpopts.dport); + 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. - fprintf(stdout, "\t\t\tICMP Options\n"); - fprintf(stdout, "\t\t\t\tICMP Enabled => %d\n", filter->icmpopts.enabled); - fprintf(stdout, "\t\t\t\tICMP Code => %d\n", filter->icmpopts.code); - fprintf(stdout, "\t\t\t\tICMP Type => %d\n", filter->icmpopts.type); + 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); - fprintf(stdout, "\n\n"); + printf("\n\n"); } } \ No newline at end of file diff --git a/src/loader/utils/config.h b/src/loader/utils/config.h index 6afeb4c..a1ea302 100644 --- a/src/loader/utils/config.h +++ b/src/loader/utils/config.h @@ -18,18 +18,30 @@ struct config int verbose; char *log_file; char *interface; - u16 update_time; + int update_time; unsigned int no_stats : 1; unsigned int stats_per_second : 1; int stdout_update_time; filter_t filters[MAX_FILTERS]; } typedef config__t; // config_t is taken by libconfig -.- -int LoadConfig(config__t *cfg, char *cfg_file); +struct config_overrides +{ + int verbose; + const char* log_file; + const char* interface; + int update_time; + int no_stats; + int stats_per_second; + int stdout_update_time; + +} typedef config_overrides_t; + +int LoadConfig(config__t *cfg, char *cfg_file, config_overrides_t* overrides); void SetCfgDefaults(config__t *cfg); void PrintConfig(config__t* cfg); int OpenCfg(const char *filename); -int ReadCfg(config__t *cfg); +int ReadCfg(config__t *cfg, config_overrides_t* overrides); #include \ No newline at end of file