]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
kernel-netlink: Add simple wrapper for Netlink event sockets
authorTobias Brunner <tobias@strongswan.org>
Thu, 26 Jan 2023 16:43:18 +0000 (17:43 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 16 Feb 2023 12:25:35 +0000 (13:25 +0100)
src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c
src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.h

index 8af3016f6de8bf882ab89aab67177673c6335071..9292b5ec224d129b9036eb6ad4c6027559cfa861 100644 (file)
@@ -79,9 +79,6 @@
 #define ROUTING_TABLE_PRIO 0
 #endif
 
-/** multicast groups (for groups > 31 setsockopt has to be used) */
-#define nl_group(group) (1 << (group - 1))
-
 ENUM(rt_msg_names, RTM_NEWLINK, RTM_GETRULE,
        "RTM_NEWLINK",
        "RTM_DELLINK",
index abb9d16f803c10a0b870bfc880e5ffcc85e36c66..71905e3c9c8343f48b5b18958d5261001d49f20d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2022 Tobias Brunner
+ * Copyright (C) 2008-2023 Tobias Brunner
  * Copyright (C) 2014 Martin Willi
  *
  * Copyright (C) secunet Security Networks AG
@@ -58,6 +58,7 @@
 #endif
 
 typedef struct private_netlink_socket_t private_netlink_socket_t;
+typedef struct private_netlink_event_socket_t private_netlink_event_socket_t;
 
 /**
  * Private variables and functions of netlink_socket_t class.
@@ -125,6 +126,37 @@ struct private_netlink_socket_t {
        bool ignore_retransmit_errors;
 };
 
+/**
+ * Private data of netlink_event_socket_t class
+ */
+struct private_netlink_event_socket_t {
+
+       /**
+        * Public interface
+        */
+       netlink_event_socket_t public;
+
+       /**
+        * Registered callback
+        */
+       netlink_event_cb_t cb;
+
+       /**
+        * User data to pass to callback
+        */
+       void *user;
+
+       /**
+        * Netlink socket
+        */
+       int socket;
+
+       /**
+        * Buffer size for received Netlink messages
+        */
+       u_int buflen;
+};
+
 /**
  * #definable hook to simulate request message loss
  */
@@ -700,6 +732,92 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,
        return &this->public;
 }
 
+CALLBACK(watch_event, bool,
+       private_netlink_event_socket_t *this, int fd, watcher_event_t event)
+{
+       char buf[this->buflen];
+       struct nlmsghdr *hdr = (struct nlmsghdr*)buf;
+       struct sockaddr_nl addr;
+       socklen_t addr_len = sizeof(addr);
+       int len;
+
+       len = recvfrom(this->socket, buf, sizeof(buf), MSG_DONTWAIT,
+                                  (struct sockaddr*)&addr, &addr_len);
+       if (len < 0)
+       {
+               if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
+               {
+                       DBG1(DBG_KNL, "netlink event read error: %s", strerror(errno));
+               }
+               return TRUE;
+       }
+       else if (addr.nl_pid != 0)
+       {       /* ignore non-kernel messages */
+               return TRUE;
+       }
+
+       while (NLMSG_OK(hdr, len))
+       {
+               this->cb(this->user, hdr);
+               hdr = NLMSG_NEXT(hdr, len);
+       }
+       return TRUE;
+}
+
+METHOD(netlink_event_socket_t, destroy_event, void,
+       private_netlink_event_socket_t *this)
+{
+       if (this->socket != -1)
+       {
+               lib->watcher->remove(lib->watcher, this->socket);
+               close(this->socket);
+       }
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+netlink_event_socket_t *netlink_event_socket_create(int protocol, uint32_t groups,
+                                                                                                       netlink_event_cb_t cb, void *user)
+{
+       private_netlink_event_socket_t *this;
+       struct sockaddr_nl addr = {
+               .nl_family = AF_NETLINK,
+               .nl_groups = groups,
+       };
+
+       INIT(this,
+               .public = {
+                       .destroy = _destroy_event,
+               },
+               .cb = cb,
+               .user = user,
+               .buflen = netlink_get_buflen(),
+       );
+
+       this->socket = socket(AF_NETLINK, SOCK_RAW, protocol);
+       if (this->socket == -1)
+       {
+               DBG1(DBG_KNL, "unable to create netlink event socket: %s (%d)",
+                        strerror(errno), errno);
+               destroy_event(this);
+               return NULL;
+       }
+
+       if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)))
+       {
+               DBG1(DBG_KNL, "unable to bind netlink event socket: %s (%d)",
+                        strerror(errno), errno);
+               destroy_event(this);
+               return NULL;
+       }
+
+       lib->watcher->add(lib->watcher, this->socket, WATCHER_READ, watch_event, this);
+
+       return &this->public;
+}
+
 /*
  * Described in header
  */
index cec3e5e5b9263a019533de5a21a79ac6075e3af2..9638129704954b4f0c14846c56a76a91fce8336b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2022 Tobias Brunner
+ * Copyright (C) 2008-2023 Tobias Brunner
  *
  * Copyright (C) secunet Security Networks AG
  *
@@ -40,7 +40,16 @@ typedef union {
        u_char bytes[KERNEL_NETLINK_BUFSIZE];
 } netlink_buf_t __attribute__((aligned(RTA_ALIGNTO)));
 
+/**
+ * Callback function for netlink events.
+ *
+ * @param user         user data, as passed to constructor
+ * @param hdr          received netlink message
+ */
+typedef void (*netlink_event_cb_t)(void *user, struct nlmsghdr *hdr);
+
 typedef struct netlink_socket_t netlink_socket_t;
+typedef struct netlink_event_socket_t netlink_event_socket_t;
 
 /**
  * Wrapper around a netlink socket.
@@ -80,6 +89,45 @@ struct netlink_socket_t {
 netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,
                                                                                bool parallel);
 
+/**
+ * Wrapper around a bound netlink event socket.
+ */
+struct netlink_event_socket_t {
+
+       /**
+        * Destroy the event socket.
+        */
+       void (*destroy)(netlink_event_socket_t *this);
+};
+
+/**
+ * Create a netlink_event_socket_t object.
+ *
+ * @param protocol     protocol type (e.g. NETLINK_XFRM or NETLINK_ROUTE)
+ * @param groups       event groups to bind (use nl_group())
+ * @param cb           callback to invoke for each event
+ * @param user         user data passed to callback
+ */
+netlink_event_socket_t *netlink_event_socket_create(int protocol, uint32_t groups,
+                                                                                                       netlink_event_cb_t cb, void *user);
+
+/**
+ * Helper to create bitmask for Netlink multicast groups.
+ *
+ * For groups > 31, setsockopt() with NETLINK_ADD_MEMBERSHIP has to be used,
+ * which is currently not supported by the event socket.
+ */
+static inline uint32_t nl_group(uint32_t group)
+{
+       if (group > 31)
+       {
+               DBG1(DBG_KNL, "netlink multicast group %d currently not supported",
+                        group);
+               return 0;
+       }
+       return group ? (1 << (group - 1)) : 0;
+}
+
 /**
  * Creates an rtattr and adds it to the given netlink message.
  *