forked from mirrors/linux
		
	dev: always advertise rx_flags changes via netlink
When flags IFF_PROMISC and IFF_ALLMULTI are changed, netlink messages are not consistent. For example, if a multicast daemon is running (flag IFF_ALLMULTI set in dev->flags but not dev->gflags, ie not exported to userspace) and then a user sets it via netlink (flag IFF_ALLMULTI set in dev->flags and dev->gflags, ie exported to userspace), no netlink message is sent. Same for IFF_PROMISC and because dev->promiscuity is exported via IFLA_PROMISCUITY, we may send a netlink message after each change of this counter. Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									a528c219df
								
							
						
					
					
						commit
						991fb3f74c
					
				
					 1 changed files with 37 additions and 23 deletions
				
			
		| 
						 | 
					@ -4988,7 +4988,7 @@ static void dev_change_rx_flags(struct net_device *dev, int flags)
 | 
				
			||||||
		ops->ndo_change_rx_flags(dev, flags);
 | 
							ops->ndo_change_rx_flags(dev, flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int __dev_set_promiscuity(struct net_device *dev, int inc)
 | 
					static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int old_flags = dev->flags;
 | 
						unsigned int old_flags = dev->flags;
 | 
				
			||||||
	kuid_t uid;
 | 
						kuid_t uid;
 | 
				
			||||||
| 
						 | 
					@ -5031,6 +5031,8 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dev_change_rx_flags(dev, IFF_PROMISC);
 | 
							dev_change_rx_flags(dev, IFF_PROMISC);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (notify)
 | 
				
			||||||
 | 
							__dev_notify_flags(dev, old_flags, IFF_PROMISC);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5050,7 +5052,7 @@ int dev_set_promiscuity(struct net_device *dev, int inc)
 | 
				
			||||||
	unsigned int old_flags = dev->flags;
 | 
						unsigned int old_flags = dev->flags;
 | 
				
			||||||
	int err;
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = __dev_set_promiscuity(dev, inc);
 | 
						err = __dev_set_promiscuity(dev, inc, true);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		return err;
 | 
							return err;
 | 
				
			||||||
	if (dev->flags != old_flags)
 | 
						if (dev->flags != old_flags)
 | 
				
			||||||
| 
						 | 
					@ -5059,22 +5061,9 @@ int dev_set_promiscuity(struct net_device *dev, int inc)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(dev_set_promiscuity);
 | 
					EXPORT_SYMBOL(dev_set_promiscuity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					static int __dev_set_allmulti(struct net_device *dev, int inc, bool notify)
 | 
				
			||||||
 *	dev_set_allmulti	- update allmulti count on a device
 | 
					 | 
				
			||||||
 *	@dev: device
 | 
					 | 
				
			||||||
 *	@inc: modifier
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *	Add or remove reception of all multicast frames to a device. While the
 | 
					 | 
				
			||||||
 *	count in the device remains above zero the interface remains listening
 | 
					 | 
				
			||||||
 *	to all interfaces. Once it hits zero the device reverts back to normal
 | 
					 | 
				
			||||||
 *	filtering operation. A negative @inc value is used to drop the counter
 | 
					 | 
				
			||||||
 *	when releasing a resource needing all multicasts.
 | 
					 | 
				
			||||||
 *	Return 0 if successful or a negative errno code on error.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int dev_set_allmulti(struct net_device *dev, int inc)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int old_flags = dev->flags;
 | 
						unsigned int old_flags = dev->flags, old_gflags = dev->gflags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ASSERT_RTNL();
 | 
						ASSERT_RTNL();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5097,9 +5086,30 @@ int dev_set_allmulti(struct net_device *dev, int inc)
 | 
				
			||||||
	if (dev->flags ^ old_flags) {
 | 
						if (dev->flags ^ old_flags) {
 | 
				
			||||||
		dev_change_rx_flags(dev, IFF_ALLMULTI);
 | 
							dev_change_rx_flags(dev, IFF_ALLMULTI);
 | 
				
			||||||
		dev_set_rx_mode(dev);
 | 
							dev_set_rx_mode(dev);
 | 
				
			||||||
 | 
							if (notify)
 | 
				
			||||||
 | 
								__dev_notify_flags(dev, old_flags,
 | 
				
			||||||
 | 
										   dev->gflags ^ old_gflags);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *	dev_set_allmulti	- update allmulti count on a device
 | 
				
			||||||
 | 
					 *	@dev: device
 | 
				
			||||||
 | 
					 *	@inc: modifier
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *	Add or remove reception of all multicast frames to a device. While the
 | 
				
			||||||
 | 
					 *	count in the device remains above zero the interface remains listening
 | 
				
			||||||
 | 
					 *	to all interfaces. Once it hits zero the device reverts back to normal
 | 
				
			||||||
 | 
					 *	filtering operation. A negative @inc value is used to drop the counter
 | 
				
			||||||
 | 
					 *	when releasing a resource needing all multicasts.
 | 
				
			||||||
 | 
					 *	Return 0 if successful or a negative errno code on error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dev_set_allmulti(struct net_device *dev, int inc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return __dev_set_allmulti(dev, inc, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(dev_set_allmulti);
 | 
					EXPORT_SYMBOL(dev_set_allmulti);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -5124,10 +5134,10 @@ void __dev_set_rx_mode(struct net_device *dev)
 | 
				
			||||||
		 * therefore calling __dev_set_promiscuity here is safe.
 | 
							 * therefore calling __dev_set_promiscuity here is safe.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
 | 
							if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
 | 
				
			||||||
			__dev_set_promiscuity(dev, 1);
 | 
								__dev_set_promiscuity(dev, 1, false);
 | 
				
			||||||
			dev->uc_promisc = true;
 | 
								dev->uc_promisc = true;
 | 
				
			||||||
		} else if (netdev_uc_empty(dev) && dev->uc_promisc) {
 | 
							} else if (netdev_uc_empty(dev) && dev->uc_promisc) {
 | 
				
			||||||
			__dev_set_promiscuity(dev, -1);
 | 
								__dev_set_promiscuity(dev, -1, false);
 | 
				
			||||||
			dev->uc_promisc = false;
 | 
								dev->uc_promisc = false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -5216,9 +5226,13 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((flags ^ dev->gflags) & IFF_PROMISC) {
 | 
						if ((flags ^ dev->gflags) & IFF_PROMISC) {
 | 
				
			||||||
		int inc = (flags & IFF_PROMISC) ? 1 : -1;
 | 
							int inc = (flags & IFF_PROMISC) ? 1 : -1;
 | 
				
			||||||
 | 
							unsigned int old_flags = dev->flags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dev->gflags ^= IFF_PROMISC;
 | 
							dev->gflags ^= IFF_PROMISC;
 | 
				
			||||||
		dev_set_promiscuity(dev, inc);
 | 
					
 | 
				
			||||||
 | 
							if (__dev_set_promiscuity(dev, inc, false) >= 0)
 | 
				
			||||||
 | 
								if (dev->flags != old_flags)
 | 
				
			||||||
 | 
									dev_set_rx_mode(dev);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
 | 
						/* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI
 | 
				
			||||||
| 
						 | 
					@ -5229,7 +5243,7 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags)
 | 
				
			||||||
		int inc = (flags & IFF_ALLMULTI) ? 1 : -1;
 | 
							int inc = (flags & IFF_ALLMULTI) ? 1 : -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dev->gflags ^= IFF_ALLMULTI;
 | 
							dev->gflags ^= IFF_ALLMULTI;
 | 
				
			||||||
		dev_set_allmulti(dev, inc);
 | 
							__dev_set_allmulti(dev, inc, false);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					@ -5271,13 +5285,13 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
 | 
				
			||||||
int dev_change_flags(struct net_device *dev, unsigned int flags)
 | 
					int dev_change_flags(struct net_device *dev, unsigned int flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
	unsigned int changes, old_flags = dev->flags;
 | 
						unsigned int changes, old_flags = dev->flags, old_gflags = dev->gflags;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = __dev_change_flags(dev, flags);
 | 
						ret = __dev_change_flags(dev, flags);
 | 
				
			||||||
	if (ret < 0)
 | 
						if (ret < 0)
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	changes = old_flags ^ dev->flags;
 | 
						changes = (old_flags ^ dev->flags) | (old_gflags ^ dev->gflags);
 | 
				
			||||||
	__dev_notify_flags(dev, old_flags, changes);
 | 
						__dev_notify_flags(dev, old_flags, changes);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue