[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