1
0
mirror of https://github.com/systemd/systemd.git synced 2025-02-27 01:57:35 +03:00

nspawn: add new switch --network-macvlan= to add a macvlan device to the container

This commit is contained in:
Lennart Poettering 2014-02-25 02:27:39 +01:00
parent bdd13f6be4
commit c74e630d0c
3 changed files with 196 additions and 36 deletions

View File

@ -260,7 +260,7 @@
<listitem><para>Assign the specified
network interface to the
container. This will move the
container. This will remove the
specified interface from the calling
namespace and place it in the
container. When the container
@ -274,12 +274,36 @@
container.</para></listitem>
</varlistentry>
<varlistentry>
<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
<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></listitem>
</varlistentry>
<varlistentry>
<term><option>--network-veth</option></term>
<listitem><para>Create a virtual
Ethernet link between host and
container. The host side of the
Ethernet link
(<literal>veth</literal>) between host
and container. The host side of the
Ethernet link will be available as a
network interface named after the
container's name (as specified with

View File

@ -521,6 +521,7 @@ int sd_rtnl_message_append_u32(sd_rtnl_message *m, unsigned short type, uint32_t
case IFLA_PROMISCUITY:
case IFLA_NUM_TX_QUEUES:
case IFLA_NUM_RX_QUEUES:
case IFLA_MACVLAN_MODE:
break;
default:
return -ENOTSUP;

View File

@ -95,8 +95,8 @@ static char *arg_directory = NULL;
static char *arg_user = NULL;
static sd_id128_t arg_uuid = {};
static char *arg_machine = NULL;
static char *arg_selinux_context = NULL;
static char *arg_selinux_apifs_context = NULL;
static const char *arg_selinux_context = NULL;
static const char *arg_selinux_apifs_context = NULL;
static const char *arg_slice = NULL;
static bool arg_private_network = false;
static bool arg_read_only = false;
@ -137,8 +137,9 @@ static bool arg_share_system = false;
static bool arg_register = true;
static bool arg_keep_unit = false;
static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
static bool arg_network_veth = false;
static char *arg_network_bridge = NULL;
static const char *arg_network_bridge = NULL;
static unsigned long arg_personality = 0xffffffffLU;
static int help(void) {
@ -158,6 +159,9 @@ static int help(void) {
" --network-interface=INTERFACE\n"
" Assign an existing network interface to the\n"
" container\n"
" --network-macvlan=INTERFACE\n"
" Create a macvlan network interface based on an\n"
" existing network interface to the container\n"
" --network-veth Add a virtual ethernet connection between host\n"
" and container\n"
" --network-bridge=INTERFACE\n"
@ -206,6 +210,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_REGISTER,
ARG_KEEP_UNIT,
ARG_NETWORK_INTERFACE,
ARG_NETWORK_MACVLAN,
ARG_NETWORK_VETH,
ARG_NETWORK_BRIDGE,
ARG_PERSONALITY,
@ -235,6 +240,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "register", required_argument, NULL, ARG_REGISTER },
{ "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
{ "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
{ "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
{ "network-veth", no_argument, NULL, ARG_NETWORK_VETH },
{ "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE },
{ "personality", required_argument, NULL, ARG_PERSONALITY },
@ -278,9 +284,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NETWORK_BRIDGE:
arg_network_bridge = strdup(optarg);
if (!arg_network_bridge)
return log_oom();
arg_network_bridge = optarg;
/* fall through */
@ -290,7 +294,14 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_NETWORK_INTERFACE:
if (strv_push(&arg_network_interfaces, optarg) < 0)
if (strv_extend(&arg_network_interfaces, optarg) < 0)
return log_oom();
arg_private_network = true;
break;
case ARG_NETWORK_MACVLAN:
if (strv_extend(&arg_network_macvlan, optarg) < 0)
return log_oom();
/* fall through */
@ -312,10 +323,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case 'S':
arg_slice = strdup(optarg);
if (!arg_slice)
return log_oom();
arg_slice = optarg;
break;
case 'M':
@ -1520,6 +1528,32 @@ static int setup_bridge(const char veth_name[]) {
return 0;
}
static int parse_interface(struct udev *udev, const char *name) {
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
char ifi_str[2 + DECIMAL_STR_MAX(int)];
int ifi;
ifi = (int) if_nametoindex(name);
if (ifi <= 0) {
log_error("Failed to resolve interface %s: %m", name);
return -errno;
}
sprintf(ifi_str, "n%i", ifi);
d = udev_device_new_from_device_id(udev, ifi_str);
if (!d) {
log_error("Failed to get udev device for interface %s: %m", name);
return -errno;
}
if (udev_device_get_is_initialized(d) <= 0) {
log_error("Network interface %s is not initialized yet.", name);
return -EBUSY;
}
return ifi;
}
static int move_network_interfaces(pid_t pid) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
@ -1546,27 +1580,11 @@ static int move_network_interfaces(pid_t pid) {
STRV_FOREACH(i, arg_network_interfaces) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
_cleanup_udev_device_unref_ struct udev_device *d = NULL;
char ifi_str[2 + DECIMAL_STR_MAX(int)];
int ifi;
ifi = (int) if_nametoindex(*i);
if (ifi <= 0) {
log_error("Failed to resolve interface %s: %m", *i);
return -errno;
}
sprintf(ifi_str, "n%i", ifi);
d = udev_device_new_from_device_id(udev, ifi_str);
if (!d) {
log_error("Failed to get udev device for interface %s: %m", *i);
return -errno;
}
if (udev_device_get_is_initialized(d) <= 0) {
log_error("Network interface %s is not initialized yet.", *i);
return -EBUSY;
}
ifi = parse_interface(udev, *i);
if (ifi < 0)
return ifi;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, ifi);
if (r < 0) {
@ -1590,6 +1608,115 @@ static int move_network_interfaces(pid_t pid) {
return 0;
}
static int setup_macvlan(pid_t pid) {
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
char **i;
int r;
if (!arg_private_network)
return 0;
if (strv_isempty(arg_network_macvlan))
return 0;
r = sd_rtnl_open(&rtnl, 0);
if (r < 0) {
log_error("Failed to connect to netlink: %s", strerror(-r));
return r;
}
udev = udev_new();
if (!udev) {
log_error("Failed to connect to udev.");
return -ENOMEM;
}
STRV_FOREACH(i, arg_network_macvlan) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
_cleanup_free_ char *n = NULL;
int ifi;
ifi = parse_interface(udev, *i);
if (ifi < 0)
return ifi;
r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
if (r < 0) {
log_error("Failed to allocate netlink message: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_LINK, ifi);
if (r < 0) {
log_error("Failed to add netlink interface index: %s", strerror(-r));
return r;
}
n = strappend("mv-", *i);
if (!n)
return log_oom();
strshorten(n, IFNAMSIZ-1);
r = sd_rtnl_message_append_string(m, IFLA_IFNAME, n);
if (r < 0) {
log_error("Failed to add netlink interface name: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid);
if (r < 0) {
log_error("Failed to add netlink namespace field: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
if (r < 0) {
log_error("Failed to open netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_string(m, IFLA_INFO_KIND, "macvlan");
if (r < 0) {
log_error("Failed to append netlink kind: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_open_container(m, IFLA_INFO_DATA);
if (r < 0) {
log_error("Failed to open netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE);
if (r < 0) {
log_error("Failed to append macvlan mode: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_close_container(m);
if (r < 0) {
log_error("Failed to close netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_message_close_container(m);
if (r < 0) {
log_error("Failed to close netlink container: %s", strerror(-r));
return r;
}
r = sd_rtnl_call(rtnl, m, 0, NULL);
if (r < 0) {
log_error("Failed to add new macvlan interfaces: %s", strerror(-r));
return r;
}
}
return 0;
}
static int audit_still_doesnt_work_in_containers(void) {
#ifdef HAVE_SECCOMP
@ -2094,7 +2221,7 @@ int main(int argc, char *argv[]) {
#ifdef HAVE_SELINUX
if (arg_selinux_context)
if (setexeccon(arg_selinux_context) < 0)
if (setexeccon((security_context_t) arg_selinux_context) < 0)
log_error("setexeccon(\"%s\") failed: %m", arg_selinux_context);
#endif
if (arg_boot) {
@ -2148,6 +2275,10 @@ int main(int argc, char *argv[]) {
if (r < 0)
goto finish;
r = setup_macvlan(pid);
if (r < 0)
goto finish;
eventfd_write(sync_fd, 1);
close_nointr_nofail(sync_fd);
sync_fd = -1;
@ -2217,8 +2348,12 @@ finish:
free(arg_directory);
free(arg_machine);
free(arg_setenv);
free(arg_network_interfaces);
free(arg_user);
strv_free(arg_setenv);
strv_free(arg_network_interfaces);
strv_free(arg_network_macvlan);
strv_free(arg_bind);
strv_free(arg_bind_ro);
return r;
}