Coverage Report

Created: 2026-07-30 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/libsystemd/sd-netlink/netlink-util.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include "sd-netlink.h"
4
5
#include "ether-addr-util.h"
6
#include "fd-util.h"
7
#include "hashmap.h"
8
#include "iovec-util.h"
9
#include "log.h"
10
#include "memory-util.h"
11
#include "netlink-internal.h"
12
#include "netlink-util.h"
13
#include "parse-util.h"
14
#include "process-util.h"
15
#include "socket-util.h"
16
#include "string-util.h"
17
#include "strv.h"
18
19
static int parse_newlink_message(
20
                  sd_netlink_message *message,
21
                  char **ret_name,
22
5.99k
                  char ***ret_altnames) {
23
24
5.99k
        _cleanup_strv_free_ char **altnames = NULL;
25
5.99k
        int r, ifindex;
26
27
5.99k
        assert(message);
28
29
5.99k
        uint16_t type;
30
5.99k
        r = sd_netlink_message_get_type(message, &type);
31
5.99k
        if (r < 0)
32
0
                return r;
33
5.99k
        if (type != RTM_NEWLINK)
34
0
                return -EPROTO;
35
36
5.99k
        r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
37
5.99k
        if (r < 0)
38
0
                return r;
39
5.99k
        if (ifindex <= 0)
40
0
                return -EPROTO;
41
42
5.99k
        if (ret_altnames) {
43
0
                r = sd_netlink_message_read_strv(message, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
44
0
                if (r < 0 && r != -ENODATA)
45
0
                        return r;
46
0
        }
47
48
5.99k
        if (ret_name) {
49
32
                r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, ret_name);
50
32
                if (r < 0)
51
0
                        return r;
52
32
        }
53
54
5.99k
        if (ret_altnames)
55
0
                *ret_altnames = TAKE_PTR(altnames);
56
57
5.99k
        return ifindex;
58
5.99k
}
59
60
int rtnl_resolve_ifname_full(
61
                  sd_netlink **rtnl,
62
                  ResolveInterfaceNameFlag flags,
63
                  const char *name,
64
                  char **ret_name,
65
23.6k
                  char ***ret_altnames) {
66
67
23.6k
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
68
23.6k
        int r;
69
70
23.6k
        assert(name);
71
23.6k
        assert(flags > 0);
72
73
        /* This is similar to if_nametoindex(), but also resolves alternative names and decimal formatted
74
         * ifindex too. Returns ifindex, and optionally provides the main interface name and alternative
75
         * names. */
76
77
23.6k
        if (!rtnl)
78
23.6k
                rtnl = &our_rtnl;
79
23.6k
        if (!*rtnl) {
80
23.6k
                r = sd_netlink_open(rtnl);
81
23.6k
                if (r < 0)
82
0
                        return r;
83
23.6k
        }
84
85
        /* First, use IFLA_IFNAME */
86
23.6k
        if (FLAGS_SET(flags, RESOLVE_IFNAME_MAIN) && ifname_valid(name)) {
87
17.6k
                _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
88
89
17.6k
                r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
90
17.6k
                if (r < 0)
91
0
                        return r;
92
93
17.6k
                r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
94
17.6k
                if (r < 0)
95
0
                        return r;
96
97
17.6k
                r = sd_netlink_call(*rtnl, message, 0, &reply);
98
17.6k
                if (r >= 0)
99
1.58k
                        return parse_newlink_message(reply, ret_name, ret_altnames);
100
16.1k
                if (r != -ENODEV)
101
0
                        return r;
102
16.1k
        }
103
104
        /* Next, try IFLA_ALT_IFNAME */
105
22.0k
        if (FLAGS_SET(flags, RESOLVE_IFNAME_ALTERNATIVE) &&
106
22.0k
            ifname_valid_full(name, IFNAME_VALID_ALTERNATIVE)) {
107
16.8k
                _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
108
109
16.8k
                r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
110
16.8k
                if (r < 0)
111
0
                        return r;
112
113
16.8k
                r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
114
16.8k
                if (r < 0)
115
0
                        return r;
116
117
16.8k
                r = sd_netlink_call(*rtnl, message, 0, &reply);
118
16.8k
                if (r >= 0)
119
0
                        return parse_newlink_message(reply, ret_name, ret_altnames);
120
16.8k
                if (r != -ENODEV)
121
0
                        return r;
122
16.8k
        }
123
124
        /* Finally, assume the string is a decimal formatted ifindex. */
125
22.0k
        if (FLAGS_SET(flags, RESOLVE_IFNAME_NUMERIC)) {
126
22.0k
                int ifindex;
127
128
22.0k
                ifindex = parse_ifindex(name);
129
22.0k
                if (ifindex <= 0)
130
16.8k
                        return -ENODEV;
131
132
5.27k
                return rtnl_get_ifname_full(rtnl, ifindex, ret_name, ret_altnames);
133
22.0k
        }
134
135
0
        return -ENODEV;
136
22.0k
}
137
138
0
int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
139
0
        int r;
140
141
0
        assert(name);
142
143
0
        r = rtnl_resolve_interface(rtnl, name);
144
0
        if (r < 0)
145
0
                return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
146
0
        return r;
147
0
}
148
149
0
static int set_link_name(sd_netlink *rtnl, int ifindex, const char *name) {
150
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
151
0
        int r;
152
153
0
        assert(rtnl);
154
0
        assert(ifindex > 0);
155
0
        assert(name);
156
157
        /* Assign the requested name. */
158
159
0
        r = sd_rtnl_message_new_link(rtnl, &message, RTM_SETLINK, ifindex);
160
0
        if (r < 0)
161
0
                return r;
162
163
0
        r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
164
0
        if (r < 0)
165
0
                return r;
166
167
0
        return sd_netlink_call(rtnl, message, 0, NULL);
168
0
}
169
170
0
int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name) {
171
0
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
172
0
        int r, ifindex;
173
174
0
        assert(orig_name);
175
0
        assert(new_name);
176
177
        /* This does not check alternative names. Callers must check the requested name is not used as an
178
         * alternative name. */
179
180
0
        if (streq(orig_name, new_name))
181
0
                return 0;
182
183
0
        if (!ifname_valid(new_name))
184
0
                return -EINVAL;
185
186
0
        if (!rtnl)
187
0
                rtnl = &our_rtnl;
188
0
        if (!*rtnl) {
189
0
                r = sd_netlink_open(rtnl);
190
0
                if (r < 0)
191
0
                        return r;
192
0
        }
193
194
0
        ifindex = rtnl_resolve_ifname(rtnl, orig_name);
195
0
        if (ifindex < 0)
196
0
                return ifindex;
197
198
0
        return set_link_name(*rtnl, ifindex, new_name);
199
0
}
200
201
0
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const *alternative_names) {
202
0
        _cleanup_strv_free_ char **original_altnames = NULL, **new_altnames = NULL;
203
0
        bool altname_deleted = false;
204
0
        int r;
205
206
0
        assert(ifindex > 0);
207
208
0
        if (isempty(name) && strv_isempty(alternative_names))
209
0
                return 0;
210
211
0
        if (name && !ifname_valid(name))
212
0
                return -EINVAL;
213
214
0
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
215
0
        if (!rtnl)
216
0
                rtnl = &our_rtnl;
217
218
        /* If the requested name is already assigned as an alternative name, then first drop it. */
219
0
        r = rtnl_get_link_alternative_names(rtnl, ifindex, &original_altnames);
220
0
        if (r < 0)
221
0
                log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m",
222
0
                                ifindex);
223
224
0
        if (name) {
225
0
                if (strv_contains(original_altnames, name)) {
226
0
                        r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
227
0
                        if (r < 0)
228
0
                                return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m",
229
0
                                                       name, ifindex);
230
231
0
                        altname_deleted = true;
232
0
                }
233
234
0
                r = set_link_name(*rtnl, ifindex, name);
235
0
                if (r < 0)
236
0
                        goto fail;
237
0
        }
238
239
        /* Filter out already assigned names from requested alternative names. Also, dedup the request. */
240
0
        STRV_FOREACH(a, alternative_names) {
241
0
                if (streq_ptr(name, *a))
242
0
                        continue;
243
244
0
                if (strv_contains(original_altnames, *a))
245
0
                        continue;
246
247
0
                if (strv_contains(new_altnames, *a))
248
0
                        continue;
249
250
0
                if (!ifname_valid_full(*a, IFNAME_VALID_ALTERNATIVE))
251
0
                        continue;
252
253
0
                r = strv_extend(&new_altnames, *a);
254
0
                if (r < 0)
255
0
                        return r;
256
0
        }
257
258
0
        strv_sort(new_altnames);
259
260
        /* Finally, assign alternative names. */
261
0
        r = rtnl_set_link_alternative_names(rtnl, ifindex, new_altnames);
262
0
        if (r == -EEXIST) /* Already assigned to another interface? */
263
0
                STRV_FOREACH(a, new_altnames) {
264
0
                        r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(*a));
265
0
                        if (r < 0)
266
0
                                log_debug_errno(r, "Failed to assign '%s' as an alternative name on network interface %i, ignoring: %m",
267
0
                                                *a, ifindex);
268
0
                }
269
0
        else if (r < 0)
270
0
                log_debug_errno(r, "Failed to assign alternative names on network interface %i, ignoring: %m", ifindex);
271
272
0
        return 0;
273
274
0
fail:
275
0
        if (altname_deleted) {
276
0
                int q = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
277
0
                if (q < 0)
278
0
                        log_debug_errno(q, "Failed to restore '%s' as an alternative name on network interface %i, ignoring: %m",
279
0
                                        name, ifindex);
280
0
        }
281
282
0
        return r;
283
0
}
284
285
int rtnl_set_link_properties(
286
                sd_netlink **rtnl,
287
                int ifindex,
288
                const char *alias,
289
                const struct hw_addr_data *hw_addr,
290
                uint32_t txqueues,
291
                uint32_t rxqueues,
292
                uint32_t txqueuelen,
293
                uint32_t mtu,
294
                uint32_t gso_max_size,
295
0
                size_t gso_max_segments) {
296
297
0
        int r;
298
299
0
        assert(ifindex > 0);
300
301
0
        if (!alias &&
302
0
            (!hw_addr || hw_addr->length == 0) &&
303
0
            txqueues == 0 &&
304
0
            rxqueues == 0 &&
305
0
            txqueuelen == UINT32_MAX &&
306
0
            mtu == 0 &&
307
0
            gso_max_size == 0 &&
308
0
            gso_max_segments == 0)
309
0
                return 0;
310
311
0
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
312
0
        if (!rtnl)
313
0
                rtnl = &our_rtnl;
314
0
        if (!*rtnl) {
315
0
                r = sd_netlink_open(rtnl);
316
0
                if (r < 0)
317
0
                        return r;
318
0
        }
319
320
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
321
0
        r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
322
0
        if (r < 0)
323
0
                return r;
324
325
0
        if (alias) {
326
0
                r = sd_netlink_message_append_string(message, IFLA_IFALIAS, alias);
327
0
                if (r < 0)
328
0
                        return r;
329
0
        }
330
331
0
        if (hw_addr && hw_addr->length > 0) {
332
0
                r = netlink_message_append_hw_addr(message, IFLA_ADDRESS, hw_addr);
333
0
                if (r < 0)
334
0
                        return r;
335
0
        }
336
337
0
        if (txqueues > 0) {
338
0
                r = sd_netlink_message_append_u32(message, IFLA_NUM_TX_QUEUES, txqueues);
339
0
                if (r < 0)
340
0
                        return r;
341
0
        }
342
343
0
        if (rxqueues > 0) {
344
0
                r = sd_netlink_message_append_u32(message, IFLA_NUM_RX_QUEUES, rxqueues);
345
0
                if (r < 0)
346
0
                        return r;
347
0
        }
348
349
0
        if (txqueuelen < UINT32_MAX) {
350
0
                r = sd_netlink_message_append_u32(message, IFLA_TXQLEN, txqueuelen);
351
0
                if (r < 0)
352
0
                        return r;
353
0
        }
354
355
0
        if (mtu != 0) {
356
0
                r = sd_netlink_message_append_u32(message, IFLA_MTU, mtu);
357
0
                if (r < 0)
358
0
                        return r;
359
0
        }
360
361
0
        if (gso_max_size > 0) {
362
0
                r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SIZE, gso_max_size);
363
0
                if (r < 0)
364
0
                        return r;
365
0
        }
366
367
0
        if (gso_max_segments > 0) {
368
0
                r = sd_netlink_message_append_u32(message, IFLA_GSO_MAX_SEGS, gso_max_segments);
369
0
                if (r < 0)
370
0
                        return r;
371
0
        }
372
373
0
        r = sd_netlink_call(*rtnl, message, 0, NULL);
374
0
        if (r < 0)
375
0
                return r;
376
377
0
        return 0;
378
0
}
379
380
static int rtnl_update_link_alternative_names(
381
                sd_netlink **rtnl,
382
                uint16_t nlmsg_type,
383
                int ifindex,
384
0
                char* const *alternative_names) {
385
386
0
        int r;
387
388
0
        assert(ifindex > 0);
389
0
        assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP));
390
391
0
        if (strv_isempty(alternative_names))
392
0
                return 0;
393
394
0
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
395
0
        if (!rtnl)
396
0
                rtnl = &our_rtnl;
397
0
        if (!*rtnl) {
398
0
                r = sd_netlink_open(rtnl);
399
0
                if (r < 0)
400
0
                        return r;
401
0
        }
402
403
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
404
0
        r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex);
405
0
        if (r < 0)
406
0
                return r;
407
408
0
        r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
409
0
        if (r < 0)
410
0
                return r;
411
412
0
        r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, (const char**) alternative_names);
413
0
        if (r < 0)
414
0
                return r;
415
416
0
        r = sd_netlink_message_close_container(message);
417
0
        if (r < 0)
418
0
                return r;
419
420
0
        r = sd_netlink_call(*rtnl, message, 0, NULL);
421
0
        if (r < 0)
422
0
                return r;
423
424
0
        return 0;
425
0
}
426
427
0
int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
428
0
        return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names);
429
0
}
430
431
0
int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
432
0
        return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names);
433
0
}
434
435
int rtnl_set_link_alternative_names_by_ifname(
436
                sd_netlink **rtnl,
437
                const char *ifname,
438
0
                char* const *alternative_names) {
439
440
0
        int r;
441
442
0
        assert(rtnl);
443
0
        assert(ifname);
444
445
0
        if (strv_isempty(alternative_names))
446
0
                return 0;
447
448
0
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
449
0
        if (!rtnl)
450
0
                rtnl = &our_rtnl;
451
0
        if (!*rtnl) {
452
0
                r = sd_netlink_open(rtnl);
453
0
                if (r < 0)
454
0
                        return r;
455
0
        }
456
457
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
458
0
        r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, 0);
459
0
        if (r < 0)
460
0
                return r;
461
462
0
        r = sd_netlink_message_append_string(message, IFLA_IFNAME, ifname);
463
0
        if (r < 0)
464
0
                return r;
465
466
0
        r = sd_netlink_message_open_container(message, IFLA_PROP_LIST);
467
0
        if (r < 0)
468
0
                return r;
469
470
0
        r = sd_netlink_message_append_strv(message, IFLA_ALT_IFNAME, (const char**) alternative_names);
471
0
        if (r < 0)
472
0
                return r;
473
474
0
        r = sd_netlink_message_close_container(message);
475
0
        if (r < 0)
476
0
                return r;
477
478
0
        r = sd_netlink_call(*rtnl, message, 0, NULL);
479
0
        if (r < 0)
480
0
                return r;
481
482
0
        return 0;
483
0
}
484
485
int rtnl_get_link_info_full(
486
                sd_netlink **rtnl,
487
                int ifindex,
488
                char **ret_name,
489
                char ***ret_altnames,
490
                unsigned short *ret_iftype,
491
                unsigned *ret_flags,
492
                char **ret_kind,
493
                struct hw_addr_data *ret_hw_addr,
494
5.35k
                struct hw_addr_data *ret_permanent_hw_addr) {
495
496
5.35k
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
497
5.35k
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
498
5.35k
        struct hw_addr_data addr = HW_ADDR_NULL, perm_addr = HW_ADDR_NULL;
499
5.35k
        _cleanup_free_ char *name = NULL, *kind = NULL;
500
5.35k
        _cleanup_strv_free_ char **altnames = NULL;
501
5.35k
        unsigned short iftype;
502
5.35k
        unsigned flags;
503
5.35k
        int r;
504
505
5.35k
        assert(ifindex > 0);
506
507
5.35k
        if (!rtnl)
508
87
                rtnl = &our_rtnl;
509
5.35k
        if (!*rtnl) {
510
87
                r = sd_netlink_open(rtnl);
511
87
                if (r < 0)
512
0
                        return r;
513
87
        }
514
515
5.35k
        r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
516
5.35k
        if (r < 0)
517
0
                return r;
518
519
5.35k
        r = sd_netlink_call(*rtnl, message, 0, &reply);
520
5.35k
        if (r == -EINVAL)
521
0
                return -ENODEV; /* The device does not exist */
522
5.35k
        if (r < 0)
523
943
                return r;
524
525
4.41k
        r = parse_newlink_message(reply, ret_name ? &name : NULL, ret_altnames ? &altnames : NULL);
526
4.41k
        if (r < 0)
527
0
                return r;
528
4.41k
        if (r != ifindex)
529
0
                return -EIO;
530
531
4.41k
        if (ret_iftype) {
532
0
                r = sd_rtnl_message_link_get_type(reply, &iftype);
533
0
                if (r < 0)
534
0
                        return r;
535
0
        }
536
537
4.41k
        if (ret_flags) {
538
0
                r = sd_rtnl_message_link_get_flags(reply, &flags);
539
0
                if (r < 0)
540
0
                        return r;
541
0
        }
542
543
4.41k
        if (ret_kind) {
544
0
                r = sd_netlink_message_enter_container(reply, IFLA_LINKINFO);
545
0
                if (r >= 0) {
546
0
                        r = sd_netlink_message_read_string_strdup(reply, IFLA_INFO_KIND, &kind);
547
0
                        if (r < 0 && r != -ENODATA)
548
0
                                return r;
549
550
0
                        r = sd_netlink_message_exit_container(reply);
551
0
                        if (r < 0)
552
0
                                return r;
553
0
                }
554
0
        }
555
556
4.41k
        if (ret_hw_addr) {
557
0
                r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &addr);
558
0
                if (r < 0 && r != -ENODATA)
559
0
                        return r;
560
0
        }
561
562
4.41k
        if (ret_permanent_hw_addr) {
563
0
                r = netlink_message_read_hw_addr(reply, IFLA_PERM_ADDRESS, &perm_addr);
564
0
                if (r < 0 && r != -ENODATA)
565
0
                        return r;
566
0
        }
567
568
4.41k
        if (ret_name)
569
32
                *ret_name = TAKE_PTR(name);
570
4.41k
        if (ret_altnames)
571
0
                *ret_altnames = TAKE_PTR(altnames);
572
4.41k
        if (ret_iftype)
573
0
                *ret_iftype = iftype;
574
4.41k
        if (ret_flags)
575
0
                *ret_flags = flags;
576
4.41k
        if (ret_kind)
577
0
                *ret_kind = TAKE_PTR(kind);
578
4.41k
        if (ret_hw_addr)
579
0
                *ret_hw_addr = addr;
580
4.41k
        if (ret_permanent_hw_addr)
581
0
                *ret_permanent_hw_addr = perm_addr;
582
4.41k
        return ifindex;
583
4.41k
}
584
585
0
int rtnl_log_parse_error(int r) {
586
0
        return log_error_errno(r, "Failed to parse netlink message: %m");
587
0
}
588
589
0
int rtnl_log_create_error(int r) {
590
0
        return log_error_errno(r, "Failed to create netlink message: %m");
591
0
}
592
593
34.5k
void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) {
594
34.5k
        size_t padding_length;
595
34.5k
        uint8_t *padding;
596
597
34.5k
        assert(rta);
598
34.5k
        assert(!data || data_length > 0);
599
600
        /* fill in the attribute */
601
34.5k
        rta->rta_type = type;
602
34.5k
        rta->rta_len = RTA_LENGTH(data_length);
603
34.5k
        if (data)
604
                /* we don't deal with the case where the user lies about the type
605
                 * and gives us too little data (so don't do that)
606
                 */
607
34.5k
                padding = mempcpy(RTA_DATA(rta), data, data_length);
608
609
0
        else
610
                /* if no data was passed, make sure we still initialize the padding
611
                   note that we can have data_length > 0 (used by some containers) */
612
0
                padding = RTA_DATA(rta);
613
614
        /* make sure also the padding at the end of the message is initialized */
615
34.5k
        padding_length = (uint8_t *) rta + RTA_SPACE(data_length) - padding;
616
34.5k
        memzero(padding, padding_length);
617
34.5k
}
618
619
0
int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) {
620
0
        struct rtattr *new_rta, *sub_rta;
621
0
        size_t message_length;
622
623
0
        assert(rta);
624
0
        assert(!data || data_length > 0);
625
626
        /* get the new message size (with padding at the end) */
627
0
        message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length);
628
629
        /* buffer should be smaller than both one page or 8K to be accepted by the kernel */
630
0
        if (message_length > MIN(page_size(), 8192UL))
631
0
                return -ENOBUFS;
632
633
        /* realloc to fit the new attribute */
634
0
        new_rta = realloc(*rta, message_length);
635
0
        if (!new_rta)
636
0
                return -ENOMEM;
637
0
        *rta = new_rta;
638
639
        /* get pointer to the attribute we are about to add */
640
0
        sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len));
641
642
0
        rtattr_append_attribute_internal(sub_rta, type, data, data_length);
643
644
        /* update rta_len */
645
0
        (*rta)->rta_len = message_length;
646
647
0
        return 0;
648
0
}
649
650
159k
bool netlink_pid_changed(sd_netlink *nl) {
651
        /* We don't support people creating an nl connection and
652
         * keeping it around over a fork(). Let's complain. */
653
159k
        return ASSERT_PTR(nl)->original_pid != getpid_cached();
654
159k
}
655
656
23.7k
static int socket_open(int family) {
657
23.7k
        int fd;
658
659
23.7k
        fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
660
23.7k
        if (fd < 0)
661
0
                return -errno;
662
663
23.7k
        return fd_move_above_stdio(fd);
664
23.7k
}
665
666
23.7k
int netlink_open_family(sd_netlink **ret, int family) {
667
23.7k
        _cleanup_close_ int fd = -EBADF;
668
23.7k
        int r;
669
670
23.7k
        fd = socket_open(family);
671
23.7k
        if (fd < 0)
672
0
                return fd;
673
674
23.7k
        r = sd_netlink_open_fd(ret, fd);
675
23.7k
        if (r < 0)
676
0
                return r;
677
23.7k
        TAKE_FD(fd);
678
679
23.7k
        return 0;
680
23.7k
}
681
682
39.8k
static bool serial_used(sd_netlink *nl, uint32_t serial) {
683
39.8k
        assert(nl);
684
685
39.8k
        return
686
39.8k
                hashmap_contains(nl->reply_callbacks, UINT32_TO_PTR(serial)) ||
687
39.8k
                hashmap_contains(nl->rqueue_by_serial, UINT32_TO_PTR(serial)) ||
688
39.8k
                hashmap_contains(nl->rqueue_partial_by_serial, UINT32_TO_PTR(serial));
689
39.8k
}
690
691
39.8k
void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m) {
692
39.8k
        uint32_t picked;
693
694
39.8k
        assert(nl);
695
39.8k
        assert(!netlink_pid_changed(nl));
696
39.8k
        assert(m);
697
39.8k
        assert(m->hdr);
698
699
        /* Avoid collisions with outstanding requests */
700
39.8k
        do {
701
39.8k
                picked = nl->serial;
702
703
                /* Don't use seq == 0, as that is used for broadcasts, so we would get confused by replies to
704
                   such messages */
705
39.8k
                nl->serial = nl->serial == UINT32_MAX ? 1 : nl->serial + 1;
706
707
39.8k
        } while (serial_used(nl, picked));
708
709
39.8k
        m->hdr->nlmsg_seq = picked;
710
39.8k
        message_seal(m);
711
39.8k
}
712
713
0
static int socket_writev_message(sd_netlink *nl, sd_netlink_message **m, size_t msgcount) {
714
0
        _cleanup_free_ struct iovec *iovs = NULL;
715
0
        ssize_t k;
716
717
0
        assert(nl);
718
0
        assert(m);
719
0
        assert(msgcount > 0);
720
721
0
        iovs = new(struct iovec, msgcount);
722
0
        if (!iovs)
723
0
                return -ENOMEM;
724
725
0
        for (size_t i = 0; i < msgcount; i++) {
726
0
                assert(m[i]->hdr);
727
0
                assert(m[i]->hdr->nlmsg_len > 0);
728
729
0
                iovs[i] = IOVEC_MAKE(m[i]->hdr, m[i]->hdr->nlmsg_len);
730
0
        }
731
732
0
        k = writev(nl->fd, iovs, msgcount);
733
0
        if (k < 0)
734
0
                return -errno;
735
736
0
        return k;
737
0
}
738
739
int sd_netlink_sendv(
740
                sd_netlink *nl,
741
                sd_netlink_message **messages,
742
                size_t msgcount,
743
0
                uint32_t **ret_serial) {
744
745
0
        _cleanup_free_ uint32_t *serials = NULL;
746
0
        int r;
747
748
0
        assert_return(nl, -EINVAL);
749
0
        assert_return(!netlink_pid_changed(nl), -ECHILD);
750
0
        assert_return(messages, -EINVAL);
751
0
        assert_return(msgcount > 0, -EINVAL);
752
753
0
        if (ret_serial) {
754
0
                serials = new(uint32_t, msgcount);
755
0
                if (!serials)
756
0
                        return -ENOMEM;
757
0
        }
758
759
0
        for (size_t i = 0; i < msgcount; i++) {
760
0
                assert_return(!messages[i]->sealed, -EPERM);
761
762
0
                netlink_seal_message(nl, messages[i]);
763
0
                if (serials)
764
0
                        serials[i] = message_get_serial(messages[i]);
765
0
        }
766
767
0
        r = socket_writev_message(nl, messages, msgcount);
768
0
        if (r < 0)
769
0
                return r;
770
771
0
        if (ret_serial)
772
0
                *ret_serial = TAKE_PTR(serials);
773
774
0
        return r;
775
0
}