From: Michael Tremer Date: Tue, 11 Nov 2025 17:16:46 +0000 (+0000) Subject: sources: Add a simply source for measuring the gateway latency on IPv4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bcabaa2c22d3b0c8cd0a83cb23552bb19f715e1b;p=collecty.git sources: Add a simply source for measuring the gateway latency on IPv4 Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index b40ca2a..56d03f4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/src/daemon/sources.c b/src/daemon/sources.c index cf06119..9accd26 100644 --- a/src/daemon/sources.c +++ b/src/daemon/sources.c @@ -52,6 +52,13 @@ # 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 index 0000000..39547a4 --- /dev/null +++ b/src/daemon/sources/legacy-gateway-latency4.c @@ -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 . # +# # +#############################################################################*/ + +#include +#include +#include + +// libnl-3 +#include +#include +#include +#include + +#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 index 0000000..0b14dca --- /dev/null +++ b/src/daemon/sources/legacy-gateway-latency4.h @@ -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 . # +# # +#############################################################################*/ + +#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 */