Merge branch 'net-bridge-vlan-options-add-support-for-tunnel-mapping'

Nikolay Aleksandrov says:

====================
net: bridge: vlan options: add support for tunnel mapping

In order to bring the new vlan API on par with the old one and be able
to completely migrate to the new one we need to support vlan tunnel mapping
and statistics. This patch-set takes care of the former by making it a
vlan option. There are two notable issues to deal with:
 - vlan range to tunnel range mapping
   * The tunnel ids are globally unique for the vlan code and a vlan can
     be mapped to one tunnel, so the old API took care of ranges by
     taking the starting tunnel id value and incrementally mapping
     vlan id(i) -> tunnel id(i). This set takes the same approach and
     uses one new attribute - BRIDGE_VLANDB_ENTRY_TUNNEL_ID. If used
     with a vlan range then it's the starting tunnel id to map.

 - tunnel mapping removal
   * Since there are no reserved/special tunnel ids defined, we can't
     encode mapping removal within the new attribute, in order to be
     able to remove a mapping we add a vlan flag which makes the new
     tunnel option remove the mapping

The rest is pretty straight-forward, in fact we directly re-use the old
code for manipulating tunnels by just mapping the command (set/del). In
order to be able to keep detecting vlan ranges we check that the current
vlan has a tunnel and it's extending the current vlan range end's tunnel
id.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2020-03-17 22:47:13 -07:00
commit 54e1dc70dc
7 changed files with 94 additions and 23 deletions

View File

@ -131,6 +131,7 @@ enum {
#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ #define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */
#define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */ #define BRIDGE_VLAN_INFO_BRENTRY (1<<5) /* Global bridge VLAN entry */
#define BRIDGE_VLAN_INFO_ONLY_OPTS (1<<6) /* Skip create/delete/flags */ #define BRIDGE_VLAN_INFO_ONLY_OPTS (1<<6) /* Skip create/delete/flags */
#define BRIDGE_VLAN_INFO_REMOVE_TUN (1<<7) /* Remove tunnel mapping */
struct bridge_vlan_info { struct bridge_vlan_info {
__u16 flags; __u16 flags;
@ -192,6 +193,7 @@ enum {
BRIDGE_VLANDB_ENTRY_INFO, BRIDGE_VLANDB_ENTRY_INFO,
BRIDGE_VLANDB_ENTRY_RANGE, BRIDGE_VLANDB_ENTRY_RANGE,
BRIDGE_VLANDB_ENTRY_STATE, BRIDGE_VLANDB_ENTRY_STATE,
BRIDGE_VLANDB_ENTRY_TUNNEL_ID,
__BRIDGE_VLANDB_ENTRY_MAX, __BRIDGE_VLANDB_ENTRY_MAX,
}; };
#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) #define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1)

View File

@ -26,8 +26,8 @@ static size_t __get_vlan_tinfo_size(void)
nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */ nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
} }
static bool vlan_tunid_inrange(struct net_bridge_vlan *v_curr, bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr,
struct net_bridge_vlan *v_last) const struct net_bridge_vlan *v_last)
{ {
__be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id); __be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
__be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id); __be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
@ -193,8 +193,8 @@ static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX +
[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 }, [IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
}; };
static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd, int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd,
u16 vid, u32 tun_id, bool *changed) u16 vid, u32 tun_id, bool *changed)
{ {
int err = 0; int err = 0;
@ -250,8 +250,8 @@ int br_parse_vlan_tunnel_info(struct nlattr *attr,
return 0; return 0;
} }
int br_process_vlan_tunnel_info(struct net_bridge *br, int br_process_vlan_tunnel_info(const struct net_bridge *br,
struct net_bridge_port *p, int cmd, const struct net_bridge_port *p, int cmd,
struct vtunnel_info *tinfo_curr, struct vtunnel_info *tinfo_curr,
struct vtunnel_info *tinfo_last, struct vtunnel_info *tinfo_last,
bool *changed) bool *changed)

View File

@ -1199,8 +1199,8 @@ static inline void br_vlan_notify(const struct net_bridge *br,
/* br_vlan_options.c */ /* br_vlan_options.c */
#ifdef CONFIG_BRIDGE_VLAN_FILTERING #ifdef CONFIG_BRIDGE_VLAN_FILTERING
bool br_vlan_opts_eq(const struct net_bridge_vlan *v1, bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *v2); const struct net_bridge_vlan *range_end);
bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v); bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v);
size_t br_vlan_opts_nl_size(void); size_t br_vlan_opts_nl_size(void);
int br_vlan_process_options(const struct net_bridge *br, int br_vlan_process_options(const struct net_bridge *br,

View File

@ -18,8 +18,8 @@ struct vtunnel_info {
/* br_netlink_tunnel.c */ /* br_netlink_tunnel.c */
int br_parse_vlan_tunnel_info(struct nlattr *attr, int br_parse_vlan_tunnel_info(struct nlattr *attr,
struct vtunnel_info *tinfo); struct vtunnel_info *tinfo);
int br_process_vlan_tunnel_info(struct net_bridge *br, int br_process_vlan_tunnel_info(const struct net_bridge *br,
struct net_bridge_port *p, const struct net_bridge_port *p,
int cmd, int cmd,
struct vtunnel_info *tinfo_curr, struct vtunnel_info *tinfo_curr,
struct vtunnel_info *tinfo_last, struct vtunnel_info *tinfo_last,
@ -32,8 +32,9 @@ int br_fill_vlan_tunnel_info(struct sk_buff *skb,
/* br_vlan_tunnel.c */ /* br_vlan_tunnel.c */
int vlan_tunnel_init(struct net_bridge_vlan_group *vg); int vlan_tunnel_init(struct net_bridge_vlan_group *vg);
void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg); void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg);
int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid); int nbp_vlan_tunnel_info_delete(const struct net_bridge_port *port, u16 vid);
int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id); int nbp_vlan_tunnel_info_add(const struct net_bridge_port *port, u16 vid,
u32 tun_id);
void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port); void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port);
void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg, void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan); struct net_bridge_vlan *vlan);
@ -42,19 +43,23 @@ int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_vlan_group *vg); struct net_bridge_vlan_group *vg);
int br_handle_egress_vlan_tunnel(struct sk_buff *skb, int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_vlan *vlan); struct net_bridge_vlan *vlan);
bool vlan_tunid_inrange(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *v_last);
int br_vlan_tunnel_info(const struct net_bridge_port *p, int cmd,
u16 vid, u32 tun_id, bool *changed);
#else #else
static inline int vlan_tunnel_init(struct net_bridge_vlan_group *vg) static inline int vlan_tunnel_init(struct net_bridge_vlan_group *vg)
{ {
return 0; return 0;
} }
static inline int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, static inline int nbp_vlan_tunnel_info_delete(const struct net_bridge_port *port,
u16 vid) u16 vid)
{ {
return 0; return 0;
} }
static inline int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, static inline int nbp_vlan_tunnel_info_add(const struct net_bridge_port *port,
u16 vid, u32 tun_id) u16 vid, u32 tun_id)
{ {
return 0; return 0;

View File

@ -1694,7 +1694,7 @@ bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
{ {
return v_curr->vid - range_end->vid == 1 && return v_curr->vid - range_end->vid == 1 &&
range_end->flags == v_curr->flags && range_end->flags == v_curr->flags &&
br_vlan_opts_eq(v_curr, range_end); br_vlan_opts_eq_range(v_curr, range_end);
} }
static int br_vlan_dump_dev(const struct net_device *dev, static int br_vlan_dump_dev(const struct net_device *dev,
@ -1839,6 +1839,7 @@ static const struct nla_policy br_vlan_db_policy[BRIDGE_VLANDB_ENTRY_MAX + 1] =
.len = sizeof(struct bridge_vlan_info) }, .len = sizeof(struct bridge_vlan_info) },
[BRIDGE_VLANDB_ENTRY_RANGE] = { .type = NLA_U16 }, [BRIDGE_VLANDB_ENTRY_RANGE] = { .type = NLA_U16 },
[BRIDGE_VLANDB_ENTRY_STATE] = { .type = NLA_U8 }, [BRIDGE_VLANDB_ENTRY_STATE] = { .type = NLA_U8 },
[BRIDGE_VLANDB_ENTRY_TUNNEL_ID] = { .type = NLA_U32 },
}; };
static int br_vlan_rtm_process_one(struct net_device *dev, static int br_vlan_rtm_process_one(struct net_device *dev,

View File

@ -4,25 +4,48 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <net/ip_tunnels.h>
#include "br_private.h" #include "br_private.h"
#include "br_private_tunnel.h"
/* check if the options between two vlans are equal */ static bool __vlan_tun_put(struct sk_buff *skb, const struct net_bridge_vlan *v)
bool br_vlan_opts_eq(const struct net_bridge_vlan *v1,
const struct net_bridge_vlan *v2)
{ {
return v1->state == v2->state; __be32 tid = tunnel_id_to_key32(v->tinfo.tunnel_id);
if (!v->tinfo.tunnel_dst)
return true;
return !nla_put_u32(skb, BRIDGE_VLANDB_ENTRY_TUNNEL_ID,
be32_to_cpu(tid));
}
static bool __vlan_tun_can_enter_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end)
{
return (!v_curr->tinfo.tunnel_dst && !range_end->tinfo.tunnel_dst) ||
vlan_tunid_inrange(v_curr, range_end);
}
/* check if the options' state of v_curr allow it to enter the range */
bool br_vlan_opts_eq_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end)
{
return v_curr->state == range_end->state &&
__vlan_tun_can_enter_range(v_curr, range_end);
} }
bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v) bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v)
{ {
return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE, return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE,
br_vlan_get_state(v)); br_vlan_get_state(v)) &&
__vlan_tun_put(skb, v);
} }
size_t br_vlan_opts_nl_size(void) size_t br_vlan_opts_nl_size(void)
{ {
return nla_total_size(sizeof(u8)); /* BRIDGE_VLANDB_ENTRY_STATE */ return nla_total_size(sizeof(u8)) /* BRIDGE_VLANDB_ENTRY_STATE */
+ nla_total_size(sizeof(u32)); /* BRIDGE_VLANDB_ENTRY_TUNNEL_ID */
} }
static int br_vlan_modify_state(struct net_bridge_vlan_group *vg, static int br_vlan_modify_state(struct net_bridge_vlan_group *vg,
@ -62,6 +85,40 @@ static int br_vlan_modify_state(struct net_bridge_vlan_group *vg,
return 0; return 0;
} }
static int br_vlan_modify_tunnel(const struct net_bridge_port *p,
struct net_bridge_vlan *v,
struct nlattr **tb,
bool *changed,
struct netlink_ext_ack *extack)
{
struct bridge_vlan_info *vinfo;
int cmdmap;
u32 tun_id;
if (!p) {
NL_SET_ERR_MSG_MOD(extack, "Can't modify tunnel mapping of non-port vlans");
return -EINVAL;
}
if (!(p->flags & BR_VLAN_TUNNEL)) {
NL_SET_ERR_MSG_MOD(extack, "Port doesn't have tunnel flag set");
return -EINVAL;
}
/* vlan info attribute is guaranteed by br_vlan_rtm_process_one */
vinfo = nla_data(tb[BRIDGE_VLANDB_ENTRY_INFO]);
cmdmap = vinfo->flags & BRIDGE_VLAN_INFO_REMOVE_TUN ? RTM_DELLINK :
RTM_SETLINK;
/* when working on vlan ranges this represents the starting tunnel id */
tun_id = nla_get_u32(tb[BRIDGE_VLANDB_ENTRY_TUNNEL_ID]);
/* tunnel ids are mapped to each vlan in increasing order,
* the starting vlan is in BRIDGE_VLANDB_ENTRY_INFO and v is the
* current vlan, so we compute: tun_id + v - vinfo->vid
*/
tun_id += v->vid - vinfo->vid;
return br_vlan_tunnel_info(p, cmdmap, v->vid, tun_id, changed);
}
static int br_vlan_process_one_opts(const struct net_bridge *br, static int br_vlan_process_one_opts(const struct net_bridge *br,
const struct net_bridge_port *p, const struct net_bridge_port *p,
struct net_bridge_vlan_group *vg, struct net_bridge_vlan_group *vg,
@ -80,6 +137,11 @@ static int br_vlan_process_one_opts(const struct net_bridge *br,
if (err) if (err)
return err; return err;
} }
if (tb[BRIDGE_VLANDB_ENTRY_TUNNEL_ID]) {
err = br_vlan_modify_tunnel(p, v, tb, changed, extack);
if (err)
return err;
}
return 0; return 0;
} }

View File

@ -89,7 +89,8 @@ out:
/* Must be protected by RTNL. /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive. * 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) int nbp_vlan_tunnel_info_add(const struct net_bridge_port *port, u16 vid,
u32 tun_id)
{ {
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *vlan; struct net_bridge_vlan *vlan;
@ -107,7 +108,7 @@ int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id)
/* Must be protected by RTNL. /* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive. * 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) int nbp_vlan_tunnel_info_delete(const struct net_bridge_port *port, u16 vid)
{ {
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v; struct net_bridge_vlan *v;