summaryrefslogtreecommitdiffstats
path: root/src/nspawn
diff options
context:
space:
mode:
authorThierry Martin <tmartin@haproxy.com>2022-09-05 15:02:06 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2023-04-12 14:28:43 +0200
commit2f091b1b49543aade4aad9ec3b35b3665abac3e7 (patch)
tree9fc428bbdeec30c3870c4f7ea771486aa1668009 /src/nspawn
parentMerge pull request #25608 from poettering/dissect-moar (diff)
downloadsystemd-2f091b1b49543aade4aad9ec3b35b3665abac3e7.tar.xz
systemd-2f091b1b49543aade4aad9ec3b35b3665abac3e7.zip
nspawn: container network interface naming
systemd-nspawn now optionally supports colon-separated pair of host interface name and container interface name for --network-macvlan, --network-ipvlan and --network-interface options. Also supported in .nspawn configuration files (i.e Interface=, MACVLAN=, IPVLAN= parameters). man page changed for ntwk interface naming
Diffstat (limited to 'src/nspawn')
-rw-r--r--src/nspawn/nspawn-gperf.gperf6
-rw-r--r--src/nspawn/nspawn-network.c116
-rw-r--r--src/nspawn/nspawn-network.h12
-rw-r--r--src/nspawn/nspawn-settings.c63
-rw-r--r--src/nspawn/nspawn-settings.h3
-rw-r--r--src/nspawn/nspawn.c60
6 files changed, 194 insertions, 66 deletions
diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf
index a93b8c38c9..9e1210f876 100644
--- a/src/nspawn/nspawn-gperf.gperf
+++ b/src/nspawn/nspawn-gperf.gperf
@@ -72,9 +72,9 @@ Files.PrivateUsersChown, config_parse_userns_chown, 0,
Files.PrivateUsersOwnership, config_parse_userns_ownership, 0, offsetof(Settings, userns_ownership)
Files.BindUser, config_parse_bind_user, 0, offsetof(Settings, bind_user)
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
-Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
-Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)
-Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan)
+Network.Interface, config_parse_network_iface_pair, 0, offsetof(Settings, network_interfaces)
+Network.MACVLAN, config_parse_macvlan_iface_pair, 0, offsetof(Settings, network_macvlan)
+Network.IPVLAN, config_parse_ipvlan_iface_pair, 0, offsetof(Settings, network_ipvlan)
Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth)
Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0
Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge)
diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c
index 143156484b..d898f0d4c9 100644
--- a/src/nspawn/nspawn-network.c
+++ b/src/nspawn/nspawn-network.c
@@ -463,7 +463,7 @@ int remove_bridge(const char *bridge_name) {
return remove_one_link(rtnl, bridge_name);
}
-int test_network_interface_initialized(const char *name) {
+static int test_network_interface_initialized(const char *name) {
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
int r;
@@ -491,18 +491,28 @@ int test_network_interface_initialized(const char *name) {
return 0;
}
-int move_network_interfaces(int netns_fd, char **ifaces) {
+int test_network_interfaces_initialized(char **iface_pairs) {
+ int r;
+ STRV_FOREACH_PAIR(a, b, iface_pairs) {
+ r = test_network_interface_initialized(*a);
+ if (r < 0)
+ return r;
+ }
+ return 0;
+}
+
+int move_network_interfaces(int netns_fd, char **iface_pairs) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r;
- if (strv_isempty(ifaces))
+ if (strv_isempty(iface_pairs))
return 0;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- STRV_FOREACH(i, ifaces) {
+ STRV_FOREACH_PAIR(i, b, iface_pairs) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int ifi;
@@ -518,6 +528,12 @@ int move_network_interfaces(int netns_fd, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to append namespace fd to netlink message: %m");
+ if (!streq(*b, *i)) {
+ r = sd_netlink_message_append_string(m, IFLA_IFNAME, *b);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add netlink interface name: %m");
+ }
+
r = sd_netlink_call(rtnl, m, 0, NULL);
if (r < 0)
return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i);
@@ -526,23 +542,23 @@ int move_network_interfaces(int netns_fd, char **ifaces) {
return 0;
}
-int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
+int setup_macvlan(const char *machine_name, pid_t pid, char **iface_pairs) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
unsigned idx = 0;
int r;
- if (strv_isempty(ifaces))
+ if (strv_isempty(iface_pairs))
return 0;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- STRV_FOREACH(i, ifaces) {
+ STRV_FOREACH_PAIR(i, b, iface_pairs) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- _cleanup_free_ char *n = NULL, *a = NULL;
+ _cleanup_free_ char *n = NULL;
+ int shortened, ifi;
struct ether_addr mac;
- int ifi;
ifi = rtnl_resolve_interface_or_warn(&rtnl, *i);
if (ifi < 0)
@@ -560,16 +576,11 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface index: %m");
- n = strjoin("mv-", *i);
+ n = strdup(*b);
if (!n)
return log_oom();
- r = shorten_ifname(n);
- if (r > 0) {
- a = strjoin("mv-", *i);
- if (!a)
- return log_oom();
- }
+ shortened = shorten_ifname(n);
r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)
@@ -607,27 +618,28 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to add new macvlan interfaces: %m");
- (void) set_alternative_ifname(rtnl, n, a);
+ if (shortened > 0)
+ (void) set_alternative_ifname(rtnl, n, *b);
}
return 0;
}
-int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
+int setup_ipvlan(const char *machine_name, pid_t pid, char **iface_pairs) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int r;
- if (strv_isempty(ifaces))
+ if (strv_isempty(iface_pairs))
return 0;
r = sd_netlink_open(&rtnl);
if (r < 0)
return log_error_errno(r, "Failed to connect to netlink: %m");
- STRV_FOREACH(i, ifaces) {
+ STRV_FOREACH_PAIR(i, b, iface_pairs) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
- _cleanup_free_ char *n = NULL, *a = NULL;
- int ifi;
+ _cleanup_free_ char *n = NULL;
+ int shortened, ifi ;
ifi = rtnl_resolve_interface_or_warn(&rtnl, *i);
if (ifi < 0)
@@ -641,16 +653,11 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to add netlink interface index: %m");
- n = strjoin("iv-", *i);
+ n = strdup(*b);
if (!n)
return log_oom();
- r = shorten_ifname(n);
- if (r > 0) {
- a = strjoin("iv-", *i);
- if (!a)
- return log_oom();
- }
+ shortened = shorten_ifname(n);
r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)
@@ -684,7 +691,8 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
if (r < 0)
return log_error_errno(r, "Failed to add new ipvlan interfaces: %m");
- (void) set_alternative_ifname(rtnl, n, a);
+ if (shortened > 0)
+ (void) set_alternative_ifname(rtnl, n, *b);
}
return 0;
@@ -742,3 +750,51 @@ int remove_veth_links(const char *primary, char **pairs) {
return 0;
}
+
+static int network_iface_pair_parse(const char* iftype, char ***l, const char *p, const char* ifprefix) {
+ _cleanup_free_ char *a = NULL, *b = NULL;
+ int r;
+
+ r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r < 0)
+ return log_error_errno(r, "Failed to extract first word in %s parameter: %m", iftype);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Short read while reading %s parameter: %m", iftype);
+ if (!ifname_valid(a))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s, interface name not valid: %s", iftype, a);
+
+ if (isempty(p)) {
+ if (ifprefix)
+ b = strjoin(ifprefix, a);
+ else
+ b = strdup(a);
+ } else
+ b = strdup(p);
+ if (!b)
+ return log_oom();
+
+ if (!ifname_valid(b))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s, interface name not valid: %s", iftype, b);
+
+ r = strv_push_pair(l, a, b);
+ if (r < 0)
+ return log_oom();
+
+ a = b = NULL;
+ return 0;
+}
+
+int interface_pair_parse(char ***l, const char *p) {
+ return network_iface_pair_parse("Network interface", l, p, NULL);
+}
+
+int macvlan_pair_parse(char ***l, const char *p) {
+ return network_iface_pair_parse("MACVLAN network interface", l, p, "mv-");
+}
+
+int ipvlan_pair_parse(char ***l, const char *p) {
+ return network_iface_pair_parse("IPVLAN network interface", l, p, "iv-");
+}
diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h
index 5c2d983418..355d813c96 100644
--- a/src/nspawn/nspawn-network.h
+++ b/src/nspawn/nspawn-network.h
@@ -5,7 +5,7 @@
#include <stdbool.h>
#include <sys/types.h>
-int test_network_interface_initialized(const char *name);
+int test_network_interfaces_initialized(char **iface_pairs);
int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge);
int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs);
@@ -13,11 +13,15 @@ int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs);
int setup_bridge(const char *veth_name, const char *bridge_name, bool create);
int remove_bridge(const char *bridge_name);
-int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces);
-int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces);
+int setup_macvlan(const char *machine_name, pid_t pid, char **iface_pairs);
+int setup_ipvlan(const char *machine_name, pid_t pid, char **iface_pairs);
-int move_network_interfaces(int netns_fd, char **ifaces);
+int move_network_interfaces(int netns_fd, char **iface_pairs);
int veth_extra_parse(char ***l, const char *p);
int remove_veth_links(const char *primary, char **pairs);
+
+int interface_pair_parse(char ***l, const char *p);
+int macvlan_pair_parse(char ***l, const char *p);
+int ipvlan_pair_parse(char ***l, const char *p);
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
index 05bde1c756..7500eabd18 100644
--- a/src/nspawn/nspawn-settings.c
+++ b/src/nspawn/nspawn-settings.c
@@ -469,6 +469,69 @@ int config_parse_veth_extra(
return 0;
}
+int config_parse_network_iface_pair(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char*** l = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ return interface_pair_parse(l, rvalue);
+}
+
+int config_parse_macvlan_iface_pair(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char*** l = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ return macvlan_pair_parse(l, rvalue);
+}
+
+int config_parse_ipvlan_iface_pair(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char*** l = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ return ipvlan_pair_parse(l, rvalue);
+}
+
int config_parse_network_zone(
const char *unit,
const char *filename,
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
index 004b663e9e..0a3d975364 100644
--- a/src/nspawn/nspawn-settings.h
+++ b/src/nspawn/nspawn-settings.h
@@ -259,6 +259,9 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tmpfs);
CONFIG_PARSER_PROTOTYPE(config_parse_overlay);
CONFIG_PARSER_PROTOTYPE(config_parse_inaccessible);
CONFIG_PARSER_PROTOTYPE(config_parse_veth_extra);
+CONFIG_PARSER_PROTOTYPE(config_parse_network_iface_pair);
+CONFIG_PARSER_PROTOTYPE(config_parse_macvlan_iface_pair);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_iface_pair);
CONFIG_PARSER_PROTOTYPE(config_parse_network_zone);
CONFIG_PARSER_PROTOTYPE(config_parse_boot);
CONFIG_PARSER_PROTOTYPE(config_parse_pid2);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 3882676216..ff6a437573 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -377,13 +377,13 @@ static int help(void) {
" --private-users-ownership=auto\n\n"
"%3$sNetworking:%4$s\n"
" --private-network Disable network in container\n"
- " --network-interface=INTERFACE\n"
+ " --network-interface=HOSTIF[:CONTAINERIF]\n"
" Assign an existing network interface to the\n"
" container\n"
- " --network-macvlan=INTERFACE\n"
+ " --network-macvlan=HOSTIF[:CONTAINERIF]\n"
" Create a macvlan network interface based on an\n"
" existing network interface to the container\n"
- " --network-ipvlan=INTERFACE\n"
+ " --network-ipvlan=HOSTIF[:CONTAINERIF]\n"
" Create an ipvlan network interface based on an\n"
" existing network interface to the container\n"
" -n --network-veth Add a virtual Ethernet connection between host\n"
@@ -924,51 +924,28 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NETWORK_INTERFACE:
- if (!ifname_valid(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Network interface name not valid: %s", optarg);
-
- r = test_network_interface_initialized(optarg);
+ r = interface_pair_parse(&arg_network_interfaces, optarg);
if (r < 0)
return r;
- if (strv_extend(&arg_network_interfaces, optarg) < 0)
- return log_oom();
-
arg_private_network = true;
arg_settings_mask |= SETTING_NETWORK;
break;
case ARG_NETWORK_MACVLAN:
-
- if (!ifname_valid(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "MACVLAN network interface name not valid: %s", optarg);
-
- r = test_network_interface_initialized(optarg);
+ r = macvlan_pair_parse(&arg_network_macvlan, optarg);
if (r < 0)
return r;
- if (strv_extend(&arg_network_macvlan, optarg) < 0)
- return log_oom();
-
arg_private_network = true;
arg_settings_mask |= SETTING_NETWORK;
break;
case ARG_NETWORK_IPVLAN:
-
- if (!ifname_valid(optarg))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "IPVLAN network interface name not valid: %s", optarg);
-
- r = test_network_interface_initialized(optarg);
+ r = ipvlan_pair_parse(&arg_network_ipvlan, optarg);
if (r < 0)
return r;
- if (strv_extend(&arg_network_ipvlan, optarg) < 0)
- return log_oom();
-
_fallthrough_;
case ARG_PRIVATE_NETWORK:
arg_private_network = true;
@@ -1894,6 +1871,23 @@ static int verify_arguments(void) {
return 0;
}
+static int verify_network_interfaces_initialized(void) {
+ int r;
+ r = test_network_interfaces_initialized(arg_network_interfaces);
+ if (r < 0)
+ return r;
+
+ r = test_network_interfaces_initialized(arg_network_macvlan);
+ if (r < 0)
+ return r;
+
+ r = test_network_interfaces_initialized(arg_network_ipvlan);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
int userns_lchown(const char *p, uid_t uid, gid_t gid) {
assert(p);
@@ -5288,6 +5282,10 @@ static int run_container(
_exit(EXIT_FAILURE);
}
+ /* Reverse network interfaces pair list so that interfaces get their initial name back.
+ * This is about ensuring interfaces get their old name back when being moved back. */
+ arg_network_interfaces = strv_reverse(arg_network_interfaces);
+
r = move_network_interfaces(parent_netns_fd, arg_network_interfaces);
if (r < 0)
log_error_errno(r, "Failed to move network interfaces back to parent network namespace: %m");
@@ -5506,6 +5504,10 @@ static int run(int argc, char *argv[]) {
if (r < 0)
goto finish;
+ r = verify_network_interfaces_initialized();
+ if (r < 0)
+ goto finish;
+
/* Reapply environment settings. */
(void) detect_unified_cgroup_hierarchy_from_environment();