[PATCH iproute2-next] ipmaddr: use RTM_GETMULTICAST to list multicast addresses
From: Yuyang Huang <hidden>
Date: 2026-07-11 03:07:41
Subsystem:
library code, the rest · Maintainers:
Andrew Morton, Linus Torvalds
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 <redacted> --- 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