]> git.ipfire.org Git - collecty.git/commitdiff
sources: Add a simply source for measuring the gateway latency on IPv4
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 11 Nov 2025 17:16:46 +0000 (17:16 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 11 Nov 2025 17:16:46 +0000 (17:16 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/daemon/sources.c
src/daemon/sources/legacy-gateway-latency4.c [new file with mode: 0644]
src/daemon/sources/legacy-gateway-latency4.h [new file with mode: 0644]

index b40ca2a354fdf09aea9a8c564d0120446413c845..56d03f40578b41f4dafe18a639ad67eddbc9b7c4 100644 (file)
@@ -174,6 +174,8 @@ dist_telemetryd_SOURCES = \
        src/daemon/sources/ipfrag4.h \
        src/daemon/sources/iptables.c \
        src/daemon/sources/iptables.h \
+       src/daemon/sources/legacy-gateway-latency4.c \
+       src/daemon/sources/legacy-gateway-latency4.h \
        src/daemon/sources/loadavg.c \
        src/daemon/sources/loadavg.h \
        src/daemon/sources/memory.c \
index cf06119e8ae534ad5c1c11ab919a112080de6edf..9accd2630936f87974b1a274a445e819da692bcb 100644 (file)
 # include "sources/iptables.h"
 #endif /* HAVE_LIBIPTC */
 
+// legacy gateway latency4
+#ifdef HAVE_LIBNL3
+#ifdef HAVE_LIBNL3_ROUTE
+# include "sources/legacy-gateway-latency4.h"
+#endif /* HAVE_LIBNL3_ROUTE */
+#endif /* HAVE_LIBNL3 */
+
 // sensors
 #ifdef HAVE_SENSORS
 # include "sources/sensors.h"
@@ -89,6 +96,13 @@ static const td_source_impl* source_impls[] = {
        &iptables_source,
 #endif /* HAVE_LIBIPTC */
 
+       // legacy gateway latency4
+#ifdef HAVE_LIBNL3
+#ifdef HAVE_LIBNL3_ROUTE
+       &legacy_gateway_latency4_source,
+#endif /* HAVE_LIBNL3_ROUTE */
+#endif /* HAVE_LIBNL3 */
+
        // sensors
 #ifdef HAVE_SENSORS
        &sensors_input_source,
diff --git a/src/daemon/sources/legacy-gateway-latency4.c b/src/daemon/sources/legacy-gateway-latency4.c
new file mode 100644 (file)
index 0000000..39547a4
--- /dev/null
@@ -0,0 +1,260 @@
+/*#############################################################################
+#                                                                             #
+# 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 <errno.h>
+#include <limits.h>
+#include <linux/if_arp.h>
+
+// libnl-3
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/route.h>
+#include <netlink/socket.h>
+
+#include "../command.h"
+#include "../ctx.h"
+#include "../source.h"
+#include "legacy-gateway-latency4.h"
+
+static int fetch_default_gateway(td_ctx* ctx,
+               char* address, size_t length, unsigned int* type) {
+       struct nl_sock* sock = NULL;
+       struct nl_cache* routes = NULL;
+       struct rtnl_route* route = NULL;
+       struct nl_cache* links = NULL;
+       struct rtnl_link* link = NULL;
+       struct rtnl_nexthop* nh = NULL;
+       struct nl_addr* gw = NULL;
+       int ifindex = -1;
+       int r;
+
+       // Create a new netlink socket
+       sock = nl_socket_alloc();
+       if (!sock) {
+               ERROR(ctx, "Failed to create a netlink socket: %m\n");
+               r = -errno;
+               goto ERROR;
+       }
+
+       // Select routing
+       r = nl_connect(sock, NETLINK_ROUTE);
+       if (r < 0) {
+               ERROR(ctx, "Failed to select routing: %s\n", nl_geterror(r));
+               r = -ENOTSUP;
+               goto ERROR;
+       }
+
+       // Fetch the route cache
+       r = rtnl_route_alloc_cache(sock, AF_INET, 0, &routes);
+       if (r < 0) {
+               ERROR(ctx, "Failed to fetch the route cache: %s\n", nl_geterror(r));
+               r = -ENOTSUP;
+               goto ERROR;
+       }
+
+       // Fetch the links
+       r = rtnl_link_alloc_cache(sock, AF_UNSPEC, &links);
+       if (r < 0) {
+               ERROR(ctx, "Failed to fetch the links: %s\n", nl_geterror(r));
+               r = -ENOTSUP;
+               goto ERROR;
+       }
+
+       // Walk through all routes
+       for (struct nl_object* o = nl_cache_get_first(routes); o; o = nl_cache_get_next(o)) {
+               route = (struct rtnl_route*)o;
+
+               // Ignore anything that isn't IPv4
+               if (rtnl_route_get_family(route) != AF_INET)
+                       continue;
+
+               // Ignore anything but the main routing table
+               if (rtnl_route_get_table(route) != RT_TABLE_MAIN)
+                       continue;
+
+               // Ignore anything but the default route
+               if (!rtnl_route_get_dst(route))
+                       continue;
+
+               // Fetch the nexthop
+               nh = rtnl_route_nexthop_n(route, 0);
+               if (!nh)
+                       continue;
+
+               // Fetch the gateway
+               gw = rtnl_route_nh_get_gateway(nh);
+               if (!gw)
+                       continue;
+
+               // Return the gateway address
+               if (!nl_addr2str(gw, address, length))
+                       continue;
+
+               // Fetch the interface
+               ifindex = rtnl_route_nh_get_ifindex(nh);
+
+               // Fetch the link
+               link = rtnl_link_get(links, ifindex);
+               if (!link)
+                       continue;
+
+               // Fetch the type of the interface
+               *type = rtnl_link_get_arptype(link);
+       }
+
+ERROR:
+       if (routes)
+               nl_cache_free(routes);
+       if (links)
+               nl_cache_free(links);
+       if (sock)
+                nl_socket_free(sock);
+
+       return r;
+}
+
+static int legacy_gateway_latency_on_success(td_ctx* ctx,
+               int rc, td_file* stdout, void* data) {
+       td_source* source = data;
+       double min = -1.0;
+       double avg = -1.0;
+       double max = -1.0;
+       double mdev = -1.0;
+       double loss = 0;
+       int r;
+
+       td_file_parser parser[] = {
+               // ping
+               PARSE4("rtt min/avg/max/mdev = %lf/%lf/%lf/%lf ms", &min, &avg, &max, &mdev),
+               PARSE1("%*d packets transmitted, %*d received, %lf%% packet loss", &loss),
+
+               // arping
+               PARSE4("rtt min/avg/max/std-dev = %lf/%lf/%lf/%lf ms", &min, &avg, &max, &mdev),
+               PARSE1("%*d packets transmitted, %*d packets received, %lf%% unanswered", &loss),
+               { NULL },
+       };
+
+       // Parse the output
+       r = td_file_parse(stdout, parser);
+       if (r < 0)
+               return r;
+
+       // Convert the loss
+       loss /= 100.0;
+
+       // Submit values
+       return td_source_submit_values(source, NULL, VALUES(
+               VALUE_FLOAT("latency", &avg),
+               VALUE_FLOAT("stddev",  &mdev),
+               VALUE_FLOAT("loss",    &loss)
+       ));
+}
+
+
+static int do_arping(td_ctx* ctx, td_source* source, const char* address) {
+       td_command* command = NULL;
+       int r;
+
+       // Run the ping command
+       const char* argv[] = { "arping", "-c3", address, NULL };
+
+       // Create a new command
+       r = td_source_create_command(source, &command);
+       if (r < 0)
+               goto ERROR;
+
+       // Register the success callback
+       td_command_on_success(command, legacy_gateway_latency_on_success, source);
+
+       // Execute the command
+       r = td_command_execute(command, argv);
+
+ERROR:
+       if (command)
+               td_command_unref(command);
+
+       return r;
+}
+
+static int do_ping(td_ctx* ctx, td_source* source, const char* address) {
+       td_command* command = NULL;
+       int r;
+
+       // Run the ping command
+       const char* argv[] = { "ping", "-c3", address, NULL };
+
+       // Create a new command
+       r = td_source_create_command(source, &command);
+       if (r < 0)
+               goto ERROR;
+
+       // Register the success callback
+       td_command_on_success(command, legacy_gateway_latency_on_success, source);
+
+       // Execute the command
+       r = td_command_execute(command, argv);
+
+ERROR:
+       if (command)
+               td_command_unref(command);
+
+       return r;
+}
+
+static int legacy_gateway_latency4_heartbeat(td_ctx* ctx, td_source* source) {
+       char address[NAME_MAX] = "";
+       unsigned int type = 0;
+       int r;
+
+       // Fetch the IP address of the default gateway
+       r = fetch_default_gateway(ctx, address, sizeof(address), &type);
+       if (r < 0)
+               return r;
+
+       // Bail if we don't have a gateway address
+       if (!*address) {
+               DEBUG(ctx, "Failed to fetch the gateway address\n");
+               return 0;
+       }
+
+       // Use ARP ping for Ethernet interfaces and otherwise fall back on ICMP ping
+       switch (type) {
+               case ARPHRD_ETHER:
+                       return do_arping(ctx, source, address);
+
+               default:
+                       return do_ping(ctx, source, address);
+       }
+}
+
+const td_source_impl legacy_gateway_latency4_source = {
+       .name = "legacy-gateway-latency4",
+
+       // RRD Data Sources
+       .rrd_dss = {
+               { "latency", "GAUGE", 0, -1, },
+               { "stddev",  "GAUGE", 0, -1, },
+               { "loss",    "GAUGE", 0, -1, },
+               { NULL },
+       },
+
+       // Methods
+       .heartbeat = legacy_gateway_latency4_heartbeat,
+};
diff --git a/src/daemon/sources/legacy-gateway-latency4.h b/src/daemon/sources/legacy-gateway-latency4.h
new file mode 100644 (file)
index 0000000..0b14dca
--- /dev/null
@@ -0,0 +1,28 @@
+/*#############################################################################
+#                                                                             #
+# 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_LEGACY_GATEWAY_LATENCY4_H
+#define TELEMETRY_SOURCE_LEGACY_GATEWAY_LATENCY4_H
+
+#include "../source.h"
+
+extern const td_source_impl legacy_gateway_latency4_source;
+
+#endif /* TELEMETRY_SOURCE_LEGACY_GATEWAY_LATENCY4_H */