]> git.proxmox.com Git - mirror_lxc.git/commitdiff
network: Adds IPVLAN support
authortomponline <thomas.parrott@canonical.com>
Fri, 26 Apr 2019 10:26:45 +0000 (11:26 +0100)
committertomponline <thomas.parrott@canonical.com>
Wed, 1 May 2019 09:10:44 +0000 (10:10 +0100)
Example usage:

lxc.net[i].type=ipvlan
lxc.net[i].ipvlan.mode=[l3|l3s|l2] (defaults to l3)
lxc.net[i].ipvlan.flags=[bridge|private|vepa] (defaults to bridge)
lxc.net[i].link=eth0
lxc.net[i].flags=up

Signed-off-by: tomponline <thomas.parrott@canonical.com>
doc/api-extensions.md
doc/lxc.container.conf.sgml.in
src/lxc/api_extensions.h
src/lxc/confile.c
src/lxc/confile_utils.c
src/lxc/confile_utils.h
src/lxc/macro.h
src/lxc/network.c
src/lxc/network.h
src/tests/parse_config_file.c

index 8c95021ada4386f3e72426d0cc5c239ed089c9aa..d5a0a3af74abd21811d77ec7f20ca01eb8e5bdab 100644 (file)
@@ -51,3 +51,17 @@ The caller can read this message, inspect the syscalls including its arguments.
 This introduces the `lxc.net.[i].veth.ipv4.route` and `lxc.net.[i].veth.ipv6.route` properties
 on `veth` type network interfaces. This allows adding static routes on host to the container's
 network interface.
+
+## network\_ipvlan
+
+This introduces the `ipvlan` network type.
+
+Example usage:
+
+```
+lxc.net[i].type=ipvlan
+lxc.net[i].ipvlan.mode=[l3|l3s|l2] (defaults to l3)
+lxc.net[i].ipvlan.isolation=[bridge|private|vepa] (defaults to bridge)
+lxc.net[i].link=eth0
+lxc.net[i].flags=up
+```
index 3b3dd6ddebedb63c995341fb4bba6840f3a021af..19f3acbd7aa3efa3bd62fbcd69c299a0f3583064 100644 (file)
@@ -485,7 +485,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
               different macvlan on the same upper device. The accepted
               modes are <option>private</option>, <option>vepa</option>,
               <option>bridge</option> and <option>passthru</option>.
-             In <option>private</option> mode, the device never
+              In <option>private</option> mode, the device never
               communicates with any other device on the same upper_dev (default).
               In <option>vepa</option> mode, the new Virtual Ethernet Port
               Aggregator (VEPA) mode, it assumes that the adjacent
@@ -510,6 +510,41 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
               mode is possible for one physical interface.
             </para>
 
+            <para>
+              <option>ipvlan:</option> an ipvlan interface is linked
+              with the interface specified by
+              the <option>lxc.net.[i].link</option> and assigned to
+              the container.
+              <option>lxc.net.[i].ipvlan.mode</option> specifies the
+              mode the ipvlan will use to communicate between
+              different ipvlan on the same upper device. The accepted
+              modes are <option>l3</option>, <option>l3s</option> and
+              <option>l2</option>. It defaults to <option>l3</option> mode.
+              In <option>l3</option> mode TX processing up to L3 happens on the stack instance
+              attached to the slave device and packets are switched to the stack instance of the
+              master device for the L2 processing and routing from that instance will be
+              used before packets are queued on the outbound device. In this mode the slaves
+              will not receive nor can send multicast / broadcast traffic.
+              In <option>l3s</option> mode TX processing is very similar to the L3 mode except that
+              iptables (conn-tracking) works in this mode and hence it is L3-symmetric (L3s).
+              This will have slightly less performance but that shouldn't matter since you are
+              choosing this mode over plain-L3 mode to make conn-tracking work.
+              In <option>l2</option> mode TX processing happens on the stack instance attached to
+              the slave device and packets are switched and queued to the master device to send
+              out. In this mode the slaves will RX/TX multicast and broadcast (if applicable) as well.
+              <option>lxc.net.[i].ipvlan.isolation</option> specifies the isolation mode.
+              The accepted isolation values are <option>bridge</option>,
+              <option>private</option> and <option>vepa</option>.
+              It defaults to <option>bridge</option>.
+              In <option>bridge</option> isolation mode slaves can cross-talk among themselves
+              apart from talking through the master device.
+              In <option>private</option> isolation mode the port is set in private mode.
+              i.e. port won't allow cross communication between slaves.
+              In <option>vepa</option> isolation mode the port is set in VEPA mode.
+              i.e. port will offload switching functionality to the external entity as
+              described in 802.1Qbg.
+            </para>
+
             <para>
               <option>phys:</option> an already existing interface
               specified by the <option>lxc.net.[i].link</option> is
@@ -610,8 +645,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
               interface (as specified by the
               <option>lxc.net.[i].link</option> option) and use that as
               the gateway. <option>auto</option> is only available when
-              using the <option>veth</option> and
-              <option>macvlan</option> network types.
+              using the <option>veth</option>
+              <option>macvlan</option> and <option>ipvlan</option> network types.
             </para>
           </listitem>
         </varlistentry>
@@ -644,8 +679,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
               interface (as specified by the
               <option>lxc.net.[i].link</option> option) and use that as
               the gateway. <option>auto</option> is only available when
-              using the <option>veth</option> and
-              <option>macvlan</option> network types.
+              using the <option>veth</option>,
+              <option>macvlan</option> and <option>ipvlan</option> network types.
             </para>
           </listitem>
         </varlistentry>
@@ -680,7 +715,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
                 <listitem>
                  <para>
                  LXC_NET_TYPE: the network type. This is one of the valid
-                 network types listed here (e.g. 'macvlan', 'veth').
+                 network types listed here (e.g. 'vlan', 'macvlan', 'ipvlan', 'veth').
                   </para>
                 </listitem>
 
@@ -746,7 +781,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
                 <listitem>
                  <para>
                  LXC_NET_TYPE: the network type. This is one of the valid
-                 network types listed here (e.g. 'macvlan', 'veth').
+                 network types listed here (e.g. 'vlan', 'macvlan', 'ipvlan', 'veth').
                   </para>
                 </listitem>
 
index 529f19863e5113e82cc1b5ba93ec1d5cdaa09b44..55d5e9c96e7374ed4cafae402eb80840f7d16736 100644 (file)
@@ -45,6 +45,7 @@ static char *api_extensions[] = {
        "seccomp_allow_nesting",
        "seccomp_notify",
        "network_veth_routes",
+       "network_ipvlan",
 };
 
 static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions);
index ebed11522f7628fcf84a17c262d0832ff6894e76..ac7e78eb1beb45aa5b0bd5135f57d61074c1621c 100644 (file)
@@ -130,6 +130,8 @@ lxc_config_define(net_ipv6_address);
 lxc_config_define(net_ipv6_gateway);
 lxc_config_define(net_link);
 lxc_config_define(net_macvlan_mode);
+lxc_config_define(net_ipvlan_mode);
+lxc_config_define(net_ipvlan_isolation);
 lxc_config_define(net_mtu);
 lxc_config_define(net_name);
 lxc_config_define(net_nic);
@@ -221,6 +223,8 @@ static struct lxc_config_t config_jump_table[] = {
        { "lxc.net.ipv6.gateway",          set_config_net_ipv6_gateway,            get_config_net_ipv6_gateway,            clr_config_net_ipv6_gateway,          },
        { "lxc.net.link",                  set_config_net_link,                    get_config_net_link,                    clr_config_net_link,                  },
        { "lxc.net.macvlan.mode",          set_config_net_macvlan_mode,            get_config_net_macvlan_mode,            clr_config_net_macvlan_mode,          },
+       { "lxc.net.ipvlan.mode",           set_config_net_ipvlan_mode,             get_config_net_ipvlan_mode,             clr_config_net_ipvlan_mode,           },
+       { "lxc.net.ipvlan.isolation",      set_config_net_ipvlan_isolation,        get_config_net_ipvlan_isolation,        clr_config_net_ipvlan_isolation,      },
        { "lxc.net.mtu",                   set_config_net_mtu,                     get_config_net_mtu,                     clr_config_net_mtu,                   },
        { "lxc.net.name",                  set_config_net_name,                    get_config_net_name,                    clr_config_net_name,                  },
        { "lxc.net.script.down",           set_config_net_script_down,             get_config_net_script_down,             clr_config_net_script_down,           },
@@ -291,21 +295,24 @@ static int set_config_net_type(const char *key, const char *value,
        if (!netdev)
                return -1;
 
-       if (!strcmp(value, "veth")) {
+       if (strcmp(value, "veth") == 0) {
                netdev->type = LXC_NET_VETH;
                lxc_list_init(&netdev->priv.veth_attr.ipv4_routes);
                lxc_list_init(&netdev->priv.veth_attr.ipv6_routes);
-       } else if (!strcmp(value, "macvlan")) {
+       } else if (strcmp(value, "macvlan") == 0) {
                netdev->type = LXC_NET_MACVLAN;
-               lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode,
-                                        "private");
-       } else if (!strcmp(value, "vlan")) {
+               lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, "private");
+       } else if (strcmp(value, "ipvlan") == 0) {
+               netdev->type = LXC_NET_IPVLAN;
+               lxc_ipvlan_mode_to_flag(&netdev->priv.ipvlan_attr.mode, "l3");
+               lxc_ipvlan_isolation_to_flag(&netdev->priv.ipvlan_attr.isolation, "bridge");
+       } else if (strcmp(value, "vlan") == 0) {
                netdev->type = LXC_NET_VLAN;
-       } else if (!strcmp(value, "phys")) {
+       } else if (strcmp(value, "phys") == 0) {
                netdev->type = LXC_NET_PHYS;
-       } else if (!strcmp(value, "empty")) {
+       } else if (strcmp(value, "empty") == 0) {
                netdev->type = LXC_NET_EMPTY;
-       } else if (!strcmp(value, "none")) {
+       } else if (strcmp(value, "none") == 0) {
                netdev->type = LXC_NET_NONE;
        } else {
                ERROR("Invalid network type %s", value);
@@ -438,6 +445,44 @@ static int set_config_net_macvlan_mode(const char *key, const char *value,
        return lxc_macvlan_mode_to_flag(&netdev->priv.macvlan_attr.mode, value);
 }
 
+static int set_config_net_ipvlan_mode(const char *key, const char *value,
+                                      struct lxc_conf *lxc_conf, void *data)
+{
+       struct lxc_netdev *netdev = data;
+
+       if (lxc_config_value_empty(value))
+               return clr_config_net_ipvlan_mode(key, lxc_conf, data);
+
+       if (!netdev)
+               return minus_one_set_errno(EINVAL);
+
+       if (netdev->type != LXC_NET_IPVLAN) {
+               SYSERROR("Invalid ipvlan mode \"%s\", can only be used with ipvlan network", value);
+               return minus_one_set_errno(EINVAL);
+       }
+
+       return lxc_ipvlan_mode_to_flag(&netdev->priv.ipvlan_attr.mode, value);
+}
+
+static int set_config_net_ipvlan_isolation(const char *key, const char *value,
+                                      struct lxc_conf *lxc_conf, void *data)
+{
+       struct lxc_netdev *netdev = data;
+
+       if (lxc_config_value_empty(value))
+               return clr_config_net_ipvlan_isolation(key, lxc_conf, data);
+
+       if (!netdev)
+               return minus_one_set_errno(EINVAL);
+
+       if (netdev->type != LXC_NET_IPVLAN) {
+               SYSERROR("Invalid ipvlan isolation \"%s\", can only be used with ipvlan network", value);
+               return minus_one_set_errno(EINVAL);
+       }
+
+       return lxc_ipvlan_isolation_to_flag(&netdev->priv.ipvlan_attr.isolation, value);
+}
+
 static int set_config_net_hwaddr(const char *key, const char *value,
                                 struct lxc_conf *lxc_conf, void *data)
 {
@@ -4931,6 +4976,38 @@ static int clr_config_net_macvlan_mode(const char *key,
        return 0;
 }
 
+static int clr_config_net_ipvlan_mode(const char *key,
+                                      struct lxc_conf *lxc_conf, void *data)
+{
+       struct lxc_netdev *netdev = data;
+
+       if (!netdev)
+               return minus_one_set_errno(EINVAL);
+
+       if (netdev->type != LXC_NET_IPVLAN)
+               return 0;
+
+       netdev->priv.ipvlan_attr.mode = -1;
+
+       return 0;
+}
+
+static int clr_config_net_ipvlan_isolation(const char *key,
+                                      struct lxc_conf *lxc_conf, void *data)
+{
+       struct lxc_netdev *netdev = data;
+
+       if (!netdev)
+               return minus_one_set_errno(EINVAL);
+
+       if (netdev->type != LXC_NET_IPVLAN)
+               return 0;
+
+       netdev->priv.ipvlan_attr.isolation = -1;
+
+       return 0;
+}
+
 static int clr_config_net_veth_pair(const char *key, struct lxc_conf *lxc_conf,
                                    void *data)
 {
@@ -5268,6 +5345,84 @@ static int get_config_net_macvlan_mode(const char *key, char *retv, int inlen,
        return fulllen;
 }
 
+static int get_config_net_ipvlan_mode(const char *key, char *retv, int inlen,
+                                      struct lxc_conf *c, void *data)
+{
+       int len;
+       int fulllen = 0;
+       const char *mode;
+       struct lxc_netdev *netdev = data;
+
+       if (!retv)
+               inlen = 0;
+       else
+               memset(retv, 0, inlen);
+
+       if (!netdev)
+               return minus_one_set_errno(EINVAL);
+
+       if (netdev->type != LXC_NET_IPVLAN)
+               return 0;
+
+       switch (netdev->priv.ipvlan_attr.mode) {
+       case IPVLAN_MODE_L3:
+               mode = "l3";
+               break;
+       case IPVLAN_MODE_L3S:
+               mode = "l3s";
+               break;
+       case IPVLAN_MODE_L2:
+               mode = "l2";
+               break;
+       default:
+               mode = "(invalid)";
+               break;
+       }
+
+       strprint(retv, inlen, "%s", mode);
+
+       return fulllen;
+}
+
+static int get_config_net_ipvlan_isolation(const char *key, char *retv, int inlen,
+                                      struct lxc_conf *c, void *data)
+{
+       int len;
+       int fulllen = 0;
+       const char *mode;
+       struct lxc_netdev *netdev = data;
+
+       if (!retv)
+               inlen = 0;
+       else
+               memset(retv, 0, inlen);
+
+       if (!netdev)
+               return minus_one_set_errno(EINVAL);
+
+       if (netdev->type != LXC_NET_IPVLAN)
+               return 0;
+
+       switch (netdev->priv.ipvlan_attr.isolation) {
+       case IPVLAN_ISOLATION_BRIDGE:
+               mode = "bridge";
+               break;
+       case IPVLAN_ISOLATION_PRIVATE:
+               mode = "private";
+               break;
+       case IPVLAN_ISOLATION_VEPA:
+               mode = "vepa";
+               break;
+       default:
+               mode = "(invalid)";
+               break;
+       }
+
+       strprint(retv, inlen, "%s", mode);
+
+       return fulllen;
+}
+
 static int get_config_net_veth_pair(const char *key, char *retv, int inlen,
                                    struct lxc_conf *c, void *data)
 {
@@ -5718,6 +5873,10 @@ int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen)
        case LXC_NET_MACVLAN:
                strprint(retv, inlen, "macvlan.mode\n");
                break;
+       case LXC_NET_IPVLAN:
+               strprint(retv, inlen, "ipvlan.mode\n");
+               strprint(retv, inlen, "ipvlan.isolation\n");
+               break;
        case LXC_NET_VLAN:
                strprint(retv, inlen, "vlan.id\n");
                break;
index 67bf0824a2e4ddcac1eddab1b6ae1c7db1f3b926..5bceb96bdc6b836090076190f05433e2eae05558 100644 (file)
@@ -299,6 +299,17 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf)
                                      mode ? mode : "(invalid mode)");
                        }
                        break;
+               case LXC_NET_IPVLAN:
+                       TRACE("type: ipvlan");
+
+                       char *mode;
+                       mode = lxc_ipvlan_flag_to_mode(netdev->priv.ipvlan_attr.mode);
+                       TRACE("ipvlan mode: %s", mode ? mode : "(invalid mode)");
+
+                       char *isolation;
+                       isolation = lxc_ipvlan_flag_to_isolation(netdev->priv.ipvlan_attr.isolation);
+                       TRACE("ipvlan isolation: %s", isolation ? isolation : "(invalid isolation)");
+                       break;
                case LXC_NET_VLAN:
                        TRACE("type: vlan");
                        TRACE("vlan id: %d", netdev->priv.vlan_attr.vid);
@@ -519,6 +530,74 @@ char *lxc_macvlan_flag_to_mode(int mode)
        return NULL;
 }
 
+static struct lxc_ipvlan_mode {
+       char *name;
+       int mode;
+} ipvlan_mode[] = {
+    { "l3",  IPVLAN_MODE_L3  },
+    { "l3s", IPVLAN_MODE_L3S },
+    { "l2",  IPVLAN_MODE_L2  },
+};
+
+int lxc_ipvlan_mode_to_flag(int *mode, const char *value)
+{
+       for (size_t i = 0; i < sizeof(ipvlan_mode) / sizeof(ipvlan_mode[0]); i++) {
+               if (strcmp(ipvlan_mode[i].name, value) != 0)
+                       continue;
+
+               *mode = ipvlan_mode[i].mode;
+               return 0;
+       }
+
+       return -1;
+}
+
+char *lxc_ipvlan_flag_to_mode(int mode)
+{
+       for (size_t i = 0; i < sizeof(ipvlan_mode) / sizeof(ipvlan_mode[0]); i++) {
+               if (ipvlan_mode[i].mode != mode)
+                       continue;
+
+               return ipvlan_mode[i].name;
+       }
+
+       return NULL;
+}
+
+static struct lxc_ipvlan_isolation {
+       char *name;
+       int flag;
+} ipvlan_isolation[] = {
+    { "bridge",  IPVLAN_ISOLATION_BRIDGE  },
+    { "private", IPVLAN_ISOLATION_PRIVATE },
+    { "vepa",    IPVLAN_ISOLATION_VEPA    },
+};
+
+int lxc_ipvlan_isolation_to_flag(int *flag, const char *value)
+{
+       for (size_t i = 0; i < sizeof(ipvlan_isolation) / sizeof(ipvlan_isolation[0]); i++) {
+               if (strcmp(ipvlan_isolation[i].name, value) != 0)
+                       continue;
+
+               *flag = ipvlan_isolation[i].flag;
+               return 0;
+       }
+
+       return -1;
+}
+
+char *lxc_ipvlan_flag_to_isolation(int flag)
+{
+       for (size_t i = 0; i < sizeof(ipvlan_isolation) / sizeof(ipvlan_isolation[0]); i++) {
+               if (ipvlan_isolation[i].flag != flag)
+                       continue;
+
+               return ipvlan_isolation[i].name;
+       }
+
+       return NULL;
+}
+
 int set_config_string_item(char **conf_item, const char *value)
 {
        char *new_value;
index 5a3bcc914cd58b138ea803886169fb0cd0493877..cfed91dc0907b53be166c3beb991725ab23f2548 100644 (file)
@@ -58,6 +58,10 @@ extern bool lxc_remove_nic_by_idx(struct lxc_conf *conf, unsigned int idx);
 extern void lxc_free_networks(struct lxc_list *networks);
 extern int lxc_macvlan_mode_to_flag(int *mode, const char *value);
 extern char *lxc_macvlan_flag_to_mode(int mode);
+extern int lxc_ipvlan_mode_to_flag(int *mode, const char *value);
+extern char *lxc_ipvlan_flag_to_mode(int mode);
+extern int lxc_ipvlan_isolation_to_flag(int *mode, const char *value);
+extern char *lxc_ipvlan_flag_to_isolation(int mode);
 
 extern int set_config_string_item(char **conf_item, const char *value);
 extern int set_config_string_item_max(char **conf_item, const char *value,
index 7df3b56f0300b84321a8264f49d9a52d475b90d3..7626c5d76b338743e4e16e4db36ac720efba72dd 100644 (file)
@@ -280,6 +280,14 @@ extern int __build_bug_on_failed;
 #define IFLA_MACVLAN_MODE 1
 #endif
 
+#ifndef IFLA_IPVLAN_MODE
+#define IFLA_IPVLAN_MODE 1
+#endif
+
+#ifndef IFLA_IPVLAN_ISOLATION
+#define IFLA_IPVLAN_ISOLATION 2
+#endif
+
 #ifndef IFLA_NEW_NETNSID
 #define IFLA_NEW_NETNSID 45
 #endif
@@ -333,6 +341,30 @@ extern int __build_bug_on_failed;
 #define MACVLAN_MODE_PASSTHRU 8
 #endif
 
+#ifndef IPVLAN_MODE_L2
+#define IPVLAN_MODE_L2 0
+#endif
+
+#ifndef IPVLAN_MODE_L3
+#define IPVLAN_MODE_L3 1
+#endif
+
+#ifndef IPVLAN_MODE_L3S
+#define IPVLAN_MODE_L3S 2
+#endif
+
+#ifndef IPVLAN_ISOLATION_BRIDGE
+#define IPVLAN_ISOLATION_BRIDGE 0
+#endif
+
+#ifndef IPVLAN_ISOLATION_PRIVATE
+#define IPVLAN_ISOLATION_PRIVATE 1
+#endif
+
+#ifndef IPVLAN_ISOLATION_VEPA
+#define IPVLAN_ISOLATION_VEPA 2
+#endif
+
 /* Attributes of RTM_NEWNSID/RTM_GETNSID messages */
 enum {
        __LXC_NETNSA_NONE,
index ec7dbccccfb0929727862039ea2dafa24905adf9..def484613d0d5627ffe580c18239d726a6263710 100644 (file)
@@ -376,6 +376,147 @@ on_error:
        return -1;
 }
 
+static int lxc_ipvlan_create(const char *master, const char *name, int mode, int isolation)
+{
+       int err, index, len;
+       struct ifinfomsg *ifi;
+       struct nl_handler nlh;
+       struct rtattr *nest, *nest2;
+       struct nlmsg *answer = NULL, *nlmsg = NULL;
+
+       len = strlen(master);
+       if (len == 1 || len >= IFNAMSIZ)
+               return minus_one_set_errno(EINVAL);
+
+       len = strlen(name);
+       if (len == 1 || len >= IFNAMSIZ)
+               return minus_one_set_errno(EINVAL);
+
+       index = if_nametoindex(master);
+       if (!index)
+               return minus_one_set_errno(EINVAL);
+
+       err = netlink_open(&nlh, NETLINK_ROUTE);
+       if (err)
+               return minus_one_set_errno(-err);
+
+       err = -ENOMEM;
+       nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE);
+       if (!nlmsg)
+               goto out;
+
+       answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE);
+       if (!answer)
+               goto out;
+
+       nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
+       nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK;
+
+       ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg));
+       if (!ifi) {
+               goto out;
+       }
+       ifi->ifi_family = AF_UNSPEC;
+
+       err = -EPROTO;
+       nest = nla_begin_nested(nlmsg, IFLA_LINKINFO);
+       if (!nest)
+               goto out;
+
+       if (nla_put_string(nlmsg, IFLA_INFO_KIND, "ipvlan"))
+               goto out;
+
+       if (mode) {
+               nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA);
+               if (!nest2)
+                       goto out;
+
+               if (nla_put_u32(nlmsg, IFLA_IPVLAN_MODE, mode))
+                       goto out;
+
+               /* if_link.h does not define the isolation flag value for bridge mode so we define it as 0
+                * and only send mode if mode >0 as default mode is bridge anyway according to ipvlan docs.
+                */
+               if (isolation > 0) {
+                       if (nla_put_u16(nlmsg, IFLA_IPVLAN_ISOLATION, isolation))
+                               goto out;
+               }
+
+               nla_end_nested(nlmsg, nest2);
+       }
+
+       nla_end_nested(nlmsg, nest);
+
+       if (nla_put_u32(nlmsg, IFLA_LINK, index))
+               goto out;
+
+       if (nla_put_string(nlmsg, IFLA_IFNAME, name))
+               goto out;
+
+       err = netlink_transaction(&nlh, nlmsg, answer);
+out:
+       netlink_close(&nlh);
+       nlmsg_free(answer);
+       nlmsg_free(nlmsg);
+       if (err < 0)
+               return minus_one_set_errno(-err);
+       return 0;
+}
+
+static int instantiate_ipvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
+{
+       char peerbuf[IFNAMSIZ], *peer;
+       int err;
+
+       if (netdev->link[0] == '\0') {
+               ERROR("No link for ipvlan network device specified");
+               return -1;
+       }
+
+       err = snprintf(peerbuf, sizeof(peerbuf), "ipXXXXXX");
+       if (err < 0 || (size_t)err >= sizeof(peerbuf))
+               return -1;
+
+       peer = lxc_mkifname(peerbuf);
+       if (!peer)
+               return -1;
+
+       err = lxc_ipvlan_create(netdev->link, peer, netdev->priv.ipvlan_attr.mode, netdev->priv.ipvlan_attr.isolation);
+       if (err) {
+               SYSERROR("Failed to create ipvlan interface \"%s\" on \"%s\"", peer, netdev->link);
+               goto on_error;
+       }
+
+       netdev->ifindex = if_nametoindex(peer);
+       if (!netdev->ifindex) {
+               ERROR("Failed to retrieve ifindex for \"%s\"", peer);
+               goto on_error;
+       }
+
+       if (netdev->upscript) {
+               char *argv[] = {
+                   "ipvlan",
+                   netdev->link,
+                   NULL,
+               };
+
+               err = run_script_argv(handler->name,
+                               handler->conf->hooks_version, "net",
+                               netdev->upscript, "up", argv);
+               if (err < 0)
+                       goto on_error;
+       }
+
+       DEBUG("Instantiated ipvlan \"%s\" with ifindex is %d and mode %d",
+             peer, netdev->ifindex, netdev->priv.macvlan_attr.mode);
+
+       return 0;
+
+on_error:
+       lxc_netdev_delete_by_name(peer);
+       return -1;
+}
+
 static int instantiate_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
 {
        char peer[IFNAMSIZ];
@@ -518,6 +659,7 @@ static int instantiate_none(struct lxc_handler *handler, struct lxc_netdev *netd
 static  instantiate_cb netdev_conf[LXC_NET_MAXCONFTYPE + 1] = {
        [LXC_NET_VETH]    = instantiate_veth,
        [LXC_NET_MACVLAN] = instantiate_macvlan,
+       [LXC_NET_IPVLAN]  = instantiate_ipvlan,
        [LXC_NET_VLAN]    = instantiate_vlan,
        [LXC_NET_PHYS]    = instantiate_phys,
        [LXC_NET_EMPTY]   = instantiate_empty,
@@ -571,6 +713,26 @@ static int shutdown_macvlan(struct lxc_handler *handler, struct lxc_netdev *netd
        return 0;
 }
 
+static int shutdown_ipvlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
+{
+       int ret;
+       char *argv[] = {
+               "ipvlan",
+               netdev->link,
+               NULL,
+       };
+
+       if (!netdev->downscript)
+               return 0;
+
+       ret = run_script_argv(handler->name, handler->conf->hooks_version,
+                             "net", netdev->downscript, "down", argv);
+       if (ret < 0)
+               return -1;
+
+       return 0;
+}
+
 static int shutdown_vlan(struct lxc_handler *handler, struct lxc_netdev *netdev)
 {
        int ret;
@@ -638,6 +800,7 @@ static int shutdown_none(struct lxc_handler *handler, struct lxc_netdev *netdev)
 static  instantiate_cb netdev_deconf[LXC_NET_MAXCONFTYPE + 1] = {
        [LXC_NET_VETH]    = shutdown_veth,
        [LXC_NET_MACVLAN] = shutdown_macvlan,
+       [LXC_NET_IPVLAN]  = shutdown_ipvlan,
        [LXC_NET_VLAN]    = shutdown_vlan,
        [LXC_NET_PHYS]    = shutdown_phys,
        [LXC_NET_EMPTY]   = shutdown_empty,
@@ -2012,6 +2175,7 @@ static const char *const lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = {
        [LXC_NET_EMPTY]   = "empty",
        [LXC_NET_VETH]    = "veth",
        [LXC_NET_MACVLAN] = "macvlan",
+       [LXC_NET_IPVLAN]  = "ipvlan",
        [LXC_NET_PHYS]    = "phys",
        [LXC_NET_VLAN]    = "vlan",
        [LXC_NET_NONE]    = "none",
index e2757c1dba85ba7ccb0f104475fea79f3f6693ee..fa80404bc23cb7d0c83600ac5f4c6766b26da2f0 100644 (file)
@@ -40,6 +40,7 @@ enum {
        LXC_NET_EMPTY,
        LXC_NET_VETH,
        LXC_NET_MACVLAN,
+       LXC_NET_IPVLAN,
        LXC_NET_PHYS,
        LXC_NET_VLAN,
        LXC_NET_NONE,
@@ -110,6 +111,11 @@ struct ifla_macvlan {
        int mode; /* private, vepa, bridge, passthru */
 };
 
+struct ifla_ipvlan {
+       int mode; /* l3, l3s, l2 */
+       int isolation; /* bridge, private, vepa */
+};
+
 /* Contains information about the physical network device as seen from the host.
  * @ifindex : The ifindex of the physical network device in the host's network
  *            namespace.
@@ -120,6 +126,7 @@ struct ifla_phys {
 
 union netdev_p {
        struct ifla_macvlan macvlan_attr;
+       struct ifla_ipvlan ipvlan_attr;
        struct ifla_phys phys_attr;
        struct ifla_veth veth_attr;
        struct ifla_vlan vlan_attr;
index f4b4e9a287e402090bd6c82a67af5f4894dba60c..ad17867b43a8cc94bc6e2a93a44ebdd4d7e0fd93 100644 (file)
@@ -666,6 +666,11 @@ int main(int argc, char *argv[])
                goto non_test_error;
        }
 
+       if (set_get_compare_clear_save_load(c, "lxc.net.0.type", "ipvlan", tmpf, true)) {
+               lxc_error("%s\n", "lxc.net.0.type");
+               goto non_test_error;
+       }
+
        if (set_get_compare_clear_save_load(c, "lxc.net.1000.type", "phys", tmpf, true)) {
                lxc_error("%s\n", "lxc.net.1000.type");
                goto non_test_error;
@@ -701,6 +706,36 @@ int main(int argc, char *argv[])
                goto non_test_error;
        }
 
+       if (set_get_compare_clear_save_load_network(c, "lxc.net.0.ipvlan.mode", "l3", tmpf, true, "ipvlan")) {
+               lxc_error("%s\n", "lxc.net.0.ipvlan.mode");
+               goto non_test_error;
+       }
+
+       if (set_get_compare_clear_save_load_network(c, "lxc.net.0.ipvlan.mode", "l3s", tmpf, true, "ipvlan")) {
+               lxc_error("%s\n", "lxc.net.0.ipvlan.mode");
+               goto non_test_error;
+       }
+
+       if (set_get_compare_clear_save_load_network(c, "lxc.net.0.ipvlan.mode", "l2", tmpf, true, "ipvlan")) {
+               lxc_error("%s\n", "lxc.net.0.ipvlan.mode");
+               goto non_test_error;
+       }
+
+       if (set_get_compare_clear_save_load_network(c, "lxc.net.0.ipvlan.isolation", "bridge", tmpf, true, "ipvlan")) {
+               lxc_error("%s\n", "lxc.net.0.ipvlan.isolation");
+               goto non_test_error;
+       }
+
+       if (set_get_compare_clear_save_load_network(c, "lxc.net.0.ipvlan.isolation", "private", tmpf, true, "ipvlan")) {
+               lxc_error("%s\n", "lxc.net.0.ipvlan.isolation");
+               goto non_test_error;
+       }
+
+       if (set_get_compare_clear_save_load_network(c, "lxc.net.0.ipvlan.isolation", "vepa", tmpf, true, "ipvlan")) {
+               lxc_error("%s\n", "lxc.net.0.ipvlan.isolation");
+               goto non_test_error;
+       }
+
        if (set_get_compare_clear_save_load_network(c, "lxc.net.0.veth.pair", "clusterfuck", tmpf, true, "veth")) {
                lxc_error("%s\n", "lxc.net.0.veth.pair");
                goto non_test_error;