Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 3205

kernel-2.6.18-238.el5.src.rpm

From: Neil Horman <nhorman@redhat.com>
Date: Tue, 26 May 2009 06:54:23 -0400
Subject: [net] undo vlan promiscuity count when unregistered
Message-id: 20090526105423.GA725@hmsreliant.think-freely.org
O-Subject: [RHEL 5.5] PATCH: properly undo vlan promiscuity count when device is unregistered (bz481283)
Bugzilla: 481283
RH-Acked-by: Thomas Graf <tgraf@redhat.com>
RH-Acked-by: David Miller <davem@redhat.com>

Hey
	Currently our vlans leak promiscuity counts when unregistered.  Vlans do
various things to the underlying real devices promiscuity setings when the vlan
itself changes mac address, or its own promiscuity, but it never tracks or
undoes them when the device is unregistered.  This results in underlying
physical devices having strange promiscuity counts and not being able to clear
the IFF_PROMISC flag on the interface.  Upstream has solved this with a re-write
of how unicast and multicast address lists are handled, but the change is an ABI
breaker.  Flavio and I developed this patch instead to fix the problem locally
in the vlan device driver.  Validated by internal testing.  Fixes bz 481283

Neil

diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index f2f0ba8..2d18f2c 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -112,6 +112,7 @@ struct vlan_dev_info {
                                            */
 	int old_allmulti;               /* similar to above. */
 	int old_promiscuity;            /* similar to above. */
+	int promisc_count;		/* our delta on real_devs promisc */
 	struct net_device *real_dev;    /* the underlying device/interface */
 	struct proc_dir_entry *dent;    /* Holds the proc data */
 	unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 18fcb9f..7440493 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -275,6 +275,8 @@ static int unregister_vlan_dev(struct net_device *real_dev,
 static int unregister_vlan_device(const char *vlan_IF_name)
 {
 	struct net_device *dev = NULL;
+	struct net_device *real_dev;
+	int undo_count;
 	int ret;
 
 
@@ -284,9 +286,15 @@ static int unregister_vlan_device(const char *vlan_IF_name)
 		if (dev->priv_flags & IFF_802_1Q_VLAN) {
 			rtnl_lock();
 
-			ret = unregister_vlan_dev(VLAN_DEV_INFO(dev)->real_dev,
+			real_dev = VLAN_DEV_INFO(dev)->real_dev;
+			ret = unregister_vlan_dev(real_dev,
 						  VLAN_DEV_INFO(dev)->vlan_id);
 
+			undo_count = VLAN_DEV_INFO(dev)->promisc_count * -1;
+			dev_set_promiscuity(real_dev, undo_count);
+			if (!(real_dev->flags & IFF_PROMISC))
+				real_dev->gflags &= ~IFF_PROMISC;
+
 			dev_put(dev);
 			unregister_netdevice(dev);
 
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 3d0c784..15b5719 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -680,18 +680,22 @@ int vlan_dev_set_mac_address(struct net_device *dev, void *addr_struct_p)
 	if (memcmp(VLAN_DEV_INFO(dev)->real_dev->dev_addr,
 		   dev->dev_addr,
 		   dev->addr_len) != 0) {
-		if (!(VLAN_DEV_INFO(dev)->real_dev->flags & IFF_PROMISC)) {
-			int flgs = VLAN_DEV_INFO(dev)->real_dev->flags;
-
-			/* Increment our in-use promiscuity counter */
-			dev_set_promiscuity(VLAN_DEV_INFO(dev)->real_dev, 1);
+		int flgs = VLAN_DEV_INFO(dev)->real_dev->flags;
+		int gflgs = VLAN_DEV_INFO(dev)->real_dev->gflags;
 
+		if (!(flgs & IFF_PROMISC)) {
 			/* Make PROMISC visible to the user. */
 			flgs |= IFF_PROMISC;
 			printk("VLAN (%s):  Setting underlying device (%s) to promiscious mode.\n",
 			       dev->name, VLAN_DEV_INFO(dev)->real_dev->name);
 			dev_change_flags(VLAN_DEV_INFO(dev)->real_dev, flgs);
+			if (!(gflgs & IFF_PROMISC))
+				VLAN_DEV_INFO(dev)->promisc_count++;
 		}
+
+		/* Increment our in-use promiscuity counter */
+		dev_set_promiscuity(VLAN_DEV_INFO(dev)->real_dev, 1);
+		VLAN_DEV_INFO(dev)->promisc_count++;
 	} else {
 		printk("VLAN (%s):  Underlying device (%s) has same MAC, not checking promiscious mode.\n",
 		       dev->name, VLAN_DEV_INFO(dev)->real_dev->name);
@@ -843,6 +847,7 @@ void vlan_dev_set_multicast_list(struct net_device *vlan_dev)
 			       vlan_dev->name, inc);
 			dev_set_promiscuity(real_dev, inc); /* found in dev.c */
 			VLAN_DEV_INFO(vlan_dev)->old_promiscuity = vlan_dev->promiscuity;
+			VLAN_DEV_INFO(vlan_dev)->promisc_count += inc;
 		}
 
 		inc = vlan_dev->allmulti - VLAN_DEV_INFO(vlan_dev)->old_allmulti;