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_detach_impl(Request
*req
) {
18 ordered_set_remove(req
->manager
->request_queue
, req
);
23 void request_detach(Request
*req
) {
24 request_unref(request_detach_impl(req
));
27 static Request
*request_free(Request
*req
) {
31 /* To prevent from triggering assertions in the hash and compare functions, remove this request
32 * from the set before freeing userdata below. */
33 request_detach_impl(req
);
36 req
->free_func(req
->userdata
);
41 link_unref(req
->link
); /* link may be NULL, but link_unref() can handle it gracefully. */
46 DEFINE_TRIVIAL_REF_UNREF_FUNC(Request
, request
, request_free
);
48 static void request_destroy_callback(Request
*req
) {
55 static void request_hash_func(const Request
*req
, struct siphash
*state
) {
59 siphash24_compress_typesafe(req
->type
, state
);
61 if (!IN_SET(req
->type
, REQUEST_TYPE_NEXTHOP
, REQUEST_TYPE_ROUTE
)) {
62 siphash24_compress_boolean(req
->link
, state
);
64 siphash24_compress_typesafe(req
->link
->ifindex
, state
);
67 siphash24_compress_typesafe(req
->hash_func
, state
);
68 siphash24_compress_typesafe(req
->compare_func
, state
);
71 req
->hash_func(req
->userdata
, state
);
74 static int request_compare_func(const struct Request
*a
, const struct Request
*b
) {
80 r
= CMP(a
->type
, b
->type
);
84 if (!IN_SET(a
->type
, REQUEST_TYPE_NEXTHOP
, REQUEST_TYPE_ROUTE
)) {
85 r
= CMP(!!a
->link
, !!b
->link
);
90 r
= CMP(a
->link
->ifindex
, b
->link
->ifindex
);
96 r
= CMP(PTR_TO_UINT64(a
->hash_func
), PTR_TO_UINT64(b
->hash_func
));
100 r
= CMP(PTR_TO_UINT64(a
->compare_func
), PTR_TO_UINT64(b
->compare_func
));
105 return a
->compare_func(a
->userdata
, b
->userdata
);
110 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
114 request_compare_func
,
117 static int request_new(
122 mfree_func_t free_func
,
123 hash_func_t hash_func
,
124 compare_func_t compare_func
,
125 request_process_func_t process
,
127 request_netlink_handler_t netlink_handler
,
130 _cleanup_(request_unrefp
) Request
*req
= NULL
;
137 req
= new(Request
, 1);
143 .link
= link_ref(link
), /* link may be NULL, but link_ref() handles it gracefully. */
145 .userdata
= userdata
,
146 .hash_func
= hash_func
,
147 .compare_func
= compare_func
,
149 .netlink_handler
= netlink_handler
,
152 existing
= ordered_set_get(manager
->request_queue
, req
);
159 r
= ordered_set_ensure_put(&manager
->request_queue
, &request_hash_ops
, req
);
163 req
->manager
= manager
;
164 req
->free_func
= free_func
;
165 req
->counter
= counter
;
169 /* If this is called in the ORDERED_SET_FOREACH() loop of manager_process_requests(), we need to
170 * exit from the loop, due to the limitation of the iteration on OrderedSet. */
171 manager
->request_queued
= true;
180 int netdev_queue_request(
182 request_process_func_t process
,
189 r
= request_new(netdev
->manager
, NULL
, REQUEST_TYPE_NETDEV_INDEPENDENT
,
190 netdev
, (mfree_func_t
) netdev_unref
,
191 trivial_hash_func
, trivial_compare_func
,
192 process
, NULL
, NULL
, ret
);
200 int link_queue_request_full(
204 mfree_func_t free_func
,
205 hash_func_t hash_func
,
206 compare_func_t compare_func
,
207 request_process_func_t process
,
209 request_netlink_handler_t netlink_handler
,
214 return request_new(link
->manager
, link
, type
,
215 userdata
, free_func
, hash_func
, compare_func
,
216 process
, counter
, netlink_handler
, ret
);
219 int link_requeue_request(Link
*link
, Request
*req
, void *userdata
, Request
**ret
) {
223 return link_queue_request_full(
232 req
->netlink_handler
,
236 int manager_process_requests(Manager
*manager
) {
242 /* Process only when no remove request is queued. */
243 if (!ordered_set_isempty(manager
->remove_request_queue
))
246 manager
->request_queued
= false;
248 ORDERED_SET_FOREACH(req
, manager
->request_queue
) {
249 if (req
->waiting_reply
)
250 continue; /* Already processed, and waiting for netlink reply. */
252 /* Typically, requests send netlink message asynchronously. If there are many requests
253 * queued, then this event may make reply callback queue in sd-netlink full. */
254 if (netlink_get_reply_callback_count(manager
->rtnl
) >= REPLY_CALLBACK_COUNT_THRESHOLD
||
255 netlink_get_reply_callback_count(manager
->genl
) >= REPLY_CALLBACK_COUNT_THRESHOLD
||
256 fw_ctx_get_reply_callback_count(manager
->fw_ctx
) >= REPLY_CALLBACK_COUNT_THRESHOLD
)
259 /* Avoid the request and link freed by req->process() and request_detach(). */
260 _unused_
_cleanup_(request_unrefp
) Request
*req_unref
= request_ref(req
);
261 _cleanup_(link_unrefp
) Link
*link
= link_ref(req
->link
);
263 assert(req
->process
);
264 r
= req
->process(req
, link
, req
->userdata
);
269 link_enter_failed(link
);
270 /* link_enter_failed() may detach multiple requests from the queue.
271 * Hence, we need to exit from the loop. */
275 if (r
> 0 && !req
->waiting_reply
)
276 /* If the request sends netlink message, e.g. for Address or so, the Request object is
277 * referenced by the netlink slot, and will be detached later by its destroy callback.
278 * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
281 if (manager
->request_queued
)
282 break; /* New request is queued. Exit from the loop. */
288 static int request_netlink_handler(sd_netlink
*nl
, sd_netlink_message
*m
, Request
*req
) {
292 assert(*req
->counter
> 0);
294 req
->counter
= NULL
; /* To prevent double decrement on free. */
297 if (req
->link
&& IN_SET(req
->link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
300 if (req
->netlink_handler
)
301 return req
->netlink_handler(nl
, m
, req
, req
->link
, req
->userdata
);
306 int request_call_netlink_async(sd_netlink
*nl
, sd_netlink_message
*m
, Request
*req
) {
313 r
= netlink_call_async(nl
, NULL
, m
, request_netlink_handler
, request_destroy_callback
, req
);
318 req
->waiting_reply
= true;
322 static const char *const request_type_table
[_REQUEST_TYPE_MAX
] = {
323 [REQUEST_TYPE_ACTIVATE_LINK
] = "activate link",
324 [REQUEST_TYPE_ADDRESS
] = "address",
325 [REQUEST_TYPE_ADDRESS_LABEL
] = "address label",
326 [REQUEST_TYPE_BRIDGE_FDB
] = "bridge FDB",
327 [REQUEST_TYPE_BRIDGE_MDB
] = "bridge MDB",
328 [REQUEST_TYPE_DHCP_SERVER
] = "DHCP server",
329 [REQUEST_TYPE_DHCP4_CLIENT
] = "DHCPv4 client",
330 [REQUEST_TYPE_DHCP6_CLIENT
] = "DHCPv6 client",
331 [REQUEST_TYPE_IPV6_PROXY_NDP
] = "IPv6 proxy NDP",
332 [REQUEST_TYPE_NDISC
] = "NDisc",
333 [REQUEST_TYPE_NEIGHBOR
] = "neighbor",
334 [REQUEST_TYPE_NETDEV_INDEPENDENT
] = "independent netdev",
335 [REQUEST_TYPE_NETDEV_STACKED
] = "stacked netdev",
336 [REQUEST_TYPE_NEXTHOP
] = "nexthop",
337 [REQUEST_TYPE_RADV
] = "RADV",
338 [REQUEST_TYPE_ROUTE
] = "route",
339 [REQUEST_TYPE_ROUTING_POLICY_RULE
] = "routing policy rule",
340 [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE
] = "IPv6LL address generation mode",
341 [REQUEST_TYPE_SET_LINK_BOND
] = "bond configurations",
342 [REQUEST_TYPE_SET_LINK_BRIDGE
] = "bridge configurations",
343 [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN
] = "bridge VLAN configurations (step 1)",
344 [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN
] = "bridge VLAN configurations (step 2)",
345 [REQUEST_TYPE_SET_LINK_CAN
] = "CAN interface configurations",
346 [REQUEST_TYPE_SET_LINK_FLAGS
] = "link flags",
347 [REQUEST_TYPE_SET_LINK_GROUP
] = "interface group",
348 [REQUEST_TYPE_SET_LINK_IPOIB
] = "IPoIB configurations",
349 [REQUEST_TYPE_SET_LINK_MAC
] = "MAC address",
350 [REQUEST_TYPE_SET_LINK_MASTER
] = "master interface",
351 [REQUEST_TYPE_SET_LINK_MTU
] = "MTU",
352 [REQUEST_TYPE_SRIOV
] = "SR-IOV",
353 [REQUEST_TYPE_TC_QDISC
] = "QDisc",
354 [REQUEST_TYPE_TC_CLASS
] = "TClass",
355 [REQUEST_TYPE_UP_DOWN
] = "bring link up or down",
358 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type
, RequestType
);
360 static RemoveRequest
* remove_request_free(RemoveRequest
*req
) {
365 ordered_set_remove(req
->manager
->remove_request_queue
, req
);
368 req
->unref_func(req
->userdata
);
370 link_unref(req
->link
);
371 sd_netlink_unref(req
->netlink
);
372 sd_netlink_message_unref(req
->message
);
377 DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest
*, remove_request_free
);
378 DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback
, RemoveRequest
, remove_request_free
);
379 DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
380 remove_request_hash_ops
,
383 trivial_compare_func
,
384 remove_request_free
);
386 int remove_request_add(
390 mfree_func_t unref_func
,
392 sd_netlink_message
*message
,
393 remove_request_netlink_handler_t netlink_handler
) {
395 _cleanup_(remove_request_freep
) RemoveRequest
*req
= NULL
;
403 req
= new(RemoveRequest
, 1);
407 *req
= (RemoveRequest
) {
408 .link
= link_ref(link
), /* link may be NULL, but link_ref() handles it gracefully. */
409 .userdata
= userdata
,
410 .netlink
= sd_netlink_ref(netlink
),
411 .message
= sd_netlink_message_ref(message
),
412 .netlink_handler
= netlink_handler
,
415 r
= ordered_set_ensure_put(&manager
->remove_request_queue
, &remove_request_hash_ops
, req
);
420 req
->manager
= manager
;
421 req
->unref_func
= unref_func
;
427 int manager_process_remove_requests(Manager
*manager
) {
433 while ((req
= ordered_set_first(manager
->remove_request_queue
))) {
435 /* Do not make the reply callback queue in sd-netlink full. */
436 if (netlink_get_reply_callback_count(req
->netlink
) >= REPLY_CALLBACK_COUNT_THRESHOLD
)
439 r
= netlink_call_async(
440 req
->netlink
, NULL
, req
->message
,
441 req
->netlink_handler
,
442 remove_request_destroy_callback
,
445 _cleanup_(link_unrefp
) Link
*link
= link_ref(req
->link
);
447 log_link_warning_errno(link
, r
, "Failed to call netlink message: %m");
449 /* First free the request. */
450 remove_request_free(req
);
452 /* Then, make the link enter the failed state. */
454 link_enter_failed(link
);
457 /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
458 * request may not freed on shutting down. */
459 req
->netlink
= sd_netlink_unref(req
->netlink
);
460 ordered_set_remove(manager
->remove_request_queue
, req
);