aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2008-10-07 18:26:48 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-07 18:26:48 -0400
commitb6c40d68ff6498b7f63ddf97cf0aa818d748dee7 (patch)
tree456b4b762260901bb0d1d311ff8b01a37f1f55f7 /net/core
parent859f4c74d8de4dc344b3a115367d5e22a79bddaf (diff)
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: Jesper Dangaard Brouer <jdb@comx.dk> Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/dev.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index e8eb2b478344..fd992c0f2717 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2918,6 +2918,12 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
2918 return 0; 2918 return 0;
2919} 2919}
2920 2920
2921static void dev_change_rx_flags(struct net_device *dev, int flags)
2922{
2923 if (dev->flags & IFF_UP && dev->change_rx_flags)
2924 dev->change_rx_flags(dev, flags);
2925}
2926
2921static int __dev_set_promiscuity(struct net_device *dev, int inc) 2927static int __dev_set_promiscuity(struct net_device *dev, int inc)
2922{ 2928{
2923 unsigned short old_flags = dev->flags; 2929 unsigned short old_flags = dev->flags;
@@ -2955,8 +2961,7 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
2955 current->uid, current->gid, 2961 current->uid, current->gid,
2956 audit_get_sessionid(current)); 2962 audit_get_sessionid(current));
2957 2963
2958 if (dev->change_rx_flags) 2964 dev_change_rx_flags(dev, IFF_PROMISC);
2959 dev->change_rx_flags(dev, IFF_PROMISC);
2960 } 2965 }
2961 return 0; 2966 return 0;
2962} 2967}
@@ -3022,8 +3027,7 @@ int dev_set_allmulti(struct net_device *dev, int inc)
3022 } 3027 }
3023 } 3028 }
3024 if (dev->flags ^ old_flags) { 3029 if (dev->flags ^ old_flags) {
3025 if (dev->change_rx_flags) 3030 dev_change_rx_flags(dev, IFF_ALLMULTI);
3026 dev->change_rx_flags(dev, IFF_ALLMULTI);
3027 dev_set_rx_mode(dev); 3031 dev_set_rx_mode(dev);
3028 } 3032 }
3029 return 0; 3033 return 0;
@@ -3347,8 +3351,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
3347 * Load in the correct multicast list now the flags have changed. 3351 * Load in the correct multicast list now the flags have changed.
3348 */ 3352 */
3349 3353
3350 if (dev->change_rx_flags && (old_flags ^ flags) & IFF_MULTICAST) 3354 if ((old_flags ^ flags) & IFF_MULTICAST)
3351 dev->change_rx_flags(dev, IFF_MULTICAST); 3355 dev_change_rx_flags(dev, IFF_MULTICAST);
3352 3356
3353 dev_set_rx_mode(dev); 3357 dev_set_rx_mode(dev);
3354 3358