From mboxrd@z Thu Jan 1 00:00:00 1970 From: Patrick Ruddy Subject: [PATCH net-next v3 2/2] netlink: ipv6 MLD join notifications Date: Thu, 6 Sep 2018 10:10:56 +0100 Message-ID: <20180906091056.21109-2-pruddy@vyatta.att-mail.com> References: <20180830093545.29465-2-pruddy@vyatta.att-mail.com> <20180906091056.21109-1-pruddy@vyatta.att-mail.com> Cc: roopa@cumulusnetworks.com, jiri@resnulli.us, stephen@networkplumber.org To: netdev@vger.kernel.org Return-path: Received: from mx0a-00191d01.pphosted.com ([67.231.149.140]:60420 "EHLO mx0a-00191d01.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727817AbeIFNpu (ORCPT ); Thu, 6 Sep 2018 09:45:50 -0400 In-Reply-To: <20180906091056.21109-1-pruddy@vyatta.att-mail.com> Sender: netdev-owner@vger.kernel.org List-ID: Some userspace applications need to know about MLD joins from the kernel for 2 reasons: 1. To allow the programming of multicast MAC filters in hardware 2. To form a multicast FORUS list for non link-local multicast groups to be sent to the kernel and from there to the interested party. (1) can be fulfilled but simply sending the hardware multicast MAC address to be programmed but (2) requires the L3 address to be sent since this cannot be constructed from the MAC address whereas the reverse translation is a standard library function. This commit provides addition and deletion of multicast addresses using the RTM_NEWMDB and RTM_DELMDB messages with AF_INET6. It also provides the RTM_GETMDB extension to allow multicast join state to be read from the kernel. Signed-off-by: Patrick Ruddy --- v3 rework to use RTM_***MDB messages as per review comments. net/ipv6/addrconf.c | 34 ++++++++++++++++------- net/ipv6/mcast.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d51a8c0b3372..d23955c21650 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4860,6 +4860,8 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, struct nlmsghdr *nlh; u8 scope = RT_SCOPE_UNIVERSE; int ifindex = ifmca->idev->dev->ifindex; + int addr_type = (event == RTM_GETMULTICAST) ? IFA_MULTICAST : + IFA_ADDRESS; if (ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE) scope = RT_SCOPE_SITE; @@ -4869,7 +4871,7 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca, return -EMSGSIZE; put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex); - if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 || + if (nla_put_in6_addr(skb, addr_type, &ifmca->mca_addr) < 0 || put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) { nlmsg_cancel(skb, nlh); @@ -4916,7 +4918,7 @@ enum addr_type_t { /* called with rcu_read_lock() */ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, struct netlink_callback *cb, enum addr_type_t type, - int s_ip_idx, int *p_ip_idx) + int s_ip_idx, int *p_ip_idx, int msg_type) { struct ifmcaddr6 *ifmca; struct ifacaddr6 *ifaca; @@ -4935,7 +4937,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, - RTM_NEWADDR, + msg_type, NLM_F_MULTI); if (err < 0) break; @@ -4952,7 +4954,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, err = inet6_fill_ifmcaddr(skb, ifmca, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, - RTM_GETMULTICAST, + msg_type, NLM_F_MULTI); if (err < 0) break; @@ -4967,7 +4969,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, err = inet6_fill_ifacaddr(skb, ifaca, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, - RTM_GETANYCAST, + msg_type, NLM_F_MULTI); if (err < 0) break; @@ -4982,7 +4984,7 @@ static int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb, } static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, - enum addr_type_t type) + enum addr_type_t type, int msg_type) { struct net *net = sock_net(skb->sk); int h, s_h; @@ -5012,7 +5014,7 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, goto cont; if (in6_dump_addrs(idev, skb, cb, type, - s_ip_idx, &ip_idx) < 0) + s_ip_idx, &ip_idx, msg_type) < 0) goto done; cont: idx++; @@ -5031,14 +5033,22 @@ static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) { enum addr_type_t type = UNICAST_ADDR; - return inet6_dump_addr(skb, cb, type); + return inet6_dump_addr(skb, cb, type, RTM_NEWADDR); } static int inet6_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb) { enum addr_type_t type = MULTICAST_ADDR; - return inet6_dump_addr(skb, cb, type); + return inet6_dump_addr(skb, cb, type, RTM_GETMULTICAST); +} + +static int inet6_mdb_dump_ifmcaddr(struct sk_buff *skb, + struct netlink_callback *cb) +{ + enum addr_type_t type = MULTICAST_ADDR; + + return inet6_dump_addr(skb, cb, type, RTM_NEWMDB); } @@ -5046,7 +5056,7 @@ static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb) { enum addr_type_t type = ANYCAST_ADDR; - return inet6_dump_addr(skb, cb, type); + return inet6_dump_addr(skb, cb, type, RTM_GETANYCAST); } static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh, @@ -6774,6 +6784,10 @@ int __init addrconf_init(void) NULL, inet6_dump_ifmcaddr, 0); if (err < 0) goto errout; + err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETMDB, + NULL, inet6_mdb_dump_ifmcaddr, 0); + if (err < 0) + goto errout; err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETANYCAST, NULL, inet6_dump_ifacaddr, 0); if (err < 0) diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 4ae54aaca373..5108dbf73516 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -880,6 +880,67 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev, return mc; } +static int fill_addr(struct sk_buff *skb, struct net_device *dev, + const struct in6_addr *addr, int type, unsigned int flags) +{ + struct nlmsghdr *nlh; + struct ifaddrmsg *ifm; + u8 scope = RT_SCOPE_UNIVERSE; + + if (ipv6_addr_scope(addr) & IFA_SITE) + scope = RT_SCOPE_SITE; + + nlh = nlmsg_put(skb, 0, 0, type, sizeof(*ifm), flags); + if (!nlh) + return -EMSGSIZE; + + ifm = nlmsg_data(nlh); + ifm->ifa_family = AF_INET6; + ifm->ifa_prefixlen = 128; + ifm->ifa_flags = IFA_F_PERMANENT; + ifm->ifa_scope = scope; + ifm->ifa_index = dev->ifindex; + + if (nla_put_in6_addr(skb, IFA_ADDRESS, 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(sizeof(struct in6_addr)); +} + +static void ipv6_mc_addr_notify(struct net_device *dev, + const struct in6_addr *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) + goto errout; + + err = fill_addr(skb, dev, addr, type, 0); + if (err < 0) { + WARN_ON(err == -EMSGSIZE); + kfree_skb(skb); + goto errout; + } + rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC); + return; +errout: + if (err < 0) + rtnl_set_sk_err(net, RTNLGRP_MDB, err); +} + /* * device multicast group inc (add if not found) */ @@ -932,6 +993,9 @@ static int __ipv6_dev_mc_inc(struct net_device *dev, mld_del_delrec(idev, mc); igmp6_group_added(mc); + + ipv6_mc_addr_notify(dev, addr, RTM_NEWMDB); + ma_put(mc); return 0; } @@ -960,6 +1024,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) igmp6_group_dropped(ma); ip6_mc_clear_src(ma); + ipv6_mc_addr_notify(idev->dev, addr, + RTM_DELMDB); ma_put(ma); return 0; } -- 2.17.1