summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiri Benc <jbenc@redhat.com>2012-07-27 02:58:22 +0000
committerDavid S. Miller <davem@davemloft.net>2012-07-27 13:45:50 -0700
commitb1beb681cba5358f62e6187340660ade226a5fcc (patch)
tree72dfe8df581bee6b804847794fa77af207327aea
parente4c7f259c5be99dcfc3d98f913590663b0305bf8 (diff)
downloadkernel-common-b1beb681cba5358f62e6187340660ade226a5fcc.tar.gz
kernel-common-b1beb681cba5358f62e6187340660ade226a5fcc.tar.bz2
kernel-common-b1beb681cba5358f62e6187340660ade226a5fcc.zip
net: fix rtnetlink IFF_PROMISC and IFF_ALLMULTI handling
When device flags are set using rtnetlink, IFF_PROMISC and IFF_ALLMULTI flags are handled specially. Function dev_change_flags sets IFF_PROMISC and IFF_ALLMULTI bits in dev->gflags according to the passed value but do_setlink passes a result of rtnl_dev_combine_flags which takes those bits from dev->flags. This can be easily trigerred by doing: tcpdump -i eth0 & ip l s up eth0 ip sets IFF_UP flag in ifi_flags and ifi_change, which is combined with IFF_PROMISC by rtnl_dev_combine_flags, causing __dev_change_flags to set IFF_PROMISC in gflags. Reported-by: Max Matveev <makc@redhat.com> Signed-off-by: Jiri Benc <jbenc@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/core/rtnetlink.c8
1 files changed, 7 insertions, 1 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 334b930e0de3..bc9e380f0abf 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -659,6 +659,12 @@ static void set_operstate(struct net_device *dev, unsigned char transition)
}
}
+static unsigned int rtnl_dev_get_flags(const struct net_device *dev)
+{
+ return (dev->flags & ~(IFF_PROMISC | IFF_ALLMULTI)) |
+ (dev->gflags & (IFF_PROMISC | IFF_ALLMULTI));
+}
+
static unsigned int rtnl_dev_combine_flags(const struct net_device *dev,
const struct ifinfomsg *ifm)
{
@@ -667,7 +673,7 @@ static unsigned int rtnl_dev_combine_flags(const struct net_device *dev,
/* bugwards compatibility: ifi_change == 0 is treated as ~0 */
if (ifm->ifi_change)
flags = (flags & ifm->ifi_change) |
- (dev->flags & ~ifm->ifi_change);
+ (rtnl_dev_get_flags(dev) & ~ifm->ifi_change);
return flags;
}