Commit 9aba624d authored by Nikolay Aleksandrov's avatar Nikolay Aleksandrov Committed by David S. Miller

net: bridge: vlan: notify when global options change

Add support for global options notifications. They use only RTM_NEWVLAN
since global options can only be set and are contained in a separate
vlan global options attribute. Notifications are compressed in ranges
where possible, i.e. the sequential vlan options are equal.
Signed-off-by: default avatarNikolay Aleksandrov <nikolay@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 743a53d9
...@@ -290,6 +290,57 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range, ...@@ -290,6 +290,57 @@ bool br_vlan_global_opts_fill(struct sk_buff *skb, u16 vid, u16 vid_range,
return false; return false;
} }
static size_t rtnl_vlan_global_opts_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct br_vlan_msg))
+ nla_total_size(0) /* BRIDGE_VLANDB_GLOBAL_OPTIONS */
+ nla_total_size(sizeof(u16)) /* BRIDGE_VLANDB_GOPTS_ID */
+ nla_total_size(sizeof(u16)); /* BRIDGE_VLANDB_GOPTS_RANGE */
}
static void br_vlan_global_opts_notify(const struct net_bridge *br,
u16 vid, u16 vid_range)
{
struct net_bridge_vlan *v;
struct br_vlan_msg *bvm;
struct nlmsghdr *nlh;
struct sk_buff *skb;
int err = -ENOBUFS;
/* right now notifications are done only with rtnl held */
ASSERT_RTNL();
skb = nlmsg_new(rtnl_vlan_global_opts_nlmsg_size(), GFP_KERNEL);
if (!skb)
goto out_err;
err = -EMSGSIZE;
nlh = nlmsg_put(skb, 0, 0, RTM_NEWVLAN, sizeof(*bvm), 0);
if (!nlh)
goto out_err;
bvm = nlmsg_data(nlh);
memset(bvm, 0, sizeof(*bvm));
bvm->family = AF_BRIDGE;
bvm->ifindex = br->dev->ifindex;
/* need to find the vlan due to flags/options */
v = br_vlan_find(br_vlan_group(br), vid);
if (!v)
goto out_kfree;
if (!br_vlan_global_opts_fill(skb, vid, vid_range, v))
goto out_err;
nlmsg_end(skb, nlh);
rtnl_notify(skb, dev_net(br->dev), 0, RTNLGRP_BRVLAN, NULL, GFP_KERNEL);
return;
out_err:
rtnl_set_sk_err(dev_net(br->dev), RTNLGRP_BRVLAN, err);
out_kfree:
kfree_skb(skb);
}
static int br_vlan_process_global_one_opts(const struct net_bridge *br, static int br_vlan_process_global_one_opts(const struct net_bridge *br,
struct net_bridge_vlan_group *vg, struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *v, struct net_bridge_vlan *v,
...@@ -311,9 +362,9 @@ int br_vlan_rtm_process_global_options(struct net_device *dev, ...@@ -311,9 +362,9 @@ int br_vlan_rtm_process_global_options(struct net_device *dev,
int cmd, int cmd,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL;
struct nlattr *tb[BRIDGE_VLANDB_GOPTS_MAX + 1]; struct nlattr *tb[BRIDGE_VLANDB_GOPTS_MAX + 1];
struct net_bridge_vlan_group *vg; struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v;
u16 vid, vid_range = 0; u16 vid, vid_range = 0;
struct net_bridge *br; struct net_bridge *br;
int err = 0; int err = 0;
...@@ -370,7 +421,34 @@ int br_vlan_rtm_process_global_options(struct net_device *dev, ...@@ -370,7 +421,34 @@ int br_vlan_rtm_process_global_options(struct net_device *dev,
extack); extack);
if (err) if (err)
break; break;
if (changed) {
/* vlan options changed, check for range */
if (!curr_start) {
curr_start = v;
curr_end = v;
continue;
}
if (!br_vlan_global_opts_can_enter_range(v, curr_end)) {
br_vlan_global_opts_notify(br, curr_start->vid,
curr_end->vid);
curr_start = v;
}
curr_end = v;
} else {
/* nothing changed and nothing to notify yet */
if (!curr_start)
continue;
br_vlan_global_opts_notify(br, curr_start->vid,
curr_end->vid);
curr_start = NULL;
curr_end = NULL;
}
} }
if (curr_start)
br_vlan_global_opts_notify(br, curr_start->vid, curr_end->vid);
return err; return err;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment