[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
@ -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
|
|||||||
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,
|
|||||||
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);
|
||||||
|
152
net/core/dev.c
152
net/core/dev.c
@ -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,6 +2498,27 @@ 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)
|
||||||
|
{
|
||||||
|
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_set_promiscuity - update promiscuity count on a device
|
||||||
* @dev: device
|
* @dev: device
|
||||||
@ -2512,22 +2533,9 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
|
|||||||
{
|
{
|
||||||
unsigned short old_flags = dev->flags;
|
unsigned short old_flags = dev->flags;
|
||||||
|
|
||||||
if ((dev->promiscuity += inc) == 0)
|
__dev_set_promiscuity(dev, inc);
|
||||||
dev->flags &= ~IFF_PROMISC;
|
if (dev->flags != old_flags)
|
||||||
else
|
dev_set_rx_mode(dev);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
* 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)
|
|||||||
* 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)
|
|||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user