]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-netlink: revamp message serial handling
authorLennart Poettering <lennart@poettering.net>
Wed, 10 Feb 2021 16:59:46 +0000 (17:59 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 10 Feb 2021 21:01:24 +0000 (22:01 +0100)
Let's use uint32_t everywhere to maintain the seqno, since that's what
the kernel does. Prviously in the reply_callback logic we used 64bit,
for no apparent reason.

Using 32bit also provides us with the benefit that we can avoid using
uint64_hash_ops, and can use trivial_hash_ops instead for the reply
hashmap, so that we can store the seqno in the key pointer directly.

While we are at it, let's make sure we never run into serial collisions
internally (32bit is a lot, but not that much), and let's put a limit on
outstanding serials, to catch programming errors.

src/libsystemd/sd-netlink/netlink-internal.h
src/libsystemd/sd-netlink/sd-netlink.c

index b433bd21dc02ab0eedf9fa4ca0dd7796e262be7e..fd7f07a6c1ff384092c493c13b611b1fd297b68d 100644 (file)
@@ -19,7 +19,7 @@
 struct reply_callback {
         sd_netlink_message_handler_t callback;
         usec_t timeout;
-        uint64_t serial;
+        uint32_t serial;
         unsigned prioq_idx;
 };
 
index db9fcd79dd050673d02d2eb834bdcfc38c00294e..6220798b76f9a39663ac736476ad9ec3e1d70d9c 100644 (file)
@@ -17,6 +17,9 @@
 #include "string-util.h"
 #include "util.h"
 
+/* Some really high limit, to catch programming errors */
+#define REPLY_CALLBACKS_MAX UINT16_MAX
+
 static int sd_netlink_new(sd_netlink **ret) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
 
@@ -190,18 +193,25 @@ static sd_netlink *netlink_free(sd_netlink *rtnl) {
 DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free);
 
 static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
+        uint32_t picked;
+
         assert(rtnl);
         assert(!rtnl_pid_changed(rtnl));
         assert(m);
         assert(m->hdr);
 
-        /* don't use seq == 0, as that is used for broadcasts, so we
-           would get confused by replies to such messages */
-        m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++;
+        /* Avoid collisions with outstanding requests */
+        do {
+                picked = rtnl->serial;
 
-        rtnl_message_seal(m);
+                /* Don't use seq == 0, as that is used for broadcasts, so we would get confused by replies to
+                   such messages */
+                rtnl->serial = rtnl->serial == UINT32_MAX ? 1 : rtnl->serial + 1;
+
+        } while (hashmap_contains(rtnl->reply_callbacks, UINT32_TO_PTR(picked)));
 
-        return;
+        m->hdr->nlmsg_seq = picked;
+        rtnl_message_seal(m);
 }
 
 int sd_netlink_send(sd_netlink *nl,
@@ -339,7 +349,7 @@ static int process_timeout(sd_netlink *rtnl) {
 
         assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
         c->timeout = 0;
-        hashmap_remove(rtnl->reply_callbacks, &c->serial);
+        hashmap_remove(rtnl->reply_callbacks, UINT32_TO_PTR(c->serial));
 
         slot = container_of(c, sd_netlink_slot, reply_callback);
 
@@ -359,7 +369,7 @@ static int process_timeout(sd_netlink *rtnl) {
 static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
         struct reply_callback *c;
         sd_netlink_slot *slot;
-        uint64_t serial;
+        uint32_t serial;
         uint16_t type;
         int r;
 
@@ -367,7 +377,7 @@ static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
         assert(m);
 
         serial = rtnl_message_get_serial(m);
-        c = hashmap_remove(rtnl->reply_callbacks, &serial);
+        c = hashmap_remove(rtnl->reply_callbacks, UINT32_TO_PTR(serial));
         if (!c)
                 return 0;
 
@@ -568,7 +578,6 @@ int sd_netlink_call_async(
                 uint64_t usec,
                 const char *description) {
         _cleanup_free_ sd_netlink_slot *slot = NULL;
-        uint32_t s;
         int r, k;
 
         assert_return(nl, -EINVAL);
@@ -576,7 +585,10 @@ int sd_netlink_call_async(
         assert_return(callback, -EINVAL);
         assert_return(!rtnl_pid_changed(nl), -ECHILD);
 
-        r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
+        if (hashmap_size(nl->reply_callbacks) >= REPLY_CALLBACKS_MAX)
+                return -ERANGE;
+
+        r = hashmap_ensure_allocated(&nl->reply_callbacks, &trivial_hash_ops);
         if (r < 0)
                 return r;
 
@@ -593,20 +605,18 @@ int sd_netlink_call_async(
         slot->reply_callback.callback = callback;
         slot->reply_callback.timeout = calc_elapse(usec);
 
-        k = sd_netlink_send(nl, m, &s);
+        k = sd_netlink_send(nl, m, &slot->reply_callback.serial);
         if (k < 0)
                 return k;
 
-        slot->reply_callback.serial = s;
-
-        r = hashmap_put(nl->reply_callbacks, &slot->reply_callback.serial, &slot->reply_callback);
+        r = hashmap_put(nl->reply_callbacks, UINT32_TO_PTR(slot->reply_callback.serial), &slot->reply_callback);
         if (r < 0)
                 return r;
 
         if (slot->reply_callback.timeout != 0) {
                 r = prioq_put(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
                 if (r < 0) {
-                        (void) hashmap_remove(nl->reply_callbacks, &slot->reply_callback.serial);
+                        (void) hashmap_remove(nl->reply_callbacks, UINT32_TO_PTR(slot->reply_callback.serial));
                         return r;
                 }
         }