bridge: netlink: make setlink/dellink notifications more accurate
Before this patch we had cases that either sent notifications when there were in fact no changes (e.g. non-existent vlan delete) or didn't send notifications when there were changes (e.g. vlan add range with an error in the middle, port flags change + vlan update error). This patch sends down a boolean to the functions setlink/dellink use and if there is even a single configuration change (port flag, vlan add/del, port state) then we always send a notification. This is all done to keep backwards compatibility with the opportunistic vlan delete, where one could specify a vlan range that has missing vlans inside and still everything in that range will be cleared, this is mostly used to clear the whole vlan config with a single call, i.e. range 1-4094. Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Acked-by: Stephen Hemminger <stephen@networkplumber.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
5b52a4c3ac
commit
e19b42a1a0
@ -506,7 +506,7 @@ int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
|
||||
}
|
||||
|
||||
static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
|
||||
int cmd, struct bridge_vlan_info *vinfo)
|
||||
int cmd, struct bridge_vlan_info *vinfo, bool *changed)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
@ -517,21 +517,24 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
|
||||
* per-VLAN entry as well
|
||||
*/
|
||||
err = nbp_vlan_add(p, vinfo->vid, vinfo->flags);
|
||||
if (err)
|
||||
break;
|
||||
} else {
|
||||
vinfo->flags |= BRIDGE_VLAN_INFO_BRENTRY;
|
||||
err = br_vlan_add(br, vinfo->vid, vinfo->flags);
|
||||
}
|
||||
if (!err)
|
||||
*changed = true;
|
||||
break;
|
||||
|
||||
case RTM_DELLINK:
|
||||
if (p) {
|
||||
nbp_vlan_delete(p, vinfo->vid);
|
||||
if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER)
|
||||
br_vlan_delete(p->br, vinfo->vid);
|
||||
} else {
|
||||
br_vlan_delete(br, vinfo->vid);
|
||||
if (!nbp_vlan_delete(p, vinfo->vid))
|
||||
*changed = true;
|
||||
|
||||
if ((vinfo->flags & BRIDGE_VLAN_INFO_MASTER) &&
|
||||
!br_vlan_delete(p->br, vinfo->vid))
|
||||
*changed = true;
|
||||
} else if (!br_vlan_delete(br, vinfo->vid)) {
|
||||
*changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -542,7 +545,8 @@ static int br_vlan_info(struct net_bridge *br, struct net_bridge_port *p,
|
||||
static int br_process_vlan_info(struct net_bridge *br,
|
||||
struct net_bridge_port *p, int cmd,
|
||||
struct bridge_vlan_info *vinfo_curr,
|
||||
struct bridge_vlan_info **vinfo_last)
|
||||
struct bridge_vlan_info **vinfo_last,
|
||||
bool *changed)
|
||||
{
|
||||
if (!vinfo_curr->vid || vinfo_curr->vid >= VLAN_VID_MASK)
|
||||
return -EINVAL;
|
||||
@ -572,7 +576,7 @@ static int br_process_vlan_info(struct net_bridge *br,
|
||||
sizeof(struct bridge_vlan_info));
|
||||
for (v = (*vinfo_last)->vid; v <= vinfo_curr->vid; v++) {
|
||||
tmp_vinfo.vid = v;
|
||||
err = br_vlan_info(br, p, cmd, &tmp_vinfo);
|
||||
err = br_vlan_info(br, p, cmd, &tmp_vinfo, changed);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
@ -581,13 +585,13 @@ static int br_process_vlan_info(struct net_bridge *br,
|
||||
return err;
|
||||
}
|
||||
|
||||
return br_vlan_info(br, p, cmd, vinfo_curr);
|
||||
return br_vlan_info(br, p, cmd, vinfo_curr, changed);
|
||||
}
|
||||
|
||||
static int br_afspec(struct net_bridge *br,
|
||||
struct net_bridge_port *p,
|
||||
struct nlattr *af_spec,
|
||||
int cmd)
|
||||
int cmd, bool *changed)
|
||||
{
|
||||
struct bridge_vlan_info *vinfo_curr = NULL;
|
||||
struct bridge_vlan_info *vinfo_last = NULL;
|
||||
@ -607,7 +611,8 @@ static int br_afspec(struct net_bridge *br,
|
||||
return err;
|
||||
err = br_process_vlan_tunnel_info(br, p, cmd,
|
||||
&tinfo_curr,
|
||||
&tinfo_last);
|
||||
&tinfo_last,
|
||||
changed);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
@ -616,7 +621,7 @@ static int br_afspec(struct net_bridge *br,
|
||||
return -EINVAL;
|
||||
vinfo_curr = nla_data(attr);
|
||||
err = br_process_vlan_info(br, p, cmd, vinfo_curr,
|
||||
&vinfo_last);
|
||||
&vinfo_last, changed);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
@ -804,6 +809,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
struct nlattr *afspec;
|
||||
struct net_bridge_port *p;
|
||||
struct nlattr *tb[IFLA_BRPORT_MAX + 1];
|
||||
bool changed = false;
|
||||
int err = 0;
|
||||
|
||||
protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_PROTINFO);
|
||||
@ -839,14 +845,15 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
}
|
||||
if (err)
|
||||
goto out;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (afspec) {
|
||||
err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
|
||||
afspec, RTM_SETLINK);
|
||||
afspec, RTM_SETLINK, &changed);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
if (changed)
|
||||
br_ifinfo_notify(RTM_NEWLINK, p);
|
||||
out:
|
||||
return err;
|
||||
@ -857,6 +864,7 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
{
|
||||
struct nlattr *afspec;
|
||||
struct net_bridge_port *p;
|
||||
bool changed = false;
|
||||
int err = 0;
|
||||
|
||||
afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
|
||||
@ -869,8 +877,8 @@ int br_dellink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags)
|
||||
return -EINVAL;
|
||||
|
||||
err = br_afspec((struct net_bridge *)netdev_priv(dev), p,
|
||||
afspec, RTM_DELLINK);
|
||||
if (err == 0)
|
||||
afspec, RTM_DELLINK, &changed);
|
||||
if (changed)
|
||||
/* Send RTM_NEWLINK because userspace
|
||||
* expects RTM_NEWLINK for vlan dels
|
||||
*/
|
||||
|
@ -198,7 +198,7 @@ static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX +
|
||||
};
|
||||
|
||||
static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd,
|
||||
u16 vid, u32 tun_id)
|
||||
u16 vid, u32 tun_id, bool *changed)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
@ -208,9 +208,12 @@ static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd,
|
||||
switch (cmd) {
|
||||
case RTM_SETLINK:
|
||||
err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
|
||||
if (!err)
|
||||
*changed = true;
|
||||
break;
|
||||
case RTM_DELLINK:
|
||||
nbp_vlan_tunnel_info_delete(p, vid);
|
||||
if (!nbp_vlan_tunnel_info_delete(p, vid))
|
||||
*changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -254,7 +257,8 @@ int br_parse_vlan_tunnel_info(struct nlattr *attr,
|
||||
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)
|
||||
struct vtunnel_info *tinfo_last,
|
||||
bool *changed)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -272,7 +276,7 @@ int br_process_vlan_tunnel_info(struct net_bridge *br,
|
||||
return -EINVAL;
|
||||
t = tinfo_last->tunid;
|
||||
for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
|
||||
err = br_vlan_tunnel_info(p, cmd, v, t);
|
||||
err = br_vlan_tunnel_info(p, cmd, v, t, changed);
|
||||
if (err)
|
||||
return err;
|
||||
t++;
|
||||
@ -283,7 +287,7 @@ int br_process_vlan_tunnel_info(struct net_bridge *br,
|
||||
if (tinfo_last->flags)
|
||||
return -EINVAL;
|
||||
err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
|
||||
tinfo_curr->tunid);
|
||||
tinfo_curr->tunid, changed);
|
||||
if (err)
|
||||
return err;
|
||||
memset(tinfo_last, 0, sizeof(struct vtunnel_info));
|
||||
|
@ -26,7 +26,8 @@ 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);
|
||||
struct vtunnel_info *tinfo_last,
|
||||
bool *changed);
|
||||
int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg);
|
||||
int br_fill_vlan_tunnel_info(struct sk_buff *skb,
|
||||
struct net_bridge_vlan_group *vg);
|
||||
|
Loading…
Reference in New Issue
Block a user