From c567a028ed4e77dff36dbd766b6be8359480bf02 Mon Sep 17 00:00:00 2001 From: Christian Deacon Date: Wed, 26 Feb 2025 18:53:14 -0500 Subject: [PATCH] Add option to calculate packet counters per second. --- README.md | 1 + src/loader/prog.c | 10 ++++---- src/loader/utils/config.c | 29 +++++++++++++-------- src/loader/utils/config.h | 5 ++-- src/loader/utils/stats.c | 54 ++++++++++++++++++++++++++++++++++++--- src/loader/utils/stats.h | 4 ++- 6 files changed, 81 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2f3ae9b..44edc6c 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ The following table quickly explains the data types used within the configuratio | interface | string | `NULL` | The network interface name to attach the XDP program to (usually retrieved with `ip a` or `ifconfig`). | | update_time | uint | `0` | How often to update the config and filtering rules from the file system in seconds (0 disables). | | no_stats | bool | `false` | Whether to enable or disable packet counters. Disabling packet counters will improve performance, but result in less visibility on what the XDP Firewall is doing. | +| 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. | diff --git a/src/loader/prog.c b/src/loader/prog.c index 7a4d0ca..8449f0a 100644 --- a/src/loader/prog.c +++ b/src/loader/prog.c @@ -196,7 +196,7 @@ int main(int argc, char *argv[]) struct stat conf_stat; // Check if we're doing stats. - if (!cfg.nostats) + if (!cfg.no_stats) { doing_stats = 1; } @@ -213,7 +213,7 @@ int main(int argc, char *argv[]) } // Check for auto-update. - if (cfg.updatetime > 0 && (cur_time - last_update_check) > cfg.updatetime) + 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) { @@ -234,7 +234,7 @@ int main(int argc, char *argv[]) last_config_check = time(NULL); // Make sure we set doing stats if needed. - if (!cfg.nostats && !doing_stats) + if (!cfg.no_stats && !doing_stats) { doing_stats = 1; } @@ -245,9 +245,9 @@ int main(int argc, char *argv[]) } // Calculate and display stats if enabled. - if (!cfg.nostats) + if (!cfg.no_stats) { - if (CalculateStats(stats_map, cpus)) + if (CalculateStats(stats_map, cpus, cfg.stats_per_second)) { LogMsg(&cfg, 1, 0, "[WARNING] Failed to calculate packet stats. Stats map FD => %d...\n", stats_map); } diff --git a/src/loader/utils/config.c b/src/loader/utils/config.c index 8288bc1..3591daf 100644 --- a/src/loader/utils/config.c +++ b/src/loader/utils/config.c @@ -46,9 +46,10 @@ void SetCfgDefaults(config__t *cfg) { cfg->verbose = 2; cfg->log_file = strdup("/var/log/xdpfw/xdpfw.log"); - cfg->updatetime = 0; + cfg->update_time = 0; cfg->interface = NULL; - cfg->nostats = 0; + cfg->no_stats = 0; + cfg->stats_per_second = 0; cfg->stdout_update_time = 1000; for (int i = 0; i < MAX_FILTERS; i++) @@ -214,11 +215,11 @@ int ReadCfg(config__t *cfg) cfg->interface = strdup(interface); // Get auto update time. - int updatetime; + int update_time; - if (config_lookup_int(&conf, "update_time", &updatetime) == CONFIG_TRUE) + if (config_lookup_int(&conf, "update_time", &update_time) == CONFIG_TRUE) { - cfg->updatetime = updatetime; + cfg->update_time = update_time; } // Get stdout update time. @@ -230,11 +231,19 @@ int ReadCfg(config__t *cfg) } // Get no stats. - int nostats; + int no_stats; - if (config_lookup_bool(&conf, "no_stats", &nostats) == CONFIG_TRUE) + if (config_lookup_bool(&conf, "no_stats", &no_stats) == CONFIG_TRUE) { - cfg->nostats = nostats; + 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) + { + cfg->stats_per_second = stats_per_second; } // Read filters in filters_map structure. @@ -586,9 +595,9 @@ 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->updatetime); + 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->nostats); + fprintf(stdout, "\t\tNo Stats => %d\n\n", cfg->no_stats); fprintf(stdout, "\tFilters\n"); diff --git a/src/loader/utils/config.h b/src/loader/utils/config.h index 6b4ba6c..6afeb4c 100644 --- a/src/loader/utils/config.h +++ b/src/loader/utils/config.h @@ -18,8 +18,9 @@ struct config int verbose; char *log_file; char *interface; - u16 updatetime; - unsigned int nostats : 1; + u16 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 -.- diff --git a/src/loader/utils/stats.c b/src/loader/utils/stats.c index f317a7e..7b788fb 100644 --- a/src/loader/utils/stats.c +++ b/src/loader/utils/stats.c @@ -1,14 +1,21 @@ #include +struct timespec last_update_time = {0}; + +u64 last_allowed = 0; +u64 last_dropped = 0; +u64 last_passed = 0; + /** * Calculates and displays packet counters/stats. * * @param stats_map The stats map BPF FD. * @param cpus The amount of CPUs the host has. + * @param per_second Calculate packet counters per second (PPS). * * @return 0 on success or 1 on failure. */ -int CalculateStats(int stats_map, int cpus) +int CalculateStats(int stats_map, int cpus, int per_second) { u32 key = 0; @@ -40,10 +47,49 @@ int CalculateStats(int stats_map, int cpus) dropped += stats[i].dropped; passed += stats[i].passed; } + + u64 allowed_val = allowed, dropped_val = dropped, passed_val = passed; + + if (per_second) + { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); // Get precise time + double elapsed_time = (now.tv_sec - last_update_time.tv_sec) + + (now.tv_nsec - last_update_time.tv_nsec) / 1e9; + + if (elapsed_time > 0) + { + allowed_val = (allowed - last_allowed) / elapsed_time; + dropped_val = (dropped - last_dropped) / elapsed_time; + passed_val = (passed - last_passed) / elapsed_time; + } + + last_allowed = allowed; + last_dropped = dropped; + last_passed = passed; + last_update_time = now; + } + + char allowed_str[12]; + char dropped_str[12]; + char passed_str[12]; + + if (per_second) + { + snprintf(allowed_str, sizeof(allowed_str), "%llu PPS", allowed_val); + snprintf(dropped_str, sizeof(dropped_str), "%llu PPS", dropped_val); + snprintf(passed_str, sizeof(passed_str), "%llu PPS", passed_val); + } + else + { + snprintf(allowed_str, sizeof(allowed_str), "%llu", allowed_val); + snprintf(dropped_str, sizeof(dropped_str), "%llu", dropped_val); + snprintf(passed_str, sizeof(passed_str), "%llu", passed_val); + } - printf("\r\033[1;32mAllowed:\033[0m %llu | ", allowed); - printf("\033[1;31mDropped:\033[0m %llu | ", dropped); - printf("\033[1;34mPassed:\033[0m %llu", passed); + printf("\r\033[1;32mAllowed:\033[0m %s | ", allowed_str); + printf("\033[1;31mDropped:\033[0m %s | ", dropped_str); + printf("\033[1;34mPassed:\033[0m %s", passed_str); fflush(stdout); diff --git a/src/loader/utils/stats.h b/src/loader/utils/stats.h index 71cbc91..c750570 100644 --- a/src/loader/utils/stats.h +++ b/src/loader/utils/stats.h @@ -8,4 +8,6 @@ #include #include -int CalculateStats(int stats_map, int cpus); \ No newline at end of file +#include + +int CalculateStats(int stats_map, int cpus, int per_second); \ No newline at end of file