--- /dev/null
+/*#############################################################################
+# #
+# telemetryd - The IPFire Telemetry Collection Service #
+# Copyright (C) 2025 IPFire Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <libiptc/libiptc.h>
+
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_comment.h>
+
+#include "../ctx.h"
+#include "../source.h"
+#include "../string.h"
+#include "iptables.h"
+
+typedef struct xtc_handle iptc_handle_t;
+typedef struct ipt_entry iptc_rule_t;
+
+static int skip_comment(const char* comment) {
+ // Skip empty comments
+ if (!*comment)
+ return 1;
+
+ // Skip comments that contain anything else but uppercase characters, digits and _
+ for (const char* p = comment; *p; p++) {
+ // Allow uppercase characters
+ if (isupper(*p))
+ continue;
+
+ // Allow digits
+ else if (isdigit(*p))
+ continue;
+
+ // Allow underscore
+ else if (*p == '_')
+ continue;
+
+ // Invalid character found, skip!
+ return 1;
+ }
+
+ return 0;
+}
+
+static int iptables_iterate_matches(td_ctx* ctx, td_source* source, const iptc_rule_t* rule) {
+ const struct xt_comment_info* comment = NULL;
+ const struct xt_entry_match* match = NULL;
+ int r;
+
+ // Start with the first match and determine the end
+ const unsigned char* p = rule->elems;
+ const unsigned char* e = (const unsigned char*)rule + rule->target_offset;
+
+ // Walk through all matches
+ for (; p + sizeof(*match) < e; p += match->u.match_size) {
+ match = (const struct xt_entry_match*)p;
+
+ // Ensure we did not reach the target area
+ if (p + match->u.match_size > e) {
+ DEBUG(ctx, "Match reaches the target area\n");
+ return -EBADMSG;
+ }
+
+ // Only care about comments
+ if (!td_string_equals(match->u.user.name, "comment"))
+ continue;
+
+ // Pointer to the comment
+ comment = (const struct xt_comment_info*)match->data;
+
+ // Skip the comment?
+ 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)
+ ));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int iptables_iterate_chain(td_ctx* ctx, td_source* source,
+ iptc_handle_t* handle, const char* chain) {
+ 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);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int iptables_iterate_table(td_ctx* ctx, td_source* source, const char* table) {
+ iptc_handle_t* handle = NULL;
+ const char* chain = NULL;
+ int r;
+
+ // Create a new handle
+ handle = iptc_init(table);
+ if (!handle) {
+ ERROR(ctx, "Failed to open iptables table '%s': %s\n", iptc_strerror(errno));
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Iterate through all chains
+ for (chain = iptc_first_chain(handle); chain; chain = iptc_next_chain(handle)) {
+ r = iptables_iterate_chain(ctx, source, handle, chain);
+ if (r < 0)
+ goto ERROR;
+ }
+
+ERROR:
+ if (handle)
+ iptc_free(handle);
+
+ return r;
+
+}
+
+static int iptables_heartbeat(td_ctx* ctx, td_source* source) {
+ int r = 0;
+
+ const char* tables[] = {
+ "filter", "mangle", "nat", "raw", NULL,
+ };
+
+ // Walk through all known tables
+ for (const char** table = tables; *table; table++) {
+ r = iptables_iterate_table(ctx, source, *table);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+const td_source_impl iptables_source = {
+ .name = "iptables",
+
+ // RRD Data Sources
+ .rrd_dss = {
+ { "packets", "DERIVE", 0, -1, },
+ { "bytes", "DERIVE", 0, -1, },
+ { NULL },
+ },
+
+ // Methods
+ .heartbeat = iptables_heartbeat,
+};
--- /dev/null
+/*#############################################################################
+# #
+# telemetryd - The IPFire Telemetry Collection Service #
+# Copyright (C) 2025 IPFire Development Team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#ifndef TELEMETRY_SOURCE_IPTABLES_H
+#define TELEMETRY_SOURCE_IPTABLES_H
+
+#include "../source.h"
+
+extern const td_source_impl iptables_source;
+
+#endif /* TELEMETRY_SOURCE_IPTABLES_H */