net: bridge: vlan: add rtnetlink group and notify support

Add a new rtnetlink group for bridge vlan notifications - RTNLGRP_BRVLAN
and add support for sending vlan notifications (both single and ranges).
No functional changes intended, the notification support will be used by
later patches.

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Nikolay Aleksandrov 2020-01-14 19:56:13 +02:00 committed by David S. Miller
parent 0ab5587951
commit cf5bddb95c
3 changed files with 92 additions and 0 deletions

View File

@ -730,6 +730,8 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R
RTNLGRP_NEXTHOP,
#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP
RTNLGRP_BRVLAN,
#define RTNLGRP_BRVLAN RTNLGRP_BRVLAN
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)

View File

@ -960,6 +960,10 @@ int br_vlan_bridge_event(struct net_device *dev, unsigned long event,
void *ptr);
void br_vlan_rtnl_init(void);
void br_vlan_rtnl_uninit(void);
void br_vlan_notify(const struct net_bridge *br,
const struct net_bridge_port *p,
u16 vid, u16 vid_range,
int cmd);
static inline struct net_bridge_vlan_group *br_vlan_group(
const struct net_bridge *br)
@ -1166,6 +1170,13 @@ static inline void br_vlan_rtnl_init(void)
static inline void br_vlan_rtnl_uninit(void)
{
}
static inline void br_vlan_notify(const struct net_bridge *br,
const struct net_bridge_port *p,
u16 vid, u16 vid_range,
int cmd)
{
}
#endif
struct nf_br_ops {

View File

@ -1540,6 +1540,85 @@ out_err:
return false;
}
static size_t rtnl_vlan_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct br_vlan_msg))
+ nla_total_size(0) /* BRIDGE_VLANDB_ENTRY */
+ nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_ENTRY_RANGE */
+ nla_total_size(sizeof(struct bridge_vlan_info)); /* BRIDGE_VLANDB_ENTRY_INFO */
}
void br_vlan_notify(const struct net_bridge *br,
const struct net_bridge_port *p,
u16 vid, u16 vid_range,
int cmd)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v;
struct br_vlan_msg *bvm;
struct nlmsghdr *nlh;
struct sk_buff *skb;
int err = -ENOBUFS;
struct net *net;
u16 flags = 0;
int ifindex;
/* right now notifications are done only with rtnl held */
ASSERT_RTNL();
if (p) {
ifindex = p->dev->ifindex;
vg = nbp_vlan_group(p);
net = dev_net(p->dev);
} else {
ifindex = br->dev->ifindex;
vg = br_vlan_group(br);
net = dev_net(br->dev);
}
skb = nlmsg_new(rtnl_vlan_nlmsg_size(), GFP_KERNEL);
if (!skb)
goto out_err;
err = -EMSGSIZE;
nlh = nlmsg_put(skb, 0, 0, cmd, sizeof(*bvm), 0);
if (!nlh)
goto out_err;
bvm = nlmsg_data(nlh);
memset(bvm, 0, sizeof(*bvm));
bvm->family = AF_BRIDGE;
bvm->ifindex = ifindex;
switch (cmd) {
case RTM_NEWVLAN:
/* need to find the vlan due to flags/options */
v = br_vlan_find(vg, vid);
if (!v || !br_vlan_should_use(v))
goto out_kfree;
flags = v->flags;
if (br_get_pvid(vg) == v->vid)
flags |= BRIDGE_VLAN_INFO_PVID;
break;
case RTM_DELVLAN:
break;
default:
goto out_kfree;
}
if (!br_vlan_fill_vids(skb, vid, vid_range, flags))
goto out_err;
nlmsg_end(skb, nlh);
rtnl_notify(skb, net, 0, RTNLGRP_BRVLAN, NULL, GFP_KERNEL);
return;
out_err:
rtnl_set_sk_err(net, RTNLGRP_BRVLAN, err);
out_kfree:
kfree_skb(skb);
}
/* check if v_curr can enter a range ending in range_end */
static bool br_vlan_can_enter_range(const struct net_bridge_vlan *v_curr,
const struct net_bridge_vlan *range_end)