]> git.99rst.org Git - openwrt-packages.git/commitdiff
prometheus-node-exporter-ucode: add nat_traffic collector
authorRicard Nacher <redacted>
Wed, 10 Jun 2026 14:54:07 +0000 (16:54 +0200)
committerEtienne Champetier <redacted>
Fri, 3 Jul 2026 05:36:06 +0000 (08:36 +0300)
Add a nat_traffic collector for prometheus-node-exporter-ucode,
ported from the equivalent Lua collector in prometheus-node-exporter-lua.

Reads /proc/net/nf_conntrack and exports per-(src, dst) pair byte
totals (both directions summed) as node_nat_traffic gauge metrics.

Also includes a ucode-based unit test with a fixture file that can be
run locally with: ucode test/nat_traffic.uc

Signed-off-by: Ricard Nacher <redacted>
utils/prometheus-node-exporter-ucode/Makefile
utils/prometheus-node-exporter-ucode/files/extra/nat_traffic.uc [new file with mode: 0644]
utils/prometheus-node-exporter-ucode/test.sh
utils/prometheus-node-exporter-ucode/test/fixtures/nf_conntrack [new file with mode: 0644]
utils/prometheus-node-exporter-ucode/test/nat_traffic.uc [new file with mode: 0644]

index 3cef0f63ac6b12b2f884815215a13ff3d76b3c17..d91b8663b5564f99eaf39c48d0014f126b1e6997 100644 (file)
@@ -4,7 +4,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=prometheus-node-exporter-ucode
 PKG_VERSION:=2024.02.07
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_MAINTAINER:=Andre Heider <a.heider@gmail.com>
 PKG_LICENSE:=Apache-2.0
@@ -65,6 +65,7 @@ define Collector
 endef
 
 $(eval $(call Collector,dnsmasq,Dnsmasq collector,@dnsmasq))
+$(eval $(call Collector,nat_traffic,NAT traffic collector,))
 $(eval $(call Collector,ltq-dsl,Lantiq/Intel/MaxLinear DSL collector,@ltq-dsl-app))
 $(eval $(call Collector,netstat,netstat collector,))
 $(eval $(call Collector,openwrt,OpenWrt collector,))
diff --git a/utils/prometheus-node-exporter-ucode/files/extra/nat_traffic.uc b/utils/prometheus-node-exporter-ucode/files/extra/nat_traffic.uc
new file mode 100644 (file)
index 0000000..e42924b
--- /dev/null
@@ -0,0 +1,36 @@
+let f = fs.open("/proc/net/nf_conntrack");
+if (!f)
+       return false;
+
+let nat = {};
+let nat_metric = gauge("node_nat_traffic");
+
+let line;
+while ((line = nextline(f)) != null) {
+       const fields = wsplit(line);
+       let src, dst;
+       let bytes = 0;
+
+       for (let field in fields) {
+               if (src == null && substr(field, 0, 4) == "src=")
+                       src = substr(field, 4);
+               else if (dst == null && substr(field, 0, 4) == "dst=")
+                       dst = substr(field, 4);
+               else if (substr(field, 0, 6) == "bytes=")
+                       bytes += +substr(field, 6);
+       }
+
+       if (src == null || dst == null)
+               continue;
+
+       if (nat[src] == null)
+               nat[src] = {};
+
+       nat[src][dst] = (nat[src][dst] || 0) + bytes;
+}
+
+f.close();
+
+for (let src in nat)
+       for (let dst in nat[src])
+               nat_metric({ src, dst }, nat[src][dst]);
index e72942b8e07dfdab22dac2b14b4f60911b0628cd..7ba638b0122f34feecbb11ebaa9c392bce3661be 100755 (executable)
@@ -1,3 +1,10 @@
 #!/bin/sh
 
-prometheus-node-exporter-ucode time
+case "$1" in
+prometheus-node-exporter-ucode-nat_traffic)
+       [ -f /proc/net/nf_conntrack ] && prometheus-node-exporter-ucode nat_traffic
+       ;;
+*)
+       prometheus-node-exporter-ucode time
+       ;;
+esac
diff --git a/utils/prometheus-node-exporter-ucode/test/fixtures/nf_conntrack b/utils/prometheus-node-exporter-ucode/test/fixtures/nf_conntrack
new file mode 100644 (file)
index 0000000..d04e856
--- /dev/null
@@ -0,0 +1,4 @@
+ipv4     2 tcp      6 431999 ESTABLISHED src=192.168.1.2 dst=1.2.3.4 sport=49152 dport=443 packets=10 bytes=1234 src=1.2.3.4 dst=192.168.1.2 sport=443 dport=49152 packets=8 bytes=5678 [ASSURED] mark=0 use=2
+ipv4     2 tcp      6 120 TIME_WAIT src=192.168.1.2 dst=1.2.3.4 sport=49153 dport=80 packets=5 bytes=100 src=1.2.3.4 dst=192.168.1.2 sport=80 dport=49153 packets=3 bytes=200 mark=0 use=2
+ipv4     2 tcp      6 431999 ESTABLISHED src=192.168.1.3 dst=1.2.3.4 sport=49154 dport=443 packets=2 bytes=300 src=1.2.3.4 dst=192.168.1.3 sport=443 dport=49154 packets=1 bytes=400 [ASSURED] mark=0 use=2
+ipv4     2 udp      17 30 src=192.168.1.2 dst=8.8.8.8 sport=12345 dport=53 packets=1 bytes=60 src=8.8.8.8 dst=192.168.1.2 sport=53 dport=12345 packets=1 bytes=120 mark=0 use=2
diff --git a/utils/prometheus-node-exporter-ucode/test/nat_traffic.uc b/utils/prometheus-node-exporter-ucode/test/nat_traffic.uc
new file mode 100644 (file)
index 0000000..38b1dc7
--- /dev/null
@@ -0,0 +1,77 @@
+'use strict';
+
+import * as real_fs from "fs";
+
+const fixture = "./test/fixtures/nf_conntrack";
+const collector_path = "./files/extra/nat_traffic.uc";
+
+// Capture metric emissions
+let metrics = {};
+
+function gauge(name) {
+       let func;
+       func = function(labels, value) {
+               let key = name;
+               if (labels && length(labels)) {
+                       let parts = [];
+                       for (let k in labels)
+                               push(parts, k + '="' + labels[k] + '"');
+                       key += "{" + join(",", parts) + "}";
+               }
+               metrics[key] = value;
+               return func;
+       };
+       return func;
+}
+
+// Redirect /proc/net/nf_conntrack to fixture file
+const fs = {
+       open: function(path) {
+               if (path == "/proc/net/nf_conntrack")
+                       return real_fs.open(fixture);
+               return real_fs.open(path);
+       }
+};
+
+function wsplit(line) {
+       return split(line, /\s+/);
+}
+
+function nextline(f) {
+       return rtrim(f.read("line"), "\n");
+}
+
+let func;
+try {
+       func = loadfile(collector_path, { strict_declarations: true, raw_mode: true });
+} catch(e) {
+       die("Failed to load collector: " + e.message + "\n");
+}
+
+if (call(func, null, { fs, gauge, wsplit, nextline, config: {} }) == false)
+       die("Collector returned false — is the fixture file missing?\n");
+
+// Assertions
+let passed = 0;
+let failed = 0;
+
+function assert_eq(key, expected) {
+       const actual = metrics[key];
+       if (actual == expected) {
+               print("PASS: " + key + " == " + expected + "\n");
+               passed++;
+       } else {
+               print("FAIL: " + key + ": expected " + expected + ", got " + actual + "\n");
+               failed++;
+       }
+}
+
+// line1 (1234+5678=6912) + line2 (100+200=300) share same src/dst => aggregated to 7212
+assert_eq('node_nat_traffic{src="192.168.1.2",dst="1.2.3.4"}', 7212);
+// line3 (300+400=700)
+assert_eq('node_nat_traffic{src="192.168.1.3",dst="1.2.3.4"}', 700);
+// line4 (60+120=180)
+assert_eq('node_nat_traffic{src="192.168.1.2",dst="8.8.8.8"}', 180);
+
+print("\n" + passed + " passed, " + failed + " failed\n");
+exit(failed > 0 ? 1 : 0);
git clone https://git.99rst.org/PROJECT