1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "netlink-util.h"
5 #include "networkd-link.h"
6 #include "networkd-manager.h"
7 #include "networkd-queue.h"
8 #include "string-table.h"
10 #define REPLY_CALLBACK_COUNT_THRESHOLD 128
12 static Request
*request_free(Request
*req
) {
16 /* To prevent from triggering assertions in the hash and compare functions, remove this request
17 * from the set before freeing userdata below. */
19 ordered_set_remove(req
->manager
->request_queue
, req
);
22 req
->free_func(req
->userdata
);
27 link_unref(req
->link
); /* link may be NULL, but link_unref() can handle it gracefully. */
32 DEFINE_TRIVIAL_REF_UNREF_FUNC(Request
, request
, request_free
);
34 void request_detach(Manager
*manager
, Request
*req
) {
40 req
= ordered_set_remove(manager
->request_queue
, req
);
48 static void request_destroy_callback(Request
*req
) {
52 request_detach(req
->manager
, req
);
57 static void request_hash_func(const Request
*req
, struct siphash
*state
) {
61 siphash24_compress_typesafe(req
->type
, state
);
63 if (req
->type
!= REQUEST_TYPE_NEXTHOP
) {
64 siphash24_compress_boolean(req
->link
, state
);
66 siphash24_compress_typesafe(req
->link
->ifindex
, state
);
69 siphash24_compress_typesafe(req
->hash_func
, state
);
70 siphash24_compress_typesafe(req
->compare_func
, state
);
73 req
->hash_func(req
->userdata
, state
);
76 static int request_compare_func(const struct Request
*a
, const struct Request
*b
) {
82 r
= CMP(a
->type
, b
->type
);
86 if (a
->type
!= REQUEST_TYPE_NEXTHOP
) {
87 r
= CMP(!!a
->link
, !!b
->link
);
92 r
= CMP(a
->link
->ifindex
, b
->link
->ifindex
);
98 r
= CMP(PTR_TO_UINT64(a
->hash_func
), PTR_TO_UINT64(b
->hash_func
));
102 r
= CMP(PTR_TO_UINT64(a
->compare_func
), PTR_TO_UINT64(b
->compare_func
));
107 return a
->compare_func(a
->userdata
, b
->userdata
);
112 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
116 request_compare_func
,
119 static int request_new(
124 mfree_func_t free_func
,
125 hash_func_t hash_func
,
126 compare_func_t compare_func
,
127 request_process_func_t process
,
129 request_netlink_handler_t netlink_handler
,
132 _cleanup_(request_unrefp
) Request
*req
= NULL
;
139 req
= new(Request
, 1);
145 .link
= link_ref(link
), /* link may be NULL, but link_ref() handles it gracefully. */
147 .userdata
= userdata
,
148 .hash_func
= hash_func
,
149 .compare_func
= compare_func
,
151 .netlink_handler
= netlink_handler
,
154 existing
= ordered_set_get(manager
->request_queue
, req
);
161 r
= ordered_set_ensure_put(&manager
->request_queue
, &request_hash_ops
, req
);
165 req
->manager
= manager
;
166 req
->free_func
= free_func
;
167 req
->counter
= counter
;
171 /* If this is called in the ORDERED_SET_FOREACH() loop of manager_process_requests(), we need to
172 * exit from the loop, due to the limitation of the iteration on OrderedSet. */
173 manager
->request_queued
= true;
182 int netdev_queue_request(
184 request_process_func_t process
,
191 r
= request_new(netdev
->manager
, NULL
, REQUEST_TYPE_NETDEV_INDEPENDENT
,
192 netdev
, (mfree_func_t
) netdev_unref
,
193 trivial_hash_func
, trivial_compare_func
,
194 process
, NULL
, NULL
, ret
);
202 int link_queue_request_full(
206 mfree_func_t free_func
,
207 hash_func_t hash_func
,
208 compare_func_t compare_func
,
209 request_process_func_t process
,
211 request_netlink_handler_t netlink_handler
,
216 return request_new(link
->manager
, link
, type
,
217 userdata
, free_func
, hash_func
, compare_func
,
218 process
, counter
, netlink_handler
, ret
);
221 int manager_process_requests(Manager
*manager
) {
227 /* Process only when no remove request is queued. */
228 if (!ordered_set_isempty(manager
->remove_request_queue
))
231 manager
->request_queued
= false;
233 ORDERED_SET_FOREACH(req
, manager
->request_queue
) {
234 _cleanup_(link_unrefp
) Link
*link
= link_ref(req
->link
);
236 assert(req
->process
);
238 if (req
->waiting_reply
)
239 continue; /* Waiting for netlink reply. */
241 /* Typically, requests send netlink message asynchronously. If there are many requests
242 * queued, then this event may make reply callback queue in sd-netlink full. */
243 if (netlink_get_reply_callback_count(manager
->rtnl
) >= REPLY_CALLBACK_COUNT_THRESHOLD
||
244 netlink_get_reply_callback_count(manager
->genl
) >= REPLY_CALLBACK_COUNT_THRESHOLD
||
245 fw_ctx_get_reply_callback_count(manager
->fw_ctx
) >= REPLY_CALLBACK_COUNT_THRESHOLD
)
248 r
= req
->process(req
, link
, req
->userdata
);
249 if (r
== 0) { /* The request is not ready. */
250 if (manager
->request_queued
)
251 break; /* a new request is queued during processing the request. */
255 /* If the request sends netlink message, e.g. for Address or so, the Request object is
256 * referenced by the netlink slot, and will be detached later by its destroy callback.
257 * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
258 if (!req
->waiting_reply
)
259 request_detach(manager
, req
);
262 link_enter_failed(link
);
263 /* link_enter_failed() may remove multiple requests,
264 * hence we need to exit from the loop. */
268 if (manager
->request_queued
)
275 static int request_netlink_handler(sd_netlink
*nl
, sd_netlink_message
*m
, Request
*req
) {
279 assert(*req
->counter
> 0);
281 req
->counter
= NULL
; /* To prevent double decrement on free. */
284 if (req
->link
&& IN_SET(req
->link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
287 if (req
->netlink_handler
)
288 return req
->netlink_handler(nl
, m
, req
, req
->link
, req
->userdata
);
293 int request_call_netlink_async(sd_netlink
*nl
, sd_netlink_message
*m
, Request
*req
) {
300 r
= netlink_call_async(nl
, NULL
, m
, request_netlink_handler
, request_destroy_callback
, req
);
305 req
->waiting_reply
= true;
309 static const char *const request_type_table
[_REQUEST_TYPE_MAX
] = {
310 [REQUEST_TYPE_ACTIVATE_LINK
] = "activate link",
311 [REQUEST_TYPE_ADDRESS
] = "address",
312 [REQUEST_TYPE_ADDRESS_LABEL
] = "address label",
313 [REQUEST_TYPE_BRIDGE_FDB
] = "bridge FDB",
314 [REQUEST_TYPE_BRIDGE_MDB
] = "bridge MDB",
315 [REQUEST_TYPE_DHCP_SERVER
] = "DHCP server",
316 [REQUEST_TYPE_DHCP4_CLIENT
] = "DHCPv4 client",
317 [REQUEST_TYPE_DHCP6_CLIENT
] = "DHCPv6 client",
318 [REQUEST_TYPE_IPV6_PROXY_NDP
] = "IPv6 proxy NDP",
319 [REQUEST_TYPE_NDISC
] = "NDisc",
320 [REQUEST_TYPE_NEIGHBOR
] = "neighbor",
321 [REQUEST_TYPE_NETDEV_INDEPENDENT
] = "independent netdev",
322 [REQUEST_TYPE_NETDEV_STACKED
] = "stacked netdev",
323 [REQUEST_TYPE_NEXTHOP
] = "nexthop",
324 [REQUEST_TYPE_RADV
] = "RADV",
325 [REQUEST_TYPE_ROUTE
] = "route",
326 [REQUEST_TYPE_ROUTING_POLICY_RULE
] = "routing policy rule",
327 [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE
] = "IPv6LL address generation mode",
328 [REQUEST_TYPE_SET_LINK_BOND
] = "bond configurations",
329 [REQUEST_TYPE_SET_LINK_BRIDGE
] = "bridge configurations",
330 [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN
] = "bridge VLAN configurations (step 1)",
331 [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN
] = "bridge VLAN configurations (step 2)",
332 [REQUEST_TYPE_SET_LINK_CAN
] = "CAN interface configurations",
333 [REQUEST_TYPE_SET_LINK_FLAGS
] = "link flags",
334 [REQUEST_TYPE_SET_LINK_GROUP
] = "interface group",
335 [REQUEST_TYPE_SET_LINK_IPOIB
] = "IPoIB configurations",
336 [REQUEST_TYPE_SET_LINK_MAC
] = "MAC address",
337 [REQUEST_TYPE_SET_LINK_MASTER
] = "master interface",
338 [REQUEST_TYPE_SET_LINK_MTU
] = "MTU",
339 [REQUEST_TYPE_SRIOV
] = "SR-IOV",
340 [REQUEST_TYPE_TC_QDISC
] = "QDisc",
341 [REQUEST_TYPE_TC_CLASS
] = "TClass",
342 [REQUEST_TYPE_UP_DOWN
] = "bring link up or down",
345 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type
, RequestType
);
347 static RemoveRequest
* remove_request_free(RemoveRequest
*req
) {
352 ordered_set_remove(req
->manager
->remove_request_queue
, req
);
355 req
->unref_func(req
->userdata
);
357 link_unref(req
->link
);
358 sd_netlink_unref(req
->netlink
);
359 sd_netlink_message_unref(req
->message
);
364 DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest
*, remove_request_free
);
365 DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback
, RemoveRequest
, remove_request_free
);
366 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
367 remove_request_hash_ops
,
370 trivial_compare_func
,
371 remove_request_free
);
373 int remove_request_add(
377 mfree_func_t unref_func
,
379 sd_netlink_message
*message
,
380 remove_request_netlink_handler_t netlink_handler
) {
382 _cleanup_(remove_request_freep
) RemoveRequest
*req
= NULL
;
390 req
= new(RemoveRequest
, 1);
394 *req
= (RemoveRequest
) {
395 .link
= link_ref(link
), /* link may be NULL, but link_ref() handles it gracefully. */
396 .userdata
= userdata
,
397 .netlink
= sd_netlink_ref(netlink
),
398 .message
= sd_netlink_message_ref(message
),
399 .netlink_handler
= netlink_handler
,
402 r
= ordered_set_ensure_put(&manager
->remove_request_queue
, &remove_request_hash_ops
, req
);
407 req
->manager
= manager
;
408 req
->unref_func
= unref_func
;
414 int manager_process_remove_requests(Manager
*manager
) {
420 while ((req
= ordered_set_first(manager
->remove_request_queue
))) {
422 /* Do not make the reply callback queue in sd-netlink full. */
423 if (netlink_get_reply_callback_count(req
->netlink
) >= REPLY_CALLBACK_COUNT_THRESHOLD
)
426 r
= netlink_call_async(
427 req
->netlink
, NULL
, req
->message
,
428 req
->netlink_handler
,
429 remove_request_destroy_callback
,
432 _cleanup_(link_unrefp
) Link
*link
= link_ref(req
->link
);
434 log_link_warning_errno(link
, r
, "Failed to call netlink message: %m");
436 /* First free the request. */
437 remove_request_free(req
);
439 /* Then, make the link enter the failed state. */
441 link_enter_failed(link
);
444 /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
445 * request may not freed on shutting down. */
446 req
->netlink
= sd_netlink_unref(req
->netlink
);
447 ordered_set_remove(manager
->remove_request_queue
, req
);