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/8021q/vlan_dev.c | |
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/8021q/vlan_dev.c')
-rw-r--r-- | net/8021q/vlan_dev.c | 38 |
1 files changed, 20 insertions, 18 deletions
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)) { |