aboutsummaryrefslogtreecommitdiff
path: root/net/core
diff options
context:
space:
mode:
authorJohn Fastabend2012-11-02 16:32:36 +0000
committerDavid S. Miller2012-11-03 15:37:36 -0400
commitc38e01b8b958cb6606bcc156d3d00c3ee99a13f8 (patch)
treed2cb212bc2126117c32d451eb0b7a92d658f239a /net/core
parent26cdfb4915a9a408f4c1f04111999a11eb0ee606 (diff)
net: fix bridge notify hook to manage flags correctly
The bridge notify hook rtnl_bridge_notify() was not handling the case where the master flags was set or with both flags set. First flags are not being passed correctly and second the logic to parse them is broken. This patch passes the original flags value and fixes the logic. Reported-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: John Fastabend <john.r.fastabend@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/rtnetlink.c26
1 files changed, 18 insertions, 8 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index a0e350763fbe..04a201abcf0e 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2364,13 +2364,19 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags)
goto errout;
}
- if (!flags && master && master->netdev_ops->ndo_bridge_getlink)
+ if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) &&
+ master && master->netdev_ops->ndo_bridge_getlink) {
err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
- else if (dev->netdev_ops->ndo_bridge_getlink)
- err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
+ if (err < 0)
+ goto errout;
+ }
- if (err < 0)
- goto errout;
+ if ((flags & BRIDGE_FLAGS_SELF) &&
+ dev->netdev_ops->ndo_bridge_getlink) {
+ err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
+ if (err < 0)
+ goto errout;
+ }
rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
return 0;
@@ -2389,7 +2395,8 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
struct net_device *dev;
struct nlattr *br_spec, *attr = NULL;
int rem, err = -EOPNOTSUPP;
- u16 flags = 0;
+ u16 oflags, flags = 0;
+ bool have_flags = false;
if (nlmsg_len(nlh) < sizeof(*ifm))
return -EINVAL;
@@ -2408,12 +2415,15 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
if (br_spec) {
nla_for_each_nested(attr, br_spec, rem) {
if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+ have_flags = true;
flags = nla_get_u16(attr);
break;
}
}
}
+ oflags = flags;
+
if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
if (!dev->master ||
!dev->master->netdev_ops->ndo_bridge_setlink) {
@@ -2438,11 +2448,11 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
flags &= ~BRIDGE_FLAGS_SELF;
}
- if (attr && nla_type(attr) == IFLA_BRIDGE_FLAGS)
+ if (have_flags)
memcpy(nla_data(attr), &flags, sizeof(flags));
/* Generate event to notify upper layer of bridge change */
if (!err)
- err = rtnl_bridge_notify(dev, flags);
+ err = rtnl_bridge_notify(dev, oflags);
out:
return err;
}