[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>
diff --git a/net/core/dev.c b/net/core/dev.c
index 18759cc..36e9bf8 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -942,7 +942,7 @@
/*
* Initialize multicasting status
*/
- dev_mc_upload(dev);
+ dev_set_rx_mode(dev);
/*
* Wakeup transmit queue engine
@@ -2498,6 +2498,27 @@
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 @@
{
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 @@
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 @@
}
}
+/**
+ * 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 @@
* 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 @@
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 @@
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)
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c
index 7029074..5cc9b44 100644
--- a/net/core/dev_mcast.c
+++ b/net/core/dev_mcast.c
@@ -64,39 +64,6 @@
*/
/*
- * 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 @@
* 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 @@
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;