]> git.ipfire.org Git - oddments/collecty.git/commitdiff
sources: iptables: Accumulate counters of the same comments
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 23 Oct 2025 10:23:24 +0000 (10:23 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 23 Oct 2025 10:23:24 +0000 (10:23 +0000)
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 <michael.tremer@ipfire.org>
src/daemon/sources/iptables.c

index 5ca37e52f2f4855455abc09178ec7e203c476d7f..c7e4fec5aea4cda877c2558c904442810d485916 100644 (file)
 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 = {