]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: cancel netlink calls for detached requests 43014/head
authorLuca Boccassi <luca.boccassi@gmail.com>
Mon, 13 Jul 2026 18:44:58 +0000 (19:44 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Wed, 15 Jul 2026 18:21:18 +0000 (19:21 +0100)
Store the asynchronous netlink slot in the Request object and release it
when the request is detached. This cancels the pending callback instead of
keeping the request alive until its reply arrives.

Follow-up for 80d62d4f1aa62c03828e4cbe2c2dfb2a19765af8

src/network/networkd-queue.c
src/network/networkd-queue.h
src/network/test-network.c

index d2a2b831ab310ded083649c657b3161fa043f6bd..d0cb61d59ff690c4194091954510e6d000e8f915 100644 (file)
@@ -22,6 +22,7 @@ static Request* request_detach_impl(Request *req) {
 
         ordered_set_remove(req->manager->request_queue, req);
         req->manager = NULL;
+        req->netlink_slot = sd_netlink_slot_unref(req->netlink_slot);
         return req;
 }
 
@@ -328,6 +329,10 @@ int manager_process_requests(Manager *manager) {
 static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) {
         assert(req);
 
+        assert(req->netlink_slot);
+        assert_se(sd_netlink_slot_set_floating(req->netlink_slot, true) >= 0);
+        req->netlink_slot = sd_netlink_slot_unref(req->netlink_slot);
+
         if (req->counter) {
                 assert(*req->counter > 0);
                 (*req->counter)--;
@@ -350,7 +355,7 @@ int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *r
         assert(m);
         assert(req);
 
-        r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req);
+        r = netlink_call_async(nl, &req->netlink_slot, m, request_netlink_handler, request_destroy_callback, req);
         if (r < 0)
                 return r;
 
index 172ff230b44879fdee7c2d2fef1c98632fe14e3a..5f33a1afeba080d501ad0fe59c0d15ac19d74692 100644 (file)
@@ -80,6 +80,7 @@ typedef struct Request {
          * request, and pass this request to the netlink_call_async(), and set the destroy function
          * to the slot. */
         request_netlink_handler_t netlink_handler;
+        sd_netlink_slot *netlink_slot;
 
         bool waiting_reply;
 } Request;
index ee0e5d13b5700e53784ac7a9264f7b951653abfa..d91f256164051cf8676b9996001e8192ce415ea7 100644 (file)
@@ -1,11 +1,18 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <net/if.h>
+
+#include "sd-netlink.h"
+
 #include "alloc-util.h"
 #include "hashmap.h"
 #include "hostname-setup.h"
+#include "netlink-util.h"
 #include "network-internal.h"
 #include "networkd-manager.h"
+#include "networkd-queue.h"
 #include "networkd-route-util.h"
+#include "ordered-set.h"
 #include "strv.h"
 #include "tests.h"
 #include "vrf.h"
@@ -126,6 +133,80 @@ TEST(vrf_table) {
         ASSERT_EQ(vrf.table, 5678U);
 }
 
+static int test_request_process(Request *req, Link *link, void *userdata) {
+        assert_not_reached();
+}
+
+static int test_request_netlink_handler(
+                sd_netlink *rtnl,
+                sd_netlink_message *message,
+                Request *req,
+                Link *link,
+                void *userdata) {
+
+        assert(rtnl);
+        assert(message);
+        assert(req);
+        assert(!link);
+        assert(userdata);
+
+        *(bool*) userdata = true;
+        return 0;
+}
+
+static void test_request_netlink_handler_one(bool detach) {
+        _cleanup_(manager_freep) Manager *manager = NULL;
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
+        bool handler_called = false;
+        unsigned counter = 0;
+        Request *req;
+
+        ASSERT_OK(manager_new(&manager, /* test_mode= */ true));
+        ASSERT_OK(sd_netlink_open(&rtnl));
+
+        ASSERT_OK_POSITIVE(manager_queue_request_full(
+                        manager,
+                        REQUEST_TYPE_ADDRESS_LABEL,
+                        &handler_called,
+                        /* free_func= */ NULL,
+                        /* hash_func= */ NULL,
+                        /* compare_func= */ NULL,
+                        test_request_process,
+                        &counter,
+                        test_request_netlink_handler,
+                        &req));
+        ASSERT_EQ(counter, 1U);
+
+        int ifindex = (int) if_nametoindex("lo");
+        ASSERT_GT(ifindex, 0);
+        ASSERT_OK(sd_rtnl_message_new_link(rtnl, &message, RTM_GETLINK, ifindex));
+        ASSERT_OK(request_call_netlink_async(rtnl, message, req));
+        ASSERT_EQ(netlink_get_reply_callback_count(rtnl), 1U);
+
+        if (detach) {
+                request_detach(req);
+                ASSERT_EQ(counter, 0U);
+                ASSERT_EQ(netlink_get_reply_callback_count(rtnl), 0U);
+        }
+
+        ASSERT_OK(sd_netlink_wait(rtnl, 0));
+        ASSERT_OK_POSITIVE(sd_netlink_process(rtnl, /* ret= */ NULL));
+
+        ASSERT_EQ(handler_called, !detach);
+        ASSERT_EQ(counter, 0U);
+        ASSERT_EQ(netlink_get_reply_callback_count(rtnl), 0U);
+        ASSERT_TRUE(ordered_set_isempty(manager->request_queue));
+}
+
+TEST(request_netlink_handler_called) {
+        test_request_netlink_handler_one(/* detach= */ false);
+}
+
+TEST(request_netlink_handler_detached) {
+        test_request_netlink_handler_one(/* detach= */ true);
+}
+
 TEST(manager_enumerate) {
         _cleanup_(manager_freep) Manager *manager = NULL;