The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxc/pull/3439
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Includes https://github.com/lxc/lxc/pull/3435 Adds openvswitch VLAN support.
From 372adece8bf59c24696462a211b53ace3c93c0e8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 8 Jun 2020 13:24:08 +0100 Subject: [PATCH 01/20] macro: Adds UINT_TO_PTR and PTR_TO_USHORT helpers Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/macro.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lxc/macro.h b/src/lxc/macro.h index 3a5bb07464..7b2ad79edd 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -433,6 +433,9 @@ enum { #define PTR_TO_UINT64(p) ((uint64_t)((intptr_t)(p))) +#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) +#define PTR_TO_USHORT(p) ((unsigned short)((uintptr_t)(p))) + #define LXC_INVALID_UID ((uid_t)-1) #define LXC_INVALID_GID ((gid_t)-1) From 26da53c3acc51d5a93476c47836508e1661c6505 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 11:26:35 +0100 Subject: [PATCH 02/20] network: Adds check for bridge link interface existence in instantiate_veth To avoid misleading errors about openvswitch when non-existent bridge link interface specified. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/network.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lxc/network.c b/src/lxc/network.c index da09141dd6..15c362fb69 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -324,11 +324,15 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd } if (!is_empty_string(netdev->link) && netdev->priv.veth_attr.mode == VETH_MODE_BRIDGE) { + if (!lxc_nic_exists(netdev->link)) { + SYSERROR("Failed to attach \"%s\" to bridge \"%s\", bridge interface doesn't exist", veth1, netdev->link); + goto out_delete; + } + err = lxc_bridge_attach(netdev->link, veth1); if (err) { errno = -err; - SYSERROR("Failed to attach \"%s\" to bridge \"%s\"", - veth1, netdev->link); + SYSERROR("Failed to attach \"%s\" to bridge \"%s\"", veth1, netdev->link); goto out_delete; } INFO("Attached \"%s\" to bridge \"%s\"", veth1, netdev->link); From d80ff1fac7c74a8e0b9f7910ad9f83da81b29cfa Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 4 Jun 2020 14:16:09 +0100 Subject: [PATCH 03/20] api/extensions: Adds network_bridge_vlan API extension Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/api_extensions.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lxc/api_extensions.h b/src/lxc/api_extensions.h index b69467f262..2bbdc5e43a 100644 --- a/src/lxc/api_extensions.h +++ b/src/lxc/api_extensions.h @@ -40,6 +40,7 @@ static char *api_extensions[] = { "cgroup2", "pidfd", "cgroup_advanced_isolation", + "network_bridge_vlan", }; static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions); From 59315a06819e42f0e7f29c294ce0625ed4a616c6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 17:44:55 +0100 Subject: [PATCH 04/20] macro: Adds bridge VLAN constants Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/macro.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lxc/macro.h b/src/lxc/macro.h index 7b2ad79edd..50cbb34341 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -380,6 +380,26 @@ extern int __build_bug_on_failed; #define IPVLAN_ISOLATION_VEPA 2 #endif +#ifndef BRIDGE_FLAGS_MASTER +#define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */ +#endif + +#ifndef BRIDGE_VLAN_INFO_PVID +#define BRIDGE_VLAN_INFO_PVID (1<<1) /* VLAN is PVID, ingress untagged */ +#endif + +#ifndef BRIDGE_VLAN_INFO_UNTAGGED +#define BRIDGE_VLAN_INFO_UNTAGGED (1<<2) /* VLAN egresses untagged */ +#endif + +#ifndef IFLA_BRIDGE_FLAGS +#define IFLA_BRIDGE_FLAGS 0 +#endif + +#ifndef IFLA_BRIDGE_VLAN_INFO +#define IFLA_BRIDGE_VLAN_INFO 2 +#endif + /* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ enum { __LXC_NETNSA_NONE, From 53a9ba7ff4e9b968b7d2e1f88413946a553cf4eb Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 4 Jun 2020 15:27:20 +0100 Subject: [PATCH 05/20] macro: Adds constant for BRIDGE_VLAN_NONE mode Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/macro.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lxc/macro.h b/src/lxc/macro.h index 50cbb34341..7ff0b77acd 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -380,6 +380,10 @@ extern int __build_bug_on_failed; #define IPVLAN_ISOLATION_VEPA 2 #endif +#ifndef BRIDGE_VLAN_NONE +#define BRIDGE_VLAN_NONE -1 /* Bridge VLAN option set to "none". */ +#endif + #ifndef BRIDGE_FLAGS_MASTER #define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */ #endif From 2abd5206d769e4867794bb200d6b43a8f4d0a702 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 4 Jun 2020 17:02:03 +0100 Subject: [PATCH 06/20] macro: Adds BRIDGE_VLAN_ID_MAX constant Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/macro.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lxc/macro.h b/src/lxc/macro.h index 7ff0b77acd..a3cb538d14 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -384,6 +384,10 @@ extern int __build_bug_on_failed; #define BRIDGE_VLAN_NONE -1 /* Bridge VLAN option set to "none". */ #endif +#ifndef BRIDGE_VLAN_ID_MAX +#define BRIDGE_VLAN_ID_MAX 4094 /* Bridge VLAN MAX VLAN ID. */ +#endif + #ifndef BRIDGE_FLAGS_MASTER #define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */ #endif From c96a27f7393fe12f50c8e1c71cc59242e85b085f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 11:08:19 +0100 Subject: [PATCH 07/20] network: Adds veth vlan_id, vlan_id_set and vlan_tagged_ids Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/network.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lxc/network.h b/src/lxc/network.h index 696380c900..cd4fde323e 100644 --- a/src/lxc/network.h +++ b/src/lxc/network.h @@ -79,6 +79,9 @@ struct ifla_veth { struct lxc_list ipv4_routes; struct lxc_list ipv6_routes; int mode; /* bridge, router */ + short vlan_id; + bool vlan_id_set; + struct lxc_list vlan_tagged_ids; }; struct ifla_vlan { From e0ef0c6c58518803feabc98982d3b8594e1111be Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 11:06:49 +0100 Subject: [PATCH 08/20] confile: Adds validation for lxc.net.veth.vlan.id Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/confile.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 13ebdd059a..69cb12e97f 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -32,6 +32,7 @@ #include "../include/netns_ifaddrs.h" #include "log.h" #include "lxcseccomp.h" +#include "macro.h" #include "memory_utils.h" #include "network.h" #include "parse.h" @@ -126,6 +127,7 @@ lxc_config_define(net_veth_mode); lxc_config_define(net_veth_pair); lxc_config_define(net_veth_ipv4_route); lxc_config_define(net_veth_ipv6_route); +lxc_config_define(net_veth_vlan_id); lxc_config_define(net_vlan_id); lxc_config_define(no_new_privs); lxc_config_define(personality); @@ -239,6 +241,7 @@ static struct lxc_config_t config_jump_table[] = { { "lxc.net.veth.pair", set_config_net_veth_pair, get_config_net_veth_pair, clr_config_net_veth_pair, }, { "lxc.net.veth.ipv4.route", set_config_net_veth_ipv4_route, get_config_net_veth_ipv4_route, clr_config_net_veth_ipv4_route, }, { "lxc.net.veth.ipv6.route", set_config_net_veth_ipv6_route, get_config_net_veth_ipv6_route, clr_config_net_veth_ipv6_route, }, + { "lxc.net.veth.vlan.id", set_config_net_veth_vlan_id, get_config_net_veth_vlan_id, clr_config_net_veth_vlan_id, }, { "lxc.net.", set_config_net_nic, get_config_net_nic, clr_config_net_nic, }, { "lxc.net", set_config_net, get_config_net, clr_config_net, }, { "lxc.no_new_privs", set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, }, @@ -487,6 +490,36 @@ static int set_config_net_veth_pair(const char *key, const char *value, return network_ifname(netdev->priv.veth_attr.pair, value, sizeof(netdev->priv.veth_attr.pair)); } +static int set_config_net_veth_vlan_id(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + int ret; + struct lxc_netdev *netdev = data; + + if (lxc_config_value_empty(value)) + return clr_config_net_veth_vlan_id(key, lxc_conf, data); + + if (!netdev) + return -1; + + if (strcmp(value, "none") == 0) { + netdev->priv.veth_attr.vlan_id = BRIDGE_VLAN_NONE; + } else { + unsigned short vlan_id; + ret = get_u16(&vlan_id, value, 0); + if (ret < 0) + return -1; + + if (vlan_id > BRIDGE_VLAN_ID_MAX) + ret_errno(EINVAL); + + netdev->priv.veth_attr.vlan_id = vlan_id; + } + + netdev->priv.veth_attr.vlan_id_set = true; + return 0; +} + static int set_config_net_macvlan_mode(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { @@ -5301,6 +5334,20 @@ static int clr_config_net_veth_pair(const char *key, struct lxc_conf *lxc_conf, return 0; } +static int clr_config_net_veth_vlan_id(const char *key, struct lxc_conf *lxc_conf, + void *data) +{ + struct lxc_netdev *netdev = data; + + if (!netdev) + return -1; + + netdev->priv.veth_attr.vlan_id = 0; + netdev->priv.veth_attr.vlan_id_set = false; + + return 0; +} + static int clr_config_net_script_up(const char *key, struct lxc_conf *lxc_conf, void *data) { @@ -5772,6 +5819,29 @@ static int get_config_net_veth_pair(const char *key, char *retv, int inlen, return fulllen; } +static int get_config_net_veth_vlan_id(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len; + int fulllen = 0; + struct lxc_netdev *netdev = data; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + if (!netdev) + return -1; + + if (netdev->type != LXC_NET_VETH) + return 0; + + strprint(retv, inlen, "%d", netdev->priv.veth_attr.vlan_id); + + return fulllen; +} + static int get_config_net_script_up(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { @@ -6200,6 +6270,7 @@ int lxc_list_net(struct lxc_conf *c, const char *key, char *retv, int inlen) strprint(retv, inlen, "veth.pair\n"); strprint(retv, inlen, "veth.ipv4.route\n"); strprint(retv, inlen, "veth.ipv6.route\n"); + strprint(retv, inlen, "veth.vlan.id\n"); break; case LXC_NET_MACVLAN: strprint(retv, inlen, "macvlan.mode\n"); From dbc69fd06a85bd955d1d2590acad8d602e8a62d8 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 17:44:13 +0100 Subject: [PATCH 09/20] confile: Adds validation for lxc.net.veth.vlan.tagged.id Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/confile.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/lxc/confile.c b/src/lxc/confile.c index 69cb12e97f..f4cb780632 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -128,6 +128,7 @@ lxc_config_define(net_veth_pair); lxc_config_define(net_veth_ipv4_route); lxc_config_define(net_veth_ipv6_route); lxc_config_define(net_veth_vlan_id); +lxc_config_define(net_veth_vlan_tagged_id); lxc_config_define(net_vlan_id); lxc_config_define(no_new_privs); lxc_config_define(personality); @@ -242,6 +243,7 @@ static struct lxc_config_t config_jump_table[] = { { "lxc.net.veth.ipv4.route", set_config_net_veth_ipv4_route, get_config_net_veth_ipv4_route, clr_config_net_veth_ipv4_route, }, { "lxc.net.veth.ipv6.route", set_config_net_veth_ipv6_route, get_config_net_veth_ipv6_route, clr_config_net_veth_ipv6_route, }, { "lxc.net.veth.vlan.id", set_config_net_veth_vlan_id, get_config_net_veth_vlan_id, clr_config_net_veth_vlan_id, }, + { "lxc.net.veth.vlan.tagged.id", set_config_net_veth_vlan_tagged_id, get_config_net_veth_vlan_tagged_id, clr_config_net_veth_vlan_tagged_id, }, { "lxc.net.", set_config_net_nic, get_config_net_nic, clr_config_net_nic, }, { "lxc.net", set_config_net, get_config_net, clr_config_net, }, { "lxc.no_new_privs", set_config_no_new_privs, get_config_no_new_privs, clr_config_no_new_privs, }, @@ -309,6 +311,7 @@ static int set_config_net_type(const char *key, const char *value, netdev->type = LXC_NET_VETH; lxc_list_init(&netdev->priv.veth_attr.ipv4_routes); lxc_list_init(&netdev->priv.veth_attr.ipv6_routes); + lxc_list_init(&netdev->priv.veth_attr.vlan_tagged_ids); if (!lxc_veth_flag_to_mode(netdev->priv.veth_attr.mode)) lxc_veth_mode_to_flag(&netdev->priv.veth_attr.mode, "bridge"); } else if (strcmp(value, "macvlan") == 0) { @@ -520,6 +523,39 @@ static int set_config_net_veth_vlan_id(const char *key, const char *value, return 0; } +static int set_config_net_veth_vlan_tagged_id(const char *key, const char *value, + struct lxc_conf *lxc_conf, void *data) +{ + __do_free struct lxc_list *list = NULL; + int ret; + unsigned short vlan_id; + struct lxc_netdev *netdev = data; + + if (lxc_config_value_empty(value)) + return clr_config_net_veth_vlan_tagged_id(key, lxc_conf, data); + + if (!netdev) + return ret_set_errno(-1, EINVAL); + + ret = get_u16(&vlan_id, value, 0); + if (ret < 0) + return -1; + + if (vlan_id > BRIDGE_VLAN_ID_MAX) + return -1; + + list = malloc(sizeof(*list)); + if (!list) + return -1; + + lxc_list_init(list); + list->elem = UINT_TO_PTR(vlan_id); + + lxc_list_add_tail(&netdev->priv.veth_attr.vlan_tagged_ids, move_ptr(list)); + + return 0; +} + static int set_config_net_macvlan_mode(const char *key, const char *value, struct lxc_conf *lxc_conf, void *data) { @@ -5348,6 +5384,24 @@ static int clr_config_net_veth_vlan_id(const char *key, struct lxc_conf *lxc_con return 0; } +static int clr_config_net_veth_vlan_tagged_id(const char *key, + struct lxc_conf *lxc_conf, void *data) +{ + struct lxc_netdev *netdev = data; + struct lxc_list *cur, *next; + + if (!netdev) + return -1; + + lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.vlan_tagged_ids, next) { + lxc_list_del(cur); + free(cur); + } + + return 0; +} + + static int clr_config_net_script_up(const char *key, struct lxc_conf *lxc_conf, void *data) { @@ -5842,6 +5896,37 @@ static int get_config_net_veth_vlan_id(const char *key, char *retv, int inlen, return fulllen; } +static int get_config_net_veth_vlan_tagged_id(const char *key, char *retv, int inlen, + struct lxc_conf *c, void *data) +{ + int len; + size_t listlen; + struct lxc_list *it; + int fulllen = 0; + struct lxc_netdev *netdev = data; + + if (!netdev) + ret_errno(EINVAL); + + if (netdev->type != LXC_NET_VETH) + return 0; + + if (!retv) + inlen = 0; + else + memset(retv, 0, inlen); + + listlen = lxc_list_len(&netdev->priv.veth_attr.vlan_tagged_ids); + + lxc_list_for_each(it, &netdev->priv.veth_attr.vlan_tagged_ids) { + unsigned short i = PTR_TO_USHORT(it->elem); + strprint(retv, inlen, "%u%s", i, + (listlen-- > 1) ? "\n" : ""); + } + + return fulllen; +} + static int get_config_net_script_up(const char *key, char *retv, int inlen, struct lxc_conf *c, void *data) { From 014a4b4783f4c268e24be031e3c5bbee8a640aa9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 11:07:15 +0100 Subject: [PATCH 10/20] confile/utils: Adds veth mode and vlan ID tracing to lxc_log_configured_netdevs Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/confile_utils.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lxc/confile_utils.c b/src/lxc/confile_utils.c index 05dadf9ec6..f8dcf2ae56 100644 --- a/src/lxc/confile_utils.c +++ b/src/lxc/confile_utils.c @@ -257,6 +257,7 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf) switch (netdev->type) { case LXC_NET_VETH: TRACE("type: veth"); + TRACE("veth mode: %d", netdev->priv.veth_attr.mode); if (netdev->priv.veth_attr.pair[0] != '\0') TRACE("veth pair: %s", @@ -269,6 +270,10 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf) if (netdev->priv.veth_attr.ifindex > 0) TRACE("host side ifindex for veth device: %d", netdev->priv.veth_attr.ifindex); + + if (netdev->priv.veth_attr.vlan_id_set) + TRACE("veth vlan id: %d", netdev->priv.veth_attr.vlan_id); + break; case LXC_NET_MACVLAN: TRACE("type: macvlan"); From 0a638a51a1f4d1ed99a9ec864b4f8d62fbc95d75 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 4 Jun 2020 15:30:34 +0100 Subject: [PATCH 11/20] confile/utils: Adds veth vlan tagged ID tracing to lxc_log_configured_netdevs Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/confile_utils.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lxc/confile_utils.c b/src/lxc/confile_utils.c index f8dcf2ae56..89fc555bc0 100644 --- a/src/lxc/confile_utils.c +++ b/src/lxc/confile_utils.c @@ -274,6 +274,11 @@ void lxc_log_configured_netdevs(const struct lxc_conf *conf) if (netdev->priv.veth_attr.vlan_id_set) TRACE("veth vlan id: %d", netdev->priv.veth_attr.vlan_id); + lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.vlan_tagged_ids, next) { + unsigned short vlan_tagged_id = PTR_TO_USHORT(cur->elem); + TRACE("veth vlan tagged id: %u", vlan_tagged_id); + } + break; case LXC_NET_MACVLAN: TRACE("type: macvlan"); From 120606e9080fb512662e59aba961e1d7833cd708 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 17:44:34 +0100 Subject: [PATCH 12/20] confile/utils: Adds freeing of priv.veth_attr.vlan_tagged_ids Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/confile_utils.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lxc/confile_utils.c b/src/lxc/confile_utils.c index 89fc555bc0..06e9f6b193 100644 --- a/src/lxc/confile_utils.c +++ b/src/lxc/confile_utils.c @@ -449,6 +449,11 @@ static void lxc_free_netdev(struct lxc_netdev *netdev) free(cur->elem); free(cur); } + + lxc_list_for_each_safe(cur, &netdev->priv.veth_attr.vlan_tagged_ids, next) { + lxc_list_del(cur); + free(cur); + } } free(netdev); From 5a5faa55141e06723fbe79540d38b160a97cb520 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 11:08:41 +0100 Subject: [PATCH 13/20] tests: Adds test for lxc.net.0.veth.vlan.id config key Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/tests/parse_config_file.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c index 9adbdd6a67..5b69fb6749 100644 --- a/src/tests/parse_config_file.c +++ b/src/tests/parse_config_file.c @@ -776,6 +776,11 @@ int main(int argc, char *argv[]) return -1; } + if (set_get_compare_clear_save_load_network(c, "lxc.net.0.veth.vlan.id", "2", tmpf, true, "veth")) { + lxc_error("%s\n", "lxc.net.0.veth.vlan.id"); + return -1; + } + if (set_get_compare_clear_save_load(c, "lxc.net.0.script.up", "/some/up/path", tmpf, true)) { lxc_error("%s\n", "lxc.net.0.script.up"); goto non_test_error; From 49fcc6369d4b01d132919c54e0a0f751184761fe Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 4 Jun 2020 15:27:48 +0100 Subject: [PATCH 14/20] tests: Adds test for bridge vlan "none" value Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/tests/parse_config_file.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c index 5b69fb6749..51dc1c0a59 100644 --- a/src/tests/parse_config_file.c +++ b/src/tests/parse_config_file.c @@ -776,6 +776,11 @@ int main(int argc, char *argv[]) return -1; } + if (set_get_compare_clear_save_load_network(c, "lxc.net.0.veth.vlan.id", "none", tmpf, false, "veth")) { + lxc_error("%s\n", "lxc.net.0.veth.vlan.id"); + return -1; + } + if (set_get_compare_clear_save_load_network(c, "lxc.net.0.veth.vlan.id", "2", tmpf, true, "veth")) { lxc_error("%s\n", "lxc.net.0.veth.vlan.id"); return -1; From 62ee8930fc033a674cce27aee47d3ec6e8727e70 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 4 Jun 2020 14:14:43 +0100 Subject: [PATCH 15/20] tests: Adds test for lxc.net.0.veth.vlan.tagged.id config key Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/tests/parse_config_file.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tests/parse_config_file.c b/src/tests/parse_config_file.c index 51dc1c0a59..6d0e88805d 100644 --- a/src/tests/parse_config_file.c +++ b/src/tests/parse_config_file.c @@ -786,6 +786,11 @@ int main(int argc, char *argv[]) return -1; } + if (set_get_compare_clear_save_load_network(c, "lxc.net.0.veth.vlan.tagged.id", "2", tmpf, true, "veth")) { + lxc_error("%s\n", "lxc.net.0.veth.vlan.tagged.id"); + return -1; + } + if (set_get_compare_clear_save_load(c, "lxc.net.0.script.up", "/some/up/path", tmpf, true)) { lxc_error("%s\n", "lxc.net.0.script.up"); goto non_test_error; From acbd9ecd8a2422d30e72e775c0f3ff0af263e7ac Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 17:45:30 +0100 Subject: [PATCH 16/20] network: Adds bridge vlan management functions Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/network.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/lxc/network.c b/src/lxc/network.c index 15c362fb69..888f1829c0 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -245,6 +245,90 @@ static int lxc_is_ip_forwarding_enabled(const char *ifname, int family) return lxc_read_file_expect(path, buf, 1, "1"); } +struct bridge_vlan_info { + __u16 flags; + __u16 vid; +}; + +static int lxc_bridge_vlan(unsigned int ifindex, unsigned short operation, unsigned short vlan_id, bool tagged) +{ + call_cleaner(nlmsg_free) struct nlmsg *answer = NULL, *nlmsg = NULL; + struct nl_handler nlh; + call_cleaner(netlink_close) struct nl_handler *nlh_ptr = &nlh; + int err; + struct ifinfomsg *ifi; + struct rtattr *nest; + unsigned short bridge_flags = 0; + struct bridge_vlan_info vlan_info; + + err = netlink_open(nlh_ptr, NETLINK_ROUTE); + if (err) + return ret_errno(-err); + + nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); + if (!nlmsg) + return ret_errno(ENOMEM); + + answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); + if (!answer) + return ret_errno(ENOMEM); + + nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlmsg->nlmsghdr->nlmsg_type = operation; + + ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); + if (!ifi) + return ret_errno(ENOMEM); + ifi->ifi_family = AF_BRIDGE; + ifi->ifi_index = ifindex; + + nest = nla_begin_nested(nlmsg, IFLA_AF_SPEC); + if (!nest) + return ret_errno(ENOMEM); + + bridge_flags |= BRIDGE_FLAGS_MASTER; + if (nla_put_u16(nlmsg, IFLA_BRIDGE_FLAGS, bridge_flags)) + return ret_errno(ENOMEM); + + vlan_info.vid = vlan_id; + vlan_info.flags = 0; + if (!tagged) + vlan_info.flags = BRIDGE_VLAN_INFO_PVID | BRIDGE_VLAN_INFO_UNTAGGED; + + if (nla_put_buffer(nlmsg, IFLA_BRIDGE_VLAN_INFO, &vlan_info, sizeof(struct bridge_vlan_info))) + return ret_errno(ENOMEM); + + nla_end_nested(nlmsg, nest); + + return netlink_transaction(nlh_ptr, nlmsg, answer); +} + +static int lxc_bridge_vlan_add(unsigned int ifindex, unsigned short vlan_id, bool tagged) +{ + return lxc_bridge_vlan(ifindex, RTM_SETLINK, vlan_id, tagged); +} + +static int lxc_bridge_vlan_del(unsigned int ifindex, unsigned short vlan_id) +{ + return lxc_bridge_vlan(ifindex, RTM_DELLINK, vlan_id, false); +} + +static int lxc_bridge_vlan_add_tagged(unsigned int ifindex, struct lxc_list *vlan_ids) +{ + struct lxc_list *iterator; + int err; + + lxc_list_for_each(iterator, vlan_ids) { + unsigned short *vlan_id = iterator->elem; + + err = lxc_bridge_vlan_add(ifindex, *vlan_id, true); + if (err) + return log_error_errno(-1, -err, "Failed to add tagged vlan \"%u\" to ifindex \"%d\"", *vlan_id, ifindex); + } + + return 0; +} + static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) { int err; From 715a4793d29558b35fd76093b03b4aa25fc7478c Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 3 Jun 2020 17:45:47 +0100 Subject: [PATCH 17/20] network: Updates instantiate_veth to set bridge vlan settings Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/network.c | 111 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/src/lxc/network.c b/src/lxc/network.c index 888f1829c0..68b0265c5b 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -319,16 +319,109 @@ static int lxc_bridge_vlan_add_tagged(unsigned int ifindex, struct lxc_list *vla int err; lxc_list_for_each(iterator, vlan_ids) { - unsigned short *vlan_id = iterator->elem; + unsigned short vlan_id = PTR_TO_USHORT(iterator->elem); - err = lxc_bridge_vlan_add(ifindex, *vlan_id, true); + err = lxc_bridge_vlan_add(ifindex, vlan_id, true); if (err) - return log_error_errno(-1, -err, "Failed to add tagged vlan \"%u\" to ifindex \"%d\"", *vlan_id, ifindex); + return log_error_errno(-1, -err, "Failed to add tagged vlan \"%u\" to ifindex \"%d\"", vlan_id, ifindex); } return 0; } +static int validate_veth(struct lxc_netdev *netdev) +{ + if (netdev->priv.veth_attr.mode != VETH_MODE_BRIDGE || is_empty_string(netdev->link)) { + /* Check that veth.vlan.id isn't being used in non bridge veth.mode. */ + if (netdev->priv.veth_attr.vlan_id_set) + return log_error_errno(-1, EINVAL, "Cannot use veth vlan.id when not in bridge mode or no bridge link specified"); + + /* Check that veth.vlan.tagged.id isn't being used in non bridge veth.mode. */ + if (lxc_list_len(&netdev->priv.veth_attr.vlan_tagged_ids) > 0) + return log_error_errno(-1, EINVAL, "Cannot use veth vlan.id when not in bridge mode or no bridge link specified"); + } + + if (netdev->priv.veth_attr.vlan_id_set) { + struct lxc_list *it; + lxc_list_for_each(it, &netdev->priv.veth_attr.vlan_tagged_ids) { + unsigned short i = PTR_TO_USHORT(it->elem); + if (i == netdev->priv.veth_attr.vlan_id) + return log_error_errno(-1, EINVAL, "Cannot use same veth vlan.id \"%u\" in vlan.tagged.id", netdev->priv.veth_attr.vlan_id); + } + } + + return 0; +} + +static int setup_veth_native_bridge_vlan(char *veth1, struct lxc_netdev *netdev) +{ + /* Skip setup if no VLAN options are specified. */ + if (!netdev->priv.veth_attr.vlan_id_set && lxc_list_len(&netdev->priv.veth_attr.vlan_tagged_ids) <= 0) + return 0; + + int err, rc, veth1index; + char path[PATH_MAX]; + char buf[5]; // Sufficient size to fit max VLAN ID (4094) and null char. + + /* Check vlan filtering is enabled on parent bridge. */ + rc = snprintf(path, sizeof(path), "/sys/class/net/%s/bridge/vlan_filtering", netdev->link); + if (rc < 0 || (size_t)rc >= sizeof(path)) + return -1; + + rc = lxc_read_from_file(path, buf, sizeof(buf)); + if (rc < 0) + return log_error_errno(rc, errno, "Failed reading from \"%s\"", path); + + buf[rc - 1] = '\0'; + + if (strcmp(buf, "1") != 0) + return log_error_errno(-1, EPERM, "vlan_filtering is not enabled on \"%s\"", netdev->link); + + /* Get veth1 ifindex for use with netlink. */ + veth1index = if_nametoindex(veth1); + if (!veth1index) + return log_error_errno(-1, errno, "Failed getting ifindex of \"%s\"", netdev->link); + + /* Configure untagged VLAN settings on bridge port if specified. */ + if (netdev->priv.veth_attr.vlan_id_set) { + unsigned short default_pvid; + + /* Get the bridge's default VLAN PVID. */ + rc = snprintf(path, sizeof(path), "/sys/class/net/%s/bridge/default_pvid", netdev->link); + if (rc < 0 || (size_t)rc >= sizeof(path)) + return -1; + + rc = lxc_read_from_file(path, buf, sizeof(buf)); + if (rc < 0) + return log_error_errno(rc, errno, "Failed reading from \"%s\"", path); + + buf[rc - 1] = '\0'; + err = get_u16(&default_pvid, buf, 0); + if (err != 0) + return log_error_errno(-1, EINVAL, "Failed parsing default_pvid of \"%s\"", netdev->link); + + /* If the default PVID on the port is not the specified untagged VLAN, then delete it. */ + if (default_pvid != netdev->priv.veth_attr.vlan_id) { + err = lxc_bridge_vlan_del(veth1index, default_pvid); + if (err != 0) + return log_error_errno(err, errno, "Failed to delete default untagged vlan \"%u\" on \"%s\"", default_pvid, veth1); + } + + if (netdev->priv.veth_attr.vlan_id > BRIDGE_VLAN_NONE) { + err = lxc_bridge_vlan_add(veth1index, netdev->priv.veth_attr.vlan_id, false); + if (err != 0) + return log_error_errno(err, errno, "Failed to add untagged vlan \"%u\" on \"%s\"", netdev->priv.veth_attr.vlan_id, veth1); + } + } + + /* Configure tagged VLAN settings on bridge port if specified. */ + err = lxc_bridge_vlan_add_tagged(veth1index, &netdev->priv.veth_attr.vlan_tagged_ids); + if (err != 0) + return log_error_errno(err, errno, "Failed to add tagged vlans on \"%s\"", veth1); + + return 0; +} + static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) { int err; @@ -336,6 +429,10 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd char *veth1, *veth2; char veth1buf[IFNAMSIZ], veth2buf[IFNAMSIZ]; + err = validate_veth(netdev); + if (err != 0) + return err; + if (!is_empty_string(netdev->priv.veth_attr.pair)) { veth1 = netdev->priv.veth_attr.pair; if (handler->conf->reboot) @@ -420,6 +517,14 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd goto out_delete; } INFO("Attached \"%s\" to bridge \"%s\"", veth1, netdev->link); + + if (!is_ovs_bridge(netdev->link)) { + err = setup_veth_native_bridge_vlan(veth1, netdev); + if (err != 0) { + SYSERROR("Failed to setup native bridge vlan on \"%s\"", veth1); + goto out_delete; + } + } } err = lxc_netdev_up(veth1); From 2490e1f8711dbffd904a66d0c57f5de52f8c7cc9 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Thu, 4 Jun 2020 15:59:58 +0100 Subject: [PATCH 18/20] doc: Adds documentation for veth vlan bridge options Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- doc/lxc.container.conf.sgml.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in index 97f88995bb..7b0099d3af 100644 --- a/doc/lxc.container.conf.sgml.in +++ b/doc/lxc.container.conf.sgml.in @@ -474,6 +474,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA <option>lxc.net.[i].veth.ipv6.route</option> options. Several lines specify several routes. The route is in format x.y.z.t/m, eg. 192.168.1.0/24. + + In <option>bridge</option> mode untagged VLAN membership can be set with the + <option>lxc.net.[i].veth.vlan.id</option> option. It accepts a special value of 'none' indicating + that the container port should be removed from the bridge's default untagged VLAN. + The <option>lxc.net.[i].veth.vlan.tagged.id</option> option can be specified multiple times to set + the container's bridge port membership to one or more tagged VLANs. </para> <para> From b75d38f02b0b5b42a1a96e1ceabec9318fb9b9fa Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 8 Jun 2020 11:34:27 +0100 Subject: [PATCH 19/20] network: Adds OVS VLAN setup functions Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/network.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/lxc/network.c b/src/lxc/network.c index 68b0265c5b..a40c9a5b70 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -422,6 +422,104 @@ static int setup_veth_native_bridge_vlan(char *veth1, struct lxc_netdev *netdev) return 0; } +struct ovs_veth_vlan_args { + const char *nic; + const char *vlan_mode; // Port VLAN mode. + short vlan_id; // PVID VLAN ID. + const char *trunks; // Comma delimited list of tagged VLAN IDs. +}; + + +static int lxc_ovs_setup_bridge_vlan_exec(void *data) +{ + struct ovs_veth_vlan_args *args = data; + const char *vlan_mode = "", *tag = "", *trunks = ""; + + vlan_mode = must_concat(NULL, "vlan_mode=", args->vlan_mode, (char *)0); + + if (args->vlan_id >= 0) { + char buf[5]; + int rc = snprintf(buf, sizeof(buf), "%u", args->vlan_id); + if (rc < 0 || (size_t)rc >= sizeof(buf)) + return log_error_errno(-1, EINVAL, "Failed to parse ovs bridge vlan \"%u\"", args->vlan_id); + + tag = must_concat(NULL, "tag=", buf, (char *)0); + } + + + if (strcmp(args->trunks, "") != 0) { + trunks = must_concat(NULL, "trunks=", args->trunks, (char *)0); + } + + /* Detect the combination of vlan_id and trunks specified and convert to ovs-vsctl command. */ + if (strcmp(tag, "") != 0 && strcmp(trunks, "") != 0) + execlp("ovs-vsctl", "ovs-vsctl", "set", "port", args->nic, vlan_mode, tag, trunks, (char *)NULL); + else if (strcmp(tag, "") != 0) + execlp("ovs-vsctl", "ovs-vsctl", "set", "port", args->nic, vlan_mode, tag, (char *)NULL); + else if (strcmp(trunks, "") != 0) + execlp("ovs-vsctl", "ovs-vsctl", "set", "port", args->nic, vlan_mode, trunks, (char *)NULL); + + return -1; +} + +static int setup_veth_ovs_bridge_vlan(char *veth1, struct lxc_netdev *netdev) +{ + int taggedLength = lxc_list_len(&netdev->priv.veth_attr.vlan_tagged_ids); + struct ovs_veth_vlan_args args; + args.nic = veth1; + args.vlan_mode = ""; + args.vlan_id = -1; + args.trunks = ""; + + /* Skip setup if no VLAN options are specified. */ + if (!netdev->priv.veth_attr.vlan_id_set && taggedLength <= 0) + return 0; + + /* Configure untagged VLAN settings on bridge port if specified. */ + if (netdev->priv.veth_attr.vlan_id_set) { + if (netdev->priv.veth_attr.vlan_id == BRIDGE_VLAN_NONE && taggedLength <= 0) + return log_error_errno(-1, EINVAL, "Cannot use vlan.id=none with openvswitch bridges when not using vlan.tagged.id"); + + // Configure the untagged 'native' membership settings of the port if VLAN ID specified. + // Also set the vlan_mode=access, which will drop any tagged frames. + // Order is important here, as vlan_mode is set to "access", assuming that vlan.tagged.id is not + // used. If vlan.tagged.id is specified, then we expect it to also change the vlan_mode as needed. + if (netdev->priv.veth_attr.vlan_id > BRIDGE_VLAN_NONE) { + args.vlan_mode = "access"; + args.vlan_id = netdev->priv.veth_attr.vlan_id; + } + } + + if (taggedLength > 0) { + args.vlan_mode = "trunk"; // Default to only allowing tagged frames (drop untagged frames). + + if (netdev->priv.veth_attr.vlan_id > BRIDGE_VLAN_NONE) { + // If untagged vlan mode isn't "none" then allow untagged frames for port's 'native' VLAN. + args.vlan_mode = "native-untagged"; + } + + struct lxc_list *iterator; + lxc_list_for_each(iterator, &netdev->priv.veth_attr.vlan_tagged_ids) { + char buf[5]; // Sufficient size to fit max VLAN ID (4094) null char. + unsigned short vlan_id = PTR_TO_USHORT(iterator->elem); + int rc = snprintf(buf, sizeof(buf), "%u", vlan_id); + if (rc < 0 || (size_t)rc >= sizeof(buf)) + return log_error_errno(-1, EINVAL, "Failed to parse tagged vlan \"%u\" for interface \"%s\"", vlan_id, veth1); + + args.trunks = must_concat(NULL, args.trunks, buf, ",", (char *)0); + } + } + + if (strcmp(args.vlan_mode, "") != 0) { + char cmd_output[PATH_MAX]; + int ret = run_command(cmd_output, sizeof(cmd_output), lxc_ovs_setup_bridge_vlan_exec, (void *)&args); + if (ret < 0) + return log_error_errno(-1, ret, "Failed to setup openvswitch vlan on port \"%s\": %s", args.nic, cmd_output); + } + + return 0; +} + static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netdev) { int err; From 7a3e4a6d6b99ddb91e2cc6b35d9279042b8c78c0 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Mon, 8 Jun 2020 11:34:43 +0100 Subject: [PATCH 20/20] network: Updates instantiate_veth to support OVS VLAN setup Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- src/lxc/network.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lxc/network.c b/src/lxc/network.c index a40c9a5b70..2218928ff1 100644 --- a/src/lxc/network.c +++ b/src/lxc/network.c @@ -616,7 +616,14 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd } INFO("Attached \"%s\" to bridge \"%s\"", veth1, netdev->link); - if (!is_ovs_bridge(netdev->link)) { + if (is_ovs_bridge(netdev->link)) { + err = setup_veth_ovs_bridge_vlan(veth1, netdev); + if (err != 0) { + SYSERROR("Failed to setup openvswitch bridge vlan on \"%s\"", veth1); + lxc_ovs_delete_port(netdev->link, veth1); + goto out_delete; + } + } else { err = setup_veth_native_bridge_vlan(veth1, netdev); if (err != 0) { SYSERROR("Failed to setup native bridge vlan on \"%s\"", veth1);
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel