[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Sun, 22 Jan 2017 13:05:59 +0100
From: Nikolay Aleksandrov <nikolay@...ulusnetworks.com>
To: Roopa Prabhu <roopa@...ulusnetworks.com>, netdev@...r.kernel.org
Cc: davem@...emloft.net, stephen@...workplumber.org, tgraf@...g.ch,
hannes@...essinduktion.org, jbenc@...hat.com, pshelar@....org,
dsa@...ulusnetworks.com, hadi@...atatu.com
Subject: Re: [RFC PATCH net-next 4/5] bridge: vlan lwt and dst_metadata
netlink support
On 21/01/17 06:46, Roopa Prabhu wrote:
> From: Roopa Prabhu <roopa@...ulusnetworks.com>
>
> This patch adds support to attach per vlan tunnel info dst
> metadata. This enables bridge driver to map vlan to tunnel_info
> at ingress and egress
>
> The initial use case is vlan to vni bridging, but the api is generic
> to extend to any tunnel_info in the future:
> - Uapi to configure/unconfigure/dump per vlan tunnel data
> - netlink functions to configure vlan and tunnel_info mapping
> - Introduces bridge port flag BR_LWT_VLAN to enable attach/detach
> dst_metadata to bridged packets on ports.
>
> Use case:
> example use for this is a vxlan bridging gateway or vtep
> which maps vlans to vn-segments (or vnis). User can configure
> per-vlan tunnel information which the bridge driver can use
> to bridge vlan into the corresponding tunnel.
>
> CC: Nikolay Aleksandrov <nikolay@...ulusnetworks.com>
> Signed-off-by: Roopa Prabhu <roopa@...ulusnetworks.com>
> ---
> CC'ing Nikolay for some more eyes as he has been trying to keep the
> bridge driver fast path lite.
>
> include/linux/if_bridge.h | 1 +
> net/bridge/br_input.c | 1 +
> net/bridge/br_netlink.c | 410 ++++++++++++++++++++++++++++++++++++++-------
> net/bridge/br_private.h | 18 ++
> net/bridge/br_vlan.c | 138 ++++++++++++++-
> 5 files changed, 507 insertions(+), 61 deletions(-)
>
> diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
> index c6587c0..36ff611 100644
> --- a/include/linux/if_bridge.h
> +++ b/include/linux/if_bridge.h
> @@ -46,6 +46,7 @@ struct br_ip_list {
> #define BR_LEARNING_SYNC BIT(9)
> #define BR_PROXYARP_WIFI BIT(10)
> #define BR_MCAST_FLOOD BIT(11)
> +#define BR_LWT_VLAN BIT(12)
>
> #define BR_DEFAULT_AGEING_TIME (300 * HZ)
>
> diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
> index 855b72f..83f356f 100644
> --- a/net/bridge/br_input.c
> +++ b/net/bridge/br_input.c
> @@ -20,6 +20,7 @@
> #include <net/arp.h>
> #include <linux/export.h>
> #include <linux/rculist.h>
> +#include <net/dst_metadata.h>
> #include "br_private.h"
>
> /* Hook for brouter */
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 71c7453..df997ad 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -17,17 +17,30 @@
> #include <net/net_namespace.h>
> #include <net/sock.h>
> #include <uapi/linux/if_bridge.h>
> +#include <net/dst_metadata.h>
>
> #include "br_private.h"
> #include "br_private_stp.h"
>
> -static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
> - u32 filter_mask)
> +static size_t br_get_vlan_tinfo_size(void)
> {
> + return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
> + nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
> + nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
> + nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
> +}
> +
> +static int __get_num_vlan_infos(struct net_bridge_port *p,
> + struct net_bridge_vlan_group *vg,
> + u32 filter_mask, int *num_vtinfos)
> +{
> + struct net_bridge_vlan *vbegin = NULL, *vend = NULL;
> + struct net_bridge_vlan *vtbegin = NULL, *vtend = NULL;
> struct net_bridge_vlan *v;
> - u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
> + bool get_tinfos = (p && p->flags & BR_LWT_VLAN) ? true: false;
> + bool vcontinue, vtcontinue;
> + int num_vinfos = 0;
> u16 flags, pvid;
> - int num_vlans = 0;
>
> if (!(filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED))
> return 0;
> @@ -36,6 +49,8 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
> /* Count number of vlan infos */
> list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
> flags = 0;
> + vcontinue = false;
> + vtcontinue = false;
> /* only a context, bridge vlan not activated */
> if (!br_vlan_should_use(v))
> continue;
> @@ -45,47 +60,79 @@ static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
> if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
> flags |= BRIDGE_VLAN_INFO_UNTAGGED;
>
> - if (vid_range_start == 0) {
> - goto initvars;
> - } else if ((v->vid - vid_range_end) == 1 &&
> - flags == vid_range_flags) {
> - vid_range_end = v->vid;
> + if (!vbegin) {
> + vbegin = v;
> + vend = v;
> + vcontinue = true;
> + } else if ((v->vid - vend->vid) == 1 &&
> + flags == vbegin->flags) {
> + vend = v;
> + vcontinue = true;
> + }
> +
> + if (!vcontinue) {
> + if ((vend->vid - vbegin->vid) > 0)
> + num_vinfos += 2;
> + else
> + num_vinfos += 1;
> + }
> +
> + if (!get_tinfos && !v->tinfo.tunnel_id)
> continue;
> - } else {
> - if ((vid_range_end - vid_range_start) > 0)
> - num_vlans += 2;
> +
> + if (!vtbegin) {
> + vtbegin = v;
> + vtend = v;
> + vtcontinue = true;
> + } else if ((v->vid - vtend->vid) == 1 &&
> + vlan_tunnel_id_isrange(vtend, v)) {
> + vtend = v;
> + vtcontinue = true;
> + }
> +
> + if (!vtcontinue) {
> + if ((vtend->vid - vtbegin->vid) > 0)
> + num_vtinfos += 2;
> else
> - num_vlans += 1;
> + num_vtinfos += 1;
> + vbegin = NULL;
> + vend = NULL;
> }
> -initvars:
> - vid_range_start = v->vid;
> - vid_range_end = v->vid;
> - vid_range_flags = flags;
> }
>
> - if (vid_range_start != 0) {
> - if ((vid_range_end - vid_range_start) > 0)
> - num_vlans += 2;
> + if (vbegin) {
> + if ((vend->vid - vbegin->vid) > 0)
> + num_vinfos += 2;
> else
> - num_vlans += 1;
> + num_vinfos += 1;
> }
>
> - return num_vlans;
> + if (get_tinfos && vtbegin && vtbegin->tinfo.tunnel_id) {
> + if ((vtend->vid - vtbegin->vid) > 0)
> + *num_vtinfos += 2;
> + else
> + *num_vtinfos += 1;
> + }
> +
> + return num_vinfos;
> }
I think this whole function should be broken into at least a few. It's really difficult
to parse what's going on.
>
> -static int br_get_num_vlan_infos(struct net_bridge_vlan_group *vg,
> - u32 filter_mask)
> +static int br_get_num_vlan_infos(struct net_bridge_port *p,
> + struct net_bridge_vlan_group *vg,
> + int *num_tinfos, u32 filter_mask)
> {
> int num_vlans;
>
> if (!vg)
> return 0;
>
> - if (filter_mask & RTEXT_FILTER_BRVLAN)
> + if (filter_mask & RTEXT_FILTER_BRVLAN) {
> + *num_tinfos = vg->num_vlans;
> return vg->num_vlans;
> + }
>
> rcu_read_lock();
> - num_vlans = __get_num_vlan_infos(vg, filter_mask);
> + num_vlans = __get_num_vlan_infos(p, vg, filter_mask, num_tinfos);
> rcu_read_unlock();
>
> return num_vlans;
> @@ -95,9 +142,10 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
> u32 filter_mask)
> {
> struct net_bridge_vlan_group *vg = NULL;
> - struct net_bridge_port *p;
> + struct net_bridge_port *p = NULL;
> struct net_bridge *br;
> - int num_vlan_infos;
> + int num_vlan_infos, num_vlan_tinfos = 0;
> + size_t retsize = 0;
>
> rcu_read_lock();
> if (br_port_exists(dev)) {
> @@ -107,11 +155,15 @@ static size_t br_get_link_af_size_filtered(const struct net_device *dev,
> br = netdev_priv(dev);
> vg = br_vlan_group_rcu(br);
> }
> - num_vlan_infos = br_get_num_vlan_infos(vg, filter_mask);
> + num_vlan_infos = br_get_num_vlan_infos(p, vg, &num_vlan_tinfos,
> + filter_mask);
> rcu_read_unlock();
>
> /* Each VLAN is returned in bridge_vlan_info along with flags */
> - return num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info));
> + retsize = num_vlan_infos * nla_total_size(sizeof(struct bridge_vlan_info)) +
> + num_vlan_tinfos * br_get_vlan_tinfo_size();
> +
> + return retsize;
> }
>
> static inline size_t br_port_info_size(void)
> @@ -191,7 +243,8 @@ static int br_port_fill_attrs(struct sk_buff *skb,
> nla_put_u16(skb, IFLA_BRPORT_NO, p->port_no) ||
> nla_put_u8(skb, IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
> p->topology_change_ack) ||
> - nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending))
> + nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) ||
> + nla_put_u8(skb, IFLA_BRPORT_LWT_VLAN, !!(p->flags & BR_LWT_VLAN)))
> return -EMSGSIZE;
>
> timerval = br_timer_value(&p->message_age_timer);
> @@ -216,6 +269,34 @@ static int br_port_fill_attrs(struct sk_buff *skb,
> return 0;
> }
>
> +static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
> + __be64 tunnel_id, u16 flags)
> +{
> + __be32 tid = tunnel_id_to_key32(tunnel_id);
> + struct nlattr *tmap;
> +
> + tmap = nla_nest_start(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
> + if (!tmap)
> + return -EMSGSIZE;
> + if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
> + be32_to_cpu(tid)))
> + goto nla_put_failure;
> + if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
> + vid))
> + goto nla_put_failure;
> + if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
> + flags))
> + goto nla_put_failure;
> + nla_nest_end(skb, tmap);
> +
> + return 0;
> +
> +nla_put_failure:
> + nla_nest_cancel(skb, tmap);
> +
> + return -EMSGSIZE;
> +}
> +
> static int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start,
> u16 vid_end, u16 flags)
> {
> @@ -249,20 +330,24 @@ static int br_fill_ifvlaninfo_range(struct sk_buff *skb, u16 vid_start,
> }
>
> static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
> + struct net_bridge_port *p,
> struct net_bridge_vlan_group *vg)
> {
> + struct net_bridge_vlan *vbegin = NULL, *vend = NULL;
> + struct net_bridge_vlan *vtbegin = NULL, *vtend = NULL;
> + bool fill_tinfos = (p && p->flags & BR_LWT_VLAN) ? true: false;
> struct net_bridge_vlan *v;
> - u16 vid_range_start = 0, vid_range_end = 0, vid_range_flags = 0;
> + bool vcontinue, vtcontinue;
> u16 flags, pvid;
> - int err = 0;
> + int err;
>
> - /* Pack IFLA_BRIDGE_VLAN_INFO's for every vlan
> - * and mark vlan info with begin and end flags
> - * if vlaninfo represents a range
> - */
> pvid = br_get_pvid(vg);
> + /* Count number of vlan infos */
> list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
> flags = 0;
> + vcontinue = false;
> + vtcontinue = false;
> + /* only a context, bridge vlan not activated */
> if (!br_vlan_should_use(v))
> continue;
> if (v->vid == pvid)
> @@ -271,44 +356,103 @@ static int br_fill_ifvlaninfo_compressed(struct sk_buff *skb,
> if (v->flags & BRIDGE_VLAN_INFO_UNTAGGED)
> flags |= BRIDGE_VLAN_INFO_UNTAGGED;
>
> - if (vid_range_start == 0) {
> - goto initvars;
> - } else if ((v->vid - vid_range_end) == 1 &&
> - flags == vid_range_flags) {
> - vid_range_end = v->vid;
> - continue;
> - } else {
> - err = br_fill_ifvlaninfo_range(skb, vid_range_start,
> - vid_range_end,
> - vid_range_flags);
> + if (!vbegin) {
> + vbegin = v;
> + vend = v;
> + vcontinue = true;
> + } else if ((v->vid - vend->vid) == 1 &&
> + flags == vbegin->flags) {
> + vend = v;
> + vcontinue = true;
> + }
> +
> + if (!vcontinue) {
> + err = br_fill_ifvlaninfo_range(skb,
> + vbegin->vid,
> + vend->vid,
> + vbegin->flags);
> if (err)
> return err;
> + vbegin = vend = NULL;
> + }
> +
> + if (!fill_tinfos || !v->tinfo.tunnel_id)
> + continue;
> +
> + if (!vtbegin) {
> + vtbegin = v;
> + vtend = v;
> + vtcontinue = true;
> + } else if ((v->vid - vtend->vid) == 1 &&
> + vlan_tunnel_id_isrange(vtend, v)) {
> + vtend = v;
> + vtcontinue = true;
> }
>
> -initvars:
> - vid_range_start = v->vid;
> - vid_range_end = v->vid;
> - vid_range_flags = flags;
> + if (!vtcontinue && vtbegin->tinfo.tunnel_id) {
> + if ((vtend->vid - vtbegin->vid) > 0) {
> + err = br_fill_vlan_tinfo(skb, vbegin->vid,
> + vbegin->tinfo.tunnel_id,
> + BRIDGE_VLAN_INFO_RANGE_BEGIN);
> + if (err)
> + return err;
> + err = br_fill_vlan_tinfo(skb, vend->vid,
> + vend->tinfo.tunnel_id,
> + BRIDGE_VLAN_INFO_RANGE_END);
> + if (err)
> + return err;
> + } else {
> + err = br_fill_vlan_tinfo(skb, vbegin->vid,
> + vbegin->tinfo.tunnel_id,
> + 0);
> + if (err)
> + return err;
> + }
> + vbegin = NULL;
> + vend = NULL;
> + }
> }
>
> - if (vid_range_start != 0) {
> - /* Call it once more to send any left over vlans */
> - err = br_fill_ifvlaninfo_range(skb, vid_range_start,
> - vid_range_end,
> - vid_range_flags);
> + if (vbegin) {
> + err = br_fill_ifvlaninfo_range(skb, vbegin->vid,
> + vend->vid,
> + vbegin->flags);
> if (err)
> return err;
> }
>
> + if (fill_tinfos && vtbegin && vtbegin->tinfo.tunnel_id) {
> + if ((vtend->vid - vtbegin->vid) > 0) {
> + err = br_fill_vlan_tinfo(skb, vbegin->vid,
> + vbegin->tinfo.tunnel_id,
> + BRIDGE_VLAN_INFO_RANGE_BEGIN);
> + if (err)
> + return err;
> + err = br_fill_vlan_tinfo(skb, vend->vid,
> + vend->tinfo.tunnel_id,
> + BRIDGE_VLAN_INFO_RANGE_END);
> + if (err)
> + return err;
> + } else {
> + err = br_fill_vlan_tinfo(skb, vbegin->vid,
> + vbegin->tinfo.tunnel_id, 0);
> + if (err)
> + return err;
> + }
> + }
> +
> return 0;
> }
Maybe look into breaking this one, too. It's getting huge and hard to follow..
>
> static int br_fill_ifvlaninfo(struct sk_buff *skb,
> + struct net_bridge_port *p,
> struct net_bridge_vlan_group *vg)
> {
> struct bridge_vlan_info vinfo;
> struct net_bridge_vlan *v;
> + bool fill_tinfos = (p && p->flags & BR_LWT_VLAN) ? true : false;
> u16 pvid;
> + int err;
>
> pvid = br_get_pvid(vg);
> list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
> @@ -326,6 +470,14 @@ static int br_fill_ifvlaninfo(struct sk_buff *skb,
> if (nla_put(skb, IFLA_BRIDGE_VLAN_INFO,
> sizeof(vinfo), &vinfo))
> goto nla_put_failure;
> +
> + if (!fill_tinfos || !v->tinfo.tunnel_id)
> + continue;
> +
> + err = br_fill_vlan_tinfo(skb, v->vid,
> + v->tinfo.tunnel_id, 0);
> + if (err)
> + return err;
> }
>
> return 0;
> @@ -411,9 +563,9 @@ static int br_fill_ifinfo(struct sk_buff *skb,
> goto nla_put_failure;
> }
> if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)
> - err = br_fill_ifvlaninfo_compressed(skb, vg);
> + err = br_fill_ifvlaninfo_compressed(skb, port, vg);
> else
> - err = br_fill_ifvlaninfo(skb, vg);
> + err = br_fill_ifvlaninfo(skb, port, vg);
> rcu_read_unlock();
> if (err)
> goto nla_put_failure;
> @@ -514,6 +666,127 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
> return err;
> }
>
> +static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
> + [IFLA_BRIDGE_VLAN_TUNNEL_ID]= { .type = NLA_U32 },
> + [IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
> + [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
> +};
> +
> +static int br_add_vlan_tunnel_info(struct net_bridge *br,
> + struct net_bridge_port *p, int cmd,
> + u16 vid, u32 tun_id)
> +{
> + int err;
> +
> + switch (cmd) {
> + case RTM_SETLINK:
> + if (p) {
> + /* if the MASTER flag is set this will act on the global
> + * per-VLAN entry as well
> + */
> + err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
> + if (err)
> + break;
> + } else {
> + return -EINVAL;
> + }
> +
> + break;
> +
> + case RTM_DELLINK:
> + if (p)
> + nbp_vlan_tunnel_info_delete(p, vid);
> + else
> + return -EINVAL;
> + break;
> + }
so if (!p) return -einval in the beginning ? :-)
> +
> + return 0;
> +}
> +
> +struct vtunnel_info {
> + u32 tunid;
> + u16 vid;
> + u16 flags;
> +};
> +
> +static int br_parse_vlan_tunnel_info(struct nlattr *attr,
> + struct vtunnel_info *tinfo)
> +{
> + struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
> + u32 tun_id;
> + u16 vid, flags;
> + int err;
> +
> + err = nla_parse_nested(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
> + attr, vlan_tunnel_policy);
> + if (err < 0)
> + return err;
> +
> + if (tb[IFLA_BRIDGE_VLAN_TUNNEL_ID])
> + tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
> + else
> + return -EINVAL;
> +
> + if (tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]) {
> + vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
> + if (vid >= VLAN_VID_MASK)
> + return -ERANGE;
!vid ||
> + } else {
> + return -EINVAL;
> + }
these attr checks can be moved in the beginning, usually there's a check for existence
of the mandatory attributes, then you can continue just using them and avoid these
"else" statements
> +
> + if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
> + flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
> +
> + tinfo->tunid = tun_id;
> + tinfo->vid = vid;
> + tinfo->flags = flags;
> +
> + return 0;
> +}
> +
> +static int br_process_vlan_tunnel_info(struct net_bridge *br,
> + struct net_bridge_port *p, int cmd,
> + struct vtunnel_info *tinfo_curr,
> + struct vtunnel_info *tinfo_last)
> +{
> + int t, v;
> + int err;
> +
> + if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
> + if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
> + return -EINVAL;
> + memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
> + } else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
> + if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
> + return -EINVAL;
> + if ((tinfo_curr->vid - tinfo_last->vid) !=
> + (tinfo_curr->tunid - tinfo_last->tunid))
> + return -EINVAL;
> + /* XXX: tun id and vlan id attrs must be same
> + */
> + t = tinfo_last->tunid;
> + for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
> + err = br_add_vlan_tunnel_info(br, p, cmd,
> + v, t);
> + if (err)
> + return err;
> + t++;
> + }
> + memset(tinfo_last, 0, sizeof(struct vtunnel_info));
> + memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
> + } else {
> + err = br_add_vlan_tunnel_info(br, p, cmd,
> + tinfo_curr->vid,
> + tinfo_curr->tunid);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> static int br_afspec(struct net_bridge *br,
> struct net_bridge_port *p,
> struct nlattr *af_spec,
> @@ -522,10 +795,30 @@ static int br_afspec(struct net_bridge *br,
> struct bridge_vlan_info *vinfo_start = NULL;
> struct bridge_vlan_info *vinfo = NULL;
> struct nlattr *attr;
> + struct vtunnel_info tinfo_last = {
> + .tunid = 0,
> + .vid = 0,
> + .flags = 0};
> + struct vtunnel_info tinfo_curr = {
> + .tunid = 0,
> + .vid = 0,
> + .flags = 0};
just { } should be enough to zero the structs
> int err = 0;
> int rem;
>
> nla_for_each_nested(attr, af_spec, rem) {
> + if (nla_type(attr) == IFLA_BRIDGE_VLAN_TUNNEL_INFO) {
> + err = br_parse_vlan_tunnel_info(attr, &tinfo_curr);
> + if (err)
> + return err;
> + err = br_process_vlan_tunnel_info(br, p, cmd,
> + &tinfo_curr,
> + &tinfo_last);
> + if (err)
> + return err;
> + continue;
> + }
> +
> if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO)
> continue;
> if (nla_len(attr) != sizeof(struct bridge_vlan_info))
> @@ -638,6 +931,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
> br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
> br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
> br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
> + br_set_port_flag(p, tb, IFLA_BRPORT_LWT_VLAN, BR_LWT_VLAN);
>
> if (tb[IFLA_BRPORT_COST]) {
> err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index 8ce621e..f68e360 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -91,6 +91,11 @@ struct br_vlan_stats {
> struct u64_stats_sync syncp;
> };
>
> +struct br_tunnel_info {
> + __be64 tunnel_id;
> + struct metadata_dst *tunnel_dst;
> +};
> +
> /**
> * struct net_bridge_vlan - per-vlan entry
> *
> @@ -113,6 +118,7 @@ struct br_vlan_stats {
> */
> struct net_bridge_vlan {
> struct rhash_head vnode;
> + struct rhash_head tnode;
> u16 vid;
> u16 flags;
> struct br_vlan_stats __percpu *stats;
> @@ -124,6 +130,9 @@ struct net_bridge_vlan {
> atomic_t refcnt;
> struct net_bridge_vlan *brvlan;
> };
> +
> + struct br_tunnel_info tinfo;
> +
> struct list_head vlist;
>
> struct rcu_head rcu;
> @@ -145,6 +154,7 @@ struct net_bridge_vlan {
> */
> struct net_bridge_vlan_group {
> struct rhashtable vlan_hash;
> + struct rhashtable tunnel_hash;
> struct list_head vlan_list;
> u16 num_vlans;
> u16 pvid;
> @@ -786,6 +796,14 @@ struct sk_buff *br_handle_vlan(struct net_bridge *br,
> int nbp_get_num_vlan_infos(struct net_bridge_port *p, u32 filter_mask);
> void br_vlan_get_stats(const struct net_bridge_vlan *v,
> struct br_vlan_stats *stats);
> +int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
> + struct net_bridge_vlan *vlan, u32 tun_id);
> +int __vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
> + struct net_bridge_vlan *vlan);
> +int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid);
> +int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id);
> +bool vlan_tunnel_id_isrange(struct net_bridge_vlan *v_end,
> + struct net_bridge_vlan *v);
>
> static inline struct net_bridge_vlan_group *br_vlan_group(
> const struct net_bridge *br)
> diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
> index b6de4f4..2040f08 100644
> --- a/net/bridge/br_vlan.c
> +++ b/net/bridge/br_vlan.c
> @@ -3,6 +3,7 @@
> #include <linux/rtnetlink.h>
> #include <linux/slab.h>
> #include <net/switchdev.h>
> +#include <net/dst_metadata.h>
>
> #include "br_private.h"
>
> @@ -31,6 +32,31 @@ static struct net_bridge_vlan *br_vlan_lookup(struct rhashtable *tbl, u16 vid)
> return rhashtable_lookup_fast(tbl, &vid, br_vlan_rht_params);
> }
>
> +static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
> + const void *ptr)
> +{
> + const struct net_bridge_vlan *vle = ptr;
> + __be64 tunid = *(__be64 *)arg->key;
> +
> + return vle->tinfo.tunnel_id != tunid;
> +}
> +
> +static const struct rhashtable_params br_vlan_tunnel_rht_params = {
> + .head_offset = offsetof(struct net_bridge_vlan, tnode),
> + .key_offset = offsetof(struct net_bridge_vlan, tinfo.tunnel_id),
> + .key_len = sizeof(__be64),
> + .nelem_hint = 3,
> + .locks_mul = 1,
> + .obj_cmpfn = br_vlan_tunid_cmp,
> + .automatic_shrinking = true,
> +};
> +
> +static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
> + u64 tunnel_id)
> +{
> + return rhashtable_lookup_fast(tbl, &tunnel_id, br_vlan_tunnel_rht_params);
> +}
> +
> static void __vlan_add_pvid(struct net_bridge_vlan_group *vg, u16 vid)
> {
> if (vg->pvid == vid)
> @@ -325,6 +351,7 @@ static void __vlan_group_free(struct net_bridge_vlan_group *vg)
> {
> WARN_ON(!list_empty(&vg->vlan_list));
> rhashtable_destroy(&vg->vlan_hash);
> + rhashtable_destroy(&vg->tunnel_hash);
> kfree(vg);
> }
>
> @@ -613,6 +640,8 @@ int br_vlan_delete(struct net_bridge *br, u16 vid)
> br_fdb_find_delete_local(br, NULL, br->dev->dev_addr, vid);
> br_fdb_delete_by_port(br, NULL, vid, 0);
>
> + __vlan_tunnel_info_del(vg, v);
> +
> return __vlan_del(v);
> }
>
> @@ -918,6 +947,9 @@ int br_vlan_init(struct net_bridge *br)
> ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
> if (ret)
> goto err_rhtbl;
> + ret = rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
> + if (ret)
> + goto err_rhtbl2;
> INIT_LIST_HEAD(&vg->vlan_list);
> br->vlan_proto = htons(ETH_P_8021Q);
> br->default_pvid = 1;
> @@ -932,6 +964,8 @@ int br_vlan_init(struct net_bridge *br)
> return ret;
>
> err_vlan_add:
> + rhashtable_destroy(&vg->tunnel_hash);
> +err_rhtbl2:
> rhashtable_destroy(&vg->vlan_hash);
> err_rhtbl:
> kfree(vg);
> @@ -960,7 +994,10 @@ int nbp_vlan_init(struct net_bridge_port *p)
>
> ret = rhashtable_init(&vg->vlan_hash, &br_vlan_rht_params);
> if (ret)
> - goto err_rhtbl;
> + goto err_rhtbl1;
> + ret = rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
> + if (ret)
> + goto err_rhtbl2;
> INIT_LIST_HEAD(&vg->vlan_list);
> rcu_assign_pointer(p->vlgrp, vg);
> if (p->br->default_pvid) {
> @@ -976,9 +1013,11 @@ int nbp_vlan_init(struct net_bridge_port *p)
> err_vlan_add:
> RCU_INIT_POINTER(p->vlgrp, NULL);
> synchronize_rcu();
> - rhashtable_destroy(&vg->vlan_hash);
> + rhashtable_destroy(&vg->tunnel_hash);
> err_vlan_enabled:
> -err_rhtbl:
> +err_rhtbl2:
> + rhashtable_destroy(&vg->vlan_hash);
> +err_rhtbl1:
> kfree(vg);
>
> goto out;
> @@ -1081,3 +1120,96 @@ void br_vlan_get_stats(const struct net_bridge_vlan *v,
> stats->tx_packets += txpackets;
> }
> }
> +
> +bool vlan_tunnel_id_isrange(struct net_bridge_vlan *v_end,
> + struct net_bridge_vlan *v)
> +{
> + /* XXX: check other tunnel attributes */
> + return (be32_to_cpu(tunnel_id_to_key32(v_end->tinfo.tunnel_id)) -
> + be32_to_cpu(tunnel_id_to_key32(v->tinfo.tunnel_id)) == 1);
> +}
> +
> +int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
> + struct net_bridge_vlan *vlan, u32 tun_id)
> +{
> + struct metadata_dst *metadata = NULL;
> + __be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
> + int err;
> +
> + if (vlan->tinfo.tunnel_dst)
> + return -EEXIST;
> +
> + metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY,
> + key, 0);
> + if (!metadata)
> + return -EINVAL;
> +
> + metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
> + vlan->tinfo.tunnel_dst = metadata;
> + vlan->tinfo.tunnel_id = key;
> +
> + err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
> + br_vlan_tunnel_rht_params);
> + if (err)
> + goto out;
> +
> + return 0;
> +out:
> + dst_release(&vlan->tinfo.tunnel_dst->dst);
> +
> + return err;
> +}
> +
> +int __vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
> + struct net_bridge_vlan *vlan)
> +{
> + if (vlan->tinfo.tunnel_dst) {
> + vlan->tinfo.tunnel_id = 0;
> + dst_release(&vlan->tinfo.tunnel_dst->dst);
> +
> + rhashtable_remove_fast(&vg->tunnel_hash, &vlan->vnode,
> + br_vlan_tunnel_rht_params);
> + }
> +
> + return 0;
> +}
I think all of these should be static, if I haven't missed something I don't see them
being used anywhere else
> +
> +/* Must be protected by RTNL.
> + * Must be called with vid in range from 1 to 4094 inclusive.
> + */
> +int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id)
> +{
> + struct net_bridge_vlan_group *vg;
> + struct net_bridge_vlan *vlan;
> +
> + ASSERT_RTNL();
> +
> + vg = nbp_vlan_group(port);
> + vlan = br_vlan_find(vg, vid);
> + if (!vlan)
> + return -EINVAL;
> +
> + __vlan_tunnel_info_add(vg, vlan, tun_id);
> +
> + return 0;
> +}
> +
> +/* Must be protected by RTNL.
> + * Must be called with vid in range from 1 to 4094 inclusive.
> + */
> +int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid)
> +{
> + struct net_bridge_vlan_group *vg;
> + struct net_bridge_vlan *v;
> +
> + ASSERT_RTNL();
> +
> + vg = nbp_vlan_group(port);
> + v = br_vlan_find(vg, vid);
> + if (!v)
> + return -ENOENT;
> +
> + __vlan_tunnel_info_del(vg, v);
> +
> + return 0;
> +}
>
Powered by blists - more mailing lists