<varlistentry>
<term><option>--network-interface=</option></term>
- <listitem><para>Assign the specified network interface to the container. This will remove the
- specified interface from the calling namespace and place it in the container. When the container
- terminates, it is moved back to the calling namespace. Note that
- <option>--network-interface=</option> implies <option>--private-network</option>. This option may be
- used more than once to add multiple network interfaces to the container.</para>
+ <listitem><para>Assign the specified network interface to the container. Either takes a single
+ interface name, referencing the name on the host, or a colon-separated pair of interfaces, in which
+ case the first one references the name on the host, and the second one the name in the container.
+ When the container terminates, the interface is moved back to the calling namespace and renamed to
+ its original name. Note that <option>--network-interface=</option> implies
+ <option>--private-network</option>. This option may be used more than once to add multiple network
+ interfaces to the container.</para>
<para>Note that any network interface specified this way must already exist at the time the container
is started. If the container shall be started automatically at boot via a
<term><option>--network-macvlan=</option></term>
<listitem><para>Create a <literal>macvlan</literal> interface of the specified Ethernet network
- interface and add it to the container. A <literal>macvlan</literal> interface is a virtual interface
- that adds a second MAC address to an existing physical Ethernet link. The interface in the container
- will be named after the interface on the host, prefixed with <literal>mv-</literal>. Note that
+ interface and add it to the container. Either takes a single interface name, referencing the name
+ on the host, or a colon-separated pair of interfaces, in which case the first one references the name
+ on the host, and the second one the name in the container. A <literal>macvlan</literal> interface is
+ a virtual interface that adds a second MAC address to an existing physical Ethernet link. If the
+ container interface name is not defined, the interface in the container will be named after the
+ interface on the host, prefixed with <literal>mv-</literal>. Note that
<option>--network-macvlan=</option> implies <option>--private-network</option>. This option may be
used more than once to add multiple network interfaces to the container.</para>
<term><option>--network-ipvlan=</option></term>
<listitem><para>Create an <literal>ipvlan</literal> interface of the specified Ethernet network
- interface and add it to the container. An <literal>ipvlan</literal> interface is a virtual interface,
+ interface and add it to the container. Either takes a single interface name, referencing the name on
+ the host, or a colon-separated pair of interfaces, in which case the first one references the name
+ on the host, and the second one the name in the container. An <literal>ipvlan</literal> interface is
+ a virtual interface,
similar to a <literal>macvlan</literal> interface, which uses the same MAC address as the underlying
- interface. The interface in the container will be named after the interface on the host, prefixed
+ interface. If the container interface name is not defined, the interface in the container will be
+ named after the interface on the host, prefixed
with <literal>iv-</literal>. Note that <option>--network-ipvlan=</option> implies
<option>--private-network</option>. This option may be used more than once to add multiple network
interfaces to the container.</para>
<varlistentry>
<term><varname>Interface=</varname></term>
- <listitem><para>Takes a space-separated list of interfaces to
- add to the container. This option corresponds to the
+ <listitem><para>Takes a space-separated list of interfaces to add to the container.
+ The interface object is defined either by a single interface name, referencing the name on the host,
+ or a colon-separated pair of interfaces, in which case the first one references the name on the host,
+ and the second one the name in the container.
+ This option corresponds to the
<option>--network-interface=</option> command line switch and
implies <varname>Private=yes</varname>. This option is
privileged (see above).</para></listitem>
<listitem><para>Takes a space-separated list of interfaces to
add MACLVAN or IPVLAN interfaces to, which are then added to
- the container. These options correspond to the
+ the container. The interface object is defined either by a single interface name, referencing the name
+ on the host, or a colon-separated pair of interfaces, in which case the first one references the name
+ on the host, and the second one the name in the container. These options correspond to the
<option>--network-macvlan=</option> and
<option>--network-ipvlan=</option> command line switches and
imply <varname>Private=yes</varname>. These options are
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)
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;
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;
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);
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)
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)
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)
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)
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;
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-");
+}
#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);
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);
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,
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);
" --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"
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;
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);
_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");
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();