Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netlink: support dumping IPv4 multicast addresses

Extended RTM_GETMULTICAST to support dumping joined IPv4 multicast
addresses, in addition to the existing IPv6 functionality. This allows
userspace applications to retrieve both IPv4 and IPv6 multicast
addresses through similar netlink command and then monitor future
changes by registering to RTNLGRP_IPV4_MCADDR and RTNLGRP_IPV6_MCADDR.

Cc: Maciej Żenczykowski <maze@google.com>
Cc: Lorenzo Colitti <lorenzo@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Yuyang Huang <yuyanghuang@google.com>
Link: https://patch.msgid.link/20250207110836.2407224-1-yuyanghuang@google.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Yuyang Huang and committed by
Paolo Abeni
eb4e17a1 67800d29

+90 -18
+63 -14
net/ipv4/devinet.c
··· 46 46 #include <linux/notifier.h> 47 47 #include <linux/inetdevice.h> 48 48 #include <linux/igmp.h> 49 + #include "igmp_internal.h" 49 50 #include <linux/slab.h> 50 51 #include <linux/hash.h> 51 52 #ifdef CONFIG_SYSCTL ··· 106 105 [IFA_RT_PRIORITY] = { .type = NLA_U32 }, 107 106 [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 108 107 [IFA_PROTO] = { .type = NLA_U8 }, 109 - }; 110 - 111 - struct inet_fill_args { 112 - u32 portid; 113 - u32 seq; 114 - int event; 115 - unsigned int flags; 116 - int netnsid; 117 - int ifindex; 118 108 }; 119 109 120 110 #define IN4_ADDR_HSIZE_SHIFT 8 ··· 1838 1846 return 0; 1839 1847 } 1840 1848 1841 - static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb, 1842 - struct netlink_callback *cb, int *s_ip_idx, 1843 - struct inet_fill_args *fillargs) 1849 + static int in_dev_dump_ifmcaddr(struct in_device *in_dev, struct sk_buff *skb, 1850 + struct netlink_callback *cb, int *s_ip_idx, 1851 + struct inet_fill_args *fillargs) 1852 + { 1853 + struct ip_mc_list *im; 1854 + int ip_idx = 0; 1855 + int err; 1856 + 1857 + for (im = rcu_dereference(in_dev->mc_list); 1858 + im; 1859 + im = rcu_dereference(im->next_rcu)) { 1860 + if (ip_idx < *s_ip_idx) { 1861 + ip_idx++; 1862 + continue; 1863 + } 1864 + err = inet_fill_ifmcaddr(skb, in_dev->dev, im, fillargs); 1865 + if (err < 0) 1866 + goto done; 1867 + 1868 + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1869 + ip_idx++; 1870 + } 1871 + err = 0; 1872 + ip_idx = 0; 1873 + done: 1874 + *s_ip_idx = ip_idx; 1875 + return err; 1876 + } 1877 + 1878 + static int in_dev_dump_ifaddr(struct in_device *in_dev, struct sk_buff *skb, 1879 + struct netlink_callback *cb, int *s_ip_idx, 1880 + struct inet_fill_args *fillargs) 1844 1881 { 1845 1882 struct in_ifaddr *ifa; 1846 1883 int ip_idx = 0; ··· 1895 1874 return err; 1896 1875 } 1897 1876 1877 + static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb, 1878 + struct netlink_callback *cb, int *s_ip_idx, 1879 + struct inet_fill_args *fillargs) 1880 + { 1881 + switch (fillargs->event) { 1882 + case RTM_NEWADDR: 1883 + return in_dev_dump_ifaddr(in_dev, skb, cb, s_ip_idx, fillargs); 1884 + case RTM_GETMULTICAST: 1885 + return in_dev_dump_ifmcaddr(in_dev, skb, cb, s_ip_idx, 1886 + fillargs); 1887 + default: 1888 + return -EINVAL; 1889 + } 1890 + } 1891 + 1898 1892 /* Combine dev_addr_genid and dev_base_seq to detect changes. 1899 1893 */ 1900 1894 static u32 inet_base_seq(const struct net *net) ··· 1925 1889 return res; 1926 1890 } 1927 1891 1928 - static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 1892 + static int inet_dump_addr(struct sk_buff *skb, struct netlink_callback *cb, 1893 + int event) 1929 1894 { 1930 1895 const struct nlmsghdr *nlh = cb->nlh; 1931 1896 struct inet_fill_args fillargs = { 1932 1897 .portid = NETLINK_CB(cb->skb).portid, 1933 1898 .seq = nlh->nlmsg_seq, 1934 - .event = RTM_NEWADDR, 1899 + .event = event, 1935 1900 .flags = NLM_F_MULTI, 1936 1901 .netnsid = -1, 1937 1902 }; ··· 1984 1947 put_net(tgt_net); 1985 1948 rcu_read_unlock(); 1986 1949 return err; 1950 + } 1951 + 1952 + static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 1953 + { 1954 + return inet_dump_addr(skb, cb, RTM_NEWADDR); 1955 + } 1956 + 1957 + static int inet_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb) 1958 + { 1959 + return inet_dump_addr(skb, cb, RTM_GETMULTICAST); 1987 1960 } 1988 1961 1989 1962 static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, ··· 2892 2845 {.protocol = PF_INET, .msgtype = RTM_GETNETCONF, 2893 2846 .doit = inet_netconf_get_devconf, .dumpit = inet_netconf_dump_devconf, 2894 2847 .flags = RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED}, 2848 + {.owner = THIS_MODULE, .protocol = PF_INET, .msgtype = RTM_GETMULTICAST, 2849 + .dumpit = inet_dump_ifmcaddr, .flags = RTNL_FLAG_DUMP_UNLOCKED}, 2895 2850 }; 2896 2851 2897 2852 void __init devinet_init(void)
+10 -4
net/ipv4/igmp.c
··· 81 81 #include <linux/skbuff.h> 82 82 #include <linux/inetdevice.h> 83 83 #include <linux/igmp.h> 84 + #include "igmp_internal.h" 84 85 #include <linux/if_arp.h> 85 86 #include <linux/rtnetlink.h> 86 87 #include <linux/times.h> ··· 1433 1432 *mc_hash = im->next_hash; 1434 1433 } 1435 1434 1436 - static int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev, 1437 - const struct ip_mc_list *im, int event) 1435 + int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev, 1436 + const struct ip_mc_list *im, 1437 + struct inet_fill_args *args) 1438 1438 { 1439 1439 struct ifa_cacheinfo ci; 1440 1440 struct ifaddrmsg *ifm; 1441 1441 struct nlmsghdr *nlh; 1442 1442 1443 - nlh = nlmsg_put(skb, 0, 0, event, sizeof(struct ifaddrmsg), 0); 1443 + nlh = nlmsg_put(skb, args->portid, args->seq, args->event, 1444 + sizeof(struct ifaddrmsg), args->flags); 1444 1445 if (!nlh) 1445 1446 return -EMSGSIZE; 1446 1447 ··· 1471 1468 static void inet_ifmcaddr_notify(struct net_device *dev, 1472 1469 const struct ip_mc_list *im, int event) 1473 1470 { 1471 + struct inet_fill_args fillargs = { 1472 + .event = event, 1473 + }; 1474 1474 struct net *net = dev_net(dev); 1475 1475 struct sk_buff *skb; 1476 1476 int err = -ENOMEM; ··· 1485 1479 if (!skb) 1486 1480 goto error; 1487 1481 1488 - err = inet_fill_ifmcaddr(skb, dev, im, event); 1482 + err = inet_fill_ifmcaddr(skb, dev, im, &fillargs); 1489 1483 if (err < 0) { 1490 1484 WARN_ON_ONCE(err == -EMSGSIZE); 1491 1485 nlmsg_free(skb);
+17
net/ipv4/igmp_internal.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + #ifndef _LINUX_IGMP_INTERNAL_H 3 + #define _LINUX_IGMP_INTERNAL_H 4 + 5 + struct inet_fill_args { 6 + u32 portid; 7 + u32 seq; 8 + int event; 9 + unsigned int flags; 10 + int netnsid; 11 + int ifindex; 12 + }; 13 + 14 + int inet_fill_ifmcaddr(struct sk_buff *skb, struct net_device *dev, 15 + const struct ip_mc_list *im, 16 + struct inet_fill_args *args); 17 + #endif