diff options
author | Patrick McHardy <kaber@trash.net> | 2008-10-07 18:26:48 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-10-07 18:26:48 -0400 |
commit | b6c40d68ff6498b7f63ddf97cf0aa818d748dee7 (patch) | |
tree | 456b4b762260901bb0d1d311ff8b01a37f1f55f7 /net/core | |
parent | 859f4c74d8de4dc344b3a115367d5e22a79bddaf (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.c | 16 |
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 | ||
2921 | static 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 | |||
2921 | static int __dev_set_promiscuity(struct net_device *dev, int inc) | 2927 | static 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 | ||