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