#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"
// 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) {
}
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)
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);
#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"
#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
#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);
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 */
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 */
#include "ctx.h"
#include "daemon.h"
#include "metrics.h"
+#include "netlink.h"
#include "source.h"
#include "string.h"
#include "time.h"
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) {
#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"
#include "ctx.h"
#include "daemon.h"
#include "metrics.h"
+#include "netlink.h"
#define MAX_DS 64
#define MAX_RRA 8
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
{ 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)
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;
}
#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;
}
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 = {