From 4105c4c5ee07b03a76f793ea139894e21e991ac7 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Thu, 23 Oct 2025 10:23:24 +0000 Subject: [PATCH] sources: iptables: Accumulate counters of the same comments This is to be compatible with collectd and what we are currently using in IPFire. When there is the same comment, we just add up the counters so that we can collect metrics in different tables/chains. Signed-off-by: Michael Tremer --- src/daemon/sources/iptables.c | 101 +++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 15 deletions(-) diff --git a/src/daemon/sources/iptables.c b/src/daemon/sources/iptables.c index 5ca37e5..c7e4fec 100644 --- a/src/daemon/sources/iptables.c +++ b/src/daemon/sources/iptables.c @@ -31,6 +31,58 @@ typedef struct xtc_handle iptc_handle_t; typedef struct ipt_entry iptc_rule_t; +typedef struct iptables_comment { + // Comment String + char comment[XT_MAX_COMMENT_LEN]; + + // Counters + uint64_t packets; + uint64_t bytes; +} iptables_comment; + +static int find_comment(iptables_comment** ret, + iptables_comment** comments, unsigned int* num_comments, const char* s) { + iptables_comment* comment = NULL; + iptables_comment* c = *comments; + iptables_comment* p = NULL; + int r; + + // Return any matching comments + for (unsigned int i = 0; i < (*num_comments); i++) { + if (td_string_equals(c[i].comment, s)) { + *ret = &c[i]; + return 0; + } + } + + // No comment found, let's increase the length of the array + p = reallocarray(c, (*num_comments) + 1, sizeof(*p)); + if (!p) + return -errno; + + // Update the pointer + *comments = p; + + // Pointer to the new comment + comment = &p[*num_comments]; + + // Store the comment string + r = td_string_set(comment->comment, s); + if (r < 0) + return r; + + // Initialize the counters + comment->packets = comment->bytes = 0; + + // The array is now longer + (*num_comments)++; + + // Return the comment + *ret = comment; + + return 0; +} + static int skip_comment(const char* comment) { // Skip empty comments if (!*comment) @@ -57,9 +109,11 @@ static int skip_comment(const char* comment) { return 0; } -static int iptables_iterate_matches(td_ctx* ctx, td_source* source, const iptc_rule_t* rule) { +static int iptables_iterate_matches(td_ctx* ctx, td_source* source, + const iptc_rule_t* rule, iptables_comment** comments, unsigned int* num_comments) { const struct xt_comment_info* comment = NULL; const struct xt_entry_match* match = NULL; + iptables_comment* c = NULL; int r; // Start with the first match and determine the end @@ -87,26 +141,27 @@ static int iptables_iterate_matches(td_ctx* ctx, td_source* source, const iptc_r if (skip_comment(comment->comment)) continue; - // Submit metrics - r = td_source_submit_values(source, comment->comment, VALUES( - VALUE_UINT64("packets", &rule->counters.pcnt), - VALUE_UINT64("bytes", &rule->counters.bcnt) - )); + // Find the comment + r = find_comment(&c, comments, num_comments, comment->comment); if (r < 0) return r; + + // Increment counters + c->packets += rule->counters.pcnt; + c->bytes += rule->counters.bcnt; } return 0; } -static int iptables_iterate_chain(td_ctx* ctx, td_source* source, - iptc_handle_t* handle, const char* chain) { +static int iptables_iterate_chain(td_ctx* ctx, td_source* source, iptc_handle_t* handle, + const char* chain, iptables_comment** comments, unsigned int* num_comments) { const iptc_rule_t* rule = NULL; int r = 0; // Iterate over all rules for (rule = iptc_first_rule(chain, handle); rule; rule = iptc_next_rule(rule, handle)) { - r = iptables_iterate_matches(ctx, source, rule); + r = iptables_iterate_matches(ctx, source, rule, comments, num_comments); if (r < 0) return r; } @@ -114,7 +169,8 @@ static int iptables_iterate_chain(td_ctx* ctx, td_source* source, return 0; } -static int iptables_iterate_table(td_ctx* ctx, td_source* source, const char* table) { +static int iptables_iterate_table(td_ctx* ctx, td_source* source, const char* table, + iptables_comment** comments, unsigned int* num_comments) { iptc_handle_t* handle = NULL; const char* chain = NULL; int r; @@ -129,7 +185,7 @@ static int iptables_iterate_table(td_ctx* ctx, td_source* source, const char* ta // Iterate through all chains for (chain = iptc_first_chain(handle); chain; chain = iptc_next_chain(handle)) { - r = iptables_iterate_chain(ctx, source, handle, chain); + r = iptables_iterate_chain(ctx, source, handle, chain, comments, num_comments); if (r < 0) goto ERROR; } @@ -139,10 +195,11 @@ ERROR: iptc_free(handle); return r; - } static int iptables_heartbeat(td_ctx* ctx, td_source* source) { + iptables_comment* comments = NULL; + unsigned int num_comments = 0; int r = 0; const char* tables[] = { @@ -151,12 +208,26 @@ static int iptables_heartbeat(td_ctx* ctx, td_source* source) { // Walk through all known tables for (const char** table = tables; *table; table++) { - r = iptables_iterate_table(ctx, source, *table); + r = iptables_iterate_table(ctx, source, *table, &comments, &num_comments); if (r < 0) - return r; + goto ERROR; } - return 0; + // Submit all comments + for (unsigned int i = 0; i < num_comments; i++) { + r = td_source_submit_values(source, comments[i].comment, VALUES( + VALUE_UINT64("packets", &comments[i].packets), + VALUE_UINT64("bytes", &comments[i].bytes) + )); + if (r < 0) + goto ERROR; + } + +ERROR: + if (comments) + free(comments); + + return r; } const td_source_impl iptables_source = { -- 2.47.3