]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
9a2a5625 | 2 | |
6b50cb5c | 3 | #include <linux/if.h> |
1c3e5b42 | 4 | #include <linux/nl80211.h> |
9a2a5625 | 5 | #include <linux/veth.h> |
1cf40697 | 6 | #include <net/if.h> |
fe993888 | 7 | #include <sys/file.h> |
38475cac | 8 | #include <sys/mount.h> |
4f18ff2e | 9 | #include <unistd.h> |
9a2a5625 | 10 | |
f55b0d3f | 11 | #include "sd-device.h" |
9a2a5625 LP |
12 | #include "sd-id128.h" |
13 | #include "sd-netlink.h" | |
9a2a5625 | 14 | |
b5efdb8a | 15 | #include "alloc-util.h" |
1c3e5b42 | 16 | #include "device-private.h" |
38475cac | 17 | #include "device-util.h" |
9a2a5625 | 18 | #include "ether-addr-util.h" |
6553db60 | 19 | #include "extract-word.h" |
cdd9988e | 20 | #include "fd-util.h" |
64e89f56 | 21 | #include "lock-util.h" |
38475cac YW |
22 | #include "mkdir.h" |
23 | #include "mount-util.h" | |
cdd9988e | 24 | #include "namespace-util.h" |
c50e7dca | 25 | #include "netif-util.h" |
9a2a5625 | 26 | #include "netlink-util.h" |
cf0fbc49 | 27 | #include "nspawn-network.h" |
1434ccf7 | 28 | #include "pidref.h" |
cdd9988e | 29 | #include "process-util.h" |
ef76dff2 LP |
30 | #include "socket-util.h" |
31 | #include "stat-util.h" | |
07630cea | 32 | #include "string-util.h" |
f55b0d3f | 33 | #include "strv.h" |
26208d5b | 34 | #include "udev-util.h" |
9a2a5625 LP |
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) | |
f6d6bad1 LP |
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) | |
9a2a5625 LP |
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 | ||
22b28dfd LP |
42 | static int remove_one_link(sd_netlink *rtnl, const char *name) { |
43 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
44 | int r; | |
45 | ||
46 | if (isempty(name)) | |
47 | return 0; | |
48 | ||
49 | r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0); | |
50 | if (r < 0) | |
51 | return log_error_errno(r, "Failed to allocate netlink message: %m"); | |
52 | ||
53 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); | |
54 | if (r < 0) | |
55 | return log_error_errno(r, "Failed to add netlink interface name: %m"); | |
56 | ||
57 | r = sd_netlink_call(rtnl, m, 0, NULL); | |
58 | if (r == -ENODEV) /* Already gone */ | |
59 | return 0; | |
60 | if (r < 0) | |
61 | return log_error_errno(r, "Failed to remove interface %s: %m", name); | |
62 | ||
63 | return 1; | |
64 | } | |
65 | ||
6b50cb5c YW |
66 | static int set_alternative_ifname(sd_netlink *rtnl, const char *ifname, const char *altifname) { |
67 | int r; | |
68 | ||
69 | assert(rtnl); | |
70 | assert(ifname); | |
71 | ||
72 | if (!altifname) | |
73 | return 0; | |
74 | ||
75 | if (strlen(altifname) >= ALTIFNAMSIZ) | |
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)); | |
81 | if (r < 0) | |
82 | return log_warning_errno(r, | |
83 | "Failed to set alternative interface name '%s' to '%s', ignoring: %m", | |
84 | altifname, ifname); | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
f6d6bad1 LP |
89 | static int add_veth( |
90 | sd_netlink *rtnl, | |
1434ccf7 | 91 | const PidRef *pid, |
f6d6bad1 | 92 | const char *ifname_host, |
6b50cb5c | 93 | const char *altifname_host, |
f6d6bad1 LP |
94 | const struct ether_addr *mac_host, |
95 | const char *ifname_container, | |
96 | const struct ether_addr *mac_container) { | |
9a2a5625 | 97 | |
4afd3348 | 98 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
f6d6bad1 | 99 | int r; |
9a2a5625 | 100 | |
f6d6bad1 | 101 | assert(rtnl); |
1434ccf7 | 102 | assert(pidref_is_set(pid)); |
f6d6bad1 LP |
103 | assert(ifname_host); |
104 | assert(mac_host); | |
105 | assert(ifname_container); | |
106 | assert(mac_container); | |
9a2a5625 LP |
107 | |
108 | r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); | |
109 | if (r < 0) | |
110 | return log_error_errno(r, "Failed to allocate netlink message: %m"); | |
111 | ||
f6d6bad1 | 112 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host); |
9a2a5625 LP |
113 | if (r < 0) |
114 | return log_error_errno(r, "Failed to add netlink interface name: %m"); | |
115 | ||
f6d6bad1 | 116 | r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host); |
9a2a5625 LP |
117 | if (r < 0) |
118 | return log_error_errno(r, "Failed to add netlink MAC address: %m"); | |
119 | ||
120 | r = sd_netlink_message_open_container(m, IFLA_LINKINFO); | |
121 | if (r < 0) | |
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"); | |
125 | if (r < 0) | |
126 | return log_error_errno(r, "Failed to open netlink container: %m"); | |
127 | ||
128 | r = sd_netlink_message_open_container(m, VETH_INFO_PEER); | |
129 | if (r < 0) | |
130 | return log_error_errno(r, "Failed to open netlink container: %m"); | |
131 | ||
f6d6bad1 | 132 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_container); |
9a2a5625 LP |
133 | if (r < 0) |
134 | return log_error_errno(r, "Failed to add netlink interface name: %m"); | |
135 | ||
f6d6bad1 | 136 | r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_container); |
9a2a5625 LP |
137 | if (r < 0) |
138 | return log_error_errno(r, "Failed to add netlink MAC address: %m"); | |
139 | ||
1434ccf7 | 140 | r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid->pid); |
9a2a5625 LP |
141 | if (r < 0) |
142 | return log_error_errno(r, "Failed to add netlink namespace field: %m"); | |
143 | ||
144 | r = sd_netlink_message_close_container(m); | |
145 | if (r < 0) | |
146 | return log_error_errno(r, "Failed to close netlink container: %m"); | |
147 | ||
148 | r = sd_netlink_message_close_container(m); | |
149 | if (r < 0) | |
150 | return log_error_errno(r, "Failed to close netlink container: %m"); | |
151 | ||
152 | r = sd_netlink_message_close_container(m); | |
153 | if (r < 0) | |
154 | return log_error_errno(r, "Failed to close netlink container: %m"); | |
155 | ||
156 | r = sd_netlink_call(rtnl, m, 0, NULL); | |
157 | if (r < 0) | |
f6d6bad1 LP |
158 | return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_container); |
159 | ||
6b50cb5c YW |
160 | (void) set_alternative_ifname(rtnl, ifname_host, altifname_host); |
161 | ||
f6d6bad1 LP |
162 | return 0; |
163 | } | |
164 | ||
165 | int setup_veth(const char *machine_name, | |
1434ccf7 | 166 | const PidRef *pid, |
f6d6bad1 | 167 | char iface_name[IFNAMSIZ], |
813dbff4 | 168 | bool bridge, |
5e21da87 | 169 | const struct ether_addr *provided_mac) { |
f6d6bad1 | 170 | |
4afd3348 | 171 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
f6d6bad1 | 172 | struct ether_addr mac_host, mac_container; |
bc5ea049 | 173 | unsigned u; |
6b50cb5c | 174 | char *n, *a = NULL; |
bc5ea049 | 175 | int r; |
f6d6bad1 LP |
176 | |
177 | assert(machine_name); | |
1434ccf7 | 178 | assert(pidref_is_set(pid)); |
f6d6bad1 LP |
179 | assert(iface_name); |
180 | ||
181 | /* Use two different interface name prefixes depending whether | |
182 | * we are in bridge mode or not. */ | |
bc5ea049 | 183 | n = strjoina(bridge ? "vb-" : "ve-", machine_name); |
c50e7dca | 184 | r = net_shorten_ifname(n, /* check_naming_scheme= */ true); |
6b50cb5c YW |
185 | if (r > 0) |
186 | a = strjoina(bridge ? "vb-" : "ve-", machine_name); | |
f6d6bad1 | 187 | |
813dbff4 | 188 | if (ether_addr_is_null(provided_mac)){ |
c50e7dca | 189 | r = net_generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0); |
813dbff4 RC |
190 | if (r < 0) |
191 | return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); | |
192 | } else | |
193 | mac_container = *provided_mac; | |
f6d6bad1 | 194 | |
c50e7dca | 195 | r = net_generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0); |
f6d6bad1 LP |
196 | if (r < 0) |
197 | return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); | |
198 | ||
199 | r = sd_netlink_open(&rtnl); | |
200 | if (r < 0) | |
201 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
202 | ||
6b50cb5c | 203 | r = add_veth(rtnl, pid, n, a, &mac_host, "host0", &mac_container); |
f6d6bad1 LP |
204 | if (r < 0) |
205 | return r; | |
9a2a5625 | 206 | |
f6e49154 | 207 | u = if_nametoindex(n); /* We don't need to use rtnl_resolve_ifname() here because the |
d308bb99 | 208 | * name we assigned is always the main name. */ |
bc5ea049 KK |
209 | if (u == 0) |
210 | return log_error_errno(errno, "Failed to resolve interface %s: %m", n); | |
9a2a5625 | 211 | |
bc5ea049 KK |
212 | strcpy(iface_name, n); |
213 | return (int) u; | |
9a2a5625 LP |
214 | } |
215 | ||
f6d6bad1 LP |
216 | int setup_veth_extra( |
217 | const char *machine_name, | |
1434ccf7 | 218 | const PidRef *pid, |
f6d6bad1 LP |
219 | char **pairs) { |
220 | ||
4afd3348 | 221 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
f6d6bad1 | 222 | uint64_t idx = 0; |
f6d6bad1 LP |
223 | int r; |
224 | ||
225 | assert(machine_name); | |
1434ccf7 | 226 | assert(pidref_is_set(pid)); |
f6d6bad1 LP |
227 | |
228 | if (strv_isempty(pairs)) | |
229 | return 0; | |
230 | ||
231 | r = sd_netlink_open(&rtnl); | |
232 | if (r < 0) | |
233 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
234 | ||
235 | STRV_FOREACH_PAIR(a, b, pairs) { | |
236 | struct ether_addr mac_host, mac_container; | |
237 | ||
c50e7dca | 238 | r = net_generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx); |
f6d6bad1 LP |
239 | if (r < 0) |
240 | return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m"); | |
241 | ||
c50e7dca | 242 | r = net_generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx); |
f6d6bad1 | 243 | if (r < 0) |
bcc0fe63 | 244 | return log_error_errno(r, "Failed to generate predictable MAC address for host side of extra veth link: %m"); |
f6d6bad1 | 245 | |
6b50cb5c | 246 | r = add_veth(rtnl, pid, *a, NULL, &mac_host, *b, &mac_container); |
f6d6bad1 LP |
247 | if (r < 0) |
248 | return r; | |
249 | ||
313cefa1 | 250 | idx++; |
f6d6bad1 LP |
251 | } |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
22b28dfd | 256 | static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *bridge_name) { |
4afd3348 | 257 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
9a2a5625 LP |
258 | int r, bridge_ifi; |
259 | ||
22b28dfd | 260 | assert(rtnl); |
9a2a5625 LP |
261 | assert(veth_name); |
262 | assert(bridge_name); | |
263 | ||
f6e49154 | 264 | bridge_ifi = rtnl_resolve_interface(&rtnl, bridge_name); |
597da51b ZJS |
265 | if (bridge_ifi < 0) |
266 | return bridge_ifi; | |
9a2a5625 LP |
267 | |
268 | r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); | |
269 | if (r < 0) | |
22b28dfd | 270 | return r; |
9a2a5625 LP |
271 | |
272 | r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP); | |
273 | if (r < 0) | |
22b28dfd | 274 | return r; |
9a2a5625 LP |
275 | |
276 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name); | |
277 | if (r < 0) | |
22b28dfd | 278 | return r; |
9a2a5625 LP |
279 | |
280 | r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi); | |
281 | if (r < 0) | |
22b28dfd | 282 | return r; |
9a2a5625 LP |
283 | |
284 | r = sd_netlink_call(rtnl, m, 0, NULL); | |
285 | if (r < 0) | |
22b28dfd | 286 | return r; |
9a2a5625 LP |
287 | |
288 | return bridge_ifi; | |
289 | } | |
290 | ||
22b28dfd LP |
291 | static int create_bridge(sd_netlink *rtnl, const char *bridge_name) { |
292 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
293 | int r; | |
294 | ||
295 | r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); | |
296 | if (r < 0) | |
297 | return r; | |
298 | ||
299 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, bridge_name); | |
300 | if (r < 0) | |
301 | return r; | |
302 | ||
303 | r = sd_netlink_message_open_container(m, IFLA_LINKINFO); | |
304 | if (r < 0) | |
305 | return r; | |
306 | ||
307 | r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "bridge"); | |
308 | if (r < 0) | |
309 | return r; | |
310 | ||
311 | r = sd_netlink_message_close_container(m); | |
312 | if (r < 0) | |
313 | return r; | |
314 | ||
315 | r = sd_netlink_message_close_container(m); | |
316 | if (r < 0) | |
317 | return r; | |
318 | ||
319 | r = sd_netlink_call(rtnl, m, 0, NULL); | |
320 | if (r < 0) | |
321 | return r; | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | int setup_bridge(const char *veth_name, const char *bridge_name, bool create) { | |
8e766630 | 327 | _cleanup_(release_lock_file) LockFile bridge_lock = LOCK_FILE_INIT; |
22b28dfd LP |
328 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
329 | int r, bridge_ifi; | |
330 | unsigned n = 0; | |
331 | ||
332 | assert(veth_name); | |
333 | assert(bridge_name); | |
334 | ||
335 | r = sd_netlink_open(&rtnl); | |
336 | if (r < 0) | |
337 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
338 | ||
339 | if (create) { | |
340 | /* We take a system-wide lock here, so that we can safely check whether there's still a member in the | |
6dd6a9c4 | 341 | * bridge before removing it, without risking interference from other nspawn instances. */ |
22b28dfd LP |
342 | |
343 | r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); | |
344 | if (r < 0) | |
345 | return log_error_errno(r, "Failed to take network zone lock: %m"); | |
346 | } | |
347 | ||
348 | for (;;) { | |
349 | bridge_ifi = join_bridge(rtnl, veth_name, bridge_name); | |
350 | if (bridge_ifi >= 0) | |
351 | return bridge_ifi; | |
352 | if (bridge_ifi != -ENODEV || !create || n > 10) | |
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++; | |
357 | ||
358 | /* The bridge doesn't exist yet. Let's create it */ | |
359 | r = create_bridge(rtnl, bridge_name); | |
360 | if (r < 0) | |
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) { | |
8e766630 | 368 | _cleanup_(release_lock_file) LockFile bridge_lock = LOCK_FILE_INIT; |
22b28dfd LP |
369 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
370 | const char *path; | |
371 | int r; | |
372 | ||
373 | /* Removes the specified bridge, but only if it is currently empty */ | |
374 | ||
375 | if (isempty(bridge_name)) | |
376 | return 0; | |
377 | ||
378 | r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); | |
379 | if (r < 0) | |
380 | return log_error_errno(r, "Failed to take network zone lock: %m"); | |
381 | ||
382 | path = strjoina("/sys/class/net/", bridge_name, "/brif"); | |
383 | ||
db55bbf2 | 384 | r = dir_is_empty(path, /* ignore_hidden_or_backup= */ false); |
22b28dfd LP |
385 | if (r == -ENOENT) /* Already gone? */ |
386 | return 0; | |
387 | if (r < 0) | |
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 */ | |
390 | return 0; | |
391 | ||
392 | r = sd_netlink_open(&rtnl); | |
393 | if (r < 0) | |
394 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
395 | ||
396 | return remove_one_link(rtnl, bridge_name); | |
397 | } | |
398 | ||
2f091b1b | 399 | static int test_network_interface_initialized(const char *name) { |
b390f178 | 400 | _cleanup_(sd_device_unrefp) sd_device *d = NULL; |
0ac655a6 | 401 | int r; |
26208d5b | 402 | |
c76b8751 | 403 | if (!udev_available()) |
b390f178 | 404 | return 0; |
26208d5b | 405 | |
b390f178 | 406 | /* udev should be around. */ |
26208d5b | 407 | |
0ac655a6 | 408 | r = sd_device_new_from_ifname(&d, name); |
b390f178 DDM |
409 | if (r < 0) |
410 | return log_error_errno(r, "Failed to get device %s: %m", name); | |
411 | ||
54e61725 | 412 | r = device_is_processed(d); |
b390f178 DDM |
413 | if (r < 0) |
414 | return log_error_errno(r, "Failed to determine whether interface %s is initialized: %m", name); | |
415 | if (r == 0) | |
416 | return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Network interface %s is not initialized yet.", name); | |
417 | ||
418 | r = device_is_renaming(d); | |
419 | if (r < 0) | |
420 | return log_error_errno(r, "Failed to determine the interface %s is being renamed: %m", name); | |
421 | if (r > 0) | |
422 | return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Interface %s is being renamed.", name); | |
423 | ||
424 | return 0; | |
9a2a5625 LP |
425 | } |
426 | ||
2f091b1b TM |
427 | int test_network_interfaces_initialized(char **iface_pairs) { |
428 | int r; | |
429 | STRV_FOREACH_PAIR(a, b, iface_pairs) { | |
430 | r = test_network_interface_initialized(*a); | |
431 | if (r < 0) | |
432 | return r; | |
433 | } | |
434 | return 0; | |
435 | } | |
436 | ||
a342d9e0 YW |
437 | int resolve_network_interface_names(char **iface_pairs) { |
438 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
439 | int r; | |
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) { | |
447 | _cleanup_free_ char *name = NULL; | |
448 | _cleanup_strv_free_ char **altnames = NULL; | |
449 | ||
450 | r = rtnl_resolve_ifname_full(&rtnl, _RESOLVE_IFNAME_ALL, *from, &name, &altnames); | |
451 | if (r < 0) | |
452 | return r; | |
453 | ||
454 | /* Always use the resolved name for 'from'. */ | |
455 | free_and_replace(*from, name); | |
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)) { | |
461 | r = free_and_strdup_warn(to, *from); | |
462 | if (r < 0) | |
463 | return r; | |
464 | } | |
465 | } | |
466 | return 0; | |
467 | } | |
468 | ||
cdd9988e YW |
469 | static int netns_child_begin(int netns_fd, int *ret_original_netns_fd) { |
470 | _cleanup_close_ int original_netns_fd = -EBADF; | |
471 | int r; | |
472 | ||
473 | assert(netns_fd >= 0); | |
474 | ||
475 | if (ret_original_netns_fd) { | |
8c0da3af LP |
476 | original_netns_fd = namespace_open_by_type(NAMESPACE_NET); |
477 | if (original_netns_fd < 0) | |
478 | return log_error_errno(original_netns_fd, "Failed to open original network namespace: %m"); | |
cdd9988e YW |
479 | } |
480 | ||
481 | r = namespace_enter(/* pidns_fd = */ -EBADF, | |
482 | /* mntns_fd = */ -EBADF, | |
483 | netns_fd, | |
484 | /* userns_fd = */ -EBADF, | |
485 | /* root_fd = */ -EBADF); | |
486 | if (r < 0) | |
487 | return log_error_errno(r, "Failed to enter child network namespace: %m"); | |
488 | ||
38475cac YW |
489 | r = umount_recursive("/sys/", /* flags = */ 0); |
490 | if (r < 0) | |
491 | log_debug_errno(r, "Failed to unmount directories below /sys/, ignoring: %m"); | |
492 | ||
493 | (void) mkdir_p("/sys/", 0755); | |
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", | |
346c62f9 | 497 | MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, /* options = */ NULL); |
38475cac YW |
498 | if (r < 0) |
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(); | |
504 | ||
cdd9988e YW |
505 | if (ret_original_netns_fd) |
506 | *ret_original_netns_fd = TAKE_FD(original_netns_fd); | |
507 | ||
508 | return 0; | |
509 | } | |
510 | ||
511 | static int netns_fork_and_wait(int netns_fd, int *ret_original_netns_fd) { | |
512 | int r; | |
513 | ||
514 | assert(netns_fd >= 0); | |
515 | ||
38475cac | 516 | r = safe_fork("(sd-netns)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL); |
cdd9988e YW |
517 | if (r < 0) |
518 | return log_error_errno(r, "Failed to fork process (sd-netns): %m"); | |
519 | if (r == 0) { | |
520 | if (netns_child_begin(netns_fd, ret_original_netns_fd) < 0) | |
521 | _exit(EXIT_FAILURE); | |
522 | ||
523 | return 0; | |
524 | } | |
525 | ||
526 | if (ret_original_netns_fd) | |
527 | *ret_original_netns_fd = -EBADF; | |
528 | ||
529 | return 1; | |
530 | } | |
531 | ||
1c3e5b42 YW |
532 | static int move_wlan_interface_impl(sd_netlink **genl, int netns_fd, sd_device *dev) { |
533 | _cleanup_(sd_netlink_unrefp) sd_netlink *our_genl = NULL; | |
534 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
535 | int r; | |
536 | ||
537 | assert(netns_fd >= 0); | |
538 | assert(dev); | |
539 | ||
540 | if (!genl) | |
541 | genl = &our_genl; | |
542 | if (!*genl) { | |
543 | r = sd_genl_socket_open(genl); | |
544 | if (r < 0) | |
545 | return log_error_errno(r, "Failed to connect to generic netlink: %m"); | |
546 | } | |
547 | ||
548 | r = sd_genl_message_new(*genl, NL80211_GENL_NAME, NL80211_CMD_SET_WIPHY_NETNS, &m); | |
549 | if (r < 0) | |
550 | return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m"); | |
551 | ||
552 | uint32_t phy_index; | |
553 | r = device_get_sysattr_u32(dev, "phy80211/index", &phy_index); | |
554 | if (r < 0) | |
555 | return log_device_error_errno(dev, r, "Failed to get phy index: %m"); | |
556 | ||
557 | r = sd_netlink_message_append_u32(m, NL80211_ATTR_WIPHY, phy_index); | |
558 | if (r < 0) | |
559 | return log_device_error_errno(dev, r, "Failed to append phy index to netlink message: %m"); | |
560 | ||
561 | r = sd_netlink_message_append_u32(m, NL80211_ATTR_NETNS_FD, netns_fd); | |
562 | if (r < 0) | |
563 | return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m"); | |
564 | ||
565 | r = sd_netlink_call(*genl, m, 0, NULL); | |
566 | if (r < 0) | |
567 | return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m"); | |
568 | ||
569 | return 0; | |
570 | } | |
571 | ||
572 | static int move_wlan_interface_one( | |
573 | sd_netlink **rtnl, | |
574 | sd_netlink **genl, | |
575 | int *temp_netns_fd, | |
576 | int netns_fd, | |
577 | sd_device *dev, | |
578 | const char *name) { | |
579 | ||
580 | int r; | |
581 | ||
582 | assert(rtnl); | |
583 | assert(genl); | |
584 | assert(temp_netns_fd); | |
585 | assert(netns_fd >= 0); | |
586 | assert(dev); | |
1c3e5b42 | 587 | |
a342d9e0 | 588 | if (!name) |
1c3e5b42 YW |
589 | return move_wlan_interface_impl(genl, netns_fd, dev); |
590 | ||
591 | /* The command NL80211_CMD_SET_WIPHY_NETNS takes phy instead of network interface, and does not take | |
592 | * an interface name in the passed network namespace. Hence, we need to move the phy and interface to | |
593 | * a temporary network namespace, rename the interface in it, and move them to the requested netns. */ | |
594 | ||
595 | if (*temp_netns_fd < 0) { | |
596 | r = netns_acquire(); | |
597 | if (r < 0) | |
598 | return log_error_errno(r, "Failed to acquire new network namespace: %m"); | |
599 | *temp_netns_fd = r; | |
600 | } | |
601 | ||
602 | r = move_wlan_interface_impl(genl, *temp_netns_fd, dev); | |
603 | if (r < 0) | |
604 | return r; | |
605 | ||
606 | const char *sysname; | |
607 | r = sd_device_get_sysname(dev, &sysname); | |
608 | if (r < 0) | |
609 | return log_device_error_errno(dev, r, "Failed to get interface name: %m"); | |
610 | ||
611 | r = netns_fork_and_wait(*temp_netns_fd, NULL); | |
612 | if (r < 0) | |
613 | return log_error_errno(r, "Failed to fork process (nspawn-rename-wlan): %m"); | |
614 | if (r == 0) { | |
615 | _cleanup_(sd_device_unrefp) sd_device *temp_dev = NULL; | |
616 | ||
617 | r = rtnl_rename_link(NULL, sysname, name); | |
618 | if (r < 0) { | |
619 | log_error_errno(r, "Failed to rename network interface '%s' to '%s': %m", sysname, name); | |
620 | goto finalize; | |
621 | } | |
622 | ||
623 | r = sd_device_new_from_ifname(&temp_dev, name); | |
624 | if (r < 0) { | |
625 | log_error_errno(r, "Failed to acquire device '%s': %m", name); | |
626 | goto finalize; | |
627 | } | |
628 | ||
629 | r = move_wlan_interface_impl(NULL, netns_fd, temp_dev); | |
630 | ||
631 | finalize: | |
632 | _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); | |
633 | } | |
634 | ||
635 | return 0; | |
636 | } | |
637 | ||
38475cac YW |
638 | static int move_network_interface_one(sd_netlink **rtnl, int netns_fd, sd_device *dev, const char *name) { |
639 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; | |
9a2a5625 LP |
640 | int r; |
641 | ||
38475cac YW |
642 | assert(rtnl); |
643 | assert(netns_fd >= 0); | |
644 | assert(dev); | |
9a2a5625 | 645 | |
38475cac YW |
646 | if (!*rtnl) { |
647 | r = sd_netlink_open(rtnl); | |
648 | if (r < 0) | |
649 | return log_error_errno(r, "Failed to connect to rtnetlink: %m"); | |
650 | } | |
651 | ||
652 | int ifindex; | |
653 | r = sd_device_get_ifindex(dev, &ifindex); | |
9a2a5625 | 654 | if (r < 0) |
38475cac YW |
655 | return log_device_error_errno(dev, r, "Failed to get ifindex: %m"); |
656 | ||
657 | r = sd_rtnl_message_new_link(*rtnl, &m, RTM_SETLINK, ifindex); | |
658 | if (r < 0) | |
659 | return log_device_error_errno(dev, r, "Failed to allocate netlink message: %m"); | |
9a2a5625 | 660 | |
38475cac YW |
661 | r = sd_netlink_message_append_u32(m, IFLA_NET_NS_FD, netns_fd); |
662 | if (r < 0) | |
663 | return log_device_error_errno(dev, r, "Failed to append namespace fd to netlink message: %m"); | |
9a2a5625 | 664 | |
a342d9e0 | 665 | if (name) { |
38475cac | 666 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); |
9a2a5625 | 667 | if (r < 0) |
38475cac YW |
668 | return log_device_error_errno(dev, r, "Failed to add netlink interface name: %m"); |
669 | } | |
9a2a5625 | 670 | |
38475cac YW |
671 | r = sd_netlink_call(*rtnl, m, 0, NULL); |
672 | if (r < 0) | |
673 | return log_device_error_errno(dev, r, "Failed to move interface to namespace: %m"); | |
674 | ||
675 | return 0; | |
676 | } | |
677 | ||
678 | int move_network_interfaces(int netns_fd, char **iface_pairs) { | |
1c3e5b42 YW |
679 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL, *genl = NULL; |
680 | _cleanup_close_ int temp_netns_fd = -EBADF; | |
38475cac YW |
681 | int r; |
682 | ||
683 | assert(netns_fd >= 0); | |
684 | ||
685 | if (strv_isempty(iface_pairs)) | |
686 | return 0; | |
9a2a5625 | 687 | |
38475cac YW |
688 | STRV_FOREACH_PAIR(from, to, iface_pairs) { |
689 | _cleanup_(sd_device_unrefp) sd_device *dev = NULL; | |
a342d9e0 YW |
690 | const char *name; |
691 | ||
692 | name = streq(*from, *to) ? NULL : *to; | |
2f091b1b | 693 | |
38475cac | 694 | r = sd_device_new_from_ifname(&dev, *from); |
9a2a5625 | 695 | if (r < 0) |
38475cac YW |
696 | return log_error_errno(r, "Unknown interface name %s: %m", *from); |
697 | ||
1c3e5b42 | 698 | if (device_is_devtype(dev, "wlan")) |
a342d9e0 | 699 | r = move_wlan_interface_one(&rtnl, &genl, &temp_netns_fd, netns_fd, dev, name); |
1c3e5b42 | 700 | else |
a342d9e0 | 701 | r = move_network_interface_one(&rtnl, netns_fd, dev, name); |
38475cac YW |
702 | if (r < 0) |
703 | return r; | |
9a2a5625 LP |
704 | } |
705 | ||
706 | return 0; | |
707 | } | |
708 | ||
cdd9988e YW |
709 | int move_back_network_interfaces(int child_netns_fd, char **interface_pairs) { |
710 | _cleanup_close_ int parent_netns_fd = -EBADF; | |
711 | int r; | |
712 | ||
713 | assert(child_netns_fd >= 0); | |
714 | ||
715 | if (strv_isempty(interface_pairs)) | |
716 | return 0; | |
717 | ||
718 | r = netns_fork_and_wait(child_netns_fd, &parent_netns_fd); | |
719 | if (r < 0) | |
720 | return r; | |
721 | if (r == 0) { | |
722 | /* Reverse network interfaces pair list so that interfaces get their initial name back. | |
723 | * This is about ensuring interfaces get their old name back when being moved back. */ | |
724 | interface_pairs = strv_reverse(interface_pairs); | |
725 | ||
726 | r = move_network_interfaces(parent_netns_fd, interface_pairs); | |
727 | _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); | |
728 | } | |
729 | ||
730 | return 0; | |
731 | } | |
732 | ||
1434ccf7 | 733 | int setup_macvlan(const char *machine_name, const PidRef *pid, char **iface_pairs) { |
4afd3348 | 734 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
9a2a5625 | 735 | unsigned idx = 0; |
9a2a5625 LP |
736 | int r; |
737 | ||
1434ccf7 LP |
738 | assert(pidref_is_set(pid)); |
739 | ||
2f091b1b | 740 | if (strv_isempty(iface_pairs)) |
9a2a5625 LP |
741 | return 0; |
742 | ||
743 | r = sd_netlink_open(&rtnl); | |
744 | if (r < 0) | |
745 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
746 | ||
2f091b1b | 747 | STRV_FOREACH_PAIR(i, b, iface_pairs) { |
4afd3348 | 748 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
2f091b1b TM |
749 | _cleanup_free_ char *n = NULL; |
750 | int shortened, ifi; | |
9a2a5625 | 751 | struct ether_addr mac; |
9a2a5625 | 752 | |
f6e49154 | 753 | ifi = rtnl_resolve_interface_or_warn(&rtnl, *i); |
9a2a5625 LP |
754 | if (ifi < 0) |
755 | return ifi; | |
756 | ||
c50e7dca | 757 | r = net_generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++); |
9a2a5625 LP |
758 | if (r < 0) |
759 | return log_error_errno(r, "Failed to create MACVLAN MAC address: %m"); | |
760 | ||
761 | r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); | |
762 | if (r < 0) | |
763 | return log_error_errno(r, "Failed to allocate netlink message: %m"); | |
764 | ||
765 | r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi); | |
766 | if (r < 0) | |
767 | return log_error_errno(r, "Failed to add netlink interface index: %m"); | |
768 | ||
2f091b1b | 769 | n = strdup(*b); |
9a2a5625 LP |
770 | if (!n) |
771 | return log_oom(); | |
772 | ||
c50e7dca | 773 | shortened = net_shorten_ifname(n, /* check_naming_scheme= */ true); |
9a2a5625 LP |
774 | |
775 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); | |
776 | if (r < 0) | |
777 | return log_error_errno(r, "Failed to add netlink interface name: %m"); | |
778 | ||
779 | r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac); | |
780 | if (r < 0) | |
781 | return log_error_errno(r, "Failed to add netlink MAC address: %m"); | |
782 | ||
1434ccf7 | 783 | r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid->pid); |
9a2a5625 LP |
784 | if (r < 0) |
785 | return log_error_errno(r, "Failed to add netlink namespace field: %m"); | |
786 | ||
787 | r = sd_netlink_message_open_container(m, IFLA_LINKINFO); | |
788 | if (r < 0) | |
789 | return log_error_errno(r, "Failed to open netlink container: %m"); | |
790 | ||
791 | r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan"); | |
792 | if (r < 0) | |
793 | return log_error_errno(r, "Failed to open netlink container: %m"); | |
794 | ||
795 | r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE); | |
796 | if (r < 0) | |
797 | return log_error_errno(r, "Failed to append macvlan mode: %m"); | |
798 | ||
799 | r = sd_netlink_message_close_container(m); | |
800 | if (r < 0) | |
801 | return log_error_errno(r, "Failed to close netlink container: %m"); | |
802 | ||
803 | r = sd_netlink_message_close_container(m); | |
804 | if (r < 0) | |
805 | return log_error_errno(r, "Failed to close netlink container: %m"); | |
806 | ||
807 | r = sd_netlink_call(rtnl, m, 0, NULL); | |
808 | if (r < 0) | |
809 | return log_error_errno(r, "Failed to add new macvlan interfaces: %m"); | |
6b50cb5c | 810 | |
2f091b1b TM |
811 | if (shortened > 0) |
812 | (void) set_alternative_ifname(rtnl, n, *b); | |
9a2a5625 LP |
813 | } |
814 | ||
815 | return 0; | |
816 | } | |
817 | ||
1bfa4741 YW |
818 | static int remove_macvlan_impl(char **interface_pairs) { |
819 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
820 | int r; | |
821 | ||
822 | assert(interface_pairs); | |
823 | ||
824 | r = sd_netlink_open(&rtnl); | |
825 | if (r < 0) | |
826 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
827 | ||
828 | STRV_FOREACH_PAIR(a, b, interface_pairs) { | |
829 | _cleanup_free_ char *n = NULL; | |
830 | ||
831 | n = strdup(*b); | |
832 | if (!n) | |
833 | return log_oom(); | |
834 | ||
835 | (void) net_shorten_ifname(n, /* check_naming_scheme= */ true); | |
836 | ||
837 | r = remove_one_link(rtnl, n); | |
838 | if (r < 0) | |
839 | log_warning_errno(r, "Failed to remove macvlan interface %s, ignoring: %m", n); | |
840 | } | |
841 | ||
842 | return 0; | |
843 | } | |
844 | ||
845 | int remove_macvlan(int child_netns_fd, char **interface_pairs) { | |
846 | _cleanup_close_ int parent_netns_fd = -EBADF; | |
847 | int r; | |
848 | ||
849 | /* In some cases the kernel might pin the macvlan links on the container even after the namespace | |
850 | * died. Hence, let's better remove them explicitly too. See issue #680. */ | |
851 | ||
852 | assert(child_netns_fd >= 0); | |
853 | ||
854 | if (strv_isempty(interface_pairs)) | |
855 | return 0; | |
856 | ||
857 | r = netns_fork_and_wait(child_netns_fd, &parent_netns_fd); | |
858 | if (r < 0) | |
859 | return r; | |
860 | if (r == 0) { | |
861 | r = remove_macvlan_impl(interface_pairs); | |
862 | _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); | |
863 | } | |
864 | ||
865 | return 0; | |
866 | } | |
867 | ||
1434ccf7 | 868 | int setup_ipvlan(const char *machine_name, const PidRef *pid, char **iface_pairs) { |
4afd3348 | 869 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
9a2a5625 LP |
870 | int r; |
871 | ||
1434ccf7 LP |
872 | assert(pidref_is_set(pid)); |
873 | ||
2f091b1b | 874 | if (strv_isempty(iface_pairs)) |
9a2a5625 LP |
875 | return 0; |
876 | ||
877 | r = sd_netlink_open(&rtnl); | |
878 | if (r < 0) | |
879 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
880 | ||
2f091b1b | 881 | STRV_FOREACH_PAIR(i, b, iface_pairs) { |
4afd3348 | 882 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
2f091b1b TM |
883 | _cleanup_free_ char *n = NULL; |
884 | int shortened, ifi ; | |
9a2a5625 | 885 | |
f6e49154 | 886 | ifi = rtnl_resolve_interface_or_warn(&rtnl, *i); |
9a2a5625 LP |
887 | if (ifi < 0) |
888 | return ifi; | |
889 | ||
890 | r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); | |
891 | if (r < 0) | |
892 | return log_error_errno(r, "Failed to allocate netlink message: %m"); | |
893 | ||
894 | r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi); | |
895 | if (r < 0) | |
896 | return log_error_errno(r, "Failed to add netlink interface index: %m"); | |
897 | ||
2f091b1b | 898 | n = strdup(*b); |
9a2a5625 LP |
899 | if (!n) |
900 | return log_oom(); | |
901 | ||
c50e7dca | 902 | shortened = net_shorten_ifname(n, /* check_naming_scheme= */ true); |
9a2a5625 LP |
903 | |
904 | r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); | |
905 | if (r < 0) | |
906 | return log_error_errno(r, "Failed to add netlink interface name: %m"); | |
907 | ||
1434ccf7 | 908 | r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid->pid); |
9a2a5625 LP |
909 | if (r < 0) |
910 | return log_error_errno(r, "Failed to add netlink namespace field: %m"); | |
911 | ||
912 | r = sd_netlink_message_open_container(m, IFLA_LINKINFO); | |
913 | if (r < 0) | |
914 | return log_error_errno(r, "Failed to open netlink container: %m"); | |
915 | ||
916 | r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan"); | |
917 | if (r < 0) | |
918 | return log_error_errno(r, "Failed to open netlink container: %m"); | |
919 | ||
920 | r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2); | |
921 | if (r < 0) | |
922 | return log_error_errno(r, "Failed to add ipvlan mode: %m"); | |
923 | ||
924 | r = sd_netlink_message_close_container(m); | |
925 | if (r < 0) | |
926 | return log_error_errno(r, "Failed to close netlink container: %m"); | |
927 | ||
928 | r = sd_netlink_message_close_container(m); | |
929 | if (r < 0) | |
930 | return log_error_errno(r, "Failed to close netlink container: %m"); | |
931 | ||
932 | r = sd_netlink_call(rtnl, m, 0, NULL); | |
933 | if (r < 0) | |
934 | return log_error_errno(r, "Failed to add new ipvlan interfaces: %m"); | |
6b50cb5c | 935 | |
2f091b1b TM |
936 | if (shortened > 0) |
937 | (void) set_alternative_ifname(rtnl, n, *b); | |
9a2a5625 LP |
938 | } |
939 | ||
940 | return 0; | |
941 | } | |
f6d6bad1 LP |
942 | |
943 | int veth_extra_parse(char ***l, const char *p) { | |
944 | _cleanup_free_ char *a = NULL, *b = NULL; | |
945 | int r; | |
946 | ||
947 | r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |
948 | if (r < 0) | |
949 | return r; | |
ef76dff2 | 950 | if (r == 0 || !ifname_valid(a)) |
f6d6bad1 LP |
951 | return -EINVAL; |
952 | ||
953 | r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |
954 | if (r < 0) | |
955 | return r; | |
ef76dff2 | 956 | if (r == 0 || !ifname_valid(b)) { |
a73e5eb9 DT |
957 | r = free_and_strdup(&b, a); |
958 | if (r < 0) | |
959 | return r; | |
f6d6bad1 LP |
960 | } |
961 | ||
962 | if (p) | |
963 | return -EINVAL; | |
964 | ||
965 | r = strv_push_pair(l, a, b); | |
966 | if (r < 0) | |
967 | return -ENOMEM; | |
968 | ||
969 | a = b = NULL; | |
970 | return 0; | |
971 | } | |
ef3b2aa7 | 972 | |
ef3b2aa7 LP |
973 | int remove_veth_links(const char *primary, char **pairs) { |
974 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; | |
ef3b2aa7 LP |
975 | int r; |
976 | ||
977 | /* In some cases the kernel might pin the veth links between host and container even after the namespace | |
978 | * died. Hence, let's better remove them explicitly too. */ | |
979 | ||
980 | if (isempty(primary) && strv_isempty(pairs)) | |
981 | return 0; | |
982 | ||
983 | r = sd_netlink_open(&rtnl); | |
984 | if (r < 0) | |
985 | return log_error_errno(r, "Failed to connect to netlink: %m"); | |
986 | ||
22b28dfd | 987 | remove_one_link(rtnl, primary); |
ef3b2aa7 LP |
988 | |
989 | STRV_FOREACH_PAIR(a, b, pairs) | |
22b28dfd | 990 | remove_one_link(rtnl, *a); |
ef3b2aa7 LP |
991 | |
992 | return 0; | |
993 | } | |
2f091b1b TM |
994 | |
995 | static int network_iface_pair_parse(const char* iftype, char ***l, const char *p, const char* ifprefix) { | |
2f091b1b TM |
996 | int r; |
997 | ||
8f4d843a FS |
998 | for (;;) { |
999 | _cleanup_free_ char *word = NULL, *a = NULL, *b = NULL; | |
1000 | const char *interface; | |
2f091b1b | 1001 | |
8f4d843a FS |
1002 | r = extract_first_word(&p, &word, NULL, 0); |
1003 | if (r < 0) | |
1004 | return log_error_errno(r, "Failed to parse interface name: %m"); | |
1005 | if (r == 0) | |
1006 | break; | |
2f091b1b | 1007 | |
8f4d843a FS |
1008 | interface = word; |
1009 | r = extract_first_word(&interface, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |
1010 | if (r < 0) | |
1011 | return log_error_errno(r, "Failed to extract first word in %s parameter: %m", iftype); | |
1012 | if (r == 0) | |
1013 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
1014 | "Short read while reading %s parameter: %m", iftype); | |
1015 | if (!ifname_valid(a)) | |
1016 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
1017 | "%s, interface name not valid: %s", iftype, a); | |
1018 | ||
927e20fa YW |
1019 | /* Here, we only check the validity of the specified second name. If it is not specified, |
1020 | * the copied or prefixed name should be already valid, except for its length. If it is too | |
1021 | * long, then it will be shortened later. */ | |
1022 | if (!isempty(interface)) { | |
1023 | if (!ifname_valid(interface)) | |
1024 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), | |
1025 | "%s, interface name not valid: %s", iftype, interface); | |
1026 | ||
8f4d843a | 1027 | b = strdup(interface); |
927e20fa YW |
1028 | } else if (ifprefix) |
1029 | b = strjoin(ifprefix, a); | |
1030 | else | |
1031 | b = strdup(a); | |
8f4d843a FS |
1032 | if (!b) |
1033 | return log_oom(); | |
1034 | ||
8f4d843a FS |
1035 | r = strv_consume_pair(l, TAKE_PTR(a), TAKE_PTR(b)); |
1036 | if (r < 0) | |
1037 | return log_oom(); | |
1038 | } | |
2f091b1b | 1039 | |
2f091b1b TM |
1040 | return 0; |
1041 | } | |
1042 | ||
1043 | int interface_pair_parse(char ***l, const char *p) { | |
1044 | return network_iface_pair_parse("Network interface", l, p, NULL); | |
1045 | } | |
1046 | ||
1047 | int macvlan_pair_parse(char ***l, const char *p) { | |
1048 | return network_iface_pair_parse("MACVLAN network interface", l, p, "mv-"); | |
1049 | } | |
1050 | ||
1051 | int ipvlan_pair_parse(char ***l, const char *p) { | |
1052 | return network_iface_pair_parse("IPVLAN network interface", l, p, "iv-"); | |
1053 | } |