From 890dd23c8343cf80e3f483980c8bfb250a3f3645 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 13 Jul 2026 19:44:58 +0100 Subject: [PATCH] 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 --- src/network/networkd-queue.c | 7 +++- src/network/networkd-queue.h | 1 + src/network/test-network.c | 81 ++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 1 deletion(-) 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; -- 2.47.3