]> git.ipfire.org Git - telemetry.git/commitdiff
netlink: Move all netlink stuff away
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 30 Mar 2026 15:37:14 +0000 (15:37 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 30 Mar 2026 15:37:14 +0000 (15:37 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/daemon/daemon.c
src/daemon/daemon.h
src/daemon/netlink.c
src/daemon/netlink.h
src/daemon/source.c
src/daemon/source.h
src/daemon/sources/interface.c
src/daemon/sources/legacy-gateway-latency4.c

index 95d7aae9b0c86c954da6cc96771464e6c5f5ad44..21eaa2f275cb9031b3383f831d97f525a5d6fcae 100644 (file)
 #include <systemd/sd-daemon.h>
 #include <systemd/sd-event.h>
 
-#ifdef HAVE_LIBNL3
-# ifdef HAVE_LIBNL3_GENL
-#  include <netlink/genl/ctrl.h>
-#  include <netlink/genl/genl.h>
-# endif
-# ifdef HAVE_LIBNL3_ROUTE
-#  include <netlink/route/route.h>
-#  include <netlink/socket.h>
-# endif /* HAVE_LIBNL3_ROUTE */
-#endif /* HAVE_LIBNL3 */
-
 #include "bus.h"
 #include "ctx.h"
 #include "daemon.h"
@@ -87,27 +76,6 @@ struct td_daemon {
 
        // Netlink
        td_netlink* netlink;
-
-       // Netlink Sockets
-#ifdef HAVE_LIBNL3
-       struct {
-               // 802.11
-# ifdef HAVE_LIBNL3_GENL
-               struct nl_sock* nl80211;
-# endif /* HAVE_LIBNL3_GENL */
-
-               // Route
-# ifdef HAVE_LIBNL3_ROUTE
-               struct nl_sock* route;
-# endif /* HAVE_LIBNL3_ROUTE */
-       } nl_sockets;
-#endif /* HAVE_LIBNL3 */
-
-#ifdef HAVE_LIBNL3
-# ifdef HAVE_LIBNL3_GENL
-       int nl80211id;
-# endif /* HAVE_LIBNL3_GENL */
-#endif /* HAVE_LIBNL3 */
 };
 
 static int td_daemon_init(sd_event_source* source, void* data) {
@@ -310,18 +278,6 @@ static int td_daemon_setup_netlink(td_daemon* self) {
 }
 
 static void td_daemon_free(td_daemon* self) {
-       // Free Netlink Sockets
-#ifdef HAVE_LIBNL3
-# ifdef HAVE_LIBNL3_GENL
-       if (self->nl_sockets.nl80211)
-               nl_socket_free(self->nl_sockets.nl80211);
-# endif /* HAVE_LIBNL3_GENL */
-# ifdef HAVE_LIBNL3_ROUTE
-       if (self->nl_sockets.route)
-               nl_socket_free(self->nl_sockets.route);
-# endif /* HAVE_LIBNL3_ROUTE */
-#endif /* HAVE_LIBNL3 */
-
        if (self->events.memory_pressure)
                sd_event_source_unref(self->events.memory_pressure);
        if (self->events.sigchld)
@@ -485,82 +441,6 @@ td_netlink* td_daemon_get_netlink(td_daemon* self) {
        return td_netlink_ref(self->netlink);
 }
 
-#ifdef HAVE_LIBNL3
-static struct nl_sock* td_daemon_get_nl_socket(td_daemon* self, int protocol) {
-       struct nl_sock* sock = NULL;
-       int r;
-
-       // Allocate a new socket
-       sock = nl_socket_alloc();
-       if (!sock) {
-               ERROR(self->ctx, "Failed to allocate a new Netlink socket: %m\n");
-               goto ERROR;
-       }
-
-       // Connect the protocol
-       r = nl_connect(sock, protocol);
-       if (r < 0) {
-               ERROR(self->ctx, "Failed to select netlink protocol %d: %s\n",
-                               protocol, nl_geterror(r));
-               errno = ENOTSUP;
-               goto ERROR;
-       }
-
-       // Return the socket
-       return sock;
-
-ERROR:
-       if (sock)
-               nl_socket_free(sock);
-
-       return NULL;
-}
-
-# ifdef HAVE_LIBNL3_GENL
-struct nl_sock* td_daemon_get_nl_80211_socket(td_daemon* self) {
-       // Create socket if not already done
-       if (!self->nl_sockets.nl80211)
-               self->nl_sockets.nl80211 = td_daemon_get_nl_socket(self, NETLINK_GENERIC);
-
-       return self->nl_sockets.nl80211;
-}
-
-int td_daemon_get_nl_80211_id(td_daemon* self) {
-       struct nl_sock* sock = NULL;
-       int r;
-
-       if (!self->nl80211id) {
-               // Fetch the nl80211 socket
-               sock = td_daemon_get_nl_80211_socket(self);
-               if (!sock)
-                       return -errno;
-
-               // Retrieve the ID
-               r = genl_ctrl_resolve(sock, "nl80211");
-               if (r < 0) {
-                       ERROR(self->ctx, "Failed to resolve nl80211 family id: %s\n", nl_geterror(r));
-                       return -ENOTSUP;
-               }
-
-               // Cache the ID
-               self->nl80211id = r;
-       }
-
-       return self->nl80211id;
-}
-# endif /* HAVE_LIBNL3_GENL */
-
-# ifdef HAVE_LIBNL3_ROUTE
-struct nl_sock* td_daemon_get_nl_route_socket(td_daemon* self) {
-       // Create socket if not already done
-       if (!self->nl_sockets.route)
-               self->nl_sockets.route = td_daemon_get_nl_socket(self, NETLINK_ROUTE);
-
-       return self->nl_sockets.route;
-}
-# endif /* HAVE_LIBNL3_ROUTE */
-#endif /* HAVE_LIBNL3 */
-
 static int td_daemon_bus_version(sd_bus* bus, const char* path, const char* interface,
                const char* property, sd_bus_message* reply, void* data, sd_bus_error* error) {
        return sd_bus_message_append(reply, "s", PACKAGE_VERSION);
index 5f48ad848d998dc350531748f081e91fb31a5909..5d8006cecf6a8d913c8fad2001f5b3e613925b83 100644 (file)
 #include <libudev.h>
 #include <systemd/sd-event.h>
 
-#ifdef HAVE_LIBNL3
-# ifdef HAVE_LIBNL3_ROUTE
-#  include <netlink/socket.h>
-# endif /* HAVE_LIBNL3_ROUTE */
-#endif /* HAVE_LIBNL3 */
-
-
 typedef struct td_daemon td_daemon;
 
 #include "bus.h"
@@ -61,15 +54,6 @@ int td_daemon_flush_source(
 
 #ifdef HAVE_LIBNL3
 td_netlink* td_daemon_get_netlink(td_daemon* self);
-
-# ifdef HAVE_LIBNL3_GENL
-struct nl_sock* td_daemon_get_nl_80211_socket(td_daemon* self);
-int td_daemon_get_nl_80211_id(td_daemon* self);
-# endif /* HAVE_LIBNL3_GENL */
-
-# ifdef HAVE_LIBNL3_ROUTE
-struct nl_sock* td_daemon_get_nl_route_socket(td_daemon* self);
-# endif /* HAVE_LIBNL3_ROUTE */
 #endif /* HAVE_LIBNL3 */
 
 // Bus
index 90a39c1931aa67798715175d0b3e3cdbd5dec2f0..2ae14b719cc5577011151c7d58a42a467685291d 100644 (file)
 #ifdef HAVE_LIBNL3
 
 #include <errno.h>
+#include <netlink/socket.h>
 #include <stdlib.h>
 
+#ifdef HAVE_LIBNL3_GENL
+# include <netlink/genl/ctrl.h>
+# include <netlink/genl/genl.h>
+#endif
+
+#ifdef HAVE_LIBNL3_ROUTE
+# include <netlink/route/link.h>
+# include <netlink/route/route.h>
+#endif /* HAVE_LIBNL3_ROUTE */
+
 #include "ctx.h"
 #include "netlink.h"
 
 struct td_netlink {
        td_ctx* ctx;
        int nrefs;
+
+       // Route Stuff
+#ifdef HAVE_LIBNL3_ROUTE
+       struct {
+               struct nl_sock* sock;
+       } route;
+#endif /* HAVE_LIBNL3_ROUTE */
+
+       // 802.11 Stuff
+#ifdef HAVE_LIBNL3_GENL
+       struct {
+               struct nl_sock* sock;
+               int id;
+       } nl80211;
+#endif /* HAVE_LIBNL3_GENL */
 };
 
 static void td_netlink_free(td_netlink* self) {
+       // Free Netlink Sockets
+#ifdef HAVE_LIBNL3_GENL
+       if (self->nl80211.sock)
+               nl_socket_free(self->nl80211.sock);
+#endif /* HAVE_LIBNL3_GENL */
+
+#ifdef HAVE_LIBNL3_ROUTE
+       if (self->route.sock)
+               nl_socket_free(self->route.sock);
+#endif /* HAVE_LIBNL3_ROUTE */
+
        if (self->ctx)
                td_ctx_unref(self->ctx);
        free(self);
@@ -70,4 +107,231 @@ td_netlink* td_netlink_unref(td_netlink* self) {
        return NULL;
 }
 
+static struct nl_sock* td_netlink_get_socket(td_netlink* self, int protocol) {
+       struct nl_sock* sock = NULL;
+       int r;
+
+       // Allocate a new socket
+       sock = nl_socket_alloc();
+       if (!sock) {
+               ERROR(self->ctx, "Failed to allocate a new Netlink socket: %m\n");
+               goto ERROR;
+       }
+
+       // Connect the protocol
+       r = nl_connect(sock, protocol);
+       if (r < 0) {
+               ERROR(self->ctx, "Failed to select netlink protocol %d: %s\n",
+                               protocol, nl_geterror(r));
+               errno = ENOTSUP;
+               goto ERROR;
+       }
+
+       // Return the socket
+       return sock;
+
+ERROR:
+       if (sock)
+               nl_socket_free(sock);
+
+       return NULL;
+}
+
+/*
+       Route Stuff
+*/
+# ifdef HAVE_LIBNL3_ROUTE
+static struct nl_sock* td_netlink_get_route_socket(td_netlink* self) {
+       // Create socket if not already done
+       if (!self->route.sock)
+               self->route.sock = td_netlink_get_socket(self, NETLINK_ROUTE);
+
+       return self->route.sock;
+}
+
+typedef struct td_netlink_enumerte_interfaces_ctx {
+       // Context
+       td_ctx* ctx;
+
+       // Callback & Data
+       td_netlink_interface_callback callback;
+       void* data;
+} td_netlink_enumerate_interfaces_ctx;
+
+static void enumerate_interface(struct nl_object* object, void* data) {
+       struct rtnl_link* link = (struct rtnl_link*)object;
+       td_netlink_enumerate_interfaces_ctx* ctx = data;
+       const char* name = NULL;
+       int r;
+
+       // Fetch the link name
+       name = rtnl_link_get_name(link);
+       if (!name)
+               return;
+
+       // Call the callback
+       r = ctx->callback(ctx->ctx, name, link, ctx->data);
+       if (r < 0)
+               ERROR(ctx->ctx, "Netlink: Enumerating interfaces failed: %s\n", strerror(-r));
+}
+
+int td_netlink_enumerate_interfaces(td_netlink* self,
+               td_netlink_interface_callback callback, void* data) {
+       struct nl_sock* sock = NULL;
+       struct nl_cache* links = NULL;
+       int r;
+
+       td_netlink_enumerate_interfaces_ctx ctx = {
+               .ctx      = self->ctx,
+               .callback = callback,
+               .data     = data,
+       };
+
+       // Fetch the Netlink socket
+       sock = td_netlink_get_route_socket(self);
+       if (!sock)
+               return -errno;
+
+       // Fetch the links
+       r = rtnl_link_alloc_cache(sock, AF_UNSPEC, &links);
+       if (r < 0) {
+               ERROR(self->ctx, "Failed to fetch the links: %s\n", nl_geterror(r));
+               r = -ENOTSUP;
+               goto ERROR;
+       }
+
+       // Walk through all interfaces
+       nl_cache_foreach(links, enumerate_interface, &ctx);
+
+ERROR:
+       if (links)
+               nl_cache_free(links);
+
+       return r;
+}
+
+int td_netlink_get_default_gateway(td_netlink* self,
+               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;
+
+       // Fetch the Netlink socket
+       sock = td_netlink_get_route_socket(self);
+       if (!sock)
+               return -errno;
+
+       // Fetch the route cache
+       r = rtnl_route_alloc_cache(sock, AF_INET, 0, &routes);
+       if (r < 0) {
+               ERROR(self->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(self->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);
+
+       return r;
+}
+
+# endif /* HAVE_LIBNL3_ROUTE */
+
+/*
+       802.11 Stuff
+*/
+# ifdef HAVE_LIBNL3_GENL
+
+static struct nl_sock* td_netlink_get_nl80211_socket(td_netlink* self) {
+       // Create socket if not already done
+       if (!self->nl80211.sock)
+               self->nl80211.sock = td_netlink_get_socket(self, NETLINK_GENERIC);
+
+       return self->nl80211.sock;
+}
+
+static int td_netlink_get_nl80211_id(td_netlink* self) {
+       struct nl_sock* sock = NULL;
+       int r;
+
+       if (!self->nl80211.id) {
+               // Fetch the nl80211 socket
+               sock = td_netlink_get_nl80211_socket(self);
+               if (!sock)
+                       return -errno;
+
+               // Retrieve the ID
+               r = genl_ctrl_resolve(sock, "nl80211");
+               if (r < 0) {
+                       ERROR(self->ctx, "Failed to resolve nl80211 family id: %s\n", nl_geterror(r));
+                       return -ENOTSUP;
+               }
+
+               // Cache the ID
+               self->nl80211.id = r;
+       }
+
+       return self->nl80211.id;
+}
+# endif /* HAVE_LIBNL3_GENL */
+
 #endif /* HAVE_LIBNL3 */
index a7ef94470779688c5471cbc816c417a34e5c04e7..39b895dd97e6ce0e34d6367a2e295b64ed22c214 100644 (file)
@@ -32,6 +32,20 @@ int td_netlink_create(td_netlink** netlink, td_ctx* ctx);
 td_netlink* td_netlink_ref(td_netlink* self);
 td_netlink* td_netlink_unref(td_netlink* self);
 
+#ifdef HAVE_LIBNL3_ROUTE
+# include <netlink/route/link.h>
+
+typedef int (*td_netlink_interface_callback)
+       (td_ctx* ctx, const char* name, struct rtnl_link* link, void* data);
+
+int td_netlink_enumerate_interfaces(td_netlink* self,
+       td_netlink_interface_callback callback, void* data);
+
+int td_netlink_get_default_gateway(td_netlink* self,
+       char* address, size_t length, unsigned int* type);
+
+#endif /* HAVE_LIBNL3_ROUTE */
+
 #endif /* HAVE_LIBNL3 */
 
 #endif /* TELEMETRY_NETLINK_H */
index 6206be4c9f4f2d58c49e46d5a432855837008e29..0a02bb016f965ab0417c97ac5042ee49624158ab 100644 (file)
@@ -35,6 +35,7 @@
 #include "ctx.h"
 #include "daemon.h"
 #include "metrics.h"
+#include "netlink.h"
 #include "source.h"
 #include "string.h"
 #include "time.h"
@@ -547,8 +548,8 @@ struct udev* td_source_get_udev(td_source* self) {
        return td_daemon_get_udev(self->daemon);
 }
 
-struct nl_sock* td_source_get_nl_route_socket(td_source* self) {
-       return td_daemon_get_nl_route_socket(self->daemon);
+td_netlink* td_source_get_netlink(td_source* self) {
+       return td_daemon_get_netlink(self->daemon);
 }
 
 unsigned int td_source_num_data_sources(td_source* self) {
index 5f9d35278f587281df888f5a920fd3b3545ddf3d..fefed4e740d450a51be409c0eeb2d67b00e6c39b 100644 (file)
 
 #include <libudev.h>
 
-#ifdef HAVE_LIBNL3
-# ifdef HAVE_LIBNL3_ROUTE
-#  include <netlink/socket.h>
-# endif /* HAVE_LIBNL3_ROUTE */
-#endif /* HAVE_LIBNL3 */
-
 typedef struct td_source td_source;
 
 #include "args.h"
@@ -38,6 +32,7 @@ typedef struct td_source td_source;
 #include "ctx.h"
 #include "daemon.h"
 #include "metrics.h"
+#include "netlink.h"
 
 #define MAX_DS 64
 #define MAX_RRA 8
@@ -92,6 +87,7 @@ const char* td_source_name(td_source* self);
 int td_source_disable(td_source* self);
 
 struct udev* td_source_get_udev(td_source* self);
+td_netlink* td_source_get_netlink(td_source* self);
 
 #ifdef HAVE_LIBNL3
 # ifdef HAVE_LIBNL3_ROUTE
index cc8515c7421e8ffee923ea7a7ac435453549974b..1aa2fe6b658d17adcd4d2d8a5f5287231dbe3b39 100644 (file)
@@ -101,18 +101,13 @@ static const interface_stats stats[] = {
        { NULL },
 };
 
-static void interface_link_callback(struct nl_object* object, void* data) {
-       struct rtnl_link* link = (struct rtnl_link*)object;
+static int interface_link_callback(td_ctx* ctx, const char* name,
+               struct rtnl_link* link, void* data) {
        td_metrics* metrics = NULL;
        td_source* source = data;
        uint64_t value = 0;
        int r;
 
-       // Fetch the link name
-       const char *name = rtnl_link_get_name(link);
-       if (!name)
-               goto ERROR;
-
        // Create a new metrics object
        r = td_source_create_metrics(source, &metrics, name);
        if (r < 0)
@@ -137,31 +132,27 @@ static void interface_link_callback(struct nl_object* object, void* data) {
 ERROR:
        if (metrics)
                td_metrics_unref(metrics);
+
+       return r;
 }
 
 static int interface_heartbeat(td_ctx* ctx, td_source* source) {
-       struct nl_cache* links = NULL;
+       td_netlink* netlink = NULL;
        int r;
 
-       // Fetch the Netlink socket
-       struct nl_sock* sock = td_source_get_nl_route_socket(source);
-       if (!sock)
+       // Fetch the netlink interface
+       netlink = td_source_get_netlink(source);
+       if (!netlink)
                return -errno;
 
-       // 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;
+       // Enumerate all interfaces
+       r = td_netlink_enumerate_interfaces(netlink, interface_link_callback, source);
+       if (r < 0)
                goto ERROR;
-       }
-
-       // Walk through all interfaces
-       nl_cache_foreach(links, interface_link_callback, source);
 
 ERROR:
-       if (links)
-               nl_cache_free(links);
+       if (netlink)
+               td_netlink_unref(netlink);
 
        return r;
 }
index 0850c8df6cf1c82609e8551b072d077a03ba24d1..947242dec6278b1e4240c939969928f2bc0af7a7 100644 (file)
 #include "../time.h"
 #include "legacy-gateway-latency4.h"
 
-static int fetch_default_gateway(td_ctx* ctx, td_source* source,
-               char* address, size_t length, unsigned int* type) {
-       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;
-
-       // Fetch the Netlink socket
-       struct nl_sock* sock = td_source_get_nl_route_socket(source);
-       if (!sock)
-               return -errno;
-
-       // 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);
-
-       return r;
-}
-
 static int legacy_gateway_latency_on_success(td_ctx* ctx,
                int rc, td_file* stdout, void* data) {
        td_source* source = data;
@@ -199,29 +116,44 @@ static int do_ping(td_ctx* ctx, td_source* source, const char* address) {
 }
 
 static int legacy_gateway_latency4_heartbeat(td_ctx* ctx, td_source* source) {
+       td_netlink* netlink = NULL;
        char address[NAME_MAX] = "";
        unsigned int type = 0;
        int r;
 
+       // Fetch netlink
+       netlink = td_source_get_netlink(source);
+       if (!netlink)
+               return -errno;
+
        // Fetch the IP address of the default gateway
-       r = fetch_default_gateway(ctx, source, address, sizeof(address), &type);
+       r = td_netlink_get_default_gateway(netlink, address, sizeof(address), &type);
        if (r < 0)
-               return r;
+               goto ERROR;
 
        // Bail if we don't have a gateway address
        if (!*address) {
                DEBUG(ctx, "Failed to fetch the gateway address\n");
-               return 0;
+               r = 0;
+               goto ERROR;
        }
 
        // Use ARP ping for Ethernet interfaces and otherwise fall back on ICMP ping
        switch (type) {
                case ARPHRD_ETHER:
-                       return do_arping(ctx, source, address);
+                       r = do_arping(ctx, source, address);
+                       break;
 
                default:
-                       return do_ping(ctx, source, address);
+                       r = do_ping(ctx, source, address);
+                       break;
        }
+
+ERROR:
+       if (netlink)
+               td_netlink_unref(netlink);
+
+       return r;
 }
 
 const td_source_impl legacy_gateway_latency4_source = {