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 80d62d4f1a
This commit is contained in:
Luca Boccassi
2026-07-15 19:21:18 +01:00
parent f65d4e6c4e
commit 890dd23c83
3 changed files with 88 additions and 1 deletions
+6 -1
View 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;
+1
View 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;
+81
View 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;