• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19841858156

01 Dec 2025 05:14PM UTC coverage: 72.904% (-0.03%) from 72.93%
19841858156

push

github

bluca
man: remove notes about limitations of unpriv containers that don't apply anymore

310163 of 425438 relevant lines covered (72.9%)

1117599.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.5
/src/network/networkd-queue.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "sd-netlink.h"
4

5
#include "alloc-util.h"
6
#include "netdev.h"
7
#include "netlink-util.h"
8
#include "networkd-link.h"
9
#include "networkd-manager.h"
10
#include "networkd-queue.h"
11
#include "ordered-set.h"
12
#include "siphash24.h"
13
#include "string-table.h"
14

15
#define REPLY_CALLBACK_COUNT_THRESHOLD 128
16

17
static Request* request_detach_impl(Request *req) {
11,533✔
18
        assert(req);
11,533✔
19

20
        if (!req->manager)
11,533✔
21
                return NULL;
22

23
        ordered_set_remove(req->manager->request_queue, req);
5,613✔
24
        req->manager = NULL;
5,613✔
25
        return req;
5,613✔
26
}
27

28
void request_detach(Request *req) {
5,637✔
29
        request_unref(request_detach_impl(req));
5,637✔
30
}
5,637✔
31

32
static Request *request_free(Request *req) {
5,896✔
33
        if (!req)
5,896✔
34
                return NULL;
35

36
        /* To prevent from triggering assertions in the hash and compare functions, remove this request
37
         * from the set before freeing userdata below. */
38
        request_detach_impl(req);
5,896✔
39

40
        if (req->free_func)
5,896✔
41
                req->free_func(req->userdata);
3,960✔
42

43
        if (req->counter)
5,896✔
44
                (*req->counter)--;
156✔
45

46
        link_unref(req->link); /* link may be NULL, but link_unref() can handle it gracefully. */
5,896✔
47

48
        return mfree(req);
5,896✔
49
}
50

51
DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
152,434✔
52

53
static void request_destroy_callback(Request *req) {
4,597✔
54
        assert(req);
4,597✔
55

56
        request_detach(req);
4,597✔
57
        request_unref(req);
4,597✔
58
}
4,597✔
59

60
static void request_hash_func(const Request *req, struct siphash *state) {
48,449✔
61
        assert(req);
48,449✔
62
        assert(state);
48,449✔
63

64
        siphash24_compress_typesafe(req->type, state);
48,449✔
65

66
        if (!IN_SET(req->type,
48,449✔
67
                    REQUEST_TYPE_NEXTHOP,
68
                    REQUEST_TYPE_ROUTE,
69
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
70

71
                siphash24_compress_boolean(req->link, state);
25,666✔
72
                if (req->link)
25,666✔
73
                        siphash24_compress_typesafe(req->link->ifindex, state);
24,368✔
74
        }
75

76
        siphash24_compress_typesafe(req->hash_func, state);
48,449✔
77
        siphash24_compress_typesafe(req->compare_func, state);
48,449✔
78

79
        if (req->hash_func)
48,449✔
80
                req->hash_func(req->userdata, state);
42,572✔
81
}
48,449✔
82

83
static int request_compare_func(const struct Request *a, const struct Request *b) {
22,981✔
84
        int r;
22,981✔
85

86
        assert(a);
22,981✔
87
        assert(b);
22,981✔
88

89
        r = CMP(a->type, b->type);
22,981✔
90
        if (r != 0)
16,188✔
91
                return r;
8,855✔
92

93
        if (!IN_SET(a->type,
14,126✔
94
                    REQUEST_TYPE_NEXTHOP,
95
                    REQUEST_TYPE_ROUTE,
96
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
97

98
                r = CMP(!!a->link, !!b->link);
9,069✔
99
                if (r != 0)
9,069✔
100
                        return r;
×
101

102
                if (a->link) {
9,069✔
103
                        r = CMP(a->link->ifindex, b->link->ifindex);
8,405✔
104
                        if (r != 0)
8,294✔
105
                                return r;
263✔
106
                }
107
        }
108

109
        r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
13,863✔
110
        if (r != 0)
13,863✔
111
                return r;
×
112

113
        r = CMP(PTR_TO_UINT64(a->compare_func), PTR_TO_UINT64(b->compare_func));
13,863✔
114
        if (r != 0)
13,863✔
115
                return r;
×
116

117
        if (a->compare_func)
13,863✔
118
                return a->compare_func(a->userdata, b->userdata);
12,248✔
119

120
        return 0;
121
}
122

123
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
4✔
124
                request_hash_ops,
125
                Request,
126
                request_hash_func,
127
                request_compare_func,
128
                request_detach);
129

130
static int request_new(
5,896✔
131
                Manager *manager,
132
                Link *link,
133
                RequestType type,
134
                void *userdata,
135
                mfree_func_t free_func,
136
                hash_func_t hash_func,
137
                compare_func_t compare_func,
138
                request_process_func_t process,
139
                unsigned *counter,
140
                request_netlink_handler_t netlink_handler,
141
                Request **ret) {
142

143
        _cleanup_(request_unrefp) Request *req = NULL;
5,896✔
144
        Request *existing;
5,896✔
145
        int r;
5,896✔
146

147
        assert(manager);
5,896✔
148
        assert(process);
5,896✔
149

150
        /* Note, requests will be processed only when the manager is in MANAGER_RUNNING. If a new operation
151
         * is requested when the manager is in MANAGER_TERMINATING or MANAGER_RESTARTING, the request will be
152
         * successfully queued but will never be processed. Then, here why we refuse new requests when the
153
         * manager is in MANAGER_STOPPED? This is because we cannot call link_ref() in that case, as this may
154
         * be called during link_free(), that means the reference counter of the link is already 0 and
155
         * calling link_ref() below triggers assertion. */
156
        if (manager->state == MANAGER_STOPPED)
5,896✔
157
                return -EBUSY;
158

159
        req = new(Request, 1);
5,896✔
160
        if (!req)
5,896✔
161
                return -ENOMEM;
162

163
        *req = (Request) {
11,792✔
164
                .n_ref = 1,
165
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
5,896✔
166
                .type = type,
167
                .userdata = userdata,
168
                .hash_func = hash_func,
169
                .compare_func = compare_func,
170
                .process = process,
171
                .netlink_handler = netlink_handler,
172
        };
173

174
        existing = ordered_set_get(manager->request_queue, req);
5,896✔
175
        if (existing) {
5,896✔
176
                if (ret)
283✔
177
                        *ret = existing;
×
178
                return 0;
283✔
179
        }
180

181
        r = ordered_set_ensure_put(&manager->request_queue, &request_hash_ops, req);
5,613✔
182
        if (r < 0)
5,613✔
183
                return r;
184

185
        req->manager = manager;
5,613✔
186
        req->free_func = free_func;
5,613✔
187
        req->counter = counter;
5,613✔
188
        if (req->counter)
5,613✔
189
                (*req->counter)++;
4,695✔
190

191
        /* If this is called in the ORDERED_SET_FOREACH() loop of manager_process_requests(), we need to
192
         * exit from the loop, due to the limitation of the iteration on OrderedSet. */
193
        manager->request_queued = true;
5,613✔
194

195
        if (ret)
5,613✔
196
                *ret = req;
360✔
197

198
        TAKE_PTR(req);
5,613✔
199
        return 1;
5,613✔
200
}
201

202
int netdev_queue_request(
471✔
203
                NetDev *netdev,
204
                request_process_func_t process,
205
                Request **ret) {
206

207
        int r;
471✔
208

209
        assert(netdev);
471✔
210
        assert(netdev->manager);
471✔
211

212
        r = request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
471✔
213
                        netdev, (mfree_func_t) netdev_unref,
214
                        trivial_hash_func, trivial_compare_func,
215
                        process, NULL, NULL, ret);
216
        if (r <= 0)
471✔
217
                return r;
218

219
        netdev_ref(netdev);
471✔
220
        return 1;
471✔
221
}
222

223
int link_queue_request_full(
5,424✔
224
                Link *link,
225
                RequestType type,
226
                void *userdata,
227
                mfree_func_t free_func,
228
                hash_func_t hash_func,
229
                compare_func_t compare_func,
230
                request_process_func_t process,
231
                unsigned *counter,
232
                request_netlink_handler_t netlink_handler,
233
                Request **ret) {
234

235
        assert(link);
5,424✔
236

237
        return request_new(link->manager, link, type,
5,424✔
238
                           userdata, free_func, hash_func, compare_func,
239
                           process, counter, netlink_handler, ret);
240
}
241

242
int manager_queue_request_full(
1✔
243
                Manager *manager,
244
                RequestType type,
245
                void *userdata,
246
                mfree_func_t free_func,
247
                hash_func_t hash_func,
248
                compare_func_t compare_func,
249
                request_process_func_t process,
250
                unsigned *counter,
251
                request_netlink_handler_t netlink_handler,
252
                Request **ret) {
253

254
        return request_new(manager, NULL, type,
1✔
255
                           userdata, free_func, hash_func, compare_func,
256
                           process, counter, netlink_handler, ret);
257
}
258

259
int link_requeue_request(Link *link, Request *req, void *userdata, Request **ret) {
49✔
260
        assert(link);
49✔
261
        assert(req);
49✔
262

263
        return link_queue_request_full(
49✔
264
                        link,
265
                        req->type,
266
                        userdata,
267
                        req->free_func,
268
                        req->hash_func,
269
                        req->compare_func,
270
                        req->process,
271
                        req->counter,
272
                        req->netlink_handler,
273
                        ret);
274
}
275

276
int manager_process_requests(Manager *manager) {
85,320✔
277
        Request *req;
85,320✔
278
        int r;
85,320✔
279

280
        assert(manager);
85,320✔
281

282
        /* Process only when no remove request is queued. */
283
        if (!ordered_set_isempty(manager->remove_request_queue))
85,320✔
284
                return 0;
85,320✔
285

286
        manager->request_queued = false;
84,503✔
287

288
        ORDERED_SET_FOREACH(req, manager->request_queue) {
361,672✔
289
                if (req->waiting_reply)
278,021✔
290
                        continue; /* Already processed, and waiting for netlink reply. */
208,574✔
291

292
                /* Typically, requests send netlink message asynchronously. If there are many requests
293
                 * queued, then this event may make reply callback queue in sd-netlink full. */
294
                if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
138,091✔
295
                    netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
137,288✔
296
                    netlink_get_reply_callback_count(manager->nfnl) >= REPLY_CALLBACK_COUNT_THRESHOLD)
68,644✔
297
                        break;
298

299
                /* Avoid the request and link freed by req->process() and request_detach(). */
300
                _unused_ _cleanup_(request_unrefp) Request *req_unref = request_ref(req);
137,288✔
301
                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
137,288✔
302

303
                assert(req->process);
68,644✔
304
                r = req->process(req, link, req->userdata);
68,644✔
305
                if (r < 0) {
68,644✔
306
                        request_detach(req);
×
307

308
                        if (link) {
×
309
                                link_enter_failed(link);
×
310
                                /* link_enter_failed() may detach multiple requests from the queue.
311
                                 * Hence, we need to exit from the loop. */
312
                                break;
313
                        }
314
                }
315
                if (r > 0 && !req->waiting_reply)
68,644✔
316
                        /* If the request sends netlink message, e.g. for Address or so, the Request object is
317
                         * referenced by the netlink slot, and will be detached later by its destroy callback.
318
                         * Otherwise, e.g. for DHCP client or so, detach the request from queue now. */
319
                        request_detach(req);
907✔
320

321
                if (manager->request_queued)
68,644✔
322
                        break; /* New request is queued. Exit from the loop. */
323
        }
324

325
        return 0;
84,503✔
326
}
327

328
static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,597✔
329
        assert(req);
4,597✔
330

331
        if (req->counter) {
4,597✔
332
                assert(*req->counter > 0);
4,525✔
333
                (*req->counter)--;
4,525✔
334
                req->counter = NULL; /* To prevent double decrement on free. */
4,525✔
335
        }
336

337
        if (req->link && IN_SET(req->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4,597✔
338
                return 0;
339

340
        if (req->netlink_handler)
4,590✔
341
                return req->netlink_handler(nl, m, req, req->link, req->userdata);
4,590✔
342

343
        return 0;
344
}
345

346
int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,597✔
347
        int r;
4,597✔
348

349
        assert(nl);
4,597✔
350
        assert(m);
4,597✔
351
        assert(req);
4,597✔
352

353
        r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req);
4,597✔
354
        if (r < 0)
4,597✔
355
                return r;
356

357
        request_ref(req);
4,597✔
358
        req->waiting_reply = true;
4,597✔
359
        return 0;
4,597✔
360
}
361

362
static const char *const request_type_table[_REQUEST_TYPE_MAX] = {
363
        [REQUEST_TYPE_ACTIVATE_LINK]                    = "activate link",
364
        [REQUEST_TYPE_ADDRESS]                          = "address",
365
        [REQUEST_TYPE_ADDRESS_LABEL]                    = "address label",
366
        [REQUEST_TYPE_BRIDGE_FDB]                       = "bridge FDB",
367
        [REQUEST_TYPE_BRIDGE_MDB]                       = "bridge MDB",
368
        [REQUEST_TYPE_DHCP_SERVER]                      = "DHCP server",
369
        [REQUEST_TYPE_DHCP4_CLIENT]                     = "DHCPv4 client",
370
        [REQUEST_TYPE_DHCP6_CLIENT]                     = "DHCPv6 client",
371
        [REQUEST_TYPE_IPV6_PROXY_NDP]                   = "IPv6 proxy NDP",
372
        [REQUEST_TYPE_NDISC]                            = "NDisc",
373
        [REQUEST_TYPE_NEIGHBOR]                         = "neighbor",
374
        [REQUEST_TYPE_NETDEV_INDEPENDENT]               = "independent netdev",
375
        [REQUEST_TYPE_NETDEV_STACKED]                   = "stacked netdev",
376
        [REQUEST_TYPE_NEXTHOP]                          = "nexthop",
377
        [REQUEST_TYPE_RADV]                             = "RADV",
378
        [REQUEST_TYPE_ROUTE]                            = "route",
379
        [REQUEST_TYPE_ROUTING_POLICY_RULE]              = "routing policy rule",
380
        [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode",
381
        [REQUEST_TYPE_SET_LINK_BOND]                    = "bond configurations",
382
        [REQUEST_TYPE_SET_LINK_BRIDGE]                  = "bridge configurations",
383
        [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN]             = "bridge VLAN configurations (step 1)",
384
        [REQUEST_TYPE_DEL_LINK_BRIDGE_VLAN]             = "bridge VLAN configurations (step 2)",
385
        [REQUEST_TYPE_SET_LINK_CAN]                     = "CAN interface configurations",
386
        [REQUEST_TYPE_SET_LINK_FLAGS]                   = "link flags",
387
        [REQUEST_TYPE_SET_LINK_GROUP]                   = "interface group",
388
        [REQUEST_TYPE_SET_LINK_IPOIB]                   = "IPoIB configurations",
389
        [REQUEST_TYPE_SET_LINK_MAC]                     = "MAC address",
390
        [REQUEST_TYPE_SET_LINK_MASTER]                  = "master interface",
391
        [REQUEST_TYPE_SET_LINK_MTU]                     = "MTU",
392
        [REQUEST_TYPE_SRIOV_VF_MAC]                     = "SR-IOV VF MAC address",
393
        [REQUEST_TYPE_SRIOV_VF_SPOOFCHK]                = "SR-IOV VF spoof check",
394
        [REQUEST_TYPE_SRIOV_VF_RSS_QUERY_EN]            = "SR-IOV VF RSS query",
395
        [REQUEST_TYPE_SRIOV_VF_TRUST]                   = "SR-IOV VF trust",
396
        [REQUEST_TYPE_SRIOV_VF_LINK_STATE]              = "SR-IOV VF link state",
397
        [REQUEST_TYPE_SRIOV_VF_VLAN_LIST]               = "SR-IOV VF vlan list",
398
        [REQUEST_TYPE_TC_QDISC]                         = "QDisc",
399
        [REQUEST_TYPE_TC_CLASS]                         = "TClass",
400
        [REQUEST_TYPE_UP_DOWN]                          = "bring link up or down",
401
};
402

403
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
990✔
404

405
static RemoveRequest* remove_request_free(RemoveRequest *req) {
1,858✔
406
        if (!req)
1,858✔
407
                return NULL;
408

409
        if (req->manager)
1,858✔
410
                ordered_set_remove(req->manager->remove_request_queue, req);
1,858✔
411

412
        if (req->unref_func)
1,858✔
413
                req->unref_func(req->userdata);
1,858✔
414

415
        link_unref(req->link);
1,858✔
416
        sd_netlink_unref(req->netlink);
1,858✔
417
        sd_netlink_message_unref(req->message);
1,858✔
418

419
        return mfree(req);
1,858✔
420
}
421

422
DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
1,858✔
423
DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
1,858✔
424
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
425
                remove_request_hash_ops,
426
                void,
427
                trivial_hash_func,
428
                trivial_compare_func,
429
                remove_request_free);
430

431
int remove_request_add(
1,858✔
432
                Manager *manager,
433
                Link *link,
434
                void *userdata,
435
                mfree_func_t unref_func,
436
                sd_netlink *netlink,
437
                sd_netlink_message *message,
438
                remove_request_netlink_handler_t netlink_handler) {
439

440
        _cleanup_(remove_request_freep) RemoveRequest *req = NULL;
1,858✔
441
        int r;
1,858✔
442

443
        assert(manager);
1,858✔
444
        assert(userdata);
1,858✔
445
        assert(netlink);
1,858✔
446
        assert(message);
1,858✔
447

448
        /* Unlike request_new(), remove requests will be also processed when the manager is in
449
         * MANAGER_TERMINATING or MANAGER_RESTARTING. When the manager is in MANAGER_STOPPED, we cannot
450
         * queue new remove requests anymore with the same reason explained in request_new(). */
451
        if (manager->state == MANAGER_STOPPED)
1,858✔
452
                return 0; /* ignored */
453

454
        req = new(RemoveRequest, 1);
1,858✔
455
        if (!req)
1,858✔
456
                return -ENOMEM;
457

458
        *req = (RemoveRequest) {
3,716✔
459
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
1,858✔
460
                .userdata = userdata,
461
                .netlink = sd_netlink_ref(netlink),
1,858✔
462
                .message = sd_netlink_message_ref(message),
1,858✔
463
                .netlink_handler = netlink_handler,
464
        };
465

466
        r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
1,858✔
467
        if (r < 0)
1,858✔
468
                return r;
469
        assert(r > 0);
1,858✔
470

471
        req->manager = manager;
1,858✔
472
        req->unref_func = unref_func;
1,858✔
473

474
        TAKE_PTR(req);
1,858✔
475
        return 1; /* queued */
1,858✔
476
}
477

478
int manager_process_remove_requests(Manager *manager) {
85,784✔
479
        RemoveRequest *req;
85,784✔
480
        int r;
85,784✔
481

482
        assert(manager);
85,784✔
483

484
        while ((req = ordered_set_first(manager->remove_request_queue))) {
87,642✔
485

486
                /* Do not make the reply callback queue in sd-netlink full. */
487
                if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
2,675✔
488
                        return 0;
489

490
                r = netlink_call_async(
1,858✔
491
                                req->netlink, NULL, req->message,
492
                                req->netlink_handler,
493
                                remove_request_destroy_callback,
494
                                req);
495
                if (r < 0) {
1,858✔
496
                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
×
497

498
                        log_link_warning_errno(link, r, "Failed to call netlink message: %m");
×
499

500
                        /* First free the request. */
501
                        remove_request_free(req);
×
502

503
                        /* Then, make the link enter the failed state. */
504
                        if (link)
×
505
                                link_enter_failed(link);
×
506

507
                } else {
508
                        /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
509
                         * request may not freed on shutting down. */
510
                        req->netlink = sd_netlink_unref(req->netlink);
1,858✔
511
                        ordered_set_remove(manager->remove_request_queue, req);
1,858✔
512
                }
513
        }
514

515
        return 0;
516
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc