bridge: netlink: make setlink/dellink notifications more accurate
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Fri, 27 Oct 2017 10:19:36 +0000 (13:19 +0300)
committerDavid S. Miller <davem@davemloft.net>
Sun, 29 Oct 2017 02:03:43 +0000 (11:03 +0900)
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>
net/bridge/br_netlink.c
net/bridge/br_netlink_tunnel.c
net/bridge/br_private_tunnel.h

index fb61b6c792358ddc451a34442fbe7f8898cf8542..d0290ede93421ddac3ba07c93137252039f6a9df 100644 (file)
@@ -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
                 */
index 3712c7f0e00cd1addd73cdfe4504b687e425d0ec..da8cb99fd2591d15b77a8fb98540a2c9b034f6c5 100644 (file)
@@ -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));
index 4a447a378ab3719b5dae18c5a90d51cb04972fac..a259471bfd7825f7e99cf3c755a2a2eacf25d081 100644 (file)
@@ -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);