On 21/01/17 06:46, Roopa Prabhu wrote:
> From: Roopa Prabhu <ro...@cumulusnetworks.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 <niko...@cumulusnetworks.com>
> Signed-off-by: Roopa Prabhu <ro...@cumulusnetworks.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;
> +}
> 

Reply via email to