From: Luca Boccassi Date: Mon, 13 Jul 2026 18:44:58 +0000 (+0100) Subject: network: cancel netlink calls for detached requests X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=890dd23c8343cf80e3f483980c8bfb250a3f3645;p=thirdparty%2Fsystemd.git network: cancel netlink calls for detached requests 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 --- diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index d2a2b831ab3..d0cb61d59ff 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -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; diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index 172ff230b44..5f33a1afeba 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -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; diff --git a/src/network/test-network.c b/src/network/test-network.c index ee0e5d13b57..d91f2561640 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -1,11 +1,18 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + +#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;