[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