[PATCH] Netlink messages for multicast HW addr programming

Patrick Ruddy
2016-01-21T11:48:30Z

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 <pruddy@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