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

systemd / systemd / 20669300594

02 Jan 2026 09:00PM UTC coverage: 72.677% (-0.02%) from 72.697%
20669300594

push

github

web-flow
clang-tidy: Enable more warnings (#39910)

25 of 27 new or added lines in 3 files covered. (92.59%)

5655 existing lines in 111 files now uncovered.

310023 of 426578 relevant lines covered (72.68%)

1136999.43 hits per line

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

82.76
/src/nspawn/nspawn-network.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/if.h>
4
#include <linux/nl80211.h>
5
#include <linux/veth.h>
6
#include <net/if.h>
7
#include <sys/file.h>
8
#include <sys/mount.h>
9
#include <unistd.h>
10

11
#include "sd-device.h"
12
#include "sd-id128.h"
13
#include "sd-netlink.h"
14

15
#include "alloc-util.h"
16
#include "device-private.h"
17
#include "device-util.h"
18
#include "ether-addr-util.h"
19
#include "extract-word.h"
20
#include "fd-util.h"
21
#include "lock-util.h"
22
#include "mkdir.h"
23
#include "mount-util.h"
24
#include "namespace-util.h"
25
#include "netif-util.h"
26
#include "netlink-util.h"
27
#include "nspawn-network.h"
28
#include "pidref.h"
29
#include "process-util.h"
30
#include "socket-util.h"
31
#include "stat-util.h"
32
#include "string-util.h"
33
#include "strv.h"
34
#include "udev-util.h"
35

36
#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1)
37
#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2)
38
#define VETH_EXTRA_HOST_HASH_KEY SD_ID128_MAKE(48,c7,f6,b7,ea,9d,4c,9e,b7,28,d4,de,91,d5,bf,66)
39
#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59)
40
#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
41

42
static int remove_one_link(sd_netlink *rtnl, const char *name) {
31✔
43
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
31✔
44
        int r;
31✔
45

46
        if (isempty(name))
62✔
47
                return 0;
48

49
        r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0);
31✔
50
        if (r < 0)
31✔
51
                return log_error_errno(r, "Failed to allocate netlink message: %m");
×
52

53
        r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
31✔
54
        if (r < 0)
31✔
55
                return log_error_errno(r, "Failed to add netlink interface name: %m");
×
56

57
        r = sd_netlink_call(rtnl, m, 0, NULL);
31✔
58
        if (r == -ENODEV) /* Already gone */
31✔
59
                return 0;
60
        if (r < 0)
7✔
61
                return log_error_errno(r, "Failed to remove interface %s: %m", name);
×
62

63
        return 1;
64
}
65

66
static int set_alternative_ifname(sd_netlink *rtnl, const char *ifname, const char *altifname) {
43✔
67
        int r;
43✔
68

69
        assert(rtnl);
43✔
70
        assert(ifname);
43✔
71

72
        if (!altifname)
43✔
73
                return 0;
43✔
74

75
        if (strlen(altifname) >= ALTIFNAMSIZ)
23✔
76
                return log_warning_errno(SYNTHETIC_ERRNO(ERANGE),
×
77
                                         "Alternative interface name '%s' for '%s' is too long, ignoring",
78
                                         altifname, ifname);
79

80
        r = rtnl_set_link_alternative_names_by_ifname(&rtnl, ifname, STRV_MAKE(altifname));
23✔
81
        if (r < 0)
23✔
82
                return log_warning_errno(r,
8✔
83
                                         "Failed to set alternative interface name '%s' to '%s', ignoring: %m",
84
                                         altifname, ifname);
85

86
        return 0;
87
}
88

89
static int add_veth(
35✔
90
                sd_netlink *rtnl,
91
                const PidRef *pid,
92
                const char *ifname_host,
93
                const char *altifname_host,
94
                const struct ether_addr *mac_host,
95
                const char *ifname_container,
96
                const struct ether_addr *mac_container) {
97

98
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
35✔
99
        int r;
35✔
100

101
        assert(rtnl);
35✔
102
        assert(pidref_is_set(pid));
35✔
103
        assert(ifname_host);
35✔
104
        assert(mac_host);
35✔
105
        assert(ifname_container);
35✔
106
        assert(mac_container);
35✔
107

108
        r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
35✔
109
        if (r < 0)
35✔
110
                return log_error_errno(r, "Failed to allocate netlink message: %m");
×
111

112
        r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host);
35✔
113
        if (r < 0)
35✔
114
                return log_error_errno(r, "Failed to add netlink interface name: %m");
×
115

116
        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host);
35✔
117
        if (r < 0)
35✔
118
                return log_error_errno(r, "Failed to add netlink MAC address: %m");
×
119

120
        r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
35✔
121
        if (r < 0)
35✔
122
                return log_error_errno(r, "Failed to open netlink container: %m");
×
123

124
        r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth");
35✔
125
        if (r < 0)
35✔
126
                return log_error_errno(r, "Failed to open netlink container: %m");
×
127

128
        r = sd_netlink_message_open_container(m, VETH_INFO_PEER);
35✔
129
        if (r < 0)
35✔
130
                return log_error_errno(r, "Failed to open netlink container: %m");
×
131

132
        r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_container);
35✔
133
        if (r < 0)
35✔
134
                return log_error_errno(r, "Failed to add netlink interface name: %m");
×
135

136
        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_container);
35✔
137
        if (r < 0)
35✔
138
                return log_error_errno(r, "Failed to add netlink MAC address: %m");
×
139

140
        r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid->pid);
35✔
141
        if (r < 0)
35✔
142
                return log_error_errno(r, "Failed to add netlink namespace field: %m");
×
143

144
        r = sd_netlink_message_close_container(m);
35✔
145
        if (r < 0)
35✔
146
                return log_error_errno(r, "Failed to close netlink container: %m");
×
147

148
        r = sd_netlink_message_close_container(m);
35✔
149
        if (r < 0)
35✔
150
                return log_error_errno(r, "Failed to close netlink container: %m");
×
151

152
        r = sd_netlink_message_close_container(m);
35✔
153
        if (r < 0)
35✔
154
                return log_error_errno(r, "Failed to close netlink container: %m");
×
155

156
        r = sd_netlink_call(rtnl, m, 0, NULL);
35✔
157
        if (r < 0)
35✔
158
                return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_container);
×
159

160
        (void) set_alternative_ifname(rtnl, ifname_host, altifname_host);
35✔
161

162
        return 0;
163
}
164

165
int setup_veth(const char *machine_name,
27✔
166
               const PidRef *pid,
167
               char iface_name[IFNAMSIZ],
168
               bool bridge,
169
               const struct ether_addr *provided_mac) {
170

171
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
27✔
172
        struct ether_addr mac_host, mac_container;
27✔
173
        unsigned u;
27✔
174
        char *n, *a = NULL;
27✔
175
        int r;
27✔
176

177
        assert(machine_name);
27✔
178
        assert(pidref_is_set(pid));
27✔
179
        assert(iface_name);
27✔
180

181
        /* Use two different interface name prefixes depending whether
182
         * we are in bridge mode or not. */
183
        n = strjoina(bridge ? "vb-" : "ve-", machine_name);
156✔
184
        r = net_shorten_ifname(n, /* check_naming_scheme= */ true);
27✔
185
        if (r > 0)
27✔
186
                a = strjoina(bridge ? "vb-" : "ve-", machine_name);
75✔
187

188
        if (ether_addr_is_null(provided_mac)){
27✔
189
                r = net_generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
27✔
190
                if (r < 0)
27✔
191
                        return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
×
192
        } else
193
                mac_container = *provided_mac;
×
194

195
        r = net_generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
27✔
196
        if (r < 0)
27✔
197
                return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
×
198

199
        r = sd_netlink_open(&rtnl);
27✔
200
        if (r < 0)
27✔
201
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
202

203
        r = add_veth(rtnl, pid, n, a, &mac_host, "host0", &mac_container);
27✔
204
        if (r < 0)
27✔
205
                return r;
206

207
        u = if_nametoindex(n); /* We don't need to use rtnl_resolve_ifname() here because the
27✔
208
                                * name we assigned is always the main name. */
209
        if (u == 0)
27✔
210
                return log_error_errno(errno, "Failed to resolve interface %s: %m", n);
×
211

212
        strcpy(iface_name, n);
27✔
213
        return (int) u;
27✔
214
}
215

216
int setup_veth_extra(
61✔
217
                const char *machine_name,
218
                const PidRef *pid,
219
                char **pairs) {
220

221
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
61✔
222
        uint64_t idx = 0;
61✔
223
        int r;
61✔
224

225
        assert(machine_name);
61✔
226
        assert(pidref_is_set(pid));
61✔
227

228
        if (strv_isempty(pairs))
65✔
229
                return 0;
230

231
        r = sd_netlink_open(&rtnl);
4✔
232
        if (r < 0)
4✔
233
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
234

235
        STRV_FOREACH_PAIR(a, b, pairs) {
12✔
236
                struct ether_addr mac_host, mac_container;
8✔
237

238
                r = net_generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx);
8✔
239
                if (r < 0)
8✔
240
                        return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
×
241

242
                r = net_generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx);
8✔
243
                if (r < 0)
8✔
244
                        return log_error_errno(r, "Failed to generate predictable MAC address for host side of extra veth link: %m");
×
245

246
                r = add_veth(rtnl, pid, *a, NULL, &mac_host, *b, &mac_container);
8✔
247
                if (r < 0)
8✔
248
                        return r;
249

250
                idx++;
8✔
251
        }
252

253
        return 0;
254
}
255

256
static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *bridge_name) {
11✔
257
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
11✔
258
        int r, bridge_ifi;
11✔
259

260
        assert(rtnl);
11✔
261
        assert(veth_name);
11✔
262
        assert(bridge_name);
11✔
263

264
        bridge_ifi = rtnl_resolve_interface(&rtnl, bridge_name);
11✔
265
        if (bridge_ifi < 0)
11✔
266
                return bridge_ifi;
267

268
        r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0);
6✔
269
        if (r < 0)
6✔
270
                return r;
271

272
        r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP);
6✔
273
        if (r < 0)
6✔
274
                return r;
275

276
        r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name);
6✔
277
        if (r < 0)
6✔
278
                return r;
279

280
        r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi);
6✔
281
        if (r < 0)
6✔
282
                return r;
283

284
        r = sd_netlink_call(rtnl, m, 0, NULL);
6✔
285
        if (r < 0)
6✔
286
                return r;
×
287

288
        return bridge_ifi;
289
}
290

291
static int create_bridge(sd_netlink *rtnl, const char *bridge_name) {
5✔
292
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
5✔
293
        int r;
5✔
294

295
        r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
5✔
296
        if (r < 0)
5✔
297
                return r;
298

299
        r = sd_netlink_message_append_string(m, IFLA_IFNAME, bridge_name);
5✔
300
        if (r < 0)
5✔
301
                return r;
302

303
        r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
5✔
304
        if (r < 0)
5✔
305
                return r;
306

307
        r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "bridge");
5✔
308
        if (r < 0)
5✔
309
                return r;
310

311
        r = sd_netlink_message_close_container(m);
5✔
312
        if (r < 0)
5✔
313
                return r;
314

315
        r = sd_netlink_message_close_container(m);
5✔
316
        if (r < 0)
5✔
317
                return r;
318

319
        r = sd_netlink_call(rtnl, m, 0, NULL);
5✔
320
        if (r < 0)
5✔
321
                return r;
×
322

323
        return 0;
324
}
325

326
int setup_bridge(const char *veth_name, const char *bridge_name, bool create) {
6✔
327
        _cleanup_(release_lock_file) LockFile bridge_lock = LOCK_FILE_INIT;
×
328
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
6✔
329
        int r, bridge_ifi;
6✔
330
        unsigned n = 0;
6✔
331

332
        assert(veth_name);
6✔
333
        assert(bridge_name);
6✔
334

335
        r = sd_netlink_open(&rtnl);
6✔
336
        if (r < 0)
6✔
337
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
338

339
        if (create) {
6✔
340
                /* We take a system-wide lock here, so that we can safely check whether there's still a member in the
341
                 * bridge before removing it, without risking interference from other nspawn instances. */
342

343
                r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock);
6✔
344
                if (r < 0)
6✔
345
                        return log_error_errno(r, "Failed to take network zone lock: %m");
×
346
        }
347

348
        for (;;) {
11✔
349
                bridge_ifi = join_bridge(rtnl, veth_name, bridge_name);
11✔
350
                if (bridge_ifi >= 0)
11✔
351
                        return bridge_ifi;
352
                if (bridge_ifi != -ENODEV || !create || n > 10)
5✔
353
                        return log_error_errno(bridge_ifi, "Failed to add interface %s to bridge %s: %m", veth_name, bridge_name);
×
354

355
                /* Count attempts, so that we don't enter an endless loop here. */
356
                n++;
5✔
357

358
                /* The bridge doesn't exist yet. Let's create it */
359
                r = create_bridge(rtnl, bridge_name);
5✔
360
                if (r < 0)
5✔
361
                        return log_error_errno(r, "Failed to create bridge interface %s: %m", bridge_name);
×
362

363
                /* Try again, now that the bridge exists */
364
        }
365
}
366

367
int remove_bridge(const char *bridge_name) {
361✔
368
        _cleanup_(release_lock_file) LockFile bridge_lock = LOCK_FILE_INIT;
×
369
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
361✔
370
        const char *path;
361✔
371
        int r;
361✔
372

373
        /* Removes the specified bridge, but only if it is currently empty */
374

375
        if (isempty(bridge_name))
370✔
376
                return 0;
377

378
        r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock);
9✔
379
        if (r < 0)
9✔
380
                return log_error_errno(r, "Failed to take network zone lock: %m");
×
381

382
        path = strjoina("/sys/class/net/", bridge_name, "/brif");
63✔
383

384
        r = dir_is_empty(path, /* ignore_hidden_or_backup= */ false);
9✔
385
        if (r == -ENOENT) /* Already gone? */
9✔
386
                return 0;
387
        if (r < 0)
3✔
388
                return log_error_errno(r, "Can't detect if bridge %s is empty: %m", bridge_name);
×
389
        if (r == 0) /* Still populated, leave it around */
3✔
390
                return 0;
391

392
        r = sd_netlink_open(&rtnl);
2✔
393
        if (r < 0)
2✔
394
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
395

396
        return remove_one_link(rtnl, bridge_name);
2✔
397
}
398

399
static int test_network_interface_initialized(const char *name) {
79✔
400
        _cleanup_(sd_device_unrefp) sd_device *d = NULL;
79✔
401
        int r;
79✔
402

403
        if (!udev_available())
79✔
404
                return 0;
405

406
        /* udev should be around. */
407

408
        r = sd_device_new_from_ifname(&d, name);
79✔
409
        if (r < 0)
79✔
410
                return log_error_errno(r, "Failed to get device %s: %m", name);
2✔
411

412
        r = device_is_processed(d);
77✔
413
        if (r < 0)
77✔
414
                return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name);
×
415
        if (r == 0)
77✔
416
                return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Network interface %s is not initialized yet.", name);
×
417

418
        r = device_is_renaming(d);
77✔
419
        if (r < 0)
77✔
420
                return log_error_errno(r, "Failed to determine the interface %s is being renamed: %m", name);
×
421
        if (r > 0)
77✔
422
                return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Interface %s is being renamed.", name);
×
423

424
        return 0;
425
}
426

427
int test_network_interfaces_initialized(char **iface_pairs) {
1,295✔
428
        int r;
1,295✔
429
        STRV_FOREACH_PAIR(a, b, iface_pairs) {
1,372✔
430
                r = test_network_interface_initialized(*a);
79✔
431
                if (r < 0)
79✔
432
                        return r;
433
        }
434
        return 0;
435
}
436

437
int resolve_network_interface_names(char **iface_pairs) {
433✔
438
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
433✔
439
        int r;
433✔
440

441
        /* Due to a bug in kernel fixed by 8e15aee621618a3ee3abecaf1fd8c1428098b7ef (v6.6, backported to
442
         * 6.1.60 and 6.5.9), an interface with alternative names cannot be resolved by the alternative name
443
         * if the interface is moved to another network namespace. Hence, we need to adjust the provided
444
         * names before moving interfaces to container namespace. */
445

446
        STRV_FOREACH_PAIR(from, to, iface_pairs) {
468✔
447
                _cleanup_free_ char *name = NULL;
36✔
448
                _cleanup_strv_free_ char **altnames = NULL;
36✔
449

450
                r = rtnl_resolve_ifname_full(&rtnl, _RESOLVE_IFNAME_ALL, *from, &name, &altnames);
36✔
451
                if (r < 0)
36✔
452
                        return r;
453

454
                /* Always use the resolved name for 'from'. */
455
                free_and_replace(*from, name);
35✔
456

457
                /* If the name 'to' is assigned as an alternative name, we cannot rename the interface.
458
                 * Hence, use the assigned interface name (including the alternative names) as is, and
459
                 * use the resolved name for 'to'. */
460
                if (strv_contains(altnames, *to)) {
35✔
461
                        r = free_and_strdup_warn(to, *from);
7✔
462
                        if (r < 0)
7✔
463
                                return r;
464
                }
465
        }
466
        return 0;
467
}
468

469
static int netns_child_begin(int netns_fd, int *ret_original_netns_fd) {
5✔
470
        _cleanup_close_ int original_netns_fd = -EBADF;
5✔
471
        int r;
5✔
472

473
        assert(netns_fd >= 0);
5✔
474

475
        if (ret_original_netns_fd) {
5✔
476
                original_netns_fd = namespace_open_by_type(NAMESPACE_NET);
3✔
477
                if (original_netns_fd < 0)
3✔
478
                        return log_error_errno(original_netns_fd, "Failed to open original network namespace: %m");
×
479
        }
480

481
        r = namespace_enter(/* pidns_fd= */ -EBADF,
5✔
482
                            /* mntns_fd= */ -EBADF,
483
                            netns_fd,
484
                            /* userns_fd= */ -EBADF,
485
                            /* root_fd= */ -EBADF);
486
        if (r < 0)
5✔
487
                return log_error_errno(r, "Failed to enter child network namespace: %m");
×
488

489
        r = umount_recursive("/sys/", /* flags= */ 0);
5✔
490
        if (r < 0)
5✔
491
                log_debug_errno(r, "Failed to unmount directories below /sys/, ignoring: %m");
×
492

493
        (void) mkdir_p("/sys/", 0755);
5✔
494

495
        /* Populate new sysfs instance associated with the client netns, to make sd_device usable. */
496
        r = mount_nofollow_verbose(LOG_ERR, "sysfs", "/sys/", "sysfs",
5✔
497
                                   MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, /* options= */ NULL);
498
        if (r < 0)
5✔
499
                return log_error_errno(r, "Failed to mount sysfs on /sys/: %m");
×
500

501
        /* udev_avaliable() might be called previously and the result may be cached.
502
         * Now, we (re-)mount sysfs. Hence, we need to reset the cache. */
503
        reset_cached_udev_availability();
5✔
504

505
        if (ret_original_netns_fd)
5✔
506
                *ret_original_netns_fd = TAKE_FD(original_netns_fd);
3✔
507

508
        return 0;
509
}
510

511
static int netns_fork_and_wait(int netns_fd, int *ret_original_netns_fd) {
13✔
512
        int r;
13✔
513

514
        assert(netns_fd >= 0);
13✔
515

516
        r = pidref_safe_fork(
13✔
517
                        "(sd-netns)",
518
                        FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE,
519
                        /* ret= */ NULL);
520
        if (r < 0)
13✔
521
                return log_error_errno(r, "Failed to fork process (sd-netns): %m");
×
522
        if (r == 0) {
13✔
523
                if (netns_child_begin(netns_fd, ret_original_netns_fd) < 0)
5✔
UNCOV
524
                        _exit(EXIT_FAILURE);
×
525

526
                return 0;
527
        }
528

529
        if (ret_original_netns_fd)
8✔
530
                *ret_original_netns_fd = -EBADF;
3✔
531

532
        return 1;
533
}
534

535
static int move_wlan_interface_impl(sd_netlink **genl, int netns_fd, sd_device *dev) {
15✔
536
        _cleanup_(sd_netlink_unrefp) sd_netlink *our_genl = NULL;
15✔
537
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
15✔
538
        int r;
15✔
539

540
        assert(netns_fd >= 0);
15✔
541
        assert(dev);
15✔
542

543
        if (!genl)
15✔
544
                genl = &our_genl;
2✔
545
        if (!*genl) {
15✔
546
                r = sd_genl_socket_open(genl);
9✔
547
                if (r < 0)
9✔
UNCOV
548
                        return log_error_errno(r, "Failed to connect to generic netlink: %m");
×
549
        }
550

551
        r = sd_genl_message_new(*genl, NL80211_GENL_NAME, NL80211_CMD_SET_WIPHY_NETNS, &m);
15✔
552
        if (r < 0)
15✔
UNCOV
553
                return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m");
×
554

555
        uint32_t phy_index;
15✔
556
        r = device_get_sysattr_u32(dev, "phy80211/index", &phy_index);
15✔
557
        if (r < 0)
15✔
UNCOV
558
                return log_device_error_errno(dev, r, "Failed to get phy index: %m");
×
559

560
        r = sd_netlink_message_append_u32(m, NL80211_ATTR_WIPHY, phy_index);
15✔
561
        if (r < 0)
15✔
UNCOV
562
                return log_device_error_errno(dev, r, "Failed to append phy index to netlink message: %m");
×
563

564
        r = sd_netlink_message_append_u32(m, NL80211_ATTR_NETNS_FD, netns_fd);
15✔
565
        if (r < 0)
15✔
UNCOV
566
                return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m");
×
567

568
        r = sd_netlink_call(*genl, m, 0, NULL);
15✔
569
        if (r < 0)
15✔
UNCOV
570
                return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m");
×
571

572
        return 0;
573
}
574

575
static int move_wlan_interface_one(
13✔
576
                        sd_netlink **rtnl,
577
                        sd_netlink **genl,
578
                        int *temp_netns_fd,
579
                        int netns_fd,
580
                        sd_device *dev,
581
                        const char *name) {
582

583
        int r;
13✔
584

585
        assert(rtnl);
13✔
586
        assert(genl);
13✔
587
        assert(temp_netns_fd);
13✔
588
        assert(netns_fd >= 0);
13✔
589
        assert(dev);
13✔
590

591
        if (!name)
13✔
592
                return move_wlan_interface_impl(genl, netns_fd, dev);
11✔
593

594
        /* The command NL80211_CMD_SET_WIPHY_NETNS takes phy instead of network interface, and does not take
595
         * an interface name in the passed network namespace. Hence, we need to move the phy and interface to
596
         * a temporary network namespace, rename the interface in it, and move them to the requested netns. */
597

598
        if (*temp_netns_fd < 0) {
7✔
599
                r = netns_acquire();
7✔
600
                if (r < 0)
7✔
UNCOV
601
                        return log_error_errno(r, "Failed to acquire new network namespace: %m");
×
602
                *temp_netns_fd = r;
7✔
603
        }
604

605
        r = move_wlan_interface_impl(genl, *temp_netns_fd, dev);
7✔
606
        if (r < 0)
7✔
607
                return r;
608

609
        const char *sysname;
7✔
610
        r = sd_device_get_sysname(dev, &sysname);
7✔
611
        if (r < 0)
7✔
UNCOV
612
                return log_device_error_errno(dev, r, "Failed to get interface name: %m");
×
613

614
        r = netns_fork_and_wait(*temp_netns_fd, NULL);
7✔
615
        if (r < 0)
7✔
UNCOV
616
                return log_error_errno(r, "Failed to fork process (nspawn-rename-wlan): %m");
×
617
        if (r == 0) {
7✔
618
                _cleanup_(sd_device_unrefp) sd_device *temp_dev = NULL;
2✔
619

620
                r = rtnl_rename_link(NULL, sysname, name);
2✔
621
                if (r < 0) {
2✔
UNCOV
622
                        log_error_errno(r, "Failed to rename network interface '%s' to '%s': %m", sysname, name);
×
UNCOV
623
                        goto finalize;
×
624
                }
625

626
                r = sd_device_new_from_ifname(&temp_dev, name);
2✔
627
                if (r < 0) {
2✔
UNCOV
628
                        log_error_errno(r, "Failed to acquire device '%s': %m", name);
×
UNCOV
629
                        goto finalize;
×
630
                }
631

632
                r = move_wlan_interface_impl(NULL, netns_fd, temp_dev);
2✔
633

634
        finalize:
2✔
635
                _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
2✔
636
        }
637

638
        return 0;
639
}
640

641
static int move_network_interface_one(sd_netlink **rtnl, int netns_fd, sd_device *dev, const char *name) {
18✔
642
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
18✔
643
        int r;
18✔
644

645
        assert(rtnl);
18✔
646
        assert(netns_fd >= 0);
18✔
647
        assert(dev);
18✔
648

649
        if (!*rtnl) {
18✔
650
                r = sd_netlink_open(rtnl);
6✔
651
                if (r < 0)
6✔
UNCOV
652
                        return log_error_errno(r, "Failed to connect to rtnetlink: %m");
×
653
        }
654

655
        int ifindex;
18✔
656
        r = sd_device_get_ifindex(dev, &ifindex);
18✔
657
        if (r < 0)
18✔
UNCOV
658
                return log_device_error_errno(dev, r, "Failed to get ifindex: %m");
×
659

660
        r = sd_rtnl_message_new_link(*rtnl, &m, RTM_SETLINK, ifindex);
18✔
661
        if (r < 0)
18✔
UNCOV
662
                return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m");
×
663

664
        r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd);
18✔
665
        if (r < 0)
18✔
UNCOV
666
                return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m");
×
667

668
        if (name) {
18✔
669
                r = sd_netlink_message_append_string(m, IFLA_IFNAME, name);
6✔
670
                if (r < 0)
6✔
UNCOV
671
                        return log_device_error_errno(dev, r, "Failed to add netlink interface name: %m");
×
672
        }
673

674
        r = sd_netlink_call(*rtnl, m, 0, NULL);
18✔
675
        if (r < 0)
18✔
UNCOV
676
                return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m");
×
677

678
        return 0;
679
}
680

681
int move_network_interfaces(int netns_fd, char **iface_pairs) {
64✔
682
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL, *genl = NULL;
67✔
683
        _cleanup_close_ int temp_netns_fd = -EBADF;
62✔
684
        int r;
64✔
685

686
        assert(netns_fd >= 0);
64✔
687

688
        if (strv_isempty(iface_pairs))
69✔
689
                return 0;
690

691
        STRV_FOREACH_PAIR(from, to, iface_pairs) {
36✔
692
                _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
29✔
693
                const char *name;
31✔
694

695
                name = streq(*from, *to) ? NULL : *to;
31✔
696

697
                r = sd_device_new_from_ifname(&dev, *from);
31✔
698
                if (r < 0)
31✔
UNCOV
699
                        return log_error_errno(r, "Unknown interface name %s: %m", *from);
×
700

701
                if (device_is_devtype(dev, "wlan"))
31✔
702
                        r = move_wlan_interface_one(&rtnl, &genl, &temp_netns_fd, netns_fd, dev, name);
13✔
703
                else
704
                        r = move_network_interface_one(&rtnl, netns_fd, dev, name);
18✔
705
                if (r < 0)
29✔
706
                        return r;
707
        }
708

709
        return 0;
710
}
711

712
int move_back_network_interfaces(int child_netns_fd, char **interface_pairs) {
50✔
713
        _cleanup_close_ int parent_netns_fd = -EBADF;
48✔
714
        int r;
50✔
715

716
        assert(child_netns_fd >= 0);
50✔
717

718
        if (strv_isempty(interface_pairs))
52✔
719
                return 0;
720

721
        r = netns_fork_and_wait(child_netns_fd, &parent_netns_fd);
4✔
722
        if (r < 0)
4✔
723
                return r;
724
        if (r == 0) {
4✔
725
                /* Reverse network interfaces pair list so that interfaces get their initial name back.
726
                 * This is about ensuring interfaces get their old name back when being moved back. */
727
                interface_pairs = strv_reverse(interface_pairs);
2✔
728

729
                r = move_network_interfaces(parent_netns_fd, interface_pairs);
2✔
730
                _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
1✔
731
        }
732

733
        return 0;
734
}
735

736
int setup_macvlan(const char *machine_name, const PidRef *pid, char **iface_pairs) {
61✔
737
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
61✔
738
        unsigned idx = 0;
61✔
739
        int r;
61✔
740

741
        assert(pidref_is_set(pid));
61✔
742

743
        if (strv_isempty(iface_pairs))
65✔
744
                return 0;
745

746
        r = sd_netlink_open(&rtnl);
4✔
747
        if (r < 0)
4✔
UNCOV
748
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
749

750
        STRV_FOREACH_PAIR(i, b, iface_pairs) {
16✔
751
                _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
12✔
752
                _cleanup_free_ char *n = NULL;
12✔
753
                int shortened, ifi;
12✔
754
                struct ether_addr mac;
12✔
755

756
                ifi = rtnl_resolve_interface_or_warn(&rtnl, *i);
12✔
757
                if (ifi < 0)
12✔
758
                        return ifi;
759

760
                r = net_generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++);
12✔
761
                if (r < 0)
12✔
UNCOV
762
                        return log_error_errno(r, "Failed to create MACVLAN MAC address: %m");
×
763

764
                r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
12✔
765
                if (r < 0)
12✔
UNCOV
766
                        return log_error_errno(r, "Failed to allocate netlink message: %m");
×
767

768
                r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi);
12✔
769
                if (r < 0)
12✔
UNCOV
770
                        return log_error_errno(r, "Failed to add netlink interface index: %m");
×
771

772
                n = strdup(*b);
12✔
773
                if (!n)
12✔
UNCOV
774
                        return log_oom();
×
775

776
                shortened = net_shorten_ifname(n, /* check_naming_scheme= */ true);
12✔
777

778
                r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
12✔
779
                if (r < 0)
12✔
UNCOV
780
                        return log_error_errno(r, "Failed to add netlink interface name: %m");
×
781

782
                r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac);
12✔
783
                if (r < 0)
12✔
UNCOV
784
                        return log_error_errno(r, "Failed to add netlink MAC address: %m");
×
785

786
                r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid->pid);
12✔
787
                if (r < 0)
12✔
UNCOV
788
                        return log_error_errno(r, "Failed to add netlink namespace field: %m");
×
789

790
                r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
12✔
791
                if (r < 0)
12✔
UNCOV
792
                        return log_error_errno(r, "Failed to open netlink container: %m");
×
793

794
                r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan");
12✔
795
                if (r < 0)
12✔
UNCOV
796
                        return log_error_errno(r, "Failed to open netlink container: %m");
×
797

798
                r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE);
12✔
799
                if (r < 0)
12✔
UNCOV
800
                        return log_error_errno(r, "Failed to append macvlan mode: %m");
×
801

802
                r = sd_netlink_message_close_container(m);
12✔
803
                if (r < 0)
12✔
UNCOV
804
                        return log_error_errno(r, "Failed to close netlink container: %m");
×
805

806
                r = sd_netlink_message_close_container(m);
12✔
807
                if (r < 0)
12✔
UNCOV
808
                        return log_error_errno(r, "Failed to close netlink container: %m");
×
809

810
                r = sd_netlink_call(rtnl, m, 0, NULL);
12✔
811
                if (r < 0)
12✔
UNCOV
812
                        return log_error_errno(r, "Failed to add new macvlan interfaces: %m");
×
813

814
                if (shortened > 0)
12✔
815
                        (void) set_alternative_ifname(rtnl, n, *b);
4✔
816
        }
817

818
        return 0;
819
}
820

821
static int remove_macvlan_impl(char **interface_pairs) {
1✔
822
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
1✔
823
        int r;
1✔
824

825
        assert(interface_pairs);
1✔
826

827
        r = sd_netlink_open(&rtnl);
1✔
828
        if (r < 0)
1✔
UNCOV
829
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
830

831
        STRV_FOREACH_PAIR(a, b, interface_pairs) {
4✔
832
                _cleanup_free_ char *n = NULL;
3✔
833

834
                n = strdup(*b);
3✔
835
                if (!n)
3✔
UNCOV
836
                        return log_oom();
×
837

838
                (void) net_shorten_ifname(n, /* check_naming_scheme= */ true);
3✔
839

840
                r = remove_one_link(rtnl, n);
3✔
841
                if (r < 0)
3✔
842
                        log_warning_errno(r, "Failed to remove macvlan interface %s, ignoring: %m", n);
3✔
843
        }
844

845
        return 0;
846
}
847

848
int remove_macvlan(int child_netns_fd, char **interface_pairs) {
48✔
849
        _cleanup_close_ int parent_netns_fd = -EBADF;
47✔
850
        int r;
48✔
851

852
        /* In some cases the kernel might pin the macvlan links on the container even after the namespace
853
         * died. Hence, let's better remove them explicitly too. See issue #680. */
854

855
        assert(child_netns_fd >= 0);
48✔
856

857
        if (strv_isempty(interface_pairs))
49✔
858
                return 0;
859

860
        r = netns_fork_and_wait(child_netns_fd, &parent_netns_fd);
2✔
861
        if (r < 0)
2✔
862
                return r;
863
        if (r == 0) {
2✔
864
                r = remove_macvlan_impl(interface_pairs);
1✔
865
                _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
1✔
866
        }
867

868
        return 0;
869
}
870

871
int setup_ipvlan(const char *machine_name, const PidRef *pid, char **iface_pairs) {
61✔
872
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
61✔
873
        int r;
61✔
874

875
        assert(pidref_is_set(pid));
61✔
876

877
        if (strv_isempty(iface_pairs))
65✔
878
                return 0;
879

880
        r = sd_netlink_open(&rtnl);
4✔
881
        if (r < 0)
4✔
UNCOV
882
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
883

884
        STRV_FOREACH_PAIR(i, b, iface_pairs) {
16✔
885
                _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
12✔
886
                _cleanup_free_ char *n = NULL;
12✔
887
                int shortened, ifi ;
12✔
888

889
                ifi = rtnl_resolve_interface_or_warn(&rtnl, *i);
12✔
890
                if (ifi < 0)
12✔
891
                        return ifi;
892

893
                r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
12✔
894
                if (r < 0)
12✔
UNCOV
895
                        return log_error_errno(r, "Failed to allocate netlink message: %m");
×
896

897
                r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi);
12✔
898
                if (r < 0)
12✔
UNCOV
899
                        return log_error_errno(r, "Failed to add netlink interface index: %m");
×
900

901
                n = strdup(*b);
12✔
902
                if (!n)
12✔
UNCOV
903
                        return log_oom();
×
904

905
                shortened = net_shorten_ifname(n, /* check_naming_scheme= */ true);
12✔
906

907
                r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
12✔
908
                if (r < 0)
12✔
UNCOV
909
                        return log_error_errno(r, "Failed to add netlink interface name: %m");
×
910

911
                r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid->pid);
12✔
912
                if (r < 0)
12✔
UNCOV
913
                        return log_error_errno(r, "Failed to add netlink namespace field: %m");
×
914

915
                r = sd_netlink_message_open_container(m, IFLA_LINKINFO);
12✔
916
                if (r < 0)
12✔
UNCOV
917
                        return log_error_errno(r, "Failed to open netlink container: %m");
×
918

919
                r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan");
12✔
920
                if (r < 0)
12✔
UNCOV
921
                        return log_error_errno(r, "Failed to open netlink container: %m");
×
922

923
                r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2);
12✔
924
                if (r < 0)
12✔
UNCOV
925
                        return log_error_errno(r, "Failed to add ipvlan mode: %m");
×
926

927
                r = sd_netlink_message_close_container(m);
12✔
928
                if (r < 0)
12✔
UNCOV
929
                        return log_error_errno(r, "Failed to close netlink container: %m");
×
930

931
                r = sd_netlink_message_close_container(m);
12✔
932
                if (r < 0)
12✔
UNCOV
933
                        return log_error_errno(r, "Failed to close netlink container: %m");
×
934

935
                r = sd_netlink_call(rtnl, m, 0, NULL);
12✔
936
                if (r < 0)
12✔
UNCOV
937
                        return log_error_errno(r, "Failed to add new ipvlan interfaces: %m");
×
938

939
                if (shortened > 0)
12✔
940
                        (void) set_alternative_ifname(rtnl, n, *b);
4✔
941
        }
942

943
        return 0;
944
}
945

946
int veth_extra_parse(char ***l, const char *p) {
22✔
947
        _cleanup_free_ char *a = NULL, *b = NULL;
22✔
948
        int r;
22✔
949

950
        r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
22✔
951
        if (r < 0)
22✔
952
                return r;
953
        if (r == 0 || !ifname_valid(a))
22✔
954
                return -EINVAL;
955

956
        r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
21✔
957
        if (r < 0)
21✔
958
                return r;
959
        if (r == 0 || !ifname_valid(b)) {
21✔
960
                r = free_and_strdup(&b, a);
14✔
961
                if (r < 0)
14✔
962
                        return r;
963
        }
964

965
        if (p)
21✔
966
                return -EINVAL;
967

968
        r = strv_push_pair(l, a, b);
21✔
969
        if (r < 0)
21✔
970
                return -ENOMEM;
971

972
        a = b = NULL;
21✔
973
        return 0;
21✔
974
}
975

976
int remove_veth_links(const char *primary, char **pairs) {
47✔
977
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
47✔
978
        int r;
47✔
979

980
        /* In some cases the kernel might pin the veth links between host and container even after the namespace
981
         * died. Hence, let's better remove them explicitly too. */
982

983
        if (isempty(primary) && strv_isempty(pairs))
94✔
984
                return 0;
985

986
        r = sd_netlink_open(&rtnl);
24✔
987
        if (r < 0)
24✔
UNCOV
988
                return log_error_errno(r, "Failed to connect to netlink: %m");
×
989

990
        remove_one_link(rtnl, primary);
24✔
991

992
        STRV_FOREACH_PAIR(a, b, pairs)
26✔
993
                remove_one_link(rtnl, *a);
2✔
994

995
        return 0;
996
}
997

998
static int network_iface_pair_parse(const char* iftype, char ***l, const char *p, const char* ifprefix) {
45✔
999
        int r;
143✔
1000

1001
        for (;;) {
98✔
1002
                _cleanup_free_ char *word = NULL, *a = NULL, *b = NULL;
143✔
1003
                const char *interface;
143✔
1004

1005
                r = extract_first_word(&p, &word, NULL, 0);
143✔
1006
                if (r < 0)
143✔
UNCOV
1007
                        return log_error_errno(r, "Failed to parse interface name: %m");
×
1008
                if (r == 0)
143✔
1009
                        break;
1010

1011
                interface = word;
101✔
1012
                r = extract_first_word(&interface, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
101✔
1013
                if (r < 0)
101✔
UNCOV
1014
                        return log_error_errno(r, "Failed to extract first word in %s parameter: %m", iftype);
×
1015
                if (r == 0)
101✔
UNCOV
1016
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1017
                                               "Short read while reading %s parameter.", iftype);
1018
                if (!ifname_valid(a))
101✔
1019
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
3✔
1020
                                               "%s, interface name not valid: %s", iftype, a);
1021

1022
                /* Here, we only check the validity of the specified second name. If it is not specified,
1023
                 * the copied or prefixed name should be already valid, except for its length. If it is too
1024
                 * long, then it will be shortened later. */
1025
                if (!isempty(interface)) {
98✔
1026
                        if (!ifname_valid(interface))
35✔
UNCOV
1027
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
1028
                                                       "%s, interface name not valid: %s", iftype, interface);
1029

1030
                        b = strdup(interface);
35✔
1031
                } else if (ifprefix)
63✔
1032
                        b = strjoin(ifprefix, a);
42✔
1033
                else
1034
                        b = strdup(a);
21✔
1035
                if (!b)
98✔
UNCOV
1036
                        return log_oom();
×
1037

1038
                r = strv_consume_pair(l, TAKE_PTR(a), TAKE_PTR(b));
98✔
1039
                if (r < 0)
98✔
UNCOV
1040
                        return log_oom();
×
1041
        }
1042

1043
        return 0;
42✔
1044
}
1045

1046
int interface_pair_parse(char ***l, const char *p) {
15✔
1047
        return network_iface_pair_parse("Network interface", l, p, NULL);
15✔
1048
}
1049

1050
int macvlan_pair_parse(char ***l, const char *p) {
15✔
1051
        return network_iface_pair_parse("MACVLAN network interface", l, p, "mv-");
15✔
1052
}
1053

1054
int ipvlan_pair_parse(char ***l, const char *p) {
15✔
1055
        return network_iface_pair_parse("IPVLAN network interface", l, p, "iv-");
15✔
1056
}
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