Skip to content

Commit 36fdf8d

Browse files
author
CKI Backport Bot
committed
netlink: add IGMP/MLD join/leave notifications
JIRA: https://issues.redhat.com/browse/RHEL-84540 commit 2c2b61d Author: Yuyang Huang <yuyanghuang@google.com> Date: Wed Dec 11 17:22:41 2024 +0900 netlink: add IGMP/MLD join/leave notifications This change introduces netlink notifications for multicast address changes. The following features are included: * Addition and deletion of multicast addresses are reported using RTM_NEWMULTICAST and RTM_DELMULTICAST messages with AF_INET and AF_INET6. * Two new notification groups: RTNLGRP_IPV4_MCADDR and RTNLGRP_IPV6_MCADDR are introduced for receiving these events. This change allows user space applications (e.g., ip monitor) to efficiently track multicast group memberships by listening for netlink events. Previously, applications relied on inefficient polling of procfs, introducing delays. With netlink notifications, applications receive realtime updates on multicast group membership changes, enabling more precise metrics collection and system monitoring.  This change also unlocks the potential for implementing a wide range of sophisticated multicast related features in user space by allowing applications to combine kernel provided multicast address information with user space data and communicate decisions back to the kernel for more fine grained control. This mechanism can be used for various purposes, including multicast filtering, IGMP/MLD offload, and IGMP/MLD snooping. Cc: Maciej Żenczykowski <maze@google.com> Cc: Lorenzo Colitti <lorenzo@google.com> Co-developed-by: Patrick Ruddy <pruddy@vyatta.att-mail.com> Signed-off-by: Patrick Ruddy <pruddy@vyatta.att-mail.com> Link: https://lore.kernel.org/r/20180906091056.21109-1-pruddy@vyatta.att-mail.com Signed-off-by: Yuyang Huang <yuyanghuang@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
1 parent 5fe1165 commit 36fdf8d

File tree

6 files changed

+144
-21
lines changed

6 files changed

+144
-21
lines changed

include/linux/igmp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ struct ip_mc_list {
8787
char loaded;
8888
unsigned char gsquery; /* check source marks? */
8989
unsigned char crcount;
90+
unsigned long mca_cstamp;
91+
unsigned long mca_tstamp;
9092
struct rcu_head rcu;
9193
};
9294

include/net/addrconf.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,23 @@ struct ifa6_config {
8888
u16 scope;
8989
};
9090

91+
enum addr_type_t {
92+
UNICAST_ADDR,
93+
MULTICAST_ADDR,
94+
ANYCAST_ADDR,
95+
};
96+
97+
struct inet6_fill_args {
98+
u32 portid;
99+
u32 seq;
100+
int event;
101+
unsigned int flags;
102+
int netnsid;
103+
int ifindex;
104+
enum addr_type_t type;
105+
bool force_rt_scope_universe;
106+
};
107+
91108
int addrconf_init(void);
92109
void addrconf_cleanup(void);
93110

@@ -525,4 +542,8 @@ int if6_proc_init(void);
525542
void if6_proc_exit(void);
526543
#endif
527544

545+
int inet6_fill_ifmcaddr(struct sk_buff *skb,
546+
const struct ifmcaddr6 *ifmca,
547+
struct inet6_fill_args *args);
548+
528549
#endif

include/uapi/linux/rtnetlink.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ enum {
9393
RTM_NEWPREFIX = 52,
9494
#define RTM_NEWPREFIX RTM_NEWPREFIX
9595

96-
RTM_GETMULTICAST = 58,
96+
RTM_NEWMULTICAST = 56,
97+
#define RTM_NEWMULTICAST RTM_NEWMULTICAST
98+
RTM_DELMULTICAST,
99+
#define RTM_DELMULTICAST RTM_DELMULTICAST
100+
RTM_GETMULTICAST,
97101
#define RTM_GETMULTICAST RTM_GETMULTICAST
98102

99103
RTM_GETANYCAST = 62,
@@ -774,6 +778,10 @@ enum rtnetlink_groups {
774778
#define RTNLGRP_TUNNEL RTNLGRP_TUNNEL
775779
RTNLGRP_STATS,
776780
#define RTNLGRP_STATS RTNLGRP_STATS
781+
RTNLGRP_IPV4_MCADDR,
782+
#define RTNLGRP_IPV4_MCADDR RTNLGRP_IPV4_MCADDR
783+
RTNLGRP_IPV6_MCADDR,
784+
#define RTNLGRP_IPV6_MCADDR RTNLGRP_IPV6_MCADDR
777785
__RTNLGRP_MAX
778786
};
779787
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)

net/ipv4/igmp.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
#include <linux/byteorder/generic.h>
8989

9090
#include <net/net_namespace.h>
91+
#include <net/netlink.h>
92+
#include <net/addrconf.h>
9193
#include <net/arp.h>
9294
#include <net/ip.h>
9395
#include <net/protocol.h>
@@ -1430,6 +1432,63 @@ static void ip_mc_hash_remove(struct in_device *in_dev,
14301432
*mc_hash = im->next_hash;
14311433
}
14321434

1435+
static int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev,
1436+
const struct ip_mc_list *im, int event)
1437+
{
1438+
struct ifa_cacheinfo ci;
1439+
struct ifaddrmsg *ifm;
1440+
struct nlmsghdr *nlh;
1441+
1442+
nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct ifaddrmsg), 0);
1443+
if (!nlh)
1444+
return -EMSGSIZE;
1445+
1446+
ifm = nlmsg_data(nlh);
1447+
ifm->ifa_family = AF_INET;
1448+
ifm->ifa_prefixlen = 32;
1449+
ifm->ifa_flags = IFA_F_PERMANENT;
1450+
ifm->ifa_scope = RT_SCOPE_UNIVERSE;
1451+
ifm->ifa_index = dev->ifindex;
1452+
1453+
ci.cstamp = (READ_ONCE(im->mca_cstamp) - INITIAL_JIFFIES) * 100UL / HZ;
1454+
ci.tstamp = ci.cstamp;
1455+
ci.ifa_prefered = INFINITY_LIFE_TIME;
1456+
ci.ifa_valid = INFINITY_LIFE_TIME;
1457+
1458+
if (nla_put_in_addr(skb, IFA_MULTICAST, im->multiaddr) < 0 ||
1459+
nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci) < 0) {
1460+
nlmsg_cancel(skb, nlh);
1461+
return -EMSGSIZE;
1462+
}
1463+
1464+
nlmsg_end(skb, nlh);
1465+
return 0;
1466+
}
1467+
1468+
static void inet_ifmcaddr_notify(struct net_device *dev,
1469+
const struct ip_mc_list *im, int event)
1470+
{
1471+
struct net *net = dev_net(dev);
1472+
struct sk_buff *skb;
1473+
int err = -ENOMEM;
1474+
1475+
skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
1476+
nla_total_size(sizeof(__be32)), GFP_ATOMIC);
1477+
if (!skb)
1478+
goto error;
1479+
1480+
err = inet_fill_ifmcaddr(skb, dev, im, event);
1481+
if (err < 0) {
1482+
WARN_ON_ONCE(err == -EMSGSIZE);
1483+
nlmsg_free(skb);
1484+
goto error;
1485+
}
1486+
1487+
rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MCADDR, NULL, GFP_ATOMIC);
1488+
return;
1489+
error:
1490+
rtnl_set_sk_err(net, RTNLGRP_IPV4_MCADDR, err);
1491+
}
14331492

14341493
/*
14351494
* A socket has joined a multicast group on device dev.
@@ -1457,6 +1516,8 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
14571516
im->interface = in_dev;
14581517
in_dev_hold(in_dev);
14591518
im->multiaddr = addr;
1519+
im->mca_cstamp = jiffies;
1520+
im->mca_tstamp = im->mca_cstamp;
14601521
/* initial mode is (EX, empty) */
14611522
im->sfmode = mode;
14621523
im->sfcount[mode] = 1;
@@ -1476,6 +1537,7 @@ static void ____ip_mc_inc_group(struct in_device *in_dev, __be32 addr,
14761537
igmpv3_del_delrec(in_dev, im);
14771538
#endif
14781539
igmp_group_added(im);
1540+
inet_ifmcaddr_notify(in_dev->dev, im, RTM_NEWMULTICAST);
14791541
if (!in_dev->dead)
14801542
ip_rt_multicast_event(in_dev);
14811543
out:
@@ -1689,6 +1751,8 @@ void __ip_mc_dec_group(struct in_device *in_dev, __be32 addr, gfp_t gfp)
16891751
*ip = i->next_rcu;
16901752
in_dev->mc_count--;
16911753
__igmp_group_dropped(i, gfp);
1754+
inet_ifmcaddr_notify(in_dev->dev, i,
1755+
RTM_DELMULTICAST);
16921756
ip_mc_clear_src(i);
16931757

16941758
if (!in_dev->dead)

net/ipv6/addrconf.c

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5130,22 +5130,6 @@ static inline int inet6_ifaddr_msgsize(void)
51305130
+ nla_total_size(4) /* IFA_RT_PRIORITY */;
51315131
}
51325132

5133-
enum addr_type_t {
5134-
UNICAST_ADDR,
5135-
MULTICAST_ADDR,
5136-
ANYCAST_ADDR,
5137-
};
5138-
5139-
struct inet6_fill_args {
5140-
u32 portid;
5141-
u32 seq;
5142-
int event;
5143-
unsigned int flags;
5144-
int netnsid;
5145-
int ifindex;
5146-
enum addr_type_t type;
5147-
};
5148-
51495133
static int inet6_fill_ifaddr(struct sk_buff *skb,
51505134
const struct inet6_ifaddr *ifa,
51515135
struct inet6_fill_args *args)
@@ -5224,15 +5208,16 @@ static int inet6_fill_ifaddr(struct sk_buff *skb,
52245208
return -EMSGSIZE;
52255209
}
52265210

5227-
static int inet6_fill_ifmcaddr(struct sk_buff *skb,
5228-
const struct ifmcaddr6 *ifmca,
5229-
struct inet6_fill_args *args)
5211+
int inet6_fill_ifmcaddr(struct sk_buff *skb,
5212+
const struct ifmcaddr6 *ifmca,
5213+
struct inet6_fill_args *args)
52305214
{
52315215
int ifindex = ifmca->idev->dev->ifindex;
52325216
u8 scope = RT_SCOPE_UNIVERSE;
52335217
struct nlmsghdr *nlh;
52345218

5235-
if (ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE)
5219+
if (!args->force_rt_scope_universe &&
5220+
ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE)
52365221
scope = RT_SCOPE_SITE;
52375222

52385223
nlh = nlmsg_put(skb, args->portid, args->seq, args->event,
@@ -5257,6 +5242,7 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb,
52575242
nlmsg_end(skb, nlh);
52585243
return 0;
52595244
}
5245+
EXPORT_SYMBOL(inet6_fill_ifmcaddr);
52605246

52615247
static int inet6_fill_ifacaddr(struct sk_buff *skb,
52625248
const struct ifacaddr6 *ifaca,
@@ -5421,6 +5407,7 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
54215407
.flags = NLM_F_MULTI,
54225408
.netnsid = -1,
54235409
.type = type,
5410+
.force_rt_scope_universe = false,
54245411
};
54255412
struct {
54265413
unsigned long ifindex;
@@ -5549,6 +5536,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh,
55495536
.event = RTM_NEWADDR,
55505537
.flags = 0,
55515538
.netnsid = -1,
5539+
.force_rt_scope_universe = false,
55525540
};
55535541
struct ifaddrmsg *ifm;
55545542
struct nlattr *tb[IFA_MAX+1];
@@ -5620,6 +5608,7 @@ static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
56205608
.event = event,
56215609
.flags = 0,
56225610
.netnsid = -1,
5611+
.force_rt_scope_universe = false,
56235612
};
56245613
int err = -ENOBUFS;
56255614

net/ipv6/mcast.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@
3333
#include <linux/in.h>
3434
#include <linux/in6.h>
3535
#include <linux/netdevice.h>
36+
#include <linux/if_addr.h>
3637
#include <linux/if_arp.h>
3738
#include <linux/route.h>
39+
#include <linux/rtnetlink.h>
3840
#include <linux/init.h>
3941
#include <linux/proc_fs.h>
4042
#include <linux/seq_file.h>
@@ -47,6 +49,7 @@
4749
#include <linux/netfilter_ipv6.h>
4850

4951
#include <net/net_namespace.h>
52+
#include <net/netlink.h>
5053
#include <net/sock.h>
5154
#include <net/snmp.h>
5255

@@ -901,6 +904,39 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
901904
return mc;
902905
}
903906

907+
static void inet6_ifmcaddr_notify(struct net_device *dev,
908+
const struct ifmcaddr6 *ifmca, int event)
909+
{
910+
struct inet6_fill_args fillargs = {
911+
.portid = 0,
912+
.seq = 0,
913+
.event = event,
914+
.flags = 0,
915+
.netnsid = -1,
916+
.force_rt_scope_universe = true,
917+
};
918+
struct net *net = dev_net(dev);
919+
struct sk_buff *skb;
920+
int err = -ENOMEM;
921+
922+
skb = nlmsg_new(NLMSG_ALIGN(sizeof(struct ifaddrmsg)) +
923+
nla_total_size(16), GFP_ATOMIC);
924+
if (!skb)
925+
goto error;
926+
927+
err = inet6_fill_ifmcaddr(skb, ifmca, &fillargs);
928+
if (err < 0) {
929+
WARN_ON_ONCE(err == -EMSGSIZE);
930+
nlmsg_free(skb);
931+
goto error;
932+
}
933+
934+
rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MCADDR, NULL, GFP_ATOMIC);
935+
return;
936+
error:
937+
rtnl_set_sk_err(net, RTNLGRP_IPV6_MCADDR, err);
938+
}
939+
904940
/*
905941
* device multicast group inc (add if not found)
906942
*/
@@ -948,6 +984,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,
948984

949985
mld_del_delrec(idev, mc);
950986
igmp6_group_added(mc);
987+
inet6_ifmcaddr_notify(dev, mc, RTM_NEWMULTICAST);
951988
mutex_unlock(&idev->mc_lock);
952989
ma_put(mc);
953990
return 0;
@@ -977,6 +1014,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
9771014
*map = ma->next;
9781015

9791016
igmp6_group_dropped(ma);
1017+
inet6_ifmcaddr_notify(idev->dev, ma,
1018+
RTM_DELMULTICAST);
9801019
ip6_mc_clear_src(ma);
9811020
mutex_unlock(&idev->mc_lock);
9821021

0 commit comments

Comments
 (0)