* [PATCH iproute2-next] ipmaddr: use RTM_GETMULTICAST to list multicast addresses
@ 2026-07-11 3:07 Yuyang Huang
0 siblings, 0 replies; only message in thread
From: Yuyang Huang @ 2026-07-11 3:07 UTC (permalink / raw)
To: Yuyang Huang; +Cc: David Ahern, netdev
Replace /proc/net/igmp and /proc/net/igmp6 parsing in "ip maddr show"
with RTM_GETMULTICAST dumps. The kernel dumps IPv6 multicast addresses
via netlink since the beginning, IPv4 since v6.15 (eb4e17a1d915), and
reports the group users count via IFA_MC_USERS since kernel commits
7cb8198761e6 and e1d0f3f08391.
The netlink result is only used when it carries the same information
as procfs: if the dump fails (e.g. no IPv4 dump support) or any entry
lacks IFA_MC_USERS, the result is discarded and the procfs parsers
run as before, so output is unchanged on older kernels.
Link-layer multicast addresses are still read from
/proc/net/dev_mcast as there is no netlink API for them.
When a device is given, its ifindex is passed in the dump request so
strict-check kernels filter the dump server side; received entries
are checked against the ifindex again for kernels that ignore the
request field. An unknown device keeps printing an empty list.
Signed-off-by: Yuyang Huang <sigefriedhyy@gmail.com>
---
include/libnetlink.h | 3 ++
ip/ipmaddr.c | 110 +++++++++++++++++++++++++++++++++++++++++--
lib/libnetlink.c | 26 ++++++++++
3 files changed, 135 insertions(+), 4 deletions(-)
diff --git a/include/libnetlink.h b/include/libnetlink.h
index e91505d9..518b8714 100644
--- a/include/libnetlink.h
+++ b/include/libnetlink.h
@@ -62,6 +62,9 @@ typedef int (*req_filter_fn_t)(struct nlmsghdr *nlh, int reqlen);
int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
req_filter_fn_t filter_fn)
__attribute__((warn_unused_result));
+int rtnl_mcaddrdump_req(struct rtnl_handle *rth, int family,
+ req_filter_fn_t filter_fn)
+ __attribute__((warn_unused_result));
int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
__attribute__((warn_unused_result));
int rtnl_routedump_req(struct rtnl_handle *rth, int family,
diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c
index 462b409e..7d8a3a09 100644
--- a/ip/ipmaddr.c
+++ b/ip/ipmaddr.c
@@ -27,6 +27,7 @@
static struct {
char *dev;
+ int index;
int family;
} filter;
@@ -207,6 +208,94 @@ static void read_igmp6(struct ma_info **result_p)
fclose(fp);
}
+struct maddr_dump_ctx {
+ struct ma_info *list;
+ bool mc_users_missing;
+};
+
+static int maddr_dump_filter(struct nlmsghdr *nlh, int reqlen)
+{
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+
+ ifm->ifa_index = filter.index;
+
+ return 0;
+}
+
+static int accept_maddr(struct nlmsghdr *n, void *arg)
+{
+ struct maddr_dump_ctx *ctx = arg;
+ struct ifaddrmsg *ifm = NLMSG_DATA(n);
+ int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifm));
+ struct rtattr *tb[IFA_MAX + 1];
+ struct ma_info *ma;
+
+ if (n->nlmsg_type != RTM_GETMULTICAST &&
+ n->nlmsg_type != RTM_NEWMULTICAST)
+ return 0;
+
+ if (len < 0)
+ return -1;
+
+ if (filter.index && filter.index != ifm->ifa_index)
+ return 0;
+
+ parse_rtattr(tb, IFA_MAX, IFA_RTA(ifm), len);
+
+ if (!tb[IFA_MULTICAST] ||
+ RTA_PAYLOAD(tb[IFA_MULTICAST]) > sizeof(ma->addr.data))
+ return 0;
+
+ if (!tb[IFA_MC_USERS]) {
+ ctx->mc_users_missing = true;
+ return 0;
+ }
+
+ ma = calloc(1, sizeof(*ma));
+ if (ma == NULL)
+ return -1;
+
+ ma->index = ifm->ifa_index;
+ strlcpy(ma->name, ll_index_to_name(ifm->ifa_index), sizeof(ma->name));
+ ma->addr.family = ifm->ifa_family;
+ ma->addr.bytelen = RTA_PAYLOAD(tb[IFA_MULTICAST]);
+ ma->addr.bitlen = ma->addr.bytelen << 3;
+ memcpy(ma->addr.data, RTA_DATA(tb[IFA_MULTICAST]), ma->addr.bytelen);
+ ma->users = rta_getattr_u32(tb[IFA_MC_USERS]);
+ maddr_ins(&ctx->list, ma);
+
+ return 0;
+}
+
+static int read_maddr_netlink(int family, struct ma_info **result_p)
+{
+ struct maddr_dump_ctx ctx = {};
+ struct ma_info *ma;
+ int err;
+
+ rth.flags |= RTNL_HANDLE_F_SUPPRESS_NLERR;
+ err = rtnl_mcaddrdump_req(&rth, family,
+ filter.index ? maddr_dump_filter : NULL);
+ if (err >= 0)
+ err = rtnl_dump_filter(&rth, accept_maddr, &ctx);
+ rth.flags &= ~RTNL_HANDLE_F_SUPPRESS_NLERR;
+
+ /* Kernels that dump multicast addresses but do not report the
+ * users count via IFA_MC_USERS cannot replace procfs.
+ */
+ if (err < 0 || ctx.mc_users_missing) {
+ maddr_clear(ctx.list);
+ return -1;
+ }
+
+ while ((ma = ctx.list) != NULL) {
+ ctx.list = ma->next;
+ maddr_ins(result_p, ma);
+ }
+
+ return 0;
+}
+
static void print_maddr(FILE *fp, struct ma_info *list)
{
print_string(PRINT_FP, NULL, "\t", NULL);
@@ -291,12 +380,25 @@ static int multiaddr_list(int argc, char **argv)
argv++; argc--;
}
+ if (filter.dev) {
+ filter.index = ll_name_to_index(filter.dev);
+ /* an unknown device has no multicast addresses */
+ if (!filter.index) {
+ print_mlist(stdout, NULL);
+ return 0;
+ }
+ }
+
if (!filter.family || filter.family == AF_PACKET)
read_dev_mcast(&list);
- if (!filter.family || filter.family == AF_INET)
- read_igmp(&list);
- if (!filter.family || filter.family == AF_INET6)
- read_igmp6(&list);
+ if (!filter.family || filter.family == AF_INET) {
+ if (read_maddr_netlink(AF_INET, &list) < 0)
+ read_igmp(&list);
+ }
+ if (!filter.family || filter.family == AF_INET6) {
+ if (read_maddr_netlink(AF_INET6, &list) < 0)
+ read_igmp6(&list);
+ }
print_mlist(stdout, list);
maddr_clear(list);
return 0;
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index 8905e297..edf3a8ba 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -336,6 +336,32 @@ int rtnl_addrdump_req(struct rtnl_handle *rth, int family,
return send(rth->fd, &req, sizeof(req), 0);
}
+int rtnl_mcaddrdump_req(struct rtnl_handle *rth, int family,
+ req_filter_fn_t filter_fn)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct ifaddrmsg ifm;
+ char buf[128];
+ } req = {
+ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+ .nlh.nlmsg_type = RTM_GETMULTICAST,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .nlh.nlmsg_seq = rth->dump = ++rth->seq,
+ .ifm.ifa_family = family,
+ };
+
+ if (filter_fn) {
+ int err;
+
+ err = filter_fn(&req.nlh, sizeof(req));
+ if (err)
+ return err;
+ }
+
+ return send(rth->fd, &req, sizeof(req), 0);
+}
+
int rtnl_addrlbldump_req(struct rtnl_handle *rth, int family)
{
struct {
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-11 3:07 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-11 3:07 [PATCH iproute2-next] ipmaddr: use RTM_GETMULTICAST to list multicast addresses Yuyang Huang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox