Commit b6c40d68 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

net: only invoke dev->change_rx_flags when device is UP

Jesper Dangaard Brouer <hawk@comx.dk> reported a bug when setting a VLAN
device down that is in promiscous mode:

When the VLAN device is set down, the promiscous count on the real
device is decremented by one by vlan_dev_stop(). When removing the
promiscous flag from the VLAN device afterwards, the promiscous
count on the real device is decremented a second time by the
vlan_change_rx_flags() callback.

The root cause for this is that the ->change_rx_flags() callback is
invoked while the device is down. The synchronization is meant to mirror
the behaviour of the ->set_rx_mode callbacks, meaning the ->open function
is responsible for doing a full sync on open, the ->close() function is
responsible for doing full cleanup on ->stop() and ->change_rx_flags()
is meant to do incremental changes while the device is UP.

Only invoke ->change_rx_flags() while the device is UP to provide the
intended behaviour.
Tested-by: default avatarJesper Dangaard Brouer <jdb@comx.dk>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 859f4c74
...@@ -2918,6 +2918,12 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) ...@@ -2918,6 +2918,12 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
return 0; return 0;
} }
static void dev_change_rx_flags(struct net_device *dev, int flags)
{
if (dev->flags & IFF_UP && dev->change_rx_flags)
dev->change_rx_flags(dev, flags);
}
static int __dev_set_promiscuity(struct net_device *dev, int inc) static int __dev_set_promiscuity(struct net_device *dev, int inc)
{ {
unsigned short old_flags = dev->flags; unsigned short old_flags = dev->flags;
...@@ -2955,8 +2961,7 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc) ...@@ -2955,8 +2961,7 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
current->uid, current->gid, current->uid, current->gid,
audit_get_sessionid(current)); audit_get_sessionid(current));
if (dev->change_rx_flags) dev_change_rx_flags(dev, IFF_PROMISC);
dev->change_rx_flags(dev, IFF_PROMISC);
} }
return 0; return 0;
} }
...@@ -3022,8 +3027,7 @@ int dev_set_allmulti(struct net_device *dev, int inc) ...@@ -3022,8 +3027,7 @@ int dev_set_allmulti(struct net_device *dev, int inc)
} }
} }
if (dev->flags ^ old_flags) { if (dev->flags ^ old_flags) {
if (dev->change_rx_flags) dev_change_rx_flags(dev, IFF_ALLMULTI);
dev->change_rx_flags(dev, IFF_ALLMULTI);
dev_set_rx_mode(dev); dev_set_rx_mode(dev);
} }
return 0; return 0;
...@@ -3347,8 +3351,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags) ...@@ -3347,8 +3351,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
* Load in the correct multicast list now the flags have changed. * Load in the correct multicast list now the flags have changed.
*/ */
if (dev->change_rx_flags && (old_flags ^ flags) & IFF_MULTICAST) if ((old_flags ^ flags) & IFF_MULTICAST)
dev->change_rx_flags(dev, IFF_MULTICAST); dev_change_rx_flags(dev, IFF_MULTICAST);
dev_set_rx_mode(dev); dev_set_rx_mode(dev);
......
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