[NETDEV]: Secondary unicast address support

Add support for configuring secondary unicast addresses on network devices.
Devices supporting this feature need to change their set_multicast_list
function to configure unicast filters as well and assign it to
dev->set_address_list instead of dev->set_multicast_list. Devices not
supporting this feature are put in promiscous mode when secondary unicast
addresses are present.

Signed-off-by: Patrick McHardy <[EMAIL PROTECTED]>

---
commit 3f3f6e18b902ee177ecf5a108ba6ecbf1b5c9ba3
tree 8883aba620211e96d7419f96960cc596506cbeef
parent 890e2ae4ef5599ee34f280af4882f97c2dcfcb7b
author Patrick McHardy <[EMAIL PROTECTED]> Wed, 20 Jun 2007 19:44:11 +0200
committer Patrick McHardy <[EMAIL PROTECTED]> Wed, 20 Jun 2007 19:44:11 +0200

 include/linux/netdevice.h |   17 ++++
 net/core/dev.c            |  172 +++++++++++++++++++++++++++++++++++++++++++--
 net/core/dev_mcast.c      |   34 +--------
 3 files changed, 185 insertions(+), 38 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 868140d..a1cc2ea 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -191,6 +191,14 @@ struct dev_mc_list
        int                     dmi_gusers;
 };
 
+struct dev_uc_list
+{
+       struct dev_uc_list      *next;
+       __u8                    duci_addr[MAX_ADDR_LEN];
+       unsigned char           duci_addrlen;
+       int                     duci_users;
+};
+
 struct hh_cache
 {
        struct hh_cache *hh_next;       /* Next entry                        */
@@ -389,7 +397,10 @@ struct net_device
        unsigned short          dev_id;         /* for shared network cards */
 
        struct dev_mc_list      *mc_list;       /* Multicast mac addresses      
*/
+       struct dev_uc_list      *uc_list;       /* Secondary unicast mac 
addresses */
        int                     mc_count;       /* Number of installed mcasts   
*/
+       int                     uc_count;       /* Number of installed ucasts   
*/
+       int                     uc_promisc;
        int                     promiscuity;
        int                     allmulti;
 
@@ -493,6 +504,8 @@ struct net_device
                                                void *saddr,
                                                unsigned len);
        int                     (*rebuild_header)(struct sk_buff *skb);
+#define HAVE_ADDRESS_LIST
+       void                    (*set_address_list)(struct net_device *dev);
 #define HAVE_MULTICAST                  
        void                    (*set_multicast_list)(struct net_device *dev);
 #define HAVE_SET_MAC_ADDR               
@@ -1006,6 +1019,10 @@ extern void              dev_mc_upload(struct net_device 
*dev);
 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);
+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 void            __dev_address_upload(struct net_device *dev);
+extern void            dev_address_upload(struct net_device *dev);
 extern void            dev_set_promiscuity(struct net_device *dev, int inc);
 extern void            dev_set_allmulti(struct net_device *dev, int inc);
 extern void            netdev_state_change(struct net_device *dev);
diff --git a/net/core/dev.c b/net/core/dev.c
index 5974e5b..4f4beb0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -943,7 +943,7 @@ int dev_open(struct net_device *dev)
                /*
                 *      Initialize multicasting status
                 */
-               dev_mc_upload(dev);
+               dev_address_upload(dev);
 
                /*
                 *      Wakeup transmit queue engine
@@ -2522,6 +2522,163 @@ int netdev_set_master(struct net_device *slave, struct 
net_device *master)
        return 0;
 }
 
+void __dev_address_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;
+
+       if (!netif_device_present(dev))
+               return;
+
+       if (dev->set_address_list)
+               dev->set_address_list(dev);
+       else {
+               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);
+       }
+}
+
+/**
+ *     dev_address_upload      - upload address lists to device
+ *     @dev: device
+ *
+ *     Upload unicast and multicast address lists to device.
+ *     When the device doesn't support unicast filtering it
+ *     is put in promiscous mode while addresses are present.
+ *
+ */
+void dev_address_upload(struct net_device *dev)
+{
+       netif_tx_lock_bh(dev);
+       __dev_address_upload(dev);
+       netif_tx_unlock_bh(dev);
+}
+
+/**
+ *     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.
+ *
+ */
+int dev_unicast_delete(struct net_device *dev, void *addr, int alen)
+{
+       int err = 0;
+       struct dev_uc_list *duci, **ducip;
+
+       netif_tx_lock_bh(dev);
+
+       for (ducip = &dev->uc_list; (duci = *ducip) != NULL;
+            ducip = &duci->next) {
+               /*
+                *      Find the entry we want to delete. The device could
+                *      have variable length entries so check these too.
+                */
+               if (memcmp(duci->duci_addr, addr, duci->duci_addrlen) == 0 &&
+                   alen == duci->duci_addrlen) {
+                       if (--duci->duci_users)
+                               goto done;
+
+                       /*
+                        *      Last user. So delete the entry.
+                        */
+                       *ducip = duci->next;
+                       dev->uc_count--;
+
+                       kfree(duci);
+
+                       /*
+                        *      We have altered the list, so the card
+                        *      loaded filter is now wrong. Fix it
+                        */
+                       __dev_address_upload(dev);
+
+                       netif_tx_unlock_bh(dev);
+                       return 0;
+               }
+       }
+       err = -ENOENT;
+done:
+       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.
+ *
+ */
+int dev_unicast_add(struct net_device *dev, void *addr, int alen)
+{
+       int err = 0;
+       struct dev_uc_list *duci, *duci1;
+
+       duci1 = kmalloc(sizeof(*duci), GFP_ATOMIC);
+
+       netif_tx_lock_bh(dev);
+       for (duci = dev->uc_list; duci != NULL; duci = duci->next) {
+               if (memcmp(duci->duci_addr, addr, duci->duci_addrlen) == 0 &&
+                   duci->duci_addrlen == alen) {
+                       duci->duci_users++;
+                       goto done;
+               }
+       }
+
+       if ((duci = duci1) == NULL) {
+               netif_tx_unlock_bh(dev);
+               return -ENOMEM;
+       }
+       memcpy(duci->duci_addr, addr, alen);
+       duci->duci_addrlen = alen;
+       duci->next = dev->uc_list;
+       duci->duci_users = 1;
+       dev->uc_list = duci;
+       dev->uc_count++;
+
+       __dev_address_upload(dev);
+
+       netif_tx_unlock_bh(dev);
+       return 0;
+
+done:
+       netif_tx_unlock_bh(dev);
+       kfree(duci1);
+       return err;
+}
+EXPORT_SYMBOL(dev_unicast_add);
+
+void dev_unicast_discard(struct net_device *dev)
+{
+       netif_tx_lock_bh(dev);
+
+       while (dev->uc_list != NULL) {
+               struct dev_uc_list *tmp = dev->uc_list;
+               dev->uc_list = tmp->next;
+               kfree(tmp);
+       }
+       dev->uc_count = 0;
+
+       netif_tx_unlock_bh(dev);
+}
+
 /**
  *     dev_set_promiscuity     - update promiscuity count on a device
  *     @dev: device
@@ -2541,7 +2698,7 @@ void dev_set_promiscuity(struct net_device *dev, int inc)
        else
                dev->flags |= IFF_PROMISC;
        if (dev->flags != old_flags) {
-               dev_mc_upload(dev);
+               dev_address_upload(dev);
                printk(KERN_INFO "device %s %s promiscuous mode\n",
                       dev->name, (dev->flags & IFF_PROMISC) ? "entered" :
                                                               "left");
@@ -2574,7 +2731,7 @@ 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_address_upload(dev);
 }
 
 unsigned dev_get_flags(const struct net_device *dev)
@@ -2617,10 +2774,10 @@ int dev_change_flags(struct net_device *dev, unsigned 
flags)
                                    IFF_ALLMULTI));
 
        /*
-        *      Load in the correct multicast list now the flags have changed.
+        *      Load in the correct address list now the flags have changed.
         */
 
-       dev_mc_upload(dev);
+       dev_address_upload(dev);
 
        /*
         *      Have we downed the interface. We handle IFF_UP ourselves
@@ -2633,7 +2790,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_address_upload(dev);
        }
 
        if (dev->flags & IFF_UP &&
@@ -3497,8 +3654,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)
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c
index 5a54053..45d616b 100644
--- a/net/core/dev_mcast.c
+++ b/net/core/dev_mcast.c
@@ -63,37 +63,9 @@
  *     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);
+       dev_address_upload(dev);
 }
 
 /*
@@ -135,7 +107,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int 
alen, int glbl)
                         *      We have altered the list, so the card
                         *      loaded filter is now wrong. Fix it
                         */
-                       __dev_mc_upload(dev);
+                       __dev_address_upload(dev);
 
                        netif_tx_unlock_bh(dev);
                        return 0;
@@ -185,7 +157,7 @@ int dev_mc_add(struct net_device *dev, void *addr, int 
alen, int glbl)
        dev->mc_list = dmi;
        dev->mc_count++;
 
-       __dev_mc_upload(dev);
+       __dev_address_upload(dev);
 
        netif_tx_unlock_bh(dev);
        return 0;
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to