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

systemd / systemd / 14766779411

30 Apr 2025 04:55PM UTC coverage: 72.225% (-0.06%) from 72.282%
14766779411

push

github

web-flow
wait-online: handle varlink connection errors while waiting for DNS (#37283)

Currently, if systemd-networkd-wait-online is started with --dns, and
systemd-resolved is not running, it will exit with an error right away.
Similarly, if systemd-resolved is restarted while waiting for DNS
configuration, systemd-networkd-wait-online will not attempt to
re-connect, and will potentially never see subsequent DNS
configurations.

Improve this by adding socket units for the systemd-resolved varlink
servers, and re-establish the connection in systemd-networkd-wait-online
when we receive `SD_VARLINK_ERROR_DISCONNECTED`.

8 of 16 new or added lines in 2 files covered. (50.0%)

5825 existing lines in 217 files now uncovered.

297168 of 411450 relevant lines covered (72.22%)

695892.62 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 "netdev.h"
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"
9

10
#define REPLY_CALLBACK_COUNT_THRESHOLD 128
11

12
static Request* request_detach_impl(Request *req) {
10,575✔
13
        assert(req);
10,575✔
14

15
        if (!req->manager)
10,575✔
16
                return NULL;
17

18
        ordered_set_remove(req->manager->request_queue, req);
5,151✔
19
        req->manager = NULL;
5,151✔
20
        return req;
5,151✔
21
}
22

23
void request_detach(Request *req) {
5,169✔
24
        request_unref(request_detach_impl(req));
5,169✔
25
}
5,169✔
26

27
static Request *request_free(Request *req) {
5,406✔
28
        if (!req)
5,406✔
29
                return NULL;
30

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);
5,406✔
34

35
        if (req->free_func)
5,406✔
36
                req->free_func(req->userdata);
3,674✔
37

38
        if (req->counter)
5,406✔
39
                (*req->counter)--;
147✔
40

41
        link_unref(req->link); /* link may be NULL, but link_unref() can handle it gracefully. */
5,406✔
42

43
        return mfree(req);
5,406✔
44
}
45

46
DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free);
134,688✔
47

48
static void request_destroy_callback(Request *req) {
4,234✔
49
        assert(req);
4,234✔
50

51
        request_detach(req);
4,234✔
52
        request_unref(req);
4,234✔
53
}
4,234✔
54

55
static void request_hash_func(const Request *req, struct siphash *state) {
44,596✔
56
        assert(req);
44,596✔
57
        assert(state);
44,596✔
58

59
        siphash24_compress_typesafe(req->type, state);
44,596✔
60

61
        if (!IN_SET(req->type,
44,596✔
62
                    REQUEST_TYPE_NEXTHOP,
63
                    REQUEST_TYPE_ROUTE,
64
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
65

66
                siphash24_compress_boolean(req->link, state);
23,686✔
67
                if (req->link)
23,686✔
68
                        siphash24_compress_typesafe(req->link->ifindex, state);
22,481✔
69
        }
70

71
        siphash24_compress_typesafe(req->hash_func, state);
44,596✔
72
        siphash24_compress_typesafe(req->compare_func, state);
44,596✔
73

74
        if (req->hash_func)
44,596✔
75
                req->hash_func(req->userdata, state);
39,401✔
76
}
44,596✔
77

78
static int request_compare_func(const struct Request *a, const struct Request *b) {
21,109✔
79
        int r;
21,109✔
80

81
        assert(a);
21,109✔
82
        assert(b);
21,109✔
83

84
        r = CMP(a->type, b->type);
21,109✔
85
        if (r != 0)
15,318✔
86
                return r;
7,743✔
87

88
        if (!IN_SET(a->type,
13,366✔
89
                    REQUEST_TYPE_NEXTHOP,
90
                    REQUEST_TYPE_ROUTE,
91
                    REQUEST_TYPE_ROUTING_POLICY_RULE)) {
92

93
                r = CMP(!!a->link, !!b->link);
8,322✔
94
                if (r != 0)
8,322✔
95
                        return r;
×
96

97
                if (a->link) {
8,322✔
98
                        r = CMP(a->link->ifindex, b->link->ifindex);
7,686✔
99
                        if (r != 0)
7,577✔
100
                                return r;
206✔
101
                }
102
        }
103

104
        r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func));
13,160✔
105
        if (r != 0)
13,160✔
106
                return r;
×
107

108
        r = CMP(PTR_TO_UINT64(a->compare_func), PTR_TO_UINT64(b->compare_func));
13,160✔
109
        if (r != 0)
13,160✔
110
                return r;
×
111

112
        if (a->compare_func)
13,160✔
113
                return a->compare_func(a->userdata, b->userdata);
11,722✔
114

115
        return 0;
116
}
117

118
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
4✔
119
                request_hash_ops,
120
                Request,
121
                request_hash_func,
122
                request_compare_func,
123
                request_detach);
124

125
static int request_new(
5,406✔
126
                Manager *manager,
127
                Link *link,
128
                RequestType type,
129
                void *userdata,
130
                mfree_func_t free_func,
131
                hash_func_t hash_func,
132
                compare_func_t compare_func,
133
                request_process_func_t process,
134
                unsigned *counter,
135
                request_netlink_handler_t netlink_handler,
136
                Request **ret) {
137

138
        _cleanup_(request_unrefp) Request *req = NULL;
5,406✔
139
        Request *existing;
5,406✔
140
        int r;
5,406✔
141

142
        assert(manager);
5,406✔
143
        assert(process);
5,406✔
144

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

154
        req = new(Request, 1);
5,406✔
155
        if (!req)
5,406✔
156
                return -ENOMEM;
157

158
        *req = (Request) {
10,812✔
159
                .n_ref = 1,
160
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
5,406✔
161
                .type = type,
162
                .userdata = userdata,
163
                .hash_func = hash_func,
164
                .compare_func = compare_func,
165
                .process = process,
166
                .netlink_handler = netlink_handler,
167
        };
168

169
        existing = ordered_set_get(manager->request_queue, req);
5,406✔
170
        if (existing) {
5,406✔
171
                if (ret)
255✔
172
                        *ret = existing;
×
173
                return 0;
255✔
174
        }
175

176
        r = ordered_set_ensure_put(&manager->request_queue, &request_hash_ops, req);
5,151✔
177
        if (r < 0)
5,151✔
178
                return r;
179

180
        req->manager = manager;
5,151✔
181
        req->free_func = free_func;
5,151✔
182
        req->counter = counter;
5,151✔
183
        if (req->counter)
5,151✔
184
                (*req->counter)++;
4,324✔
185

186
        /* If this is called in the ORDERED_SET_FOREACH() loop of manager_process_requests(), we need to
187
         * exit from the loop, due to the limitation of the iteration on OrderedSet. */
188
        manager->request_queued = true;
5,151✔
189

190
        if (ret)
5,151✔
191
                *ret = req;
330✔
192

193
        TAKE_PTR(req);
5,151✔
194
        return 1;
5,151✔
195
}
196

197
int netdev_queue_request(
436✔
198
                NetDev *netdev,
199
                request_process_func_t process,
200
                Request **ret) {
201

202
        int r;
436✔
203

204
        assert(netdev);
436✔
205
        assert(netdev->manager);
436✔
206

207
        r = request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT,
436✔
208
                        netdev, (mfree_func_t) netdev_unref,
209
                        trivial_hash_func, trivial_compare_func,
210
                        process, NULL, NULL, ret);
211
        if (r <= 0)
436✔
212
                return r;
213

214
        netdev_ref(netdev);
436✔
215
        return 1;
436✔
216
}
217

218
int link_queue_request_full(
4,969✔
219
                Link *link,
220
                RequestType type,
221
                void *userdata,
222
                mfree_func_t free_func,
223
                hash_func_t hash_func,
224
                compare_func_t compare_func,
225
                request_process_func_t process,
226
                unsigned *counter,
227
                request_netlink_handler_t netlink_handler,
228
                Request **ret) {
229

230
        assert(link);
4,969✔
231

232
        return request_new(link->manager, link, type,
4,969✔
233
                           userdata, free_func, hash_func, compare_func,
234
                           process, counter, netlink_handler, ret);
235
}
236

237
int manager_queue_request_full(
1✔
238
                Manager *manager,
239
                RequestType type,
240
                void *userdata,
241
                mfree_func_t free_func,
242
                hash_func_t hash_func,
243
                compare_func_t compare_func,
244
                request_process_func_t process,
245
                unsigned *counter,
246
                request_netlink_handler_t netlink_handler,
247
                Request **ret) {
248

249
        return request_new(manager, NULL, type,
1✔
250
                           userdata, free_func, hash_func, compare_func,
251
                           process, counter, netlink_handler, ret);
252
}
253

254
int link_requeue_request(Link *link, Request *req, void *userdata, Request **ret) {
48✔
255
        assert(link);
48✔
256
        assert(req);
48✔
257

258
        return link_queue_request_full(
48✔
259
                        link,
260
                        req->type,
261
                        userdata,
262
                        req->free_func,
263
                        req->hash_func,
264
                        req->compare_func,
265
                        req->process,
266
                        req->counter,
267
                        req->netlink_handler,
268
                        ret);
269
}
270

271
int manager_process_requests(Manager *manager) {
55,954✔
272
        Request *req;
55,954✔
273
        int r;
55,954✔
274

275
        assert(manager);
55,954✔
276

277
        /* Process only when no remove request is queued. */
278
        if (!ordered_set_isempty(manager->remove_request_queue))
55,954✔
279
                return 0;
55,954✔
280

281
        manager->request_queued = false;
55,439✔
282

283
        ORDERED_SET_FOREACH(req, manager->request_queue) {
319,533✔
284
                if (req->waiting_reply)
264,947✔
285
                        continue; /* Already processed, and waiting for netlink reply. */
203,760✔
286

287
                /* Typically, requests send netlink message asynchronously. If there are many requests
288
                 * queued, then this event may make reply callback queue in sd-netlink full. */
289
                if (netlink_get_reply_callback_count(manager->rtnl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
121,569✔
290
                    netlink_get_reply_callback_count(manager->genl) >= REPLY_CALLBACK_COUNT_THRESHOLD ||
120,764✔
291
                    fw_ctx_get_reply_callback_count(manager->fw_ctx) >= REPLY_CALLBACK_COUNT_THRESHOLD)
60,382✔
292
                        break;
293

294
                /* Avoid the request and link freed by req->process() and request_detach(). */
295
                _unused_ _cleanup_(request_unrefp) Request *req_unref = request_ref(req);
120,764✔
296
                _cleanup_(link_unrefp) Link *link = link_ref(req->link);
120,764✔
297

298
                assert(req->process);
60,382✔
299
                r = req->process(req, link, req->userdata);
60,382✔
300
                if (r < 0) {
60,382✔
301
                        request_detach(req);
×
302

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

316
                if (manager->request_queued)
60,382✔
317
                        break; /* New request is queued. Exit from the loop. */
318
        }
319

320
        return 0;
55,439✔
321
}
322

323
static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,234✔
324
        assert(req);
4,234✔
325

326
        if (req->counter) {
4,234✔
327
                assert(*req->counter > 0);
4,163✔
328
                (*req->counter)--;
4,163✔
329
                req->counter = NULL; /* To prevent double decrement on free. */
4,163✔
330
        }
331

332
        if (req->link && IN_SET(req->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4,234✔
333
                return 0;
334

335
        if (req->netlink_handler)
4,229✔
336
                return req->netlink_handler(nl, m, req, req->link, req->userdata);
4,229✔
337

338
        return 0;
339
}
340

341
int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req) {
4,234✔
342
        int r;
4,234✔
343

344
        assert(nl);
4,234✔
345
        assert(m);
4,234✔
346
        assert(req);
4,234✔
347

348
        r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req);
4,234✔
349
        if (r < 0)
4,234✔
350
                return r;
351

352
        request_ref(req);
4,234✔
353
        req->waiting_reply = true;
4,234✔
354
        return 0;
4,234✔
355
}
356

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

398
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType);
908✔
399

400
static RemoveRequest* remove_request_free(RemoveRequest *req) {
1,765✔
401
        if (!req)
1,765✔
402
                return NULL;
403

404
        if (req->manager)
1,765✔
405
                ordered_set_remove(req->manager->remove_request_queue, req);
1,765✔
406

407
        if (req->unref_func)
1,765✔
408
                req->unref_func(req->userdata);
1,765✔
409

410
        link_unref(req->link);
1,765✔
411
        sd_netlink_unref(req->netlink);
1,765✔
412
        sd_netlink_message_unref(req->message);
1,765✔
413

414
        return mfree(req);
1,765✔
415
}
416

417
DEFINE_TRIVIAL_CLEANUP_FUNC(RemoveRequest*, remove_request_free);
1,765✔
418
DEFINE_TRIVIAL_DESTRUCTOR(remove_request_destroy_callback, RemoveRequest, remove_request_free);
1,765✔
UNCOV
419
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
×
420
                remove_request_hash_ops,
421
                void,
422
                trivial_hash_func,
423
                trivial_compare_func,
424
                remove_request_free);
425

426
int remove_request_add(
1,765✔
427
                Manager *manager,
428
                Link *link,
429
                void *userdata,
430
                mfree_func_t unref_func,
431
                sd_netlink *netlink,
432
                sd_netlink_message *message,
433
                remove_request_netlink_handler_t netlink_handler) {
434

435
        _cleanup_(remove_request_freep) RemoveRequest *req = NULL;
1,765✔
436
        int r;
1,765✔
437

438
        assert(manager);
1,765✔
439
        assert(userdata);
1,765✔
440
        assert(netlink);
1,765✔
441
        assert(message);
1,765✔
442

443
        /* Unlike request_new(), remove requests will be also processed when the manager is in
444
         * MANAGER_TERMINATING or MANAGER_RESTARTING. When the manager is in MANAGER_STOPPED, we cannot
445
         * queue new remove requests anymore with the same reason explained in request_new(). */
446
        if (manager->state == MANAGER_STOPPED)
1,765✔
447
                return 0; /* ignored */
448

449
        req = new(RemoveRequest, 1);
1,765✔
450
        if (!req)
1,765✔
451
                return -ENOMEM;
452

453
        *req = (RemoveRequest) {
3,530✔
454
                .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */
1,765✔
455
                .userdata = userdata,
456
                .netlink = sd_netlink_ref(netlink),
1,765✔
457
                .message = sd_netlink_message_ref(message),
1,765✔
458
                .netlink_handler = netlink_handler,
459
        };
460

461
        r = ordered_set_ensure_put(&manager->remove_request_queue, &remove_request_hash_ops, req);
1,765✔
462
        if (r < 0)
1,765✔
463
                return r;
464
        assert(r > 0);
1,765✔
465

466
        req->manager = manager;
1,765✔
467
        req->unref_func = unref_func;
1,765✔
468

469
        TAKE_PTR(req);
1,765✔
470
        return 1; /* queued */
1,765✔
471
}
472

473
int manager_process_remove_requests(Manager *manager) {
56,392✔
474
        RemoveRequest *req;
56,392✔
475
        int r;
56,392✔
476

477
        assert(manager);
56,392✔
478

479
        while ((req = ordered_set_first(manager->remove_request_queue))) {
58,157✔
480

481
                /* Do not make the reply callback queue in sd-netlink full. */
482
                if (netlink_get_reply_callback_count(req->netlink) >= REPLY_CALLBACK_COUNT_THRESHOLD)
2,280✔
483
                        return 0;
484

485
                r = netlink_call_async(
1,765✔
486
                                req->netlink, NULL, req->message,
487
                                req->netlink_handler,
488
                                remove_request_destroy_callback,
489
                                req);
490
                if (r < 0) {
1,765✔
491
                        _cleanup_(link_unrefp) Link *link = link_ref(req->link);
×
492

UNCOV
493
                        log_link_warning_errno(link, r, "Failed to call netlink message: %m");
×
494

495
                        /* First free the request. */
UNCOV
496
                        remove_request_free(req);
×
497

498
                        /* Then, make the link enter the failed state. */
UNCOV
499
                        if (link)
×
UNCOV
500
                                link_enter_failed(link);
×
501

502
                } else {
503
                        /* On success, netlink needs to be unref()ed. Otherwise, the netlink and remove
504
                         * request may not freed on shutting down. */
505
                        req->netlink = sd_netlink_unref(req->netlink);
1,765✔
506
                        ordered_set_remove(manager->remove_request_queue, req);
1,765✔
507
                }
508
        }
509

510
        return 0;
511
}
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