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

[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
...@@ -397,6 +397,9 @@ struct net_device ...@@ -397,6 +397,9 @@ struct net_device
unsigned char addr_len; /* hardware address length */ unsigned char addr_len; /* hardware address length */
unsigned short dev_id; /* for shared network cards */ 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 */ struct dev_addr_list *mc_list; /* Multicast mac addresses */
int mc_count; /* Number of installed mcasts */ int mc_count; /* Number of installed mcasts */
int promiscuity; int promiscuity;
...@@ -502,6 +505,8 @@ struct net_device ...@@ -502,6 +505,8 @@ struct net_device
void *saddr, void *saddr,
unsigned len); unsigned len);
int (*rebuild_header)(struct sk_buff *skb); int (*rebuild_header)(struct sk_buff *skb);
#define HAVE_SET_RX_MODE
void (*set_rx_mode)(struct net_device *dev);
#define HAVE_MULTICAST #define HAVE_MULTICAST
void (*set_multicast_list)(struct net_device *dev); void (*set_multicast_list)(struct net_device *dev);
#define HAVE_SET_MAC_ADDR #define HAVE_SET_MAC_ADDR
...@@ -1008,8 +1013,11 @@ extern struct net_device *alloc_netdev(int sizeof_priv, const char *name, ...@@ -1008,8 +1013,11 @@ extern struct net_device *alloc_netdev(int sizeof_priv, const char *name,
void (*setup)(struct net_device *)); void (*setup)(struct net_device *));
extern int register_netdev(struct net_device *dev); extern int register_netdev(struct net_device *dev);
extern void unregister_netdev(struct net_device *dev); extern void unregister_netdev(struct net_device *dev);
/* Functions used for multicast support */ /* Functions used for secondary unicast and multicast support */
extern void dev_mc_upload(struct net_device *dev); 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_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 int dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly);
extern void dev_mc_discard(struct net_device *dev); extern void dev_mc_discard(struct net_device *dev);
......
...@@ -942,7 +942,7 @@ int dev_open(struct net_device *dev) ...@@ -942,7 +942,7 @@ int dev_open(struct net_device *dev)
/* /*
* Initialize multicasting status * Initialize multicasting status
*/ */
dev_mc_upload(dev); dev_set_rx_mode(dev);
/* /*
* Wakeup transmit queue engine * Wakeup transmit queue engine
...@@ -2498,17 +2498,7 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) ...@@ -2498,17 +2498,7 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
return 0; return 0;
} }
/** static 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; unsigned short old_flags = dev->flags;
...@@ -2517,7 +2507,6 @@ void dev_set_promiscuity(struct net_device *dev, int inc) ...@@ -2517,7 +2507,6 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
else else
dev->flags |= IFF_PROMISC; dev->flags |= IFF_PROMISC;
if (dev->flags != old_flags) { if (dev->flags != old_flags) {
dev_mc_upload(dev);
printk(KERN_INFO "device %s %s promiscuous mode\n", printk(KERN_INFO "device %s %s promiscuous mode\n",
dev->name, (dev->flags & IFF_PROMISC) ? "entered" : dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
"left"); "left");
...@@ -2530,6 +2519,25 @@ void dev_set_promiscuity(struct net_device *dev, int inc) ...@@ -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_set_allmulti - update allmulti count on a device
* @dev: device * @dev: device
...@@ -2550,7 +2558,48 @@ void dev_set_allmulti(struct net_device *dev, int inc) ...@@ -2550,7 +2558,48 @@ void dev_set_allmulti(struct net_device *dev, int inc)
if ((dev->allmulti += inc) == 0) if ((dev->allmulti += inc) == 0)
dev->flags &= ~IFF_ALLMULTI; dev->flags &= ~IFF_ALLMULTI;
if (dev->flags ^ old_flags) 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, 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) ...@@ -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 dev_get_flags(const struct net_device *dev)
{ {
unsigned flags; unsigned flags;
...@@ -2665,7 +2774,7 @@ int dev_change_flags(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. * 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 * Have we downed the interface. We handle IFF_UP ourselves
...@@ -2678,7 +2787,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags) ...@@ -2678,7 +2787,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev); ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
if (!ret) if (!ret)
dev_mc_upload(dev); dev_set_rx_mode(dev);
} }
if (dev->flags & IFF_UP && if (dev->flags & IFF_UP &&
...@@ -3558,8 +3667,9 @@ void unregister_netdevice(struct net_device *dev) ...@@ -3558,8 +3667,9 @@ void unregister_netdevice(struct net_device *dev)
raw_notifier_call_chain(&netdev_chain, NETDEV_UNREGISTER, 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); dev_mc_discard(dev);
if (dev->uninit) if (dev->uninit)
......
...@@ -63,39 +63,6 @@ ...@@ -63,39 +63,6 @@
* We block accesses to device mc filters with netif_tx_lock. * 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 * Delete a device level multicast
*/ */
...@@ -114,7 +81,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) ...@@ -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 * loaded filter is now wrong. Fix it
*/ */
__dev_mc_upload(dev); __dev_set_rx_mode(dev);
} }
netif_tx_unlock_bh(dev); netif_tx_unlock_bh(dev);
return err; return err;
...@@ -132,7 +99,7 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) ...@@ -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); err = __dev_addr_add(&dev->mc_list, addr, alen, glbl);
if (!err) { if (!err) {
dev->mc_count++; dev->mc_count++;
__dev_mc_upload(dev); __dev_set_rx_mode(dev);
} }
netif_tx_unlock_bh(dev); netif_tx_unlock_bh(dev);
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment