diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 8c95021ad..d5a0a3af7 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -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
+```
diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
index 3b3dd6dde..19f3acbd7 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
@@ -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 , ,
and .
- In mode, the device never
+ In mode, the device never
communicates with any other device on the same upper_dev (default).
In 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.
+
+ an ipvlan interface is linked
+ with the interface specified by
+ the and assigned to
+ the container.
+ specifies the
+ mode the ipvlan will use to communicate between
+ different ipvlan on the same upper device. The accepted
+ modes are , and
+ . It defaults to mode.
+ In 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 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 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.
+ specifies the isolation mode.
+ The accepted isolation values are ,
+ and .
+ It defaults to .
+ In isolation mode slaves can cross-talk among themselves
+ apart from talking through the master device.
+ In isolation mode the port is set in private mode.
+ i.e. port won't allow cross communication between slaves.
+ In 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.
+
+
an already existing interface
specified by the is
@@ -610,8 +645,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
interface (as specified by the
option) and use that as
the gateway. is only available when
- using the and
- network types.
+ using the ,
+ and network types.
@@ -644,8 +679,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
interface (as specified by the
option) and use that as
the gateway. is only available when
- using the and
- network types.
+ using the ,
+ and network types.
@@ -680,7 +715,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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').
@@ -746,7 +781,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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').
diff --git a/src/lxc/api_extensions.h b/src/lxc/api_extensions.h
index 529f19863..55d5e9c96 100644
--- a/src/lxc/api_extensions.h
+++ b/src/lxc/api_extensions.h
@@ -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);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index ebed11522..ac7e78eb1 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -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;
diff --git a/src/lxc/confile_utils.c b/src/lxc/confile_utils.c
index 67bf0824a..5bceb96bd 100644
--- a/src/lxc/confile_utils.c
+++ b/src/lxc/confile_utils.c
@@ -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;
diff --git a/src/lxc/confile_utils.h b/src/lxc/confile_utils.h
index 5a3bcc914..cfed91dc0 100644
--- a/src/lxc/confile_utils.h
+++ b/src/lxc/confile_utils.h
@@ -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,
diff --git a/src/lxc/macro.h b/src/lxc/macro.h
index 7df3b56f0..7626c5d76 100644
--- a/src/lxc/macro.h
+++ b/src/lxc/macro.h
@@ -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,
diff --git a/src/lxc/network.c b/src/lxc/network.c
index ec7dbcccc..def484613 100644
--- a/src/lxc/network.c
+++ b/src/lxc/network.c
@@ -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",
diff --git a/src/lxc/network.h b/src/lxc/network.h
index e2757c1db..fa80404bc 100644
--- a/src/lxc/network.h
+++ b/src/lxc/network.h
@@ -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;
diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c
index f4b4e9a28..ad17867b4 100644
--- a/src/tests/parse_config_file.c
+++ b/src/tests/parse_config_file.c
@@ -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;