Add RTM_NEWADDR and RTM_DELADDR netlink messages to indicate
interest in specific multicast hardware addresses. These messages
are sent when addressed are added or deleted from the appropriate
interface driver.

Signed-off-by: Patrick Ruddy <pru...@brocade.com>

diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index c0548d2..a0ebadd 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -12,6 +12,7 @@
  */
 
 #include <linux/netdevice.h>
+#include <net/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/export.h>
 #include <linux/list.h>
@@ -661,6 +662,62 @@ out:
 }
 EXPORT_SYMBOL(dev_mc_add_excl);
 
+static int fill_addr(struct sk_buff *skb, struct net_device *dev,
+                    const unsigned char *addr, int type)
+{
+       struct nlmsghdr *nlh;
+       struct ifaddrmsg *ifm;
+
+       nlh = nlmsg_put(skb, 0, 0, type, sizeof(*ifm), 0);
+       if (nlh == NULL)
+               return -EMSGSIZE;
+
+       ifm = nlmsg_data(nlh);
+       ifm->ifa_family = AF_UNSPEC;
+       ifm->ifa_prefixlen = 0;
+       ifm->ifa_flags = IFA_F_PERMANENT;
+       ifm->ifa_scope = RT_SCOPE_LINK;
+       ifm->ifa_index = dev->ifindex;
+       if (nla_put(skb, IFA_ADDRESS, dev->addr_len, addr))
+               goto nla_put_failure;
+       nlmsg_end(skb, nlh);
+       return 0;
+
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+       return -EMSGSIZE;
+}
+
+static inline size_t addr_nlmsg_size(void)
+{
+       return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+               + nla_total_size(MAX_ADDR_LEN);
+}
+
+static void mc_addr_notify(struct net_device *dev, const unsigned char * addr,
+                          int type)
+{
+       struct net *net = dev_net(dev);
+       struct sk_buff *skb;
+       int err = -ENOBUFS;
+
+       skb = nlmsg_new(addr_nlmsg_size(), GFP_ATOMIC);
+       if (skb == NULL)
+               goto errout;
+
+       err = fill_addr(skb, dev, addr, type);
+       if (err < 0) {
+               WARN_ON(err == -EMSGSIZE);
+               kfree_skb(skb);
+               goto errout;
+       }
+       rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+       return;
+errout:
+       if (err < 0)
+               rtnl_set_sk_err(net, RTNLGRP_LINK, err);
+}
+
 static int __dev_mc_add(struct net_device *dev, const unsigned char *addr,
                        bool global)
 {
@@ -669,8 +726,10 @@ static int __dev_mc_add(struct net_device *dev, const 
unsigned char *addr,
        netif_addr_lock_bh(dev);
        err = __hw_addr_add_ex(&dev->mc, addr, dev->addr_len,
                               NETDEV_HW_ADDR_T_MULTICAST, global, false, 0);
-       if (!err)
+       if (!err) {
                __dev_set_rx_mode(dev);
+               mc_addr_notify(dev, addr, RTM_NEWADDR);
+       }
        netif_addr_unlock_bh(dev);
        return err;
 }
@@ -709,8 +768,10 @@ static int __dev_mc_del(struct net_device *dev, const 
unsigned char *addr,
        netif_addr_lock_bh(dev);
        err = __hw_addr_del_ex(&dev->mc, addr, dev->addr_len,
                               NETDEV_HW_ADDR_T_MULTICAST, global, false);
-       if (!err)
+       if (!err) {
                __dev_set_rx_mode(dev);
+               mc_addr_notify(dev, addr, RTM_DELADDR);
+       }
        netif_addr_unlock_bh(dev);
        return err;
 }
-- 
2.1.4

Reply via email to