]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
nspawn: add new --network-veth-extra= switch for defining additional veth links
authorLennart Poettering <lennart@poettering.net>
Thu, 12 Nov 2015 20:54:28 +0000 (21:54 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 12 Nov 2015 21:04:49 +0000 (22:04 +0100)
The new switch operates like --network-veth, but may be specified
multiple times (to define multiple link pairs) and allows flexible
definition of the interface names.

This is an independent reimplementation of #1678, but defines different
semantics, keeping the behaviour completely independent of
--network-veth. It also comes will full hook-up for .nspawn files, and
the matching documentation.

man/systemd-nspawn.xml
man/systemd.nspawn.xml
src/nspawn/nspawn-gperf.gperf
src/nspawn/nspawn-network.c
src/nspawn/nspawn-network.h
src/nspawn/nspawn-settings.c
src/nspawn/nspawn-settings.h
src/nspawn/nspawn.c

index 4725604c0307732466066d8dcca0d641841eef69..a97b7c44eb913cba2eb500eb43ab9be09edd0971 100644 (file)
         <option>--private-network</option>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--network-veth-extra=</option></term>
+
+        <listitem><para>Adds an additional virtual Ethernet link
+        between host and container. Takes a colon-separated pair of
+        host interface name and container interface name. The latter
+        may be omitted in which case the container and host sides will
+        be assigned the same name. This switch is independent of
+        <option>--network-veth</option>, and -- in contrast -- may be
+        used multiple times, and allows configuration of the network
+        interface names. Note that <option>--network-bridge=</option>
+        has no effect on interfaces created with
+        <option>--network-veth-extra=</option>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--network-bridge=</option></term>
 
index c2b7a40ec1a59640c461697ece2875225f0627d2..e952688331e67016515684acb89e7c62ef684e32 100644 (file)
         above).</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>VirtualEthernetExtra=</varname></term>
+
+        <listitem><para>Takes a colon-separated pair of interface
+        names. Configures an additional virtual Ethernet connection
+        (<literal>veth</literal>) between host and the container. The
+        first specified name is the interface name on the host, the
+        second the interface name in the container. The latter may be
+        omitted in which case it is set to the same name as the host
+        side interface. This setting implies
+        <varname>Private=yes</varname>. This setting corresponds to
+        the <option>--network-veth-extra=</option> command line
+        switch, and maybe be used multiple times. It is independent of
+        <varname>VirtualEthernet=</varname>. This option is privileged
+        (see above).</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>Interface=</varname></term>
 
index b5127a387c3660f2084244ea3fa3787f8910717e..58f9f4c63544a2c9dcbc6570683507b325037cc4 100644 (file)
@@ -15,24 +15,25 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-Exec.Boot,                 config_parse_tristate,      0, offsetof(Settings, boot)
-Exec.Parameters,           config_parse_strv,          0, offsetof(Settings, parameters)
-Exec.Environment,          config_parse_strv,          0, offsetof(Settings, environment)
-Exec.User,                 config_parse_string,        0, offsetof(Settings, user)
-Exec.Capability,           config_parse_capability,    0, offsetof(Settings, capability)
-Exec.DropCapability,       config_parse_capability,    0, offsetof(Settings, drop_capability)
-Exec.KillSignal,           config_parse_signal,        0, offsetof(Settings, kill_signal)
-Exec.Personality,          config_parse_personality,   0, offsetof(Settings, personality)
-Exec.MachineID,            config_parse_id128,         0, offsetof(Settings, machine_id)
-Files.ReadOnly,            config_parse_tristate,      0, offsetof(Settings, read_only)
-Files.Volatile,            config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
-Files.Bind,                config_parse_bind,          0, 0
-Files.BindReadOnly,        config_parse_bind,          1, 0
-Files.TemporaryFileSystem, config_parse_tmpfs,         0, 0
-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.VirtualEthernet,   config_parse_tristate,      0, offsetof(Settings, network_veth)
-Network.Bridge,            config_parse_string,        0, offsetof(Settings, network_bridge)
-Network.Port,              config_parse_expose_port,   0, 0
+Exec.Boot,                    config_parse_tristate,      0, offsetof(Settings, boot)
+Exec.Parameters,              config_parse_strv,          0, offsetof(Settings, parameters)
+Exec.Environment,             config_parse_strv,          0, offsetof(Settings, environment)
+Exec.User,                    config_parse_string,        0, offsetof(Settings, user)
+Exec.Capability,              config_parse_capability,    0, offsetof(Settings, capability)
+Exec.DropCapability,          config_parse_capability,    0, offsetof(Settings, drop_capability)
+Exec.KillSignal,              config_parse_signal,        0, offsetof(Settings, kill_signal)
+Exec.Personality,             config_parse_personality,   0, offsetof(Settings, personality)
+Exec.MachineID,               config_parse_id128,         0, offsetof(Settings, machine_id)
+Files.ReadOnly,               config_parse_tristate,      0, offsetof(Settings, read_only)
+Files.Volatile,               config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode)
+Files.Bind,                   config_parse_bind,          0, 0
+Files.BindReadOnly,           config_parse_bind,          1, 0
+Files.TemporaryFileSystem,    config_parse_tmpfs,         0, 0
+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.VirtualEthernet,      config_parse_tristate,      0, offsetof(Settings, network_veth)
+Network.VirtualEthernetExtra, config_parse_veth_extra,    0, 0
+Network.Bridge,               config_parse_string,        0, offsetof(Settings, network_bridge)
+Network.Port,                 config_parse_expose_port,   0, 0
index 29384b60b222571354fa03518b5dcd94447a7b69..c71552879d83e38aa4fb609302470669174829c1 100644 (file)
@@ -37,6 +37,8 @@
 
 #define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1)
 #define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2)
+#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)
+#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)
 #define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
 
 static int generate_mac(
@@ -84,42 +86,32 @@ static int generate_mac(
         return 0;
 }
 
-int setup_veth(const char *machine_name,
-               pid_t pid,
-               char iface_name[IFNAMSIZ],
-               bool bridge) {
+static int add_veth(
+                sd_netlink *rtnl,
+                pid_t pid,
+                const char *ifname_host,
+                const struct ether_addr *mac_host,
+                const char *ifname_container,
+                const struct ether_addr *mac_container) {
 
         _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
-        _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
-        struct ether_addr mac_host, mac_container;
-        int r, i;
-
-        /* Use two different interface name prefixes depending whether
-         * we are in bridge mode or not. */
-        snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
-                 bridge ? "vb" : "ve", machine_name);
-
-        r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
-
-        r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
+        int r;
 
-        r = sd_netlink_open(&rtnl);
-        if (r < 0)
-                return log_error_errno(r, "Failed to connect to netlink: %m");
+        assert(rtnl);
+        assert(ifname_host);
+        assert(mac_host);
+        assert(ifname_container);
+        assert(mac_container);
 
         r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0);
         if (r < 0)
                 return log_error_errno(r, "Failed to allocate netlink message: %m");
 
-        r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name);
+        r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_host);
         if (r < 0)
                 return log_error_errno(r, "Failed to add netlink interface name: %m");
 
-        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host);
+        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_host);
         if (r < 0)
                 return log_error_errno(r, "Failed to add netlink MAC address: %m");
 
@@ -135,11 +127,11 @@ int setup_veth(const char *machine_name,
         if (r < 0)
                 return log_error_errno(r, "Failed to open netlink container: %m");
 
-        r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0");
+        r = sd_netlink_message_append_string(m, IFLA_IFNAME, ifname_container);
         if (r < 0)
                 return log_error_errno(r, "Failed to add netlink interface name: %m");
 
-        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container);
+        r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, mac_container);
         if (r < 0)
                 return log_error_errno(r, "Failed to add netlink MAC address: %m");
 
@@ -161,7 +153,44 @@ int setup_veth(const char *machine_name,
 
         r = sd_netlink_call(rtnl, m, 0, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name);
+                return log_error_errno(r, "Failed to add new veth interfaces (%s:%s): %m", ifname_host, ifname_container);
+
+        return 0;
+}
+
+int setup_veth(const char *machine_name,
+               pid_t pid,
+               char iface_name[IFNAMSIZ],
+               bool bridge) {
+
+        _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+        struct ether_addr mac_host, mac_container;
+        int r, i;
+
+        assert(machine_name);
+        assert(pid > 0);
+        assert(iface_name);
+
+        /* Use two different interface name prefixes depending whether
+         * we are in bridge mode or not. */
+        snprintf(iface_name, IFNAMSIZ - 1, "%s-%s",
+                 bridge ? "vb" : "ve", machine_name);
+
+        r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
+
+        r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
+
+        r = sd_netlink_open(&rtnl);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to netlink: %m");
+
+        r = add_veth(rtnl, pid, iface_name, &mac_host, "host0", &mac_container);
+        if (r < 0)
+                return r;
 
         i = (int) if_nametoindex(iface_name);
         if (i <= 0)
@@ -170,6 +199,47 @@ int setup_veth(const char *machine_name,
         return i;
 }
 
+int setup_veth_extra(
+                const char *machine_name,
+                pid_t pid,
+                char **pairs) {
+
+        _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
+        uint64_t idx = 0;
+        char **a, **b;
+        int r;
+
+        assert(machine_name);
+        assert(pid > 0);
+
+        if (strv_isempty(pairs))
+                return 0;
+
+        r = sd_netlink_open(&rtnl);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to netlink: %m");
+
+        STRV_FOREACH_PAIR(a, b, pairs) {
+                struct ether_addr mac_host, mac_container;
+
+                r = generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
+
+                r = generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
+
+                r = add_veth(rtnl, pid, *a, &mac_host, *b, &mac_container);
+                if (r < 0)
+                        return r;
+
+                idx ++;
+        }
+
+        return 0;
+}
+
 int setup_bridge(const char *veth_name, const char *bridge_name) {
         _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
         _cleanup_netlink_unref_ sd_netlink *rtnl = NULL;
@@ -439,3 +509,34 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) {
 
         return 0;
 }
+
+int veth_extra_parse(char ***l, const char *p) {
+        _cleanup_free_ char *a = NULL, *b = NULL;
+        int r;
+
+        r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+        if (r < 0)
+                return r;
+        if (r == 0 || isempty(a))
+                return -EINVAL;
+
+        r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+        if (r < 0)
+                return r;
+        if (r == 0 || isempty(b)) {
+                free(b);
+                b = strdup(a);
+                if (!b)
+                        return -ENOMEM;
+        }
+
+        if (p)
+                return -EINVAL;
+
+        r = strv_push_pair(l, a, b);
+        if (r < 0)
+                return -ENOMEM;
+
+        a = b = NULL;
+        return 0;
+}
index 311e6d06cb2964880d15f9c06ef6254b6ba10a53..b86effef47cb14e9b076ae47946227572100da2a 100644 (file)
@@ -27,6 +27,7 @@
 #include <stdbool.h>
 
 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);
 
@@ -34,3 +35,5 @@ 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 move_network_interfaces(pid_t pid, char **ifaces);
+
+int veth_extra_parse(char ***l, const char *p);
index 6885d0641eba153b573e1a330048cefaf0358856..d6b64d8d5a05c4edf2c471dccb5a7204654c5959 100644 (file)
@@ -22,6 +22,7 @@
 #include "alloc-util.h"
 #include "cap-list.h"
 #include "conf-parser.h"
+#include "nspawn-network.h"
 #include "nspawn-settings.h"
 #include "process-util.h"
 #include "strv.h"
@@ -77,6 +78,7 @@ Settings* settings_free(Settings *s) {
         strv_free(s->network_interfaces);
         strv_free(s->network_macvlan);
         strv_free(s->network_ipvlan);
+        strv_free(s->network_veth_extra);
         free(s->network_bridge);
         expose_port_free_all(s->expose_ports);
 
@@ -95,7 +97,8 @@ bool settings_private_network(Settings *s) {
                 s->network_bridge ||
                 s->network_interfaces ||
                 s->network_macvlan ||
-                s->network_ipvlan;
+                s->network_ipvlan ||
+                s->network_veth_extra;
 }
 
 bool settings_network_veth(Settings *s) {
@@ -269,15 +272,33 @@ int config_parse_tmpfs(
                 return 0;
         }
 
-        if (settings->network_bridge)
-                settings->network_veth = true;
+        return 0;
+}
 
-        if (settings->network_interfaces ||
-            settings->network_macvlan ||
-            settings->network_ipvlan ||
-            settings->network_bridge ||
-            settings->network_veth)
-                settings->private_network = true;
+int config_parse_veth_extra(
+                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) {
+
+        Settings *settings = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = veth_extra_parse(&settings->network_veth_extra, rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid extra virtual Ethernet link specification %s: %m", rvalue);
+                return 0;
+        }
 
         return 0;
 }
index 16e8c5450836bbaba12c7ce815c9e57d512a5600..dde0d8bd450fa1a1630bd0b612654a7b359e4cd4 100644 (file)
@@ -69,6 +69,7 @@ typedef struct Settings {
         char **network_interfaces;
         char **network_macvlan;
         char **network_ipvlan;
+        char **network_veth_extra;
         ExposePort *expose_ports;
 } Settings;
 
@@ -88,3 +89,4 @@ int config_parse_expose_port(const char *unit, const char *filename, unsigned li
 int config_parse_volatile_mode(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);
 int config_parse_bind(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);
 int config_parse_tmpfs(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);
+int config_parse_veth_extra(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);
index 4c48681f179e82494e8f7d2b58e4d03c9d75ea3c..d2ce731c7289b89b67b4f508c132f4ceafd8fc07 100644 (file)
@@ -165,6 +165,7 @@ static char **arg_network_interfaces = NULL;
 static char **arg_network_macvlan = NULL;
 static char **arg_network_ipvlan = NULL;
 static bool arg_network_veth = false;
+static char **arg_network_veth_extra = NULL;
 static char *arg_network_bridge = NULL;
 static unsigned long arg_personality = PERSONALITY_INVALID;
 static char *arg_image = NULL;
@@ -212,6 +213,9 @@ static void help(void) {
                "                            existing network interface to the container\n"
                "  -n --network-veth         Add a virtual Ethernet connection between host\n"
                "                            and container\n"
+               "     --network-veth-extra=HOSTIF[:CONTAINERIF]\n"
+               "                            Add an additional virtual Ethernet link between\n"
+               "                            host and container\n"
                "     --network-bridge=INTERFACE\n"
                "                            Add a virtual Ethernet connection between host\n"
                "                            and container and add it to an existing bridge on\n"
@@ -334,6 +338,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_NETWORK_MACVLAN,
                 ARG_NETWORK_IPVLAN,
                 ARG_NETWORK_BRIDGE,
+                ARG_NETWORK_VETH_EXTRA,
                 ARG_PERSONALITY,
                 ARG_VOLATILE,
                 ARG_TEMPLATE,
@@ -375,6 +380,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "network-macvlan",       required_argument, NULL, ARG_NETWORK_MACVLAN   },
                 { "network-ipvlan",        required_argument, NULL, ARG_NETWORK_IPVLAN    },
                 { "network-veth",          no_argument,       NULL, 'n'                   },
+                { "network-veth-extra",    required_argument, NULL, ARG_NETWORK_VETH_EXTRA},
                 { "network-bridge",        required_argument, NULL, ARG_NETWORK_BRIDGE    },
                 { "personality",           required_argument, NULL, ARG_PERSONALITY       },
                 { "image",                 required_argument, NULL, 'i'                   },
@@ -449,6 +455,15 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_settings_mask |= SETTING_NETWORK;
                         break;
 
+                case ARG_NETWORK_VETH_EXTRA:
+                        r = veth_extra_parse(&arg_network_veth_extra, optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --network-veth-extra= parameter: %s", optarg);
+
+                        arg_private_network = true;
+                        arg_settings_mask |= SETTING_NETWORK;
+                        break;
+
                 case ARG_NETWORK_INTERFACE:
                         if (strv_extend(&arg_network_interfaces, optarg) < 0)
                                 return log_oom();
@@ -2964,12 +2979,13 @@ static int load_settings(void) {
              settings->network_bridge ||
              settings->network_interfaces ||
              settings->network_macvlan ||
-             settings->network_ipvlan)) {
+             settings->network_ipvlan ||
+             settings->network_veth_extra)) {
 
                 if (!arg_settings_trusted)
                         log_warning("Ignoring network settings, file %s is not trusted.", p);
                 else {
-                        arg_network_veth = settings_private_network(settings);
+                        arg_network_veth = settings_network_veth(settings);
                         arg_private_network = settings_private_network(settings);
 
                         strv_free(arg_network_interfaces);
@@ -2984,6 +3000,10 @@ static int load_settings(void) {
                         arg_network_ipvlan = settings->network_ipvlan;
                         settings->network_ipvlan = NULL;
 
+                        strv_free(arg_network_veth_extra);
+                        arg_network_veth_extra = settings->network_veth_extra;
+                        settings->network_veth_extra = NULL;
+
                         free(arg_network_bridge);
                         arg_network_bridge = settings->network_bridge;
                         settings->network_bridge = NULL;
@@ -3409,6 +3429,10 @@ int main(int argc, char *argv[]) {
                                 }
                         }
 
+                        r = setup_veth_extra(arg_machine, pid, arg_network_veth_extra);
+                        if (r < 0)
+                                goto finish;
+
                         r = setup_macvlan(arg_machine, pid, arg_network_macvlan);
                         if (r < 0)
                                 goto finish;
@@ -3610,6 +3634,7 @@ finish:
         strv_free(arg_network_interfaces);
         strv_free(arg_network_macvlan);
         strv_free(arg_network_ipvlan);
+        strv_free(arg_network_veth_extra);
         strv_free(arg_parameters);
         custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts);
         expose_port_free_all(arg_expose_ports);