diff options
author | Patrick McHardy <kaber@trash.net> | 2007-07-14 21:52:56 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-07-14 21:52:56 -0400 |
commit | 6c78dcbd47a68a7d25d2bee7a6c74b9136cb5fde (patch) | |
tree | 21e4a2ea3eb7ed87ce525c59bb8c4d23d8c84589 /net | |
parent | a0a400d79e3dd7843e7e81baa3ef2957bdc292d0 (diff) |
[VLAN]: Fix promiscous/allmulti synchronization races
The set_multicast_list function may be called without holding the rtnl
mutex, resulting in races when changing the underlying device's promiscous
and allmulti state. Use the change_rx_mode hook, which is always invoked
under the rtnl.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/8021q/vlan.c | 1 | ||||
-rw-r--r-- | net/8021q/vlan.h | 1 | ||||
-rw-r--r-- | net/8021q/vlan_dev.c | 38 |
3 files changed, 22 insertions, 18 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index abb9900edb3f..39bdcc25c150 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
@@ -373,6 +373,7 @@ void vlan_setup(struct net_device *new_dev) | |||
373 | new_dev->open = vlan_dev_open; | 373 | new_dev->open = vlan_dev_open; |
374 | new_dev->stop = vlan_dev_stop; | 374 | new_dev->stop = vlan_dev_stop; |
375 | new_dev->set_multicast_list = vlan_dev_set_multicast_list; | 375 | new_dev->set_multicast_list = vlan_dev_set_multicast_list; |
376 | new_dev->change_rx_flags = vlan_change_rx_flags; | ||
376 | new_dev->destructor = free_netdev; | 377 | new_dev->destructor = free_netdev; |
377 | new_dev->do_ioctl = vlan_dev_ioctl; | 378 | new_dev->do_ioctl = vlan_dev_ioctl; |
378 | 379 | ||
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 62ce1c519aab..7df5b2935579 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h | |||
@@ -69,6 +69,7 @@ int vlan_dev_set_vlan_flag(const struct net_device *dev, | |||
69 | u32 flag, short flag_val); | 69 | u32 flag, short flag_val); |
70 | void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); | 70 | void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); |
71 | void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result); | 71 | void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result); |
72 | void vlan_change_rx_flags(struct net_device *dev, int change); | ||
72 | void vlan_dev_set_multicast_list(struct net_device *vlan_dev); | 73 | void vlan_dev_set_multicast_list(struct net_device *vlan_dev); |
73 | 74 | ||
74 | int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); | 75 | int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); |
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index d4a62d1b52b4..dec7e62b2e10 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c | |||
@@ -712,6 +712,11 @@ int vlan_dev_open(struct net_device *dev) | |||
712 | } | 712 | } |
713 | memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN); | 713 | memcpy(vlan->real_dev_addr, real_dev->dev_addr, ETH_ALEN); |
714 | 714 | ||
715 | if (dev->flags & IFF_ALLMULTI) | ||
716 | dev_set_allmulti(real_dev, 1); | ||
717 | if (dev->flags & IFF_PROMISC) | ||
718 | dev_set_promiscuity(real_dev, 1); | ||
719 | |||
715 | return 0; | 720 | return 0; |
716 | } | 721 | } |
717 | 722 | ||
@@ -721,6 +726,11 @@ int vlan_dev_stop(struct net_device *dev) | |||
721 | 726 | ||
722 | vlan_flush_mc_list(dev); | 727 | vlan_flush_mc_list(dev); |
723 | 728 | ||
729 | if (dev->flags & IFF_ALLMULTI) | ||
730 | dev_set_allmulti(real_dev, -1); | ||
731 | if (dev->flags & IFF_PROMISC) | ||
732 | dev_set_promiscuity(real_dev, -1); | ||
733 | |||
724 | if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) | 734 | if (compare_ether_addr(dev->dev_addr, real_dev->dev_addr)) |
725 | dev_unicast_delete(real_dev, dev->dev_addr, dev->addr_len); | 735 | dev_unicast_delete(real_dev, dev->dev_addr, dev->addr_len); |
726 | 736 | ||
@@ -754,34 +764,26 @@ int vlan_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
754 | return err; | 764 | return err; |
755 | } | 765 | } |
756 | 766 | ||
767 | void vlan_change_rx_flags(struct net_device *dev, int change) | ||
768 | { | ||
769 | struct net_device *real_dev = VLAN_DEV_INFO(dev)->real_dev; | ||
770 | |||
771 | if (change & IFF_ALLMULTI) | ||
772 | dev_set_allmulti(real_dev, dev->flags & IFF_ALLMULTI ? 1 : -1); | ||
773 | if (change & IFF_PROMISC) | ||
774 | dev_set_promiscuity(real_dev, dev->flags & IFF_PROMISC ? 1 : -1); | ||
775 | } | ||
776 | |||
757 | /** Taken from Gleb + Lennert's VLAN code, and modified... */ | 777 | /** Taken from Gleb + Lennert's VLAN code, and modified... */ |
758 | void vlan_dev_set_multicast_list(struct net_device *vlan_dev) | 778 | void vlan_dev_set_multicast_list(struct net_device *vlan_dev) |
759 | { | 779 | { |
760 | struct dev_mc_list *dmi; | 780 | struct dev_mc_list *dmi; |
761 | struct net_device *real_dev; | 781 | struct net_device *real_dev; |
762 | int inc; | ||
763 | 782 | ||
764 | if (vlan_dev && (vlan_dev->priv_flags & IFF_802_1Q_VLAN)) { | 783 | if (vlan_dev && (vlan_dev->priv_flags & IFF_802_1Q_VLAN)) { |
765 | /* Then it's a real vlan device, as far as we can tell.. */ | 784 | /* Then it's a real vlan device, as far as we can tell.. */ |
766 | real_dev = VLAN_DEV_INFO(vlan_dev)->real_dev; | 785 | real_dev = VLAN_DEV_INFO(vlan_dev)->real_dev; |
767 | 786 | ||
768 | /* compare the current promiscuity to the last promisc we had.. */ | ||
769 | inc = vlan_dev->promiscuity - VLAN_DEV_INFO(vlan_dev)->old_promiscuity; | ||
770 | if (inc) { | ||
771 | printk(KERN_INFO "%s: dev_set_promiscuity(master, %d)\n", | ||
772 | vlan_dev->name, inc); | ||
773 | dev_set_promiscuity(real_dev, inc); /* found in dev.c */ | ||
774 | VLAN_DEV_INFO(vlan_dev)->old_promiscuity = vlan_dev->promiscuity; | ||
775 | } | ||
776 | |||
777 | inc = vlan_dev->allmulti - VLAN_DEV_INFO(vlan_dev)->old_allmulti; | ||
778 | if (inc) { | ||
779 | printk(KERN_INFO "%s: dev_set_allmulti(master, %d)\n", | ||
780 | vlan_dev->name, inc); | ||
781 | dev_set_allmulti(real_dev, inc); /* dev.c */ | ||
782 | VLAN_DEV_INFO(vlan_dev)->old_allmulti = vlan_dev->allmulti; | ||
783 | } | ||
784 | |||
785 | /* looking for addresses to add to master's list */ | 787 | /* looking for addresses to add to master's list */ |
786 | for (dmi = vlan_dev->mc_list; dmi != NULL; dmi = dmi->next) { | 788 | for (dmi = vlan_dev->mc_list; dmi != NULL; dmi = dmi->next) { |
787 | if (vlan_should_add_mc(dmi, VLAN_DEV_INFO(vlan_dev)->old_mc_list)) { | 789 | if (vlan_should_add_mc(dmi, VLAN_DEV_INFO(vlan_dev)->old_mc_list)) { |