mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	[NET]: dev: secondary unicast address support
Add support for configuring secondary unicast addresses on network devices. To support this devices capable of filtering multiple unicast addresses need to change their set_multicast_list function to configure unicast filters as well and assign it to dev->set_rx_mode instead of dev->set_multicast_list. Other devices are put into promiscous mode when secondary unicast addresses are present. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									3fba5a8b1e
								
							
						
					
					
						commit
						4417da668c
					
				
					 3 changed files with 143 additions and 58 deletions
				
			
		| 
						 | 
				
			
			@ -397,6 +397,9 @@ struct net_device
 | 
			
		|||
	unsigned char		addr_len;	/* hardware address length	*/
 | 
			
		||||
	unsigned short          dev_id;		/* for shared network cards */
 | 
			
		||||
 | 
			
		||||
	struct dev_addr_list	*uc_list;	/* Secondary unicast mac addresses */
 | 
			
		||||
	int			uc_count;	/* Number of installed ucasts	*/
 | 
			
		||||
	int			uc_promisc;
 | 
			
		||||
	struct dev_addr_list	*mc_list;	/* Multicast mac addresses	*/
 | 
			
		||||
	int			mc_count;	/* Number of installed mcasts	*/
 | 
			
		||||
	int			promiscuity;
 | 
			
		||||
| 
						 | 
				
			
			@ -502,6 +505,8 @@ struct net_device
 | 
			
		|||
						void *saddr,
 | 
			
		||||
						unsigned len);
 | 
			
		||||
	int			(*rebuild_header)(struct sk_buff *skb);
 | 
			
		||||
#define HAVE_SET_RX_MODE
 | 
			
		||||
	void			(*set_rx_mode)(struct net_device *dev);
 | 
			
		||||
#define HAVE_MULTICAST			 
 | 
			
		||||
	void			(*set_multicast_list)(struct net_device *dev);
 | 
			
		||||
#define HAVE_SET_MAC_ADDR  		 
 | 
			
		||||
| 
						 | 
				
			
			@ -1008,8 +1013,11 @@ extern struct net_device *alloc_netdev(int sizeof_priv, const char *name,
 | 
			
		|||
				       void (*setup)(struct net_device *));
 | 
			
		||||
extern int		register_netdev(struct net_device *dev);
 | 
			
		||||
extern void		unregister_netdev(struct net_device *dev);
 | 
			
		||||
/* Functions used for multicast support */
 | 
			
		||||
extern void		dev_mc_upload(struct net_device *dev);
 | 
			
		||||
/* Functions used for secondary unicast and multicast support */
 | 
			
		||||
extern void		dev_set_rx_mode(struct net_device *dev);
 | 
			
		||||
extern void		__dev_set_rx_mode(struct net_device *dev);
 | 
			
		||||
extern int		dev_unicast_delete(struct net_device *dev, void *addr, int alen);
 | 
			
		||||
extern int		dev_unicast_add(struct net_device *dev, void *addr, int alen);
 | 
			
		||||
extern int 		dev_mc_delete(struct net_device *dev, void *addr, int alen, int all);
 | 
			
		||||
extern int		dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly);
 | 
			
		||||
extern void		dev_mc_discard(struct net_device *dev);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										152
									
								
								net/core/dev.c
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								net/core/dev.c
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -942,7 +942,7 @@ int dev_open(struct net_device *dev)
 | 
			
		|||
		/*
 | 
			
		||||
		 *	Initialize multicasting status
 | 
			
		||||
		 */
 | 
			
		||||
		dev_mc_upload(dev);
 | 
			
		||||
		dev_set_rx_mode(dev);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 *	Wakeup transmit queue engine
 | 
			
		||||
| 
						 | 
				
			
			@ -2498,6 +2498,27 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __dev_set_promiscuity(struct net_device *dev, int inc)
 | 
			
		||||
{
 | 
			
		||||
	unsigned short old_flags = dev->flags;
 | 
			
		||||
 | 
			
		||||
	if ((dev->promiscuity += inc) == 0)
 | 
			
		||||
		dev->flags &= ~IFF_PROMISC;
 | 
			
		||||
	else
 | 
			
		||||
		dev->flags |= IFF_PROMISC;
 | 
			
		||||
	if (dev->flags != old_flags) {
 | 
			
		||||
		printk(KERN_INFO "device %s %s promiscuous mode\n",
 | 
			
		||||
		       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
 | 
			
		||||
							       "left");
 | 
			
		||||
		audit_log(current->audit_context, GFP_ATOMIC,
 | 
			
		||||
			AUDIT_ANOM_PROMISCUOUS,
 | 
			
		||||
			"dev=%s prom=%d old_prom=%d auid=%u",
 | 
			
		||||
			dev->name, (dev->flags & IFF_PROMISC),
 | 
			
		||||
			(old_flags & IFF_PROMISC),
 | 
			
		||||
			audit_get_loginuid(current->audit_context));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	dev_set_promiscuity	- update promiscuity count on a device
 | 
			
		||||
 *	@dev: device
 | 
			
		||||
| 
						 | 
				
			
			@ -2512,22 +2533,9 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
 | 
			
		|||
{
 | 
			
		||||
	unsigned short old_flags = dev->flags;
 | 
			
		||||
 | 
			
		||||
	if ((dev->promiscuity += inc) == 0)
 | 
			
		||||
		dev->flags &= ~IFF_PROMISC;
 | 
			
		||||
	else
 | 
			
		||||
		dev->flags |= IFF_PROMISC;
 | 
			
		||||
	if (dev->flags != old_flags) {
 | 
			
		||||
		dev_mc_upload(dev);
 | 
			
		||||
		printk(KERN_INFO "device %s %s promiscuous mode\n",
 | 
			
		||||
		       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
 | 
			
		||||
							       "left");
 | 
			
		||||
		audit_log(current->audit_context, GFP_ATOMIC,
 | 
			
		||||
			AUDIT_ANOM_PROMISCUOUS,
 | 
			
		||||
			"dev=%s prom=%d old_prom=%d auid=%u",
 | 
			
		||||
			dev->name, (dev->flags & IFF_PROMISC),
 | 
			
		||||
			(old_flags & IFF_PROMISC),
 | 
			
		||||
			audit_get_loginuid(current->audit_context));
 | 
			
		||||
	}
 | 
			
		||||
	__dev_set_promiscuity(dev, inc);
 | 
			
		||||
	if (dev->flags != old_flags)
 | 
			
		||||
		dev_set_rx_mode(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -2550,7 +2558,48 @@ void dev_set_allmulti(struct net_device *dev, int inc)
 | 
			
		|||
	if ((dev->allmulti += inc) == 0)
 | 
			
		||||
		dev->flags &= ~IFF_ALLMULTI;
 | 
			
		||||
	if (dev->flags ^ old_flags)
 | 
			
		||||
		dev_mc_upload(dev);
 | 
			
		||||
		dev_set_rx_mode(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *	Upload unicast and multicast address lists to device and
 | 
			
		||||
 *	configure RX filtering. When the device doesn't support unicast
 | 
			
		||||
 *	filtering it is put in promiscous mode while unicast addresses
 | 
			
		||||
 *	are present.
 | 
			
		||||
 */
 | 
			
		||||
void __dev_set_rx_mode(struct net_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	/* dev_open will call this function so the list will stay sane. */
 | 
			
		||||
	if (!(dev->flags&IFF_UP))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (!netif_device_present(dev))
 | 
			
		||||
	        return;
 | 
			
		||||
 | 
			
		||||
	if (dev->set_rx_mode)
 | 
			
		||||
		dev->set_rx_mode(dev);
 | 
			
		||||
	else {
 | 
			
		||||
		/* Unicast addresses changes may only happen under the rtnl,
 | 
			
		||||
		 * therefore calling __dev_set_promiscuity here is safe.
 | 
			
		||||
		 */
 | 
			
		||||
		if (dev->uc_count > 0 && !dev->uc_promisc) {
 | 
			
		||||
			__dev_set_promiscuity(dev, 1);
 | 
			
		||||
			dev->uc_promisc = 1;
 | 
			
		||||
		} else if (dev->uc_count == 0 && dev->uc_promisc) {
 | 
			
		||||
			__dev_set_promiscuity(dev, -1);
 | 
			
		||||
			dev->uc_promisc = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (dev->set_multicast_list)
 | 
			
		||||
			dev->set_multicast_list(dev);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dev_set_rx_mode(struct net_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	netif_tx_lock_bh(dev);
 | 
			
		||||
	__dev_set_rx_mode(dev);
 | 
			
		||||
	netif_tx_unlock_bh(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int __dev_addr_delete(struct dev_addr_list **list, void *addr, int alen,
 | 
			
		||||
| 
						 | 
				
			
			@ -2622,6 +2671,66 @@ void __dev_addr_discard(struct dev_addr_list **list)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	dev_unicast_delete	- Release secondary unicast address.
 | 
			
		||||
 *	@dev: device
 | 
			
		||||
 *
 | 
			
		||||
 *	Release reference to a secondary unicast address and remove it
 | 
			
		||||
 *	from the device if the reference count drop to zero.
 | 
			
		||||
 *
 | 
			
		||||
 * 	The caller must hold the rtnl_mutex.
 | 
			
		||||
 */
 | 
			
		||||
int dev_unicast_delete(struct net_device *dev, void *addr, int alen)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	ASSERT_RTNL();
 | 
			
		||||
 | 
			
		||||
	netif_tx_lock_bh(dev);
 | 
			
		||||
	err = __dev_addr_delete(&dev->uc_list, addr, alen, 0);
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		dev->uc_count--;
 | 
			
		||||
		__dev_set_rx_mode(dev);
 | 
			
		||||
	}
 | 
			
		||||
	netif_tx_unlock_bh(dev);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(dev_unicast_delete);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	dev_unicast_add		- add a secondary unicast address
 | 
			
		||||
 *	@dev: device
 | 
			
		||||
 *
 | 
			
		||||
 *	Add a secondary unicast address to the device or increase
 | 
			
		||||
 *	the reference count if it already exists.
 | 
			
		||||
 *
 | 
			
		||||
 *	The caller must hold the rtnl_mutex.
 | 
			
		||||
 */
 | 
			
		||||
int dev_unicast_add(struct net_device *dev, void *addr, int alen)
 | 
			
		||||
{
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	ASSERT_RTNL();
 | 
			
		||||
 | 
			
		||||
	netif_tx_lock_bh(dev);
 | 
			
		||||
	err = __dev_addr_add(&dev->uc_list, addr, alen, 0);
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		dev->uc_count++;
 | 
			
		||||
		__dev_set_rx_mode(dev);
 | 
			
		||||
	}
 | 
			
		||||
	netif_tx_unlock_bh(dev);
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(dev_unicast_add);
 | 
			
		||||
 | 
			
		||||
static void dev_unicast_discard(struct net_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	netif_tx_lock_bh(dev);
 | 
			
		||||
	__dev_addr_discard(&dev->uc_list);
 | 
			
		||||
	dev->uc_count = 0;
 | 
			
		||||
	netif_tx_unlock_bh(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned dev_get_flags(const struct net_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	unsigned flags;
 | 
			
		||||
| 
						 | 
				
			
			@ -2665,7 +2774,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
 | 
			
		|||
	 *	Load in the correct multicast list now the flags have changed.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	dev_mc_upload(dev);
 | 
			
		||||
	dev_set_rx_mode(dev);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 *	Have we downed the interface. We handle IFF_UP ourselves
 | 
			
		||||
| 
						 | 
				
			
			@ -2678,7 +2787,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
 | 
			
		|||
		ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
 | 
			
		||||
 | 
			
		||||
		if (!ret)
 | 
			
		||||
			dev_mc_upload(dev);
 | 
			
		||||
			dev_set_rx_mode(dev);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (dev->flags & IFF_UP &&
 | 
			
		||||
| 
						 | 
				
			
			@ -3558,8 +3667,9 @@ void unregister_netdevice(struct net_device *dev)
 | 
			
		|||
	raw_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, dev);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 *	Flush the multicast chain
 | 
			
		||||
	 *	Flush the unicast and multicast chains
 | 
			
		||||
	 */
 | 
			
		||||
	dev_unicast_discard(dev);
 | 
			
		||||
	dev_mc_discard(dev);
 | 
			
		||||
 | 
			
		||||
	if (dev->uninit)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,39 +63,6 @@
 | 
			
		|||
 *	We block accesses to device mc filters with netif_tx_lock.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *	Update the multicast list into the physical NIC controller.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void __dev_mc_upload(struct net_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	/* Don't do anything till we up the interface
 | 
			
		||||
	 * [dev_open will call this function so the list will
 | 
			
		||||
	 * stay sane]
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (!(dev->flags&IFF_UP))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 *	Devices with no set multicast or which have been
 | 
			
		||||
	 *	detached don't get set.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (dev->set_multicast_list == NULL ||
 | 
			
		||||
	    !netif_device_present(dev))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	dev->set_multicast_list(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dev_mc_upload(struct net_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	netif_tx_lock_bh(dev);
 | 
			
		||||
	__dev_mc_upload(dev);
 | 
			
		||||
	netif_tx_unlock_bh(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *	Delete a device level multicast
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +81,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl)
 | 
			
		|||
		 *	loaded filter is now wrong. Fix it
 | 
			
		||||
		 */
 | 
			
		||||
 | 
			
		||||
		__dev_mc_upload(dev);
 | 
			
		||||
		__dev_set_rx_mode(dev);
 | 
			
		||||
	}
 | 
			
		||||
	netif_tx_unlock_bh(dev);
 | 
			
		||||
	return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +99,7 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl)
 | 
			
		|||
	err = __dev_addr_add(&dev->mc_list, addr, alen, glbl);
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		dev->mc_count++;
 | 
			
		||||
		__dev_mc_upload(dev);
 | 
			
		||||
		__dev_set_rx_mode(dev);
 | 
			
		||||
	}
 | 
			
		||||
	netif_tx_unlock_bh(dev);
 | 
			
		||||
	return err;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue