Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4417da66 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller
Browse files

[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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3fba5a8b
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -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);
+127 −17
Original line number Diff line number Diff line
@@ -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,17 +2498,7 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
	return 0;
}

/**
 *	dev_set_promiscuity	- update promiscuity count on a device
 *	@dev: device
 *	@inc: modifier
 *
 *	Add or remove promiscuity from a device. While the count in the device
 *	remains above zero the interface remains promiscuous. Once it hits zero
 *	the device reverts back to normal filtering operation. A negative inc
 *	value is used to drop promiscuity on the device.
 */
void dev_set_promiscuity(struct net_device *dev, int inc)
static void __dev_set_promiscuity(struct net_device *dev, int inc)
{
	unsigned short old_flags = dev->flags;

@@ -2517,7 +2507,6 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
	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");
@@ -2530,6 +2519,25 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
	}
}

/**
 *	dev_set_promiscuity	- update promiscuity count on a device
 *	@dev: device
 *	@inc: modifier
 *
 *	Add or remove promiscuity from a device. While the count in the device
 *	remains above zero the interface remains promiscuous. Once it hits zero
 *	the device reverts back to normal filtering operation. A negative inc
 *	value is used to drop promiscuity on the device.
 */
void dev_set_promiscuity(struct net_device *dev, int inc)
{
	unsigned short old_flags = dev->flags;

	__dev_set_promiscuity(dev, inc);
	if (dev->flags != old_flags)
		dev_set_rx_mode(dev);
}

/**
 *	dev_set_allmulti	- update allmulti count on a device
 *	@dev: device
@@ -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)
+2 −35
Original line number Diff line number Diff line
@@ -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;